إدارة القوائم
يوفر FORGE نظام قوائم تنقل هرمي يتيح للمسؤولين بناء وإدارة تنقل الموقع بدون تغييرات في الكود. تدعم القوائم التداخل متعدد المستويات وقواعد الظهور والترجمات والعرض الديناميكي في الواجهة الأمامية.
نظرة عامة
يدير نظام القوائم مكونات التنقل الديناميكية مثل رأس الموقع والتذييل. يمكن للمسؤولين إنشاء أشجار قوائم وتعيين ظهور كل عنصر بناءً على حالة المصادقة وإعادة ترتيب العناصر عبر السحب والإفلات وإدارة الترجمات لكل لغة.
مخطط قاعدة البيانات
يخزن جدول menus جميع عناصر التنقل:
| العمود | النوع | الوصف |
|---|---|---|
id | UUID | المفتاح الأساسي |
title | VARCHAR(255) | نص العرض |
url | VARCHAR(500) | هدف الرابط (مسار مطلق أو نسبي) |
icon | VARCHAR(100) | معرّف الأيقونة (اختياري) |
target | VARCHAR(10) | خاصية target للرابط (_self أو _blank) |
visibility | VARCHAR(10) | من يمكنه رؤية هذا العنصر: public، auth، guest |
translations | JSONB | النصوص المترجمة حسب اللغة |
parent_id | UUID (nullable) | مفتاح أجنبي ذاتي المرجعية للتسلسل الهرمي |
is_active | BOOLEAN | ما إذا كان العنصر مرئياً |
sort_order | INTEGER | ترتيب العرض ضمن نفس المستوى |
deleted_at | TIMESTAMP | طابع زمني للحذف الناعم |
created_at | TIMESTAMP | طابع زمني لإنشاء السجل |
updated_at | TIMESTAMP | طابع زمني لآخر تحديث |
التسلسل الهرمي
تستخدم القوائم parent_id ذاتي المرجعية لبناء بنية شجرية. العناصر الجذرية لها parent_id = NULL، والعناصر الفرعية تشير إلى العنصر الأب.
تنقل الرأس
├── الرئيسية (parent_id: NULL, sort_order: 1)
├── المنتجات (parent_id: NULL, sort_order: 2)
│ ├── البرمجيات (parent_id: المنتجات, sort_order: 1)
│ └── الأجهزة (parent_id: المنتجات, sort_order: 2)
├── من نحن (parent_id: NULL, sort_order: 3)
└── اتصل بنا (parent_id: NULL, sort_order: 4)قواعد الظهور
كل عنصر قائمة له حقل visibility يتحكم في من يمكنه رؤيته:
| القيمة | الوصف |
|---|---|
public | مرئي لجميع الزوار |
auth | مرئي فقط للمستخدمين المُصادق عليهم |
guest | مرئي فقط للزوار غير المُصادق عليهم |
نصيحة
استخدم ظهور guest للعناصر مثل "تسجيل الدخول" أو "التسجيل" التي يجب أن تختفي بعد تسجيل دخول المستخدم. استخدم auth للعناصر مثل "لوحة التحكم" أو "الملف الشخصي".
بنية كائن الترجمات
{
"en": { "title": "About Us" },
"ar": { "title": "من نحن" }
}نقاط نهاية API
| الطريقة | المسار | الوصف |
|---|---|---|
GET | /api/menus | إرجاع شجرة القوائم كاملة، مُصفاة حسب الظهور |
تُرجع API القوائم كبنية شجرية متداخلة. يتم تصفية الاستجابة تلقائياً بناءً على حالة مصادقة المستخدم الطالب.
مثال على الاستجابة
[
{
"id": "uuid-1",
"title": "الرئيسية",
"url": "/",
"icon": null,
"target": "_self",
"sort_order": 1,
"children": []
},
{
"id": "uuid-2",
"title": "المنتجات",
"url": "/products",
"icon": "package",
"target": "_self",
"sort_order": 2,
"children": [
{
"id": "uuid-3",
"title": "البرمجيات",
"url": "/products/software",
"icon": null,
"target": "_self",
"sort_order": 1,
"children": []
}
]
}
]واجهة الإدارة
توفر لوحة الإدارة محرر قوائم بعرض شجري:
- العرض الشجري -- تمثيل مرئي للتسلسل الهرمي للقائمة
- السحب والإفلات -- إعادة ترتيب العناصر وتغيير العلاقات الأب-الابن بالسحب
- إضافة / تحرير / حذف -- نماذج مضمنة لإنشاء وتعديل عناصر القائمة
- التحكم بالظهور -- تعيين من يمكنه رؤية كل عنصر (عام، مُصادق عليه، أو ضيف)
- تبويبات الترجمة -- ترجمة نصوص القائمة لكل لغة نشطة
تحذير
حذف عنصر قائمة أب سيُنفّذ حذفاً ناعماً متسلسلاً لجميع العناصر الفرعية. استعادة الأب لا تستعيد العناصر الفرعية تلقائياً.
مكونات الواجهة الأمامية
يُولّد FORGE مكونات تنقل ديناميكية تستهلك API القوائم:
SiteHeader
يعرض شريط التنقل الرئيسي مع دعم القوائم المنسدلة المتداخلة:
// components/site-header.tsx
export function SiteHeader() {
const { menus } = useMenus();
return (
<header>
<nav>
{menus.map((item) => (
<NavItem key={item.id} item={item} />
))}
</nav>
</header>
);
}SiteFooter
يعرض روابط تنقل التذييل بتخطيط مسطح أو مُجمّع:
// components/site-footer.tsx
export function SiteFooter() {
const { menus } = useMenus();
return (
<footer>
{menus.map((item) => (
<FooterLink key={item.id} item={item} />
))}
</footer>
);
}DynamicNav
مكون تنقل قابل لإعادة الاستخدام يتكيف مع بيانات القائمة:
// components/dynamic-nav.tsx
export function DynamicNav({ location }: { location: "header" | "footer" }) {
const { menus, isLoading } = useMenus(location);
if (isLoading) return <NavSkeleton />;
return (
<nav aria-label={`${location} navigation`}>
{menus.map((item) => (
<MenuItem key={item.id} item={item} />
))}
</nav>
);
}نصيحة
يتم جلب بيانات القائمة مرة واحدة عند تحميل الصفحة الأولى وتُخزّن مؤقتاً. تعرض الواجهة الأمامية تلقائياً الترجمة الصحيحة بناءً على اللغة النشطة.