🎯 الهدف من الدرس:
في هذا الدرس، سنتعلم كيف نحول عملية إضافة وتعديل الملاحظات من صفحات مستقلة إلى نوافذ منبثقة (Modal) باستخدام Bootstrap، مما يجعل التطبيق أكثر أناقة وسرعة في التفاعل.
🧱 الخطوة 1: تحديث صفحة العرض الرئيسية index.html
افتح ملف templates/index.html وضع الكود التالي المنسق بالكامل:
<!DOCTYPE html>
<html lang="ar">
<head>
<meta charset="UTF-8">
<title>📘 دفتر الملاحظات</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<style>
body {
background: #f8f9fa;
}
.card:hover {
transform: translateY(-3px);
transition: 0.2s;
}
</style>
</head>
<body class="p-4">
<div class="container mt-4">
<h1 class="text-center mb-4">📒 دفتر الملاحظات</h1>
<!-- زر إضافة ملاحظة جديدة -->
<div class="text-center mb-4">
<button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#addNoteModal">➕ إضافة ملاحظة</button>
</div>
<!-- عرض الرسائل -->
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for msg in messages %}
<div class="alert alert-info text-center">{{ msg }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<!-- شبكة الملاحظات -->
<div class="row">
{% for note in notes %}
<div class="col-md-4 mb-3">
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title">{{ note['title'] }}</h5>
<p class="card-text">{{ note['content'] }}</p>
<button class="btn btn-sm btn-warning" data-bs-toggle="modal" data-bs-target="#editNoteModal{{ note['id'] }}">✏️ تعديل</button>
<a href="/delete/{{ note['id'] }}" class="btn btn-sm btn-danger">🗑️ حذف</a>
</div>
</div>
</div>
<!-- نافذة تعديل الملاحظة -->
<div class="modal fade" id="editNoteModal{{ note['id'] }}" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST" action="/edit/{{ note['id'] }}">
<div class="modal-header bg-warning">
<h5 class="modal-title">✏️ تعديل الملاحظة</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="text" name="title" class="form-control mb-2" value="{{ note['title'] }}" required>
<textarea name="content" class="form-control" rows="4" required>{{ note['content'] }}</textarea>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success">💾 حفظ</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
</div>
</form>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<!-- نافذة إضافة ملاحظة جديدة -->
<div class="modal fade" id="addNoteModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST" action="/add">
<div class="modal-header bg-success text-white">
<h5 class="modal-title">➕ إضافة ملاحظة جديدة</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="text" name="title" class="form-control mb-2" placeholder="عنوان الملاحظة" required>
<textarea name="content" class="form-control" rows="4" placeholder="اكتب محتوى الملاحظة هنا..." required></textarea>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">💾 حفظ</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
</div>
</form>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
🧱 الخطوة 2: تحديث app.py
تأكد من أن الكود يحتوي على مسارات الإضافة والتعديل بالشكل التالي:
from flask import Flask, render_template, request, redirect, flash
import sqlite3
app = Flask(__name__)
app.secret_key = "secret"
def get_db_connection():
conn = sqlite3.connect('notes.db')
conn.row_factory = sqlite3.Row
return conn
@app.route('/')
def index():
conn = get_db_connection()
notes = conn.execute("SELECT * FROM notes ORDER BY id DESC").fetchall()
conn.close()
return render_template('index.html', notes=notes)
@app.route('/add', methods=['POST'])
def add_note():
title = request.form['title']
content = request.form['content']
conn = get_db_connection()
conn.execute("INSERT INTO notes (title, content) VALUES (?, ?)", (title, content))
conn.commit()
conn.close()
flash("✅ تم إضافة الملاحظة بنجاح!")
return redirect('/')
@app.route('/edit/<int:id>', methods=['POST'])
def edit_note(id):
title = request.form['title']
content = request.form['content']
conn = get_db_connection()
conn.execute("UPDATE notes SET title=?, content=? WHERE id=?", (title, content, id))
conn.commit()
conn.close()
flash("✏️ تم تعديل الملاحظة بنجاح!")
return redirect('/')
@app.route('/delete/<int:id>')
def delete(id):
conn = get_db_connection()
conn.execute("DELETE FROM notes WHERE id=?", (id,))
conn.commit()
conn.close()
flash("🗑️ تم حذف الملاحظة.")
return redirect('/')
🧱 الخطوة 3: تجربة المشروع
1- شغّل التطبيق:flask run2- عند الدخول إلى الصفحة الرئيسية، اضغط زر ➕ إضافة ملاحظة.ستظهر نافذة منبثقة أنيقة.
3-يمكنك تعديل أي ملاحظة بنفس الطريقة بدون الانتقال لصفحة أخرى.
تظهر تنبيهات جميلة بعد الحفظ أو التعديل أو الحذف.
🎨 النتيجة النهائية:
- واجهة احترافية بالكامل ✅
- تجربة مستخدم سلسة وسريعة ✅
- التصميم متجاوب مع الجوال ✅
💡 تمرين عملي للدرس التاسع:
أضف ميزة صغيرة وهي زر "عرض التفاصيل" داخل كل ملاحظة يظهر نافذة منبثقة تحتوي على العنوان والمحتوى فقط، مع زر "إغلاق".
✅ حل تمرين الدرس التاسع: عرض تفاصيل الملاحظة في نافذة منبثقة
🎯 الفكرة:
لكل ملاحظة، سنضيف زرًّا جديدًا بعنوان “عرض التفاصيل”، وعند الضغط عليه تظهر نافذة (Bootstrap Modal) تحتوي على:
- عنوان الملاحظة
- محتوى الملاحظة
- زر إغلاق
🧱 الخطوة 1: تعديل صفحة index.html
افتح الملف templates/index.html وأضف الكود التالي داخل كل بطاقة ملاحظة (أسفل الأزرار “تعديل” و“حذف”):
<button class="btn btn-sm btn-info text-white" data-bs-toggle="modal" data-bs-target="#viewNoteModal{{ note['id'] }}">👁️ عرض التفاصيل</button>
ثم أسفل نافذة تعديل الملاحظة، أضف هذا الكود لإنشاء نافذة العرض:
<!-- نافذة عرض تفاصيل الملاحظة -->
<div class="modal fade" id="viewNoteModal{{ note['id'] }}" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-info text-white">
<h5 class="modal-title">📘 تفاصيل الملاحظة</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<h5>{{ note['title'] }}</h5>
<hr>
<p>{{ note['content'] }}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إغلاق</button>
</div>
</div>
</div>
</div>
🔹 تأكد أن هذا الكود داخل الحلقة
{% for note in notes %}حتى ينشئ نافذة عرض خاصة بكل ملاحظة.
🧱 الخطوة 2: تحديث الأزرار في واجهة العرض
الآن داخل بطاقة الملاحظة نضع الأزرار بهذا الترتيب الجميل:
<div class="card-body">
<h5 class="card-title">{{ note['title'] }}</h5>
<p class="card-text text-truncate" style="max-height: 60px;">{{ note['content'] }}</p>
<button class="btn btn-sm btn-info text-white" data-bs-toggle="modal" data-bs-target="#viewNoteModal{{ note['id'] }}">👁️ عرض</button>
<button class="btn btn-sm btn-warning" data-bs-toggle="modal" data-bs-target="#editNoteModal{{ note['id'] }}">✏️ تعديل</button>
<a href="/delete/{{ note['id'] }}" class="btn btn-sm btn-danger">🗑️ حذف</a>
</div>
لاحظ أننا جعلنا النص في الملاحظة مختصرًا (باستخدام text-truncate) ليبدو أنيقًا ويشجع المستخدم على عرض التفاصيل.
🧱 الخطوة 3: لا حاجة لتعديل app.py
لأن جميع البيانات موجودة مسبقًا من قاعدة البيانات، ولا نحتاج لمسار جديد — العرض يتم مباشرة داخل الصفحة نفسها.
🎨 النتيجة النهائية:
عند تشغيل التطبيق وفتح صفحة الملاحظات:
- كل ملاحظة تحتوي الآن على زر 👁️ عرض التفاصيل.
- عند الضغط عليه، تظهر نافذة أنيقة تعرض العنوان والمحتوى.
- يمكن إغلاقها بسهولة دون تحديث الصفحة.
💡 تحسين اختياري:
لجعل تجربة المستخدم أجمل، يمكنك إضافة تحريك بسيط (fade-in) في CSS:
<style>
.modal-content {
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
🎯 النتيجة:
بهذا التمرين، أضفت ميزة عرض الملاحظات بتصميم أنيق وسريع التفاعل — مما جعل التطبيق أكثر احترافية وجاذبية للمستخدم 👌✨

