المكونات
تطبيقات FORGE الأمامية مبنية على shadcn/ui، التي توفر primitives قابلة للوصول وغير منسقة مدعومة بـ Radix UI ومُنسّقة بـ Tailwind CSS. فوق هذه الـ primitives، يُولّد FORGE عدة مكونات عالية المستوى مُصمّمة للوحة التحكم وتطبيق الويب.
معمارية المكونات
┌─────────────────────────────────────────────────┐
│ مكونات التطبيق │
│ (الصفحات، التخطيطات، مكونات خاصة بالميزات) │
├─────────────────────────────────────────────────┤
│ مكونات FORGE المخصصة │
│ (DataTable, RichTextEditor, LanguageSwitcher, │
│ ConfirmDialog, TranslationsInput, إلخ.) │
├─────────────────────────────────────────────────┤
│ shadcn/ui Primitives │
│ (Button, Input, Dialog, Select, Table, Tabs, │
│ Card, Badge, Skeleton, Tooltip, إلخ.) │
├─────────────────────────────────────────────────┤
│ Radix UI + Tailwind CSS │
│ (مكونات headless قابلة للوصول) │
└─────────────────────────────────────────────────┘DataTable
DataTable هو المكون الرئيسي لعرض قوائم الموارد في لوحة التحكم الإدارية. يغلف TanStack Table مع الفرز والتصفية وتبديل رؤية الأعمدة والترقيم وتحديد الصفوف.
الخصائص
| الخاصية | النوع | الوصف |
|---|---|---|
columns | ColumnDef<TData, TValue>[] | تعريفات أعمدة TanStack Table |
data | TData[] | مصفوفة صفوف البيانات |
searchKey | string (اختياري) | مفتاح العمود لتفعيل تصفية البحث النصي |
searchPlaceholder | string (اختياري) | نص placeholder لحقل البحث |
الاستخدام
import { DataTable } from "@/components/ui/data-table";
import { getColumns } from "./columns";
import type { User } from "@/lib/types";
// Define columns in a separate file (columns.tsx)
export function getColumns({
onDelete,
canEdit,
canDelete,
}: {
onDelete: (user: User) => void;
canEdit: boolean;
canDelete: boolean;
}): ColumnDef<User>[] {
return [
{
accessorKey: "name",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="الاسم" />
),
},
{
accessorKey: "email",
header: "البريد الإلكتروني",
},
{
accessorKey: "is_active",
header: "الحالة",
cell: ({ row }) => (
<Badge variant={row.original.is_active ? "default" : "secondary"}>
{row.original.is_active ? "نشط" : "غير نشط"}
</Badge>
),
},
{
id: "actions",
cell: ({ row }) => (
<DataTableRowActions
row={row}
onEdit={canEdit ? `/users/${row.original.id}/edit` : undefined}
onDelete={canDelete ? () => onDelete(row.original) : undefined}
/>
),
},
];
}
// Usage in page component
<DataTable
columns={columns}
data={users}
searchKey="name"
searchPlaceholder="تصفية المستخدمين..."
/>المكونات الفرعية المدمجة
DataTable يتضمن عدة مكونات فرعية تعمل معاً:
- DataTableColumnHeader -- رؤوس أعمدة قابلة للفرز مع عناصر تحكم تصاعدي/تنازلي/إخفاء
- DataTableRowActions -- قائمة منسدلة مع إجراءات تعديل وعرض وحذف
- DataTablePagination -- تنقل الصفحات مع صفوف لكل صفحة قابلة للتعديل (10، 20، 30، 40، 50)
- رؤية الأعمدة -- قائمة منسدلة لإظهار/إخفاء الأعمدة
نصيحة
تعريفات الأعمدة تُحفظ في ملف columns.tsx منفصل باصطلاح. هذا يُبقي مكونات الصفحة مركزة على جلب البيانات والإجراءات بينما منطق عرض الأعمدة يبقى معزولاً وقابلاً للاختبار.
ConfirmDialog
حوار تأكيد يُستخدم قبل الإجراءات التدميرية مثل الحذف. مبني على AlertDialog من Radix UI.
الخصائص
| الخاصية | النوع | الوصف |
|---|---|---|
open | boolean | يتحكم برؤية الحوار |
onOpenChange | (open: boolean) => void | يُستدعى عند تغير الرؤية |
title | string | عنوان الحوار |
description | string | نص وصفي |
confirmText | string | نص زر التأكيد |
variant | "default" | "destructive" | متغير نمط الزر |
isLoading | boolean | يعرض spinner تحميل على زر التأكيد |
onConfirm | () => void | يُستدعى عندما يؤكد المستخدم الإجراء |
الاستخدام
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
const [deleteTarget, setDeleteTarget] = useState<User | null>(null);
const [isDeleting, setIsDeleting] = useState(false);
<ConfirmDialog
open={!!deleteTarget}
onOpenChange={(open) => !open && setDeleteTarget(null)}
title="حذف المستخدم"
description="هل أنت متأكد؟ لا يمكن التراجع عن هذا الإجراء."
confirmText="حذف"
variant="destructive"
isLoading={isDeleting}
onConfirm={handleDelete}
/>RichTextEditor
محرر نص غني مبني على TinyMCE يُستخدم لتحرير صفحات المحتوى في لوحة التحكم الإدارية. يدعم محتوى RTL وإدراج الصور وينتج HTML نظيف.
الخصائص
| الخاصية | النوع | الوصف |
|---|---|---|
value | string | سلسلة محتوى HTML |
onChange | (value: string) => void | يُستدعى بـ HTML المُحدّث عند التغيير |
placeholder | string (اختياري) | نص placeholder |
disabled | boolean | يُعطّل التحرير |
height | number | ارتفاع المحرر بالبكسل (الافتراضي: 300) |
className | string (اختياري) | فئات CSS إضافية |
الاستخدام
import { RichTextEditor } from "@/components/ui/rich-text-editor";
<RichTextEditor
value={content.details}
onChange={(html) => setContent({ ...content, details: html })}
height={400}
placeholder="اكتب محتواك هنا..."
/>المحرر يُعدّل اتجاه نصه تلقائياً بناءً على سياق اللغة الحالي، مما يضمن عرض اللغات RTL مثل العربية بشكل صحيح داخل المحرر.
TranslationsInput
واجهة بتبويبات لتحرير الحقول القابلة للترجمة عبر لغات متعددة. كل تبويب يتوافق مع لغة نشطة ويعرض حقول إدخال للخصائص القابلة للترجمة.
الخصائص
| الخاصية | النوع | الوصف |
|---|---|---|
value | Translations | كائن مُفتّح بكود اللغة |
onChange | (translations: Translations) => void | يُستدعى عند أي تغيير حقل |
fields | ("display_name" | "description")[] | أي الحقول تُعرض |
disabled | boolean | يُعطّل جميع المدخلات |
الاستخدام
import { TranslationsInput } from "@/components/ui/translations-input";
<TranslationsInput
value={role.translations || {}}
onChange={(translations) =>
setRole({ ...role, translations })
}
fields={["display_name", "description"]}
/>هذا يعرض شريط تبويبات بتبويب لكل لغة نشطة (مثل "English"، "العربية")، وتحت كل تبويب، حقول إدخال للخصائص القابلة للترجمة المُحددة.
ContentTranslationsInput
نسخة متخصصة من TranslationsInput لصفحات المحتوى. تتضمن حقولاً لـ title و summary و details (نص غني) و button_text وبيانات SEO الوصفية -- الكل لكل لغة.
الاستخدام
import { ContentTranslationsInput } from "@/components/ui/content-translations-input";
<ContentTranslationsInput
value={content.translations || {}}
onChange={(translations) =>
setContent({ ...content, translations })
}
/>كل تبويب لغة يعرض:
┌─────────────────────────────────────────┐
│ [English] [العربية] │
├─────────────────────────────────────────┤
│ العنوان: [________________________] │
│ الملخص: [________________________] │
│ التفاصيل: ┌────────────────────────┐ │
│ │ محرر نص غني │ │
│ └────────────────────────┘ │
│ نص الزر: [_______________________] │
│ │
│ -- حقول SEO -- │
│ عنوان SEO: [________________________] │
│ وصف SEO: [________________________] │
│ الكلمات المفتاحية: [________________] │
└─────────────────────────────────────────┘PermissionSelector
واجهة checkbox تسلسلية لتعيين الصلاحيات للأدوار. الصلاحيات مُجمّعة حسب main_group و group، تعرض بنية شجرية تسمح بتحديد صلاحيات فردية أو مجموعات كاملة.
الاستخدام
import { PermissionSelector } from "@/components/ui/permission-selector";
<PermissionSelector
permissions={allPermissions}
selected={selectedPermissionIds}
onChange={setSelectedPermissionIds}
/>المكون يعرض الصلاحيات في شجرة:
[x] المستخدمون (تحديد الكل)
[x] عرض المستخدمين
[x] إنشاء المستخدمين
[x] تعديل المستخدمين
[ ] حذف المستخدمين
[x] المحتويات (تحديد الكل)
[x] عرض المحتويات
[x] إنشاء المحتويات
[x] تعديل المحتويات
[x] حذف المحتوياتالنقر على رأس مجموعة يُبدّل جميع الصلاحيات داخل تلك المجموعة.
LanguageSwitcher
قائمة منسدلة تعرض اللغات المتاحة وتسمح للمستخدم بتبديل اللغة النشطة. عند اختيار لغة، يُحدّث cookies (locale و direction)، يُحدّث خصائص dir و lang لعنصر <html>، ويُحدّث الصفحة.
الاستخدام
import { LanguageSwitcher } from "@/components/ui/language-switcher";
// In header component
<LanguageSwitcher />المكون يُخفي نفسه تلقائياً عندما تكون لغة واحدة فقط متاحة. يعرض الاسم الأصلي لكل لغة (مثل "English"، "العربية") ويُميّز الاختيار النشط.
نصيحة
LanguageSwitcher موجود في كلا التطبيقين (admin/components/ui/ و web/components/)، كل منها مُصمّم لتخطيط تطبيقه. كلاهما يستخدم نفس hook useI18n من I18n provider.
حالات التحميل والـ Skeletons
يستخدم FORGE مكون Skeleton من shadcn/ui لعناصر placeholder للتحميل في جميع أنحاء لوحة التحكم الإدارية:
import { Skeleton } from "@/components/ui/skeleton";
function UserListSkeleton() {
return (
<div className="space-y-4">
<Skeleton className="h-10 w-[250px]" />
<div className="space-y-2">
{Array.from({ length: 5 }).map((_, i) => (
<Skeleton key={i} className="h-16 w-full" />
))}
</div>
</div>
);
}استخدم skeletons في صفحات القوائم أثناء تحميل البيانات:
if (isLoading) return <UserListSkeleton />;
return <DataTable columns={columns} data={users} />;إشعارات Toast
يستخدم FORGE Sonner لإشعارات toast. غلاف DirectionAwareToaster يُوضع الإشعارات بناءً على اتجاه النص الحالي:
import { toast } from "sonner";
// Success notification
toast.success("تم إنشاء المستخدم بنجاح");
// Error notification
toast.error("فشل في إنشاء المستخدم");
// Custom with description
toast.success("تم حفظ الإعدادات", {
description: "تغييراتك ستصبح سارية فوراً.",
});DirectionAwareToaster
غلاف حول Toaster من Sonner يُكيّف الموضع لتخطيطات RTL:
import { DirectionAwareToaster } from "@/components/ui/direction-aware-toaster";
// In root layout
<DirectionAwareToaster />في وضع LTR، تظهر الإشعارات في الزاوية السفلية اليمنى. في وضع RTL، تظهر في الزاوية السفلية اليسرى.
Sidebar
sidebar الإدارة هو مكون تنقل متجاوب مع الميزات التالية:
- قابل للطي -- يُبدّل بين العرض الكامل (مع العناوين) ووضع الأيقونات فقط على سطح المكتب
- overlay للجوال -- ينزلق من اليسار على الجوال مع backdrop overlay
- عناصر محمية بالصلاحيات -- عناصر التنقل تُعرض فقط إذا كان للمستخدم الصلاحية المطلوبة
- قوائم فرعية قابلة للطي -- العناصر الرئيسية تتوسع لتكشف الروابط الفرعية
- حالة نشطة -- المسار الحالي مُميّز بصرياً
import { Sidebar } from "@/components/layout/sidebar";
// Used in dashboard layout
<div className="flex h-screen">
<Sidebar />
<div className="flex flex-1 flex-col overflow-hidden">
<Header />
<main className="flex-1 overflow-y-auto p-6">
{children}
</main>
</div>
</div>ThemeToggle
زر يُبدّل بين السمات الفاتحة والداكنة. يستخدم next-themes تحت الغطاء مع اكتشاف تفضيل النظام:
// In admin header
<ThemeToggle />shadcn/ui Primitives
الـ primitives التالية من shadcn/ui مُضمّنة في كلا التطبيقين:
| المكون | الوصف |
|---|---|
Button | متغيرات primary، secondary، outline، ghost، destructive |
Input | إدخال نصي مع دعم label وحالة الخطأ |
Textarea | إدخال نص متعدد الأسطر |
Select | اختيار منسدل |
Checkbox | checkbox مع label |
Switch | مفتاح تبديل |
Dialog | حوار modal |
DropdownMenu | قائمة سياق / منسدلة |
Table | primitives جدول البيانات (Header، Body، Row، Cell) |
Tabs | لوحات محتوى بتبويبات |
Card | بطاقة حاوية مع header ومحتوى و footer |
Badge | شارات حالة وعناوين |
Skeleton | رسوم placeholder للتحميل |
Tooltip | tooltip عند التمرير |
Separator | فاصل مرئي |
Label | عنوان حقل النموذج |
Accordion | أقسام محتوى قابلة للتوسيع |
Avatar | صورة رمزية للمستخدم مع صورة و fallback |
Collapsible | قسم قابل للتوسيع/الطي |
نصيحة
جميع مكونات shadcn/ui تُولّد كـ source code في components/ui/، وليست مُثبّتة كـ package. هذا يعني أنه يمكنك تخصيص أي مكون مباشرة بدون ejecting أو تجاوز الأنماط.
ما التالي
- النماذج والتحقق — كيف تتكامل هذه المكونات مع React Hook Form
- جلب البيانات — تحميل البيانات في DataTable ومكونات أخرى
- التدويل — كيف تتعامل المكونات مع لغات متعددة