- ابدأ الآن
- البيئة المحلية
- البنية المعمارية
- الهيكل
- الأنماط
- الحزمة التقنية
- قاعدة البيانات
- تعدّد المستأجرين
- الفهرس (Catalog)
- العمل دون اتصال
- المصادقة
- التهيئة (Onboarding)
- تدفق التكامل (Integration Flow)
- التهيئة (Provision)
- القبول
- الحضور
- الامتثال التنظيمي
- الملف الشخصي
- الاجتماعات
- إدارة الرسوم
- الفاتورة
- التدويل
- الترجمة
- دليل الترجمة
- أمان قاعدة البيانات
- المساهمة
- الإلهام
- المالية
- النقل
يقود نظام التهيئة المدارس الجديدة عبر التسجيل والتكوين والإعداد التجاري. نمط مرآة، ~166 ملف، 15 خطوة فعّالة في 3 مجموعات، يقودها WizardLayout. وقت الإنجاز المعتاد: 10–30 دقيقة.
الخطوات
المجموعة 1 — المعلومات الأساسية
| الخطوة | المسار | إلزامية | قاعدة البيانات |
|---|---|---|---|
| عن المدرسة | /about-school | لا | ترحيب ثابت |
| العنوان | /title | نعم | School.name |
| الوصف | /description | نعم | schoolType, schoolLevel, planType |
| الموقع | /location | نعم | address, city, state, country, latitude, longitude |
| ما يميّزك | /stand-out | لا | ثابت |
المجموعة 2 — إعداد المدرسة
| الخطوة | المسار | إلزامية | قاعدة البيانات |
|---|---|---|---|
| السعة | /capacity | نعم | maxStudents, maxTeachers, maxClasses |
| العلامة | /branding | لا | primaryColor, borderRadius, shadow |
| الاستيراد | /import | لا | إدخال جماعي (CSV / Excel) |
| إنهاء الإعداد | /finish-setup | لا | مراجعة ثابتة |
خطوة schedule معرَّفة في OnboardingStep بمجلد مكوّن خاص بها لكنها ليست ضمن التنقّل الفعّال. تعيش عند order: 5.5 في config.ts تمهيداً لتفعيلها لاحقاً.
المجموعة 3 — التجاري والقانوني
| الخطوة | المسار | إلزامية | قاعدة البيانات |
|---|---|---|---|
| الانضمام | /join | نعم | User |
| الإظهار | /visibility | لا | School.isPublished |
| السعر | /price | نعم | tuitionFee, registrationFee, applicationFee, currency |
| الخصم | /discount | لا | نموذج Discount |
| القانوني | /legal | نعم | LegalConsent |
| النطاق الفرعي | /subdomain | لا | School.domain |
بعد إنهاء كل الخطوات يصل المستخدم إلى /congratulations لإعادة توجيه ذكية (لوحة النطاق الفرعي للمدرسة أو النطاق الرئيسي بحسب حالة الجلسة).
ترتيب الخطوات الفعّال هو ONBOARDING_CONFIG في src/components/form/footer.tsx. المرجع الموثوق للبيانات الوصفية (الوسوم، التحقق، المجموعة، الترتيب) هو ONBOARDING_STEPS في src/components/onboarding/config.ts.
منتقي الموقع
مكوّن واحد هو src/components/atom/mapbox-location-picker.tsx يحدد كل عنوان في التطبيق: هذه الخطوة، وإعدادات المدرسة، ومعالجات إضافة الطلاب والمعلمين، والتقديم العام، ومحطات النقل. يحل حقل بحث واحد مع الموقع الحالي والخريطة محل حقول الدولة والمدينة والحي المنفصلة.
- البحث يستخدم Mapbox Geocoding v6، محصورًا في دولة المدرسة ومرتبًا حول موقعها. كتابة "الرياض" من مدرسة في الخرطوم تجد حي الرياض في الخرطوم لا مدينة الرياض في السعودية. إذا لم توجد نتيجة داخل دولة المدرسة يعيد البحث على مستوى العالم. تصل النتائج إلى مستوى المربعات مثل "كافوري مربع 9". للاختيار استخدم الأسهم وEnter، وللإغلاق Esc.
- العرض الأول: الدبوس المحفوظ، ثم المدرسة، ثم دولة المدرسة، ثم الخرطوم. لا تُفتح الخريطة أبدًا عند الإحداثيات 0,0. داخل المدرسة تظهر المدرسة كعلامة مرجعية.
- الهبوط: اختيار نتيجة أو استخدام الموقع الحالي أو الضغط على الخريطة يحرّك الخريطة فورًا. يصل العنوان عندما يرد البحث العكسي. اسحب الدبوس للضبط. سطر العنوان تحت الخريطة قابل للتعديل لإضافة رقم المنزل أو أقرب معلم.
- العربية: تتبع أسماء الخريطة لغة الصفحة. يُفعَّل ملحق النص من اليمين لليسار قبل إنشاء كل خريطة.
- التخزين:
address/city/state/countryكما كانت، معlatitude/longitudeفيSchoolوStudentوTeacherوApplication.
مصافحة المصادقة
تدفّق من أربع مراحل:
- البدء — مستخدم غير مصادَق يزور
/get-startedأو/onboarding. إجراءstart-onboardingيحفظ النية في cookie ويعيد التوجيه إلى/login. - المصادقة — تسجيل دخول قياسي. عنوان callback يعيد التوجيه إلى
/onboarding. - التزويد —
initializeSchoolSetup()يستدعيensureUserSchool()(src/lib/school-access.ts) —db.$transactionذرّي ينشئ صفSchool، وSchoolMember(يربط المستخدم كـ ADMIN)، ويزرعYearLevels افتراضية. عملية idempotent. - التحديث + إعادة التوجيه —
_sessionRefreshRequired: trueيُطلق إعادة جلب/api/auth/sessionمن العميل؛_redirectيرسل إلى الخطوة التالية.
الملكية مفروضة عبر requireSchoolOwnership(id) على تحميل كل خطوة. الوصول العابر للمستأجرين يرفع TenantError.
التكامل مع تعدّد المستأجرين
- توجيه النطاق الفرعي —
/onboardingوأبناؤه يعيشون على النطاق الرئيسي (ed.databayt.org)، لا على نطاق فرعي. تخصيص النطاق الفرعي يحدث في خطوة Subdomain. - تحديد نطاق قاعدة البيانات — كل قراءة تستخدم
requireSchoolOwnership(id)أولاً، ثم استعلامات محدَّدة النطاق بـschoolId. schoolIdفي الجلسة — JWT يحملschoolIdبعد الخطوة 1؛ الخطوات اللاحقة تثق به بعد إعادة التحقق.
التحقق
كل خطوة لها schema من Zod مرافقة لمكوّنها. تستخدم الـ schemas نمط مصنع i18n — نفس الـ schema تُنتج رسائل خطأ بالعربية أو الإنجليزية بحسب شريحة القاموس.
import { createTitleSchema } from "@/components/onboarding/title/validation"
const schema = createTitleSchema(dictionary)
const result = schema.safeParse(input)validation.ts الجذري يصدّر validateStep(step, data, dictionary) وgetRequiredFieldsForStep(step) لتحقّق مشترك واعٍ بالخطوات.
التزويد التلقائي
عندما يعمل completeOnboarding() عند خطوة legal، تُعلَّم المدرسة كنشطة وتنشئ سلسلة تزويد من 5 خطوات بنية تحتية أكاديمية. كل خطوة ملفوفة في try/catch لئلا يحجب فشل واحد البقية.
المصدر: src/components/onboarding/legal/actions.ts → src/components/catalog/setup.ts.
| الخطوة | تنشئ |
|---|---|
setupDefaultsForSchool() | YearLevels (8 / 6 / 14 بحسب مستوى المدرسة)، 6 Departments، 9 ScoreRanges |
setupCatalogForSchool() | جسور Subject / chapter / lesson من الكتالوج العام |
setupTermsForSchool() | فصول افتراضية للسنة الأكاديمية |
setupRolesForSchool() | قوالب صلاحيات للأدوار الـ 8 |
setupNotificationsForSchool() | تفضيلات إشعار افتراضية لدور ADMIN |
setupCatalogForSchool يحلّ المنهج عبر inferCurriculum():
| الأولوية | الاستراتيجية | مثال |
|---|---|---|
| 1 | نوع مدرسة international → US | أي بلد + international |
| 2 | تطابق دقيق للبلد | US → US, SD → SD |
| 3 | بلد غير مُربط → US | JP, KE, … |
مسارات API
| المسار | الطريقة | الغرض |
|---|---|---|
/api/onboarding/create-school | POST | إنشاء مدرسة ذرّي idempotent؛ يعالج سباق انتهاك unique لـ P2002 |
/api/onboarding/validate-access | POST | فحص وصول مسبق؛ تزويد تلقائي؛ يعيد redirect عند عدم التطابق |
/api/onboarding/extract | POST | استخراج وثائق بالذكاء الاصطناعي (10 ميجابايت كحد أقصى؛ JPEG / PNG / WebP / PDF) |
Server actions
في src/components/onboarding/actions.ts:
| الإجراء | الغرض |
|---|---|
initializeSchoolSetup() | ينشئ أو يجلب مدرسة للمستخدم الحالي؛ يعيد _redirect |
getListing(id) | جلب المدرسة مع التحقق من الملكية |
updateListing(id, data) | تحديث حقول لخطوة |
getSchoolSetupStatus(schoolId) | نسبة الإنجاز (6 فحوص) |
reserveSubdomainForSchool(id, sub) | حجز نطاق فرعي (يفوّض إلى src/lib/subdomain-actions.ts) |
getCurrentUserSchool() | يعيد schoolId للمستخدم الحالي |
getUserSchools() | يسرد المدارس المملوكة أو المرتبطة بالمستخدم الحالي |
proceedToTitle(schoolId) | يتحقق من الملكية ويعيد التوجيه إلى خطوة about-school |
الملفات المفتاحية
| المسار | الغرض |
|---|---|
src/app/[lang]/onboarding/[id]/[step]/page.tsx | صفحة كل خطوة (15 مساراً) |
src/components/onboarding/actions.ts | Server actions |
src/components/onboarding/config.ts | تكوين الخطوات (مرجع موثوق) |
src/components/onboarding/types.ts | OnboardingStep, OnboardingSchoolData |
src/components/onboarding/validation.ts | Zod schemas جذرية |
src/components/onboarding/use-listing.tsx | سياق ListingProvider |
src/components/onboarding/use-onboarding.ts | hook التنقّل + التحقق |
src/components/onboarding/auth-helpers.ts | getAuthContext, requireSchoolOwnership |
src/components/form/footer.tsx | FormFooter + ONBOARDING_CONFIG |
src/lib/school-access.ts | معاملة ensureUserSchool() الذرّية |
src/components/catalog/setup.ts | سلسلة التزويد |
استكشاف الأخطاء
| العَرَض | السبب | الحل |
|---|---|---|
| البناء يعلَق عند "Environments: .env" | أخطاء TypeScript في مكوّنات onboarding | شغّل pnpm tsc --noEmit |
الجلسة تفتقد schoolId | لم تُحدَّث الجلسة بعد المعاملة الذرّية | اجلب /api/auth/session بعد _sessionRefreshRequired: true |
| رفض وصول عابر للمستأجرين | مستخدم يصل إلى مدرسة لا يملكها | requireSchoolOwnership(id) يرمي TenantError |
| النطاق الفرعي محجوز سلفاً | مدرسة أخرى حجزته | الإجراء يعيد errorCode: "DOMAIN_TAKEN" |
| بيانات النموذج لا تثبت | غياب revalidatePath() بعد التعديل | كل إجراءات التحديث تستدعي revalidatePath("/onboarding") |
| خاصية قاموس غير موجودة | مفتاح ترجمة مفقود | استخدم optional chaining: dictionary?.onboarding?.title?.label |
انظر أيضاً
- تعدد المستأجرين — مصافحة المصادقة، سياق المستأجر
- الأنماط — نمط المرآة، أعراف الملفات
- المصادقة — تدفّق تسجيل الدخول