- مقدمة
- العرض
- MVP
- خارطة الطريق
- متطلبات المنتج
- ابدأ الآن
- البيئة المحلية
- البنية المعمارية
- الهيكل
- الأنماط
- الحزمة التقنية
- قاعدة البيانات
- تعدّد المستأجرين
- الفهرس (Catalog)
- العمل دون اتصال
- المصادقة
- التهيئة (Onboarding)
- تدفق التكامل (Integration Flow)
- التهيئة (Provision)
- القبول
- الحضور
- الامتثال التنظيمي
- الملف الشخصي
- الاجتماعات
- إدارة الرسوم
- الفاتورة
- التدويل
- الترجمة
- دليل الترجمة
- أمان قاعدة البيانات
- المساهمة
- هَوْغوارتس
- عرض حي
- الإلهام
الفهرس هو مخزن المنهج المشترك للمنصّة. توجد نسخة واحدة من كل مادة وفصل ودرس داخل
جداول عامة بلا schoolId؛ وكل مدرسة تشير إلى نفس المحتوى عبر صفوف جسر (bridge)
صغيرة بدل استنساخه. والمحتوى الذي يساهم به المعلّمون والمدارس (فيديوهات، مواد،
أسئلة، كتب) يمرّ بمراجعة فريق SaaS ثم يُثري الفهرس للجميع.
هذه الصفحة هي المرجع الموحّد للفهرس. المرجع الكامل لشبكة التوصيل (CDN) والأصول يوجد منفصلاً في CDN & Assets.
نظرة عامة
يقوم النظام كلّه على نموذج ذهني واحد: هناك جداول فهرس عامة (مشتركة، بلا
مستأجر) وصفوف جسر مُنطَقة بـschoolId تربط المستأجر بذلك المحتوى المشترك. لا
تستنسخ المدرسة الفهرس قطّ — بل تشير إليه.
prisma/models/— catalog data modelcatalog.prisma— Subject — content rootchapter.prisma— Chapterlesson.prisma— Lessoncurriculum.prisma— Curriculumvideo.prisma— Video, VideoPurchase, Attachmentqbank.prisma— Question, ExamQuestionexam.prisma— Exam, ExamTemplatematerial.prisma— Materialassignment.prisma— Assignmentbook.prisma— Booktextbook.prisma— Textbookdocument.prisma— Documentproposal.prisma— Proposalbridge.prisma— SubjectSelection, ContentOverride, InstructorPreference, BookSelectionenrollment.prisma— Enrollment, LessonProgress, SubjectCertificateacademic.prisma— AcademicLevel, AcademicGrade, AcademicStreamprisma/seeds/catalog/— load content into the DBindex.ts— orchestrator: seedCatalog (US + full SD) + seedFullCatalogengine.ts— syncCurriculumTree (deep) + seedSubjectsOnly (shallow) — shared seederregistry.ts— all 12 Curriculum records + curriculumId backfillus.ts— US — the US-curriculum source backbonesd.ts— Sudan — subjects + chapters + lessons, from curriculum/sd/gb.ts— England (GB) — deep, from curriculum/ukcbse.ts— Indian CBSE — deep, from curriculum/incaie-igcse.ts— Cambridge IGCSE — deep, from curriculum/caie-igcseib.ts— IB Diploma — deep, from curriculum/ibsa.ts · eg.ts · ae.ts · qa.ts · kw.ts · jo.ts— subjects-only nationals (split from world.ts; graduate to deep trees)concepts.ts— concept images to S3banners.ts— concept banners to S3 (owned, shared)lesson-covers.ts— per-lesson/chapter covers to S3 (US, legacy)books.ts— global catalog bookscontent.ts— materials, exams, questionsvideos.ts— lesson videosexam-templates.ts— exam blueprints + templatesdemo.ts— demo-school structure + bridgessrc/components/catalog/— shared core — every surface imports from hereconcepts-data.ts— 23 concepts, colors, nearestConcept, exact-match maps + pools — single sourcesetup.ts— provisions a school onto the catalogprovision.ts— schedule/sections/timetable/library provisioning + the repair doctorimage.ts— Sharp pipeline + S3 uploadimage-url.ts— resolve image URLs (CloudFront)src/components/saas-dashboard/catalog/— platform authoring (DEVELOPER)src/components/school-dashboard/listings/subjects/catalog/— school adoption + contributionsrc/components/lumos/data/catalog/— LMS read queriescurriculum/— on-disk content sourcesd/— Sudan — read by sduk/— British → GB (read by gb)in/— Indian CBSE (read by cbse)caie-igcse/— Cambridge IGCSE (read by caie-igcse)ib/— IB Diploma (read by ib)us/— Aldar American — unread (US uses the master inventory)fr/— French — image-only_build_tools/— engine.py — generates the treespublic/subjects/concepts/— 23 source concept imagesscripts/us-curriculum/us-inventory.json— US source datasudan-data/— Sudan source data + TOC + PDFsupload-textbooks-all.ts— upload textbook PDFs to S3 (all curriculum trees)gen-curriculum-structure.ts— spec JSON → structure.json tree- الفهرس العام (جداول
catalog_*، بلاschoolId) — هرم المنهج (Subject→Chapter→Lesson) إضافةً إلى المحتوى المرتبط به (Exam،Question،Material،Assignment،Video،Book،Quiz،Document،Image،Textbook) وسجلّاتCurriculum. - صفوف الجسر (جداول
school_*، تحملschoolId) —SubjectSelection،ContentOverride،InstructorPreference،BookSelection. تربط المستأجر بالمحتوى المشترك؛ وكل ما يخصّ المدرسة يقرأ من خلالها. - لماذا — عند 1,000 مدرسة يخزّن النموذج المشترك ~240 مادة بدل 220k، وكل مساهمة تُثري كل المدارس.
| الأسلوب | بيانات 1,000 مدرسة × ~240 مادة | المشاركة |
|---|---|---|
| نسخة لكل مدرسة | 220k مادة، 800k فصل، 4M درس | لا شيء |
| فهرس مشترك | ~240 مادة، ~800 فصل، ~4k درس + سجلّات الجسر | كل مساهمة تُثري كل المدارس |
الحالة باختصار
| القدرة | الحالة |
|---|---|
| نموذج الفهرس العام + الجسر | موصول — في الإنتاج |
| اتّساع المناهج | 12 منهجاً عرضاً، 6 بعمق (US، SD، GB، CAIE-IGCSE، IB-DP، CBSE) |
| البنية الأكاديمية لكل منهج | موصولة — academic-config.ts (ثغرة "السوداني-للجميع" القديمة أُصلِحت) |
التهيئة (setupCatalogForSchool) | موصولة — استنتاج + تراجع 4 خطوات + إصلاح ذاتي كسول |
| الاقتراح → الموافقة (اختياري) | موصول هذه الجلسة — اقتراح، طابور المُشغِّل، نشر، إشعار، مُنتقي مُثبَّت |
| الموافقة تنشر المحتوى | مُصلَح — الموافقة تضبط الآن status: PUBLISHED (كانت ثغرة DRAFT غير مرئية) |
| الفيديوهات المدفوعة | موصولة — VideoPurchase، CloudFront مُوقَّع، حجب الرابط null |
| الاختبارات/الأسئلة المدفوعة | مؤجّل — الحقول موجودة، تدفّق الشراء غير مبني |
| مصغّرات المواد | مكتملة — كل المناهج الـ12 عبر المفاهيم المشتركة |
| البانرات / أغلفة الكتب | جزئي / مفقود (راجع CDN & Assets) |
| تكرارية بذرة المحتوى (idempotency) | مُصلَحة هذه الجلسة — إعادة التشغيل تتخطّى الصفوف الموجودة |
المعمارية
نماذج الفهرس تستخدم أسماء مجرّدة مربوطة بجداول catalog_*؛ ونماذج الجسر مربوطة
بجداول school_*. مُوصِّل عميل Prisma هو صيغة camelCase من اسم النموذج
(db.subject، db.subjectSelection).
نماذج الفهرس العام
| النموذج | الجدول (@@map) | المُوصِّل | يحمل |
|---|---|---|---|
Subject | catalog_subjects | db.subject | جذر الهرم؛ slug (فريد)، curriculum، concept، أعداد مُزال تطبيعها |
Chapter | catalog_chapters | db.chapter | مفتاح أجنبي subjectId، sequenceOrder |
Lesson | catalog_lessons | db.lesson | مفتاح أجنبي chapterId، sequenceOrder، durationMinutes |
Video | lesson_videos | db.video | videoUrl، provider، visibility، price، الموافقة |
Exam | catalog_exams | db.exam | اختبار الفهرس (≠ SchoolExam المدرسي) |
ExamQuestion | catalog_exam_questions | db.examQuestion | جدول وصل Exam ↔ Question مع order/points |
Question | catalog_questions | db.question | بنك أسئلة الفهرس (≠ QuestionBank المدرسي) |
Material | catalog_materials | db.material | كتب/مناهج/أوراق عمل على أي مستوى |
Assignment | catalog_assignments | db.assignment | قوالب واجبات/مشاريع قابلة لإعادة الاستخدام |
Book | catalog_books | db.book | كتب المكتبة (≠ SchoolBook المدرسي) |
Quiz | catalog_quizzes | db.quiz | مُعرَّف؛ لا يُبذَر |
Document | catalog_documents | db.document | سجلّ ملفات موحّد (PDF/PPTX/DOCX/صوت) |
Image | catalog_images | db.image | نسخ صور المفاهيم (راجع المفاهيم) |
Textbook | catalog_textbooks | db.textbook | بيانات الكتاب الوصفية (مفتاح S3 لملف PDF على Subject.pdf) |
Curriculum | catalog_curricula | db.curriculum | سجلّات المناهج الـ12؛ هدف مفتاح curriculumId |
نماذج الجسر (تحمل schoolId)
| النموذج | الجدول (@@map) | المُوصِّل | الغرض |
|---|---|---|---|
SubjectSelection | school_subject_selections | db.subjectSelection | صفّ لكل مادة+صفّ مُتبنّى؛ isActive يُفعّل/يُعطّل المادة |
ContentOverride | school_content_overrides | db.contentOverride | تجاوز فصل/درس/فيديو لكل مدرسة؛ isHidden يُخفي المحتوى |
InstructorPreference | school_instructor_preferences | db.instructorPreference | ترتيب معلّم/فيديو لكل مدرسة |
BookSelection | school_book_selections | db.bookSelection | يجسر Book من الفهرس إلى مكتبة مدرسة |
مرايا المدرسة (مفتاح أجنبي للفهرس → catalogXxxId)
نموذج الفهرس ونظيره المُنطَق للمدرسة هما نموذجان مختلفان. عند قراءة Exam راجع
الجدول لتعرف أيّهما المقصود. مرآة المدرسة ترتبط بالفهرس عبر مفاتيح أجنبية اختيارية
catalogSubjectId / catalogChapterId / catalogLessonId.
| الفهرس (عام) | مُنطَق للمدرسة (لكل مستأجر) |
|---|---|
Exam → catalog_exams | SchoolExam → exams |
Book → catalog_books | SchoolBook → books |
Assignment → catalog_assignments | SchoolAssignment → assignments |
Question → catalog_questions | QuestionBank → question_bank |
ExamTemplate → catalog_exam_templates | SchoolExamTemplate → exam_templates |
// QuestionBank, SchoolExam — link to catalog hierarchy
catalogSubjectId String?
catalogChapterId String?
catalogLessonId String?
// SchoolAssignment — link to catalog lesson
catalogLessonId String?التعدادات (Enums)
ContentStatus DRAFT, REVIEW, PUBLISHED, ARCHIVED, DEPRECATED
ApprovalStatus PENDING, APPROVED, REJECTED
ContentVisibility PRIVATE, SCHOOL, PUBLIC, PAID
SchoolLevel ELEMENTARY, MIDDLE, HIGH
StreamType GENERAL, SCIENCE, ARTS, COMMERCE, TECHNICAL, RELIGIOUS
MaterialType TEXTBOOK, SYLLABUS, REFERENCE, STUDY_GUIDE, PROJECT, WORKSHEET,
PRESENTATION, LESSON_NOTES, VIDEO_GUIDE, LAB_MANUAL, OTHER
QuestionType MULTIPLE_CHOICE, TRUE_FALSE, SHORT_ANSWER, ESSAY, FILL_BLANK,
MATCHING, ORDERING, MULTI_SELECT
QuestionSource MANUAL, AI, IMPORTED
DifficultyLevel EASY, MEDIUM, HARD
BloomLevel REMEMBER, UNDERSTAND, APPLY, ANALYZE, EVALUATE, CREATE
ProposalType SUBJECT, CHAPTER, LESSON, VIDEO
ProposalStatus DRAFT, SUBMITTED, IN_REVIEW, APPROVED, REJECTED, PUBLISHED
EnrollmentStatus PENDING, ACTIVE, COMPLETED, CANCELLED, EXPIRED
ContentVisibility هو البوّابة التي يحملها كل عنصر مُساهَم به: PRIVATE (مدرسة
المُنشئ فقط)، SCHOOL (المدارس التي تستخدم تلك المادة، مجاناً)، PUBLIC (أي
شخص، مجاناً)، PAID (أي شخص، محجوب بالشراء). راجع
الرؤية والمحتوى المدفوع.
تفصيل نماذج المحتوى
Material (الجدول catalog_materials) — كتب ومناهج وأوراق عمل على أي مستوى:
| الحقل | النوع | ملاحظات |
|---|---|---|
catalogSubjectId / catalogChapterId / catalogLessonId | String? | أحدها مضبوط (مفتاح أجنبي إلى هرم الفهرس) |
title / lang | String | lang افتراضي "ar" |
type | MaterialType | TEXTBOOK، SYLLABUS، WORKSHEET، … |
fileUrl / fileKey / fileSize / mimeType / pageCount | متنوّع | مُخزَّن على S3 |
externalUrl | String? | للموارد غير المرفوعة |
contributedBy / contributedSchoolId | String? | النسب |
approvalStatus / visibility | تعدادات | المراجعة + الحجب |
Assignment (الجدول catalog_assignments) — نفس بنية Material بالإضافة إلى
instructions/rubric (String?)، وtotalPoints (Decimal?)، وestimatedTime
(Int?، بالدقائق)، وassignmentType (homework/project/lab/essay/presentation).
ExamQuestion (الجدول catalog_exam_questions) — جدول وصل يربط Exam ↔
Question مع order وتجاوز points لكل اختبار. الفهارس:
@@unique([catalogExamId, order])، @@unique([catalogExamId, catalogQuestionId]).
يحلّ محلّ حقل JSON القديم Exam.questions.
Video، Attachment — المخطّطات الكاملة في prisma/models/video.prisma
(lesson_videos، lesson_attachments). أهم حقول Video: videoUrl،
provider (youtube/vimeo/self-hosted)، visibility، price،
approvalStatus، إضافةً إلى التفاعل (viewCount، likeCount، averageRating،
isFeatured). الفيديوهات المدفوعة تُحجَب عبر VideoPurchase.
التهيئة (Provisioning)
لا تستنسخ المدرسة الفهرس قطّ. عند انتهاء الإعداد، يستدعي completeOnboarding
(src/components/onboarding/legal/actions.ts) الدالة setupCatalogForSchool
(src/components/catalog/setup.ts): فهي تستنتج المنهج، وتجد المواد
المطابقة عبر تراجع تدريجي، وتبني المراحل/الصفوف/المسارات الأكاديمية من إعدادات
المنهج، وتكتب صفّ جسر SubjectSelection واحداً لكل مادة وصفّ مُتبنّى.
import { setupCatalogForSchool } from "@/components/catalog/setup"
await setupCatalogForSchool(schoolId, { country: "US", curriculum: "US" })
await setupCatalogForSchool(schoolId, { country: "AE", curriculum: "IB-DP" })
await setupCatalogForSchool(schoolId, { country: "GB", curriculum: "GB" })inferCurriculum + تراجع findSubjects
صفّ المدرسة لا يخزّن رمز المنهج — inferCurriculum() يشتقّه من البلد + النوع
وقت التشغيل. ثم يحلّ findSubjects() المواد عبر تراجع تدريجي من 4 خطوات، فتحصل
المدرسة دوماً على شيء ما:
| الخطوة | المُرشِّح |
|---|---|
| 1 | country + curriculum + schoolType |
| 2 | country + curriculum |
| 3 | country="*" + curriculum (عالمي) |
| 4 | US (تراجع أساسي) |
المواد المخصّصة لصف بعينه تُنتج ربطاً 1:1 — "Math Grade 3" يُربَط مباشرةً بالصف الثالث في المدرسة.
ensureSubjectSelections — إصلاح ذاتي كسول
تعمل التهيئة بأسلوب fire-and-forget عبر after() في Next.js. إن غابت صفوف الجسر،
يُصلِحها ensureSubjectSelections بشكل كسول عند أول قراءة، فيُصحَّح فشل التهيئة
الجزئي ذاتياً — لاختيارات المواد فقط.
طبيب التهيئة — repairProvisioning
كانت الاختيارات هي المرحلة الوحيدة التي تُصلَح ذاتياً؛ انقطاع serverless كان لا
يزال قادراً على ترك مدرسة بلا حصص أو شُعَب أو خانات جدول. الطبيب في
src/components/catalog/provision.ts يغلق هذه الثغرة:
getProvisioningStatus(schoolId)— جولة عدّ واحدة مُجمَّعة عبر كل مرحلة (الافتراضات، البنية الأكاديمية، الاختيارات، الجدول الزمني، الشُّعَب، خانات الجدول، رمز الانضمام) مع تقارير إضافية للعرض فقط (المسارات، كتب المكتبة).repairProvisioning(schoolId)— يشغّل المراحل الناقصة فقط بترتيب الاعتمادية، وكل مرحلة معزولة بحيث لا يُوقف فشلُ واحدة البقيةَ. كل خطوة أساسية قابلة لإعادة التنفيذ — إعادة تشغيله على مدرسة سليمة لا تفعل شيئاً.
ثلاثة مستدعين يلتقون عليه: خطّاف after() في الإعداد الأولي، ومسار
publishSchool اليدوي، وجدول مدارس المُشغِّل (إجراء Repair Provisioning).
applyTimetableStructureForNewSchool صار هو الآخر قابلاً لإعادة التنفيذ — إعادة
تشغيله لم تعد تكرّر حصص المدرسة.
البنية الأكاديمية لكل منهج (الثغرة المُصلَحة)
كان setup.ts القديم يُضمِّن النموذج السوداني — مراحل عربية 6+3+3، أسماء صفوف
عربية، مسارات علمي/أدبي مفروضة على الصفوف 10–12 — ويطبّقه على كل مدرسة، فكانت
مدرسة أمريكية/بريطانية/IB تحصل على أسماء صفوف عربية ومسارات لا تخصّها. هذا
مُصلَح الآن. التهيئة تحلّ رمز منهج المدرسة إلى CurriculumAcademicConfig في
src/components/catalog/academic-config.ts (CURRICULUM_ACADEMIC_CONFIG،
يُختار عبر getAcademicConfig). مدخل SD يحفظ الثوابت الأصلية بايتاً ببايت، فتُهيَّأ
المدارس السودانية تماماً كما كانت.
| رمز/رموز المنهج | البنية | تسمية الصفوف | المسارات |
|---|---|---|---|
SD، SA، EG، AE، QA، KW، JO | عربي 6+3+3 | الصف الأول … | علمي / أدبي من الصف 10 |
US، CAIE-IGCSE، IB-DP | إنجليزي 5+3+4 | Grade N | لا شيء |
GB | مراحل رئيسية (خريطة 6+3+3) | Year N | لا شيء |
CBSE | 5+3+4 (ابتدائي/متوسط/ثانوي) | Class N | لا شيء |
| (غير معروف) | إنجليزي عام 6+3+3 | Grade N | لا شيء (GENERIC_FALLBACK) |
معرّفات (slugs) الصفوف غير قابلة للضبط عمداً — تبقى
grade-${n}في كل مكان لأن ربطYearLevelفيsetup.tsيطابق على شكل ذلك المعرّف. الاسم المعروض، وحدود المراحل، وسياسة المسارات فقط هي ما يتغيّر لكل منهج.
الاقتراحات والموافقة
يمكن للمدرسة أن تقترح محتوى فهرس جديداً، وعند الموافقة تتبنّاه اختيارياً. هذا هو تدفّق الطلب الذي شُحِن هذه الجلسة — لاحظ أن الموافقة تنشر إلى الفهرس فقط؛ لا جسر تلقائي (المدرسة تضيفه صراحةً).
School proposes a subject/chapter/lesson
→ Proposal { status: SUBMITTED } (requireProposer: ADMIN / DEVELOPER / TEACHER)
Operator queue at /catalog/proposals (sidebar badge shows SUBMITTED count)
→ approveProposal → creates Subject/Chapter/Lesson (status PUBLISHED) in the catalog
→ proposal PUBLISHED, catalogEntityId set
→ NO auto-bridge (opt-in)
→ notifies proposer + school ADMINs (in_app + email, school-language aware)
School catalog picker pins the new subject ("Requested by your school — now ready")
→ one-time reminder dialog + Add CTA
→ pin clears once the subject is added (SubjectSelection exists)
→ rejectProposal → notifies proposer + ADMINs with the rejection reason
الاقتراح (جانب المدرسة)
تكتب submitSubjectProposal / submitChapterProposal / submitLessonProposal
(src/components/school-dashboard/listings/subjects/catalog/proposal-actions.ts)
صفّ Proposal (catalog_proposals) بحالة status: SUBMITTED. مُصرَّح به عبر
requireProposer — ADMIN أو DEVELOPER أو TEACHER ممّن لديه schoolId. حقل
Proposal.data (JSON) يحمل حقول الكيان؛ واقتراحات الفصل/الدرس تشير إلى
parentSubjectId / parentChapterId. يمكن تحرير اقتراح DRAFT أو REJECTED وإعادة
إرساله (updateProposal).
طابور المُشغِّل
/catalog/proposals (لـDEVELOPER فقط) يسرد الاقتراحات عبر getProposalsForReview،
مع ضبط المُرشِّح افتراضياً على SUBMITTED. يحسب تخطيط الفهرس أعداد الشارة —
db.proposal.count({ where: { status: "SUBMITTED" } }) — ويعرض تسمية التنقّل
بصيغة Proposals (N) عند وجود معلّق.
الموافقة → الفهرس فقط
تُشغِّل approveProposal التدفّق كاملاً في معاملة واحدة (فحص حالة متفائل داخل
المعاملة، وإعادة محاولة عند تضارب المعرّف، والأسماء العربية تحصل على لاحقة معرّف
عشوائية). تُنشئ Subject/Chapter/Lesson بحالة status: PUBLISHED، وتعلّم
الاقتراح PUBLISHED مع catalogEntityId، ولا تُنشئ SubjectSelection —
المدرسة تتبنّاه اختيارياً. تنطلق الإشعارات بعد المعاملة (فشل الإشعار لا يتراجع
عن الموافقة أبداً):
- لغة المدرسة تُحلّ عبر
resolveSchoolLang(schoolId)(عنوان/متن ثنائي اللغة). - يُعاد استخدام
NotificationTypedocument_sharedللموافقة، و**system_alert** للرفض. metadataيحمل{ entityType: "proposal", kind: "proposal_approved" | "proposal_rejected", catalogEntityId, url: "/subjects/catalog" }.- يُسلَّم إلى المقترِح (يُتخطّى إن كان المقترِح ADMIN — توزيع الجمهور حسب الدور
يغطّيه أصلاً، تفادياً للتكرار) وكل ADMIN في المدرسة، على
in_app+email. rejectProposalيتطلّب سبباً ويضمّنه في الإشعار.
مُثبَّت في مُنتقي المدرسة
مُنتقي فهرس المدرسة (subject-picker.tsx) يأخذ pinnedSubjectIds — المواد المُوافَق
عليها التي طلبتها هذه المدرسة ولم تُضِفها بعد. تُعرَض في قسم مُثبَّت "Requested by
your school — now ready" مع زرّ Add، ويعلن عنها مرّةً واحدة حوار
CatalogReadyReminder (يُخزَّن الصرف في localStorage تحت
catalog-ready-dismissed:${schoolId}). يُرشِّح التثبيت أي مادة لها اختيار بالفعل،
فيُمسَح تلقائياً بمجرّد إضافة المادة.
الرؤية والمحتوى المدفوع
كل عنصر مُساهَم به يحمل ContentVisibility إضافةً إلى price/currency اختيارية.
دورة حياة المساهمة:
Teacher/School creates content
→ approvalStatus: PENDING, visibility: PRIVATE, status: DRAFT
SaaS admin reviews (/catalog/approvals)
→ APPROVED → status set to PUBLISHED, visibility set by reviewer
PRIVATE — only creator's school
SCHOOL — schools using that subject (free)
PUBLIC — anyone (free)
PAID — anyone, gated by purchase (priceable types only)
→ REJECTED → reason sent to creator
الموافقة تنشر الآن (الثغرة المُصلَحة)
مساهمات المدرسة المُوافَق عليها تُنشأ بحالة status: DRAFT، لكن مسارات تصفّح المدرسة
لا تُظهر إلا الصفوف PUBLISHED + APPROVED — فكانت المساهمة المُوافَق عليها تبقى
سابقاً DRAFT غير مرئية. الآن تضبط approveContent
(saas-dashboard/catalog/approval-actions.ts) أيضاً status: PUBLISHED لـ
Question وMaterial وAssignment (لا ContentStatus لـVideo؛ والكتاب يُنشَر +
يُختار تلقائياً في مكتبة المساهِم).
الفيديوهات المدفوعة (موصولة)
الفيديوهات محجوبة بالكامل عبر Stripe: صفّ VideoPurchase يسجّل الشراء، والرابط
يُقدَّم عبر CloudFront مُوقَّع، والفيديو PAID غير المُشترى يُعيد رابطاً null
فلا يستطيع المشغّل جلبه. ضبط فيديو على PAID عند الموافقة يتطلّب سعراً موجباً وعملة
من 3 أحرف.
الاختبارات / الأسئلة المدفوعة (مؤجّل)
يحمل Question وExam حقلَي price/currency ويقبلان رؤية PAID، لكن تدفّق
الشراء غير مبني — الحقول موجودة لمتجر اختبارات/أسئلة مدفوع مستقبلاً.
إدارة أعلام المُشغِّل
updateContentFlags (+ الحوار content-flags-dialog) يتيح لـDEVELOPER ضبط
الرؤية / الحالة / السعر لكل نوع محتوى، مستقلاً عن الموافقة/الرفض. يفرض الحوار قائمة
بيضاء لكل نوع:
| الحقل | مسموح على |
|---|---|
visibility | كل نوع |
status | كل نوع عدا Video (لا ContentStatus) |
price / currency | Question، Video، Exam فقط |
PAID | الأنواع القابلة للتسعير فقط — تتطلّب سعراً + عملة 3 أحرف |
ضوابط جانب المدرسة
SubjectSelection.isActive— يُفعّل/يُعطّل مادة مُتبنّاة للمدرسة.ContentOverride.isHidden— يُخفي فصلاً/درساً/فيديو. هذا الآن يُرشِّح فعلاً صفحة تفاصيل المادة للأدوار غير الإدارية (get-course.tsيقرأContentOverrideويُسقِط العُقَد المخفيّة؛ وإن فشل استعلام التجاوز يُظهر كل شيء بدل خطأ 500). الإداريون يستخدمونadmin-get-course.tsالذي يُعيد كل المحتوى موسوماً بـ_isHiddenفيستطيع لوح Customize إدارة الرؤية.- البحث العام يُرشِّح الآن إلى المواد المنشورة فقط
(
atom/generic-command-menu/server/global-search.ts) — تسريب سابق لمحتوى غير منشور أُصلِح.
هندسة المنهج
هرم الفهرس ثلاثة جداول عامة، كلّها بلا نطاق؛ والمدارس تشير إليها عبر الجسر.
slug هو مفتاح الـupsert الأساسي — يُرمِّز البلد + الصفّ + المادة (sd-g5-math،
us-g5-math)، فلا حاجة لقيد فريد مركّب على (country, curriculum, grade, name).
model Subject { // table catalog_subjects
country String @default("SD") // ISO 3166-1 alpha-2
curriculum String @default("SD") // canonical code: bare ISO (US, SD, GB) or BODY-PROGRAMME (IB-DP, CAIE-IGCSE, CBSE)
curriculumId String? // optional FK → Curriculum
concept String? // shared image key
thumbnail String? @map("thumbnailKey") // S3 key for WebP variants
grades Int[] @default([]) // [5] = grade 5 only
levels SchoolLevel[] // [ELEMENTARY]
slug String @unique // sd-g5-math, us-g5-math
totalChapters Int // denormalized counts
totalLessons Int
}أشجار المحتوى على القرص — curriculum/
دليل curriculum/ هو مصدر الحقيقة للمحتوى للمناهج العميقة. أداة البناء
(curriculum/_build_tools/engine.py) تكتب، ومحرّك البذرة يقرأ، هذا الشكل:
curriculum/<dir>/
g{N}/ # grade
<subject>/ # e.g. math, science
structure.json # { subject, chapters: [{ name, slug, lessons: [...] }] }
textbook.pdf # optional — merged free official PDF
scripts/gen-curriculum-structure.ts يحوّل مواصفة قائمة فصول مُوجزة إلى شجرة
structure.json؛ وscripts/upload-textbooks-all.ts يدفع كتب أي شجرة (PDF) إلى
S3. الصيغ القديمة (us-inventory.json، sudan-curriculum.json) تحتفظ ببذورها
الخاصّة — والمناهج الجديدة وحدها تمرّ عبر محرّك الشجرة.
syncCurriculumTree (محرّك الشجرة)
التكرار لكل بذرة (upsert، تحديث الأعداد، حلقة الفصل/الدرس، إسناد المفهوم) مُجمَّع في
مساعد واحد syncCurriculumTree (prisma/seeds/catalog/engine.ts). المنهج العميق
الجديد ملفّ مصدر بـ~20 سطراً:
// prisma/seeds/catalog/gb.ts
import { syncCurriculumTree } from "./engine"
export async function seedGbCurriculum(prisma: PrismaClient) {
return syncCurriculumTree(prisma, {
dir: "uk", // curriculum/uk/
country: "GB",
code: "GB", // canonical curriculum code
slugPrefix: "gb", // gb-g{N}-{subject}
name: "British (England)",
lang: "en",
gradeRange: [1, 12],
sortBase: 20000,
})
}يقرأ أي شجرة curriculum/<dir>/g{N}/<subject>/structure.json، ويُحدِّث/يُدرِج
(upsert) Subject/Chapter/Lesson بالرمز/البلد القانوني، ويُرفق مفتاح S3 لكتاب
PDF عند وجوده، ويُدوِّر مصغّر المفهوم المشترك عبر الفصول. وهو قابل لإعادة التنفيذ
بأمان (upsert للمادة حسب slug؛ والفصول/الدروس تُستبدَل). تحمل المواد رمز المنهج
فقط — registry.ts يملك صفّ Curriculum ويملأ curriculumId. المستدعون الأربعة
الأحياء هم gb.ts، وcbse.ts، وcaie-igcse.ts، وib.ts. والمناهج العربية الستة
مواد-فقط (sa.ts…jo.ts) تتشارك seedSubjectsOnly.
المناهج والرموز
المناهج الوطنية تستخدم رمز دولة ISO 3166-1 المجرّد — البلد هو المُعرِّف؛
والبرامج الدولية تستخدم رمز {body}-{programme}. الصفوف الموجودة رُحِّلت من
الرموز القديمة (national / us-k12 / ib) عبر ترحيل
canonicalize_curriculum_codes (المُفعَّل الآن). يحتفظ Curriculum.slug بقيمه
الخاصّة (sd-national، ib-diploma).
| المنهج | الرمز | النوع |
|---|---|---|
| السودان | SD | وطني |
| السعودية | SA | وطني |
| مصر | EG | وطني |
| الولايات المتحدة | US | وطني |
| إنجلترا | GB | وطني |
| الإمارات | AE | وطني |
| قطر | QA | وطني |
| الكويت | KW | وطني |
| الأردن | JO | وطني |
| بكالوريا IB | IB-DP | دولي |
| كامبريدج IGCSE | CAIE-IGCSE | دولي |
| الهندي CBSE | CBSE | دولي |
التغطية
كل المناهج الاثني عشر لها مواد. ستة بعمق (فصول ودروس حقيقية) — US، والسودان،
وإنجلترا (curriculum/uk)، وكامبريدج IGCSE، وIB-DP، والهندي CBSE. أما المناهج
العربية الوطنية الستة فتبقى مواد فقط حتى تُؤلَّف بنية كتبها الوزارية.
| المنهج | الرمز | الصفوف | مواد | فصول | دروس | المصدر | البذرة |
|---|---|---|---|---|---|---|---|
| الولايات المتحدة | US | 1–12 | ~240 | ~750 | ~3,993 | US-inventory | us |
| السودان | SD | 1–12 | 125 | 864 | 2,937 | curriculum/sd | sd |
| إنجلترا | GB | 1–12 | 184 | 363 | 730 | curriculum/uk | gb |
| كامبريدج IGCSE | CAIE-IGCSE | 10 | 8 | 76 | 76 | curriculum/caie-igcse | caie-igcse |
| بكالوريا IB | IB-DP | 12 | 9 | 49 | 49 | curriculum/ib | ib |
| الهندي CBSE | CBSE | 10 | 7 | 58 | 58 | curriculum/in | cbse |
| السعودية | SA | 1–12 | ~132 | — | — | sa.ts المضمّن | sa |
| مصر | EG | 1–12 | ~140 | — | — | eg.ts المضمّن | eg |
| الإمارات | AE | 1–12 | ~144 | — | — | ae.ts المضمّن | ae |
| قطر | QA | 1–12 | ~122 | — | — | qa.ts المضمّن | qa |
| الكويت | KW | 1–12 | ~139 | — | — | kw.ts المضمّن | kw |
| الأردن | JO | 1–12 | ~150 | — | — | jo.ts المضمّن | jo |
المناهج العابرة للحدود العميقة (كامبريدج، IB، CBSE) تغطّي صفّاً تمثيلياً حتى الآن؛ وتوسعتها لكل صفّ آليّة (تأليف مزيد من
structure.json). أماcurriculum/usعلى القرص (Aldar American، 128/266/535) فهي غير مُدمَجة — تتصادم على فضاء المعرّفus-g{N}-*وهي أقلّ عمقاً؛ تبقى طبقة مرجعية. لا دمج مخطّط له.
المحتوى الحقيقي — السودان (كل الصفوف)
catalog/sd-content.ts يُدمِج بنوك الأسئلة والامتحانات النهائية المؤلَّفة
الموجودة بجوار كل كتاب مدرسي سوداني
(curriculum/sd/g{1–12}/<subject>/{qbank,exams}.json) في
Question/Exam/ExamQuestion: 102 مادة، ~2,700 سؤال (100% مربوطة بالفصول؛
منها 405 مربوطة بالدروس أيضاً — وهي ما يُغذّي اختبار التدريب في صفحة الدرس)،
وامتحان نهائي مرتبط لكل مادة. بوّابة جودة تتخطّى 22 ملفاً في الصفّين 11–12
مولَّدة قالبياً (>50% علامات حشو) — تلك المواد تُبقي المحتوى الاصطناعي بدلاً
منه. الصفوف موسومة "sd"؛ والدمج حذفٌ-وإعادة-إنشاء لكل مادة ويعمل محلياً
(pnpm db:seed:single sd-content) لأن curriculum/ لا يُنشر.
دروس السودان بلا صفوف Video عمداً — مشغّل الدرس في lumos يعود إلى مقطع
story.mp4 المشترك في كل درس سوداني، فلا يظهر مشغّل فارغ ولا رابط لكائن مفقود
على CDN.
المحتوى المُولَّد
catalog/content.ts يقرأ الهرم المنشور ويُولِّد صفوفاً اصطناعية بالجملة:
اختبارات (لكل مادة/فصل/درس)، وبنك أسئلة (~10/مادة، 6/فصل، 4/درس)، ومواد (بيانات
وصفية فقط — fileUrl يكون null)، وواجباً واحداً لكل درس. المنهج مواد-فقط يحصل على
صفوف لكل مادة فقط. المواد التي تحمل محتوى حقيقياً مُدمَجاً (أسئلة موسومة "sd")
تُتخطّى، والصفوف الاصطناعية اليتيمة (مفاتيح نطاق أفرغتها إعادة بناء الفصول)
تُحذَف في كل تشغيل. والاستثناء هو catalog/exam-templates.ts الذي يكتب مخطّطات +
قوالب حقيقية مؤلَّفة يدوياً (قابلة لإعادة التنفيذ، لكل مادة). نموذج Quiz لا يُبذَر.
content.ts قابل لإعادة التنفيذ — راجع العمليات.
جودة استخراج جدول المحتويات
كثير من ملفات السودان (خاصّةً الصفوف 6–8 و10–12) أنتجت مخرجات pdftotext مشوّهة أو
فارغة، فبقيت فصول بـ0 دروس:
| نطاق الصفوف | بدروس | بـ0 دروس |
|---|---|---|
| 1–5 | 31 (89%) | 4 (11%) |
| 6–9 | 8 (40%) | 12 (60%) |
| 10–12 | 14 (29%) | 34 (71%) |
الأولويات: تمريرة OCR (Tesseract عربي) على الملفات الممسوحة، ثم إدخال يدوي للصفوف 10–12 عبر طبقة المحتوى / تدفّق الاقتراح.
خريطة الملفات
prisma/
models/catalog.prisma # Subject, Chapter, Lesson + shared enums
seeds/catalog/
engine.ts # syncCurriculumTree + seedSubjectsOnly (shared)
registry.ts # 12 Curriculum records + curriculumId backfill
index.ts # seedCatalog + seedFullCatalog
us.ts # US (240, us-inventory.json)
sd.ts # Full Sudan from curriculum/sd/ (125)
sd-content.ts # SD real qbank+exams ingest, g1–g12
gb.ts / cbse.ts / caie-igcse.ts / ib.ts # deep tree callers
{sa,eg,ae,qa,kw,jo}.ts # 6 subjects-only nationals
concepts.ts / banners.ts # shared image upload + backfill
content.ts / books.ts / videos.ts / exam-templates.ts / demo.ts
single.ts # CLI registry
curriculum/
<dir>/g{N}/<subject>/structure.json # on-disk content trees
_build_tools/engine.py # writes the trees
src/components/catalog/
setup.ts # findSubjects(), inferCurriculum()
academic-config.ts # per-curriculum CurriculumAcademicConfig
image.ts / image-url.ts # Sharp pipeline + URL resolution
concepts-data.ts # single source of concepts + nearestConcept
منهج الولايات المتحدة
مصدر US هو جرد محتوى K-12: scripts/us-curriculum/us-inventory.json. تتوسّع 62
مادة أساسية إلى ~240 صفّ Subject مخصّص لصف عبر بذرة us. تصنيف US وصوره بذرا
أصول المفاهيم المشتركة، وهو المرجع الذي تُحسَّن منه بقية المناهج.
| المقياس | العدد |
|---|---|
| المواد | 62 |
| مجموعات المواضيع | 201 |
| المواضيع الفرعية | 986 |
| الفيديوهات | 22,289 |
| الموارد | 43,075 |
| المستوى | مواد | مجموعات | مواضيع | فيديوهات | موارد | الصفوف |
|---|---|---|---|---|---|---|
| Elementary | 18 | 49 | 346 | 8,130 | 17,233 | K-5 |
| Middle | 21 | 73 | 276 | 5,490 | 10,754 | 6-8 |
| High | 23 | 79 | 364 | 8,669 | 15,088 | 9-12 |
كل موضوع فرعي في الجرد مفتاحه slug:
type SourceTopic = {
slug: string // kebab-case identifier
name: string // display name
videos: number
resources: number
coverId: string
subject: string
topicGroup: string
}# list every sub-topic for a tier
jq '.tiers.elementary.subjects[] | .topics[]' scripts/us-curriculum/us-inventory.json
jq '.tiers.middle.subjects[] | .topics[]' scripts/us-curriculum/us-inventory.json
jq '.tiers.high.subjects[] | .topics[]' scripts/us-curriculum/us-inventory.jsonمنهج السودان
يغطّي السودان الصفوف 1–12 في بنية 6+3+3 (Elementary، Middle، Secondary)، مُستخرَجة
من ملفات الكتب المدرسية المنشورة من المركز القومي للمناهج والبحث التربوي (بخت
الرضا). المواد والفصول والدروس تعيش في الفهرس بـcountry: "SD" وcurriculum: "SD".
نمط المعرّف sd-g{N}-{subject} (مثل sd-g5-math)؛ بادئة المعرّف تجميلية، ورمز
curriculum هو مفتاح الربط القانوني.
| الصف | المستوى | مواد | فصول | دروس | ملاحظات |
|---|---|---|---|---|---|
| 1 | Elementary | 4 | 17 | 112 | Arabic, Math, Islamic, English |
| 2 | Elementary | 7 | 36 | 195 | + Science, Arts, History |
| 3 | Elementary | 6 | 35 | 212 | + Geography |
| 4 | Elementary | 5 | 24 | 55 | + Computer |
| 5 | Elementary | 12 | 53 | 222 | + Earth Environment, Resources, Clothing |
| 6 | Elementary | 5 | 10 | 0 | Scanned PDFs — chapters only |
| 7 | Middle | 1 | 1 | 0 | English activity book only |
| 8 | Middle | — | — | — | No textbooks available |
| 9 | Middle | 9 | 34 | 40 | + Technical Education, Communication Tech |
| 10 | Secondary | 11 | 49 | 80 | + Physics, Chemistry, Biology, French |
| 11 | Secondary | 19 | 71 | 77 | Full catalog including streams |
| 12 | Secondary | 18 | 76 | 56 | Shahada year — all streams |
مسارات الثانوي (10–12) تُوجَّه بأنماط في setup.ts:
const SCIENCE_ONLY_PATTERNS = [
"physics",
"chemistry",
"فيزياء",
"كيمياء",
"أحياء",
]
const ARTS_ONLY_PATTERNS = [
"philosophy",
"فلسفة",
"أدب",
"بلاغة",
"مطالعة",
"نحو",
"عربية خاصة",
"رياضيات أساسية",
]خطّ معالجة البيانات
~/SD/*.pdf (260 raw, ~112 unique textbooks)
→ organize → scripts/sudan-data/pdfs/grade-{N}/sd-g{N}-{subject}.pdf
→ pdftotext → raw Arabic TOC text
→ parse TOC → scripts/sudan-data/toc/sd-g{N}-{subject}.json (97 files)
→ assemble-sudan-curriculum.ts → scripts/sudan-data/sudan-curriculum.json (master)
→ prisma/seeds/catalog/sd.ts → Subject + Chapter + Lesson (upsert)
ثغرات معروفة: ملفات الصف 6 صور ممسوحة (فصول فقط، 0 دروس)، والصف 7 له كتاب واحد، والصف 8 بلا كتب، وعدّة ملفات ثانوية مشوّهة — يُعالَج بـOCR، أو بجلب الكتب المتبقّية، أو بمساهمة المجتمع.
إضافة منهج وطني
السودان يسبق الخطّ العام (بذرة مُحرَّكة بجدول المحتويات). المنهج الوطني الجديد (مثل
مصر EG) يمرّ عبر محرّك الشجرة:
- ألِّف
curriculum/eg/g{N}/<subject>/structure.json. - أضف ملفّاً بسطر واحد
prisma/seeds/catalog/eg.tsيستدعيsyncCurriculumTreeبـcountry: "EG", code: "EG"، وسجّله فيprisma/seeds/single.ts. - أضف
EG → "EG"إلىinferCurriculum()وCOUNTRY_CURRICULUM(src/lib/timetable-reference.ts). - أضف الأسماء العربية إلى
SUBJECT_CONCEPT_BY_NAMEفيconcepts-data.ts. - (اختياري) أضف مدخل
EGإلىCURRICULUM_ACADEMIC_CONFIGإن اختلف عن الافتراضي العربي المشترك 6+3+3. - شغّل
pnpm db:seed:single egثمpnpm db:seed:single concepts.
المفاهيم (Concepts)
المفهوم (concept) مُعرِّف مستقلّ عن المنهج يربط كل مادة بأصول بصرية مشتركة.
العربية (السودان) واللغات العالمية (US) تتشاركان نفس مفهوم languages ونفس صور S3.
كانت الرسومات تُشار إليها أصلاً من مكتبة خارجية أثناء النمذجة الأوّلية؛ وتلك الأصول
الآن مملوكة، ومُعاد رسمها، ومشتركة عبر كل منهج — راجع الأصول.
Subject (any curriculum)
└── concept: "math"
├── thumbnail: catalog/concepts/g{grade}-math/thumbnail-{size}.webp
├── banner: catalog/concepts/g{grade}-math/banner-{size}.webp
└── cover: catalog/concepts/math/cover
المفاهيم الـ23
arts، biology، career-tech، celebrations، chemistry، civics،
computer-science، earth-science، economics، english، geography،
health، history، languages، life-skills، math، pe، physics،
psychology، religion، science، sociology، teacher-pd.
صور المصدر تعيش في public/subjects/concepts/{concept}.jpg. بذرة concepts تعالج
كلاً منها إلى 4 نسخ WebP (sm/md/lg/original) وترفعها إلى S3 لكل الصفوف الـ12. كل
المناهج الـ12 تربط موادها بأحد هذه المفاهيم الـ23؛ ونموذج Image (catalog_images)
يخزّن السجلّات لكل نسخة.
مصدر واحد للحقيقة — concepts-data.ts
كل ما يخصّ المفاهيم يعيش في src/components/catalog/concepts-data.ts: المفاهيم
الـ23 وألوانها، وخرائط المطابقة الدقيقة (SUBJECT_CONCEPT_BY_SLUG،
SUBJECT_CONCEPT_BY_NAME)، وأحواض التدوير (CONCEPT_POOL)، وخوارزمية كلمات مفتاحية
nearestConcept() (إنجليزي + عربي) كي لا تبقى أي مادة بلا صورة (اللون المسطّح
هو الملاذ الأخير). كل بذرة (us.ts، sd.ts، engine.ts، concepts.ts) تستورد
منه — ولا تبقى نسخ لكل بذرة.
أحواض تدوير الفصول
الفصول داخل المادة تدور عبر حوض من 5 مفاهيم مرتبطة للتنوّع البصري (تُدوَّر بـ
sequenceOrder % poolSize): مثلاً math → math، science، computer-science،
physics، economics؛ وscience → science، biology، chemistry، physics،
earth-science. البذور العميقة وsyncCurriculumTree ترث هذا؛ وللمناهج مواد-فقط،
تأتي صور الفصل/الدرس مجاناً بمجرّد توفّر العمق.
النسخ (Variants)
| النسخة | العرض | الجودة | الاستخدام |
|---|---|---|---|
-sm.webp | 200px | 95 | مصغّرات الشبكة |
-md.webp | 600px | 97 | صور البطاقات |
-lg.webp | 1200px | 100 | بانرات البطل |
-original.webp | كامل | بلا فقد | الدقّة الكاملة |
image.ts (خادم فقط — processAndUploadCatalogImage، deleteCatalogImage) يشغّل
Sharp؛ وimage-url.ts (آمن للعميل — getCatalogImageUrl) يبني {key}-{size}.webp
وقت القراءة. حلّ الرابط: CloudFront من thumbnailKey → تراجع ثابت قائم على المفهوم
→ null (خلفية لونية).
CDN والأصول
أصول الفهرس تتبع اصطلاح مفاتيح صارماً وتُقدَّم من S3 عبر CloudFront. المرجع الكامل
لشبكة توصيل المنصّة (الحاوية، مساعد asset()، أدلّة أصول الواجهة المسطّحة الخمسة)
يوجد في CDN & Assets — هذا القسم يغطّي قصّة الأصول الخاصّة بالفهرس
فقط.
يحمل Subject أربعة حقول مفاتيح S3 — thumbnail (@map("thumbnailKey"))،
banner (@map("bannerUrl"))، cover (غلاف الكتاب)، pdf (ملف الكتاب). يميّز
الاصطلاح بين *Key (مفتاح S3 خام، تُلحَق النسخ وقت القراءة) و***Url** (رابط
كامل التكوين، مثل أغلفة الكتب الخارجية). تخطيط S3:
catalog/concepts/g{n}-{concept}/thumbnail-{sm,md,lg,original}.webp
catalog/concepts/g{n}-{concept}/banner-{sm,md,lg,original}.webp
catalog/concepts/{concept}/cover
catalog/textbooks/{slug}/textbook.pdf
catalog/subjects/{slug}/…/thumbnail-{size}.webp # US lesson/chapter covers
جدول ثغرات الأصول الصادق
| الأصل | يُنتجه | الحالة |
|---|---|---|
| مصغّرات المواد | concepts.ts | مكتمل — كل المناهج الـ12، كل الصفوف الـ12 |
| البانرات | banners.ts | مُغطّى ذاتياً — ~10 من 23 مفهوماً لها بانر خاص؛ البذرة تستعير الآن بانر أقرب جار في CONCEPT_POOL للبقية (23/23 بعد التشغيل القادم) |
| أغلفة الكتب | — | مفقود — مفتاح cover مضبوط على كل مادة، لكن الكائن لم يُرفَع قطّ (تتراجع البطاقة إلى أيقونة) |
| ملفات الكتب (PDF) | upload-textbooks-all.ts | السودان فقط — 118 textbook.pdf؛ لا منهج آخر |
| مصغّرات الدروس / الفصول | lesson-covers.ts | US فقط — تُسحَب من CDN وقت البذرة |
| الفيديوهات | videos.ts / مُساهَم به | مُتحقَّق بالفحص — البذرة تفحص كل .mp4 برأسية HEAD ولا تسجّل إلا الصفوف ذات الكائن الموجود (وتحذف الصفوف القديمة)؛ الرفع الفعلي ما زال معلّقاً |
| الكتب | books.ts | خارجي — 51 كتاباً على covers.openlibrary.org؛ السكربت scripts/snapshot-book-covers.ts يوطّنها إلى CDN لدينا (يعمل ببيانات AWS الإنتاج) |
| المواد | content.ts | بيانات وصفية فقط — fileUrl يكون null؛ بطاقات المواد تصبح قابلة للنقر تلقائياً فور وصول fileUrl/externalUrl |
العمليات
أوامر البذر
seedFullCatalog (بذرة full) يشغّل كل شيء بالترتيب القانوني — كل المناهج الـ12 →
registry → صور المفاهيم. وبذرة catalog الافتراضية هي المجموعة الفرعية السريعة: US
- السودان الكامل، مُسجَّلة. كل منهج آخر هو تشغيل صريح خاصّ به. استخدم دوماً
pnpm db:seed:single <name>— لا البذرة الكاملة قطّ (فهي يدوية فقط؛ راجع القاعدة أدناه).
البذرة (pnpm db:seed:single …) | المحتوى |
|---|---|
full | seedFullCatalog — كل المناهج الـ12 → registry → صور المفاهيم (قاعدة جديدة) |
catalog | الافتراضي السريع: US (~240) + السودان الكامل + registry |
us / sd | US (الجرد) / السودان الكامل من curriculum/sd/ |
gb / cbse / caie-igcse / ib | المناهج العميقة عبر syncCurriculumTree من curriculum/<dir>/ |
sa / eg / ae / qa / kw / jo | المناهج الوطنية الستة مواد-فقط (seedSubjectsOnly) |
registry | كل سجلّات Curriculum الـ12 + ملء Subject.curriculumId |
concepts / banners | مصغّرات المفاهيم والبانرات → Sharp → S3/CloudFront |
lesson-covers | مصغّرات دروس/فصول US (تعتمد على CDN) |
books / content / videos / exam-templates | كتب، محتوى مُولَّد، فيديوهات، مخطّطات/قوالب اختبارات |
demo | البنية الأكاديمية + صفوف الجسر للمدرسة التجريبية |
pnpm db:seed:single --list # list available seeds
pnpm db:seed:single catalog # fast default: US + full Sudan + registry
pnpm db:seed:single gb # England — deep, from curriculum/uk
pnpm db:seed:single sa # Saudi Arabia — subjects-only national
pnpm db:seed:single registry # (re)link all 12 Curriculum records
pnpm db:seed:single concepts # thumbnails → S3/CDN
pnpm tsx scripts/upload-textbooks-all.ts # Sudan textbook PDFs → S3التكرارية (Idempotency)
أغلب البذور آمنة للـupsert (بذور الصور تعيد الرفع إلى مفاتيح ثابتة، آمنة أيضاً).
بذرة المحتوى الآن قابلة لإعادة التنفيذ كذلك (مُصلَحة هذه الجلسة): content.ts
يحسب seedKey حتمياً من نطاق كل صفّ + عنوانه، ويحمّل المفاتيح الموجودة مسبقاً،
ويُسقِط المرشّحين الموجودين بالفعل — فإعادة تشغيله لم تعد تكرّر
الاختبارات/الأسئلة/المواد/الواجبات. نموذج Quiz ما زال لا يُبذَر.
مزامنة قاعدة البيانات الحيّة المؤجّلة للنشر
الرموز القانونية حيّة، لكن اتّساع المناهج العميقة مشحون في الكود وليس في قاعدة البيانات الحيّة بعد. الدليل التشغيلي كله صار أمراً واحداً:
pnpm tsx scripts/catalog-deploy-sync.ts # خطة — تقرير للقراءة فقط
pnpm tsx scripts/catalog-deploy-sync.ts --execute # تطبيق (مرهون بالنشر)يجرد السكربت صفوف مواد-فقط القديمة التي حلّت محلّها الأشجار العميقة
(gb-national-g* ~156 وib-diploma-g* ~114)، ويحذف فقط ما لا تشير إليه أي
مدرسة — لأن SubjectSelection/Enrollment تُحذَف تتابعياً مع حذف المادة،
فالصفوف المُشار إليها تُدرَج للترحيل اليدوي بدل حذفها — ثم يبذر
registry → gb → cbse → caie-igcse → ib → concepts → banners.
(كامبريدج IGCSE وCBSE بلا صفوف في قاعدة البيانات الحيّة بعد.) شغّله فقط بعد
أن يصبح نشر الكود حيّاً، مع فرع Neon احتياطاً. ومعه في البيئة نفسها:
scripts/snapshot-book-covers.ts (أغلفة OpenLibrary → S3 لدينا) و
scripts/snapshot-covers.ts (مصادر أغلفة دروس US).
لا تشغّل البذرة الكاملة قطّ
وفق قاعدة المشروع، لا تشغّل pnpm db:seed قطّ — البذرة الكاملة يدوية فقط.
استخدم دوماً pnpm db:seed:single <name>. العمليات المدمّرة لقاعدة البيانات (إعادة
تشغيل SQL الترحيل، وdb push --accept-data-loss، وmigrate reset) ممنوعة بلا
موافقة صريحة.
الأصول (Origins)
تصنيف منهج US (مواد → فصول → دروس) ورسومات المفاهيم الأصلية استُلهِما من ClickView، نظام التعلّم الأمريكي الذي أُشير إليه أولاً. تلك الأصول الآن مملوكة، ومُعاد رسمها، ومشتركة عبر كل منهج، وفهرس US هو المرجع الذي تُحسَّن منه البقية — ClickView يُنسَب إليه الفضل هنا مع الشكر؛ وهو لم يعد اعتمادية وقت تشغيل.
اطّلع أيضاً
On This Page
نظرة عامةالحالة باختصارالمعماريةنماذج الفهرس العامنماذج الجسر (تحملschoolId)مرايا المدرسة (مفتاح أجنبي للفهرس → catalogXxxId)التعدادات (Enums)تفصيل نماذج المحتوىالتهيئة (Provisioning)inferCurriculum + تراجع findSubjectsensureSubjectSelections — إصلاح ذاتي كسولطبيب التهيئة — repairProvisioningالبنية الأكاديمية لكل منهج (الثغرة المُصلَحة)الاقتراحات والموافقةالاقتراح (جانب المدرسة)طابور المُشغِّلالموافقة → الفهرس فقطمُثبَّت في مُنتقي المدرسةالرؤية والمحتوى المدفوعالموافقة تنشر الآن (الثغرة المُصلَحة)الفيديوهات المدفوعة (موصولة)الاختبارات / الأسئلة المدفوعة (مؤجّل)إدارة أعلام المُشغِّلضوابط جانب المدرسةهندسة المنهجأشجار المحتوى على القرص — curriculum/syncCurriculumTree (محرّك الشجرة)المناهج والرموزالتغطيةالمحتوى الحقيقي — السودان (كل الصفوف)المحتوى المُولَّدجودة استخراج جدول المحتوياتخريطة الملفاتمنهج الولايات المتحدةمنهج السودانخطّ معالجة البياناتإضافة منهج وطنيالمفاهيم (Concepts)المفاهيم الـ23مصدر واحد للحقيقة — concepts-data.tsأحواض تدوير الفصولالنسخ (Variants)CDN والأصولجدول ثغرات الأصول الصادقالعملياتأوامر البذرالتكرارية (Idempotency)مزامنة قاعدة البيانات الحيّة المؤجّلة للنشرلا تشغّل البذرة الكاملة قطّالأصول (Origins)اطّلع أيضاً