Skip to content

إدارة القوائم

يوفر FORGE نظام قوائم تنقل هرمي يتيح للمسؤولين بناء وإدارة تنقل الموقع بدون تغييرات في الكود. تدعم القوائم التداخل متعدد المستويات وقواعد الظهور والترجمات والعرض الديناميكي في الواجهة الأمامية.

نظرة عامة

يدير نظام القوائم مكونات التنقل الديناميكية مثل رأس الموقع والتذييل. يمكن للمسؤولين إنشاء أشجار قوائم وتعيين ظهور كل عنصر بناءً على حالة المصادقة وإعادة ترتيب العناصر عبر السحب والإفلات وإدارة الترجمات لكل لغة.

مخطط قاعدة البيانات

يخزن جدول menus جميع عناصر التنقل:

العمودالنوعالوصف
idUUIDالمفتاح الأساسي
titleVARCHAR(255)نص العرض
urlVARCHAR(500)هدف الرابط (مسار مطلق أو نسبي)
iconVARCHAR(100)معرّف الأيقونة (اختياري)
targetVARCHAR(10)خاصية target للرابط (_self أو _blank)
visibilityVARCHAR(10)من يمكنه رؤية هذا العنصر: public، auth، guest
translationsJSONBالنصوص المترجمة حسب اللغة
parent_idUUID (nullable)مفتاح أجنبي ذاتي المرجعية للتسلسل الهرمي
is_activeBOOLEANما إذا كان العنصر مرئياً
sort_orderINTEGERترتيب العرض ضمن نفس المستوى
deleted_atTIMESTAMPطابع زمني للحذف الناعم
created_atTIMESTAMPطابع زمني لإنشاء السجل
updated_atTIMESTAMPطابع زمني لآخر تحديث

التسلسل الهرمي

تستخدم القوائم 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 للعناصر مثل "لوحة التحكم" أو "الملف الشخصي".

بنية كائن الترجمات

json
{
  "en": { "title": "About Us" },
  "ar": { "title": "من نحن" }
}

نقاط نهاية API

الطريقةالمسارالوصف
GET/api/menusإرجاع شجرة القوائم كاملة، مُصفاة حسب الظهور

تُرجع API القوائم كبنية شجرية متداخلة. يتم تصفية الاستجابة تلقائياً بناءً على حالة مصادقة المستخدم الطالب.

مثال على الاستجابة

json
[
  {
    "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 القوائم:

يعرض شريط التنقل الرئيسي مع دعم القوائم المنسدلة المتداخلة:

tsx
// components/site-header.tsx
export function SiteHeader() {
  const { menus } = useMenus();

  return (
    <header>
      <nav>
        {menus.map((item) => (
          <NavItem key={item.id} item={item} />
        ))}
      </nav>
    </header>
  );
}

يعرض روابط تنقل التذييل بتخطيط مسطح أو مُجمّع:

tsx
// components/site-footer.tsx
export function SiteFooter() {
  const { menus } = useMenus();

  return (
    <footer>
      {menus.map((item) => (
        <FooterLink key={item.id} item={item} />
      ))}
    </footer>
  );
}

DynamicNav

مكون تنقل قابل لإعادة الاستخدام يتكيف مع بيانات القائمة:

tsx
// 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>
  );
}

نصيحة

يتم جلب بيانات القائمة مرة واحدة عند تحميل الصفحة الأولى وتُخزّن مؤقتاً. تعرض الواجهة الأمامية تلقائياً الترجمة الصحيحة بناءً على اللغة النشطة.

Released under the MIT License.