0%
balqalam Logo
بالقلم
المميزاتالمجتمعالتسعيرالوثائق
تسجيل الدخول
  • مقدمة
  • مركز المساعدة
  • أسئلة شائعة
  • ابدأ الآن
  • البيئة المحلية
  • البنية المعمارية
  • الهيكل
  • الأنماط
  • الحزمة التقنية
  • قاعدة البيانات
  • تعدّد المستأجرين
  • الفهرس (Catalog)
  • العمل دون اتصال
  • المصادقة
  • التهيئة (Onboarding)
  • تدفق التكامل (Integration Flow)
  • التهيئة (Provision)
  • القبول
  • الحضور
  • الامتثال التنظيمي
  • الملف الشخصي
  • الاجتماعات
  • إدارة الرسوم
  • الفاتورة
  • التدويل
  • الترجمة
  • دليل الترجمة
  • أمان قاعدة البيانات
  • المساهمة
  • الإلهام
  • المالية
  • النقل

الفهرس (Catalog)

السابقالتالي

العمود الفقري للمنهج على مستوى المنصّة — جداول محتوى عامة، وجسور مُنطَقة بـ schoolId، والتهيئة، وتدفّق الاقتراح-والموافقة، والمحتوى المدفوع، ومحرّك المنهج، وتغطية كل منهج.

الفهرس هو مخزن المنهج المشترك للمنصّة. توجد نسخة واحدة من كل مادة وفصل ودرس داخل جداول عامة بلا schoolId؛ وكل مدرسة تشير إلى نفس المحتوى عبر صفوف جسر (bridge) صغيرة بدل استنساخه. والمحتوى الذي يساهم به المعلّمون والمدارس (فيديوهات، مواد، أسئلة، كتب) يمرّ بمراجعة فريق SaaS ثم يُثري الفهرس للجميع.

هذه الصفحة هي المرجع الموحّد للفهرس. المرجع الكامل لشبكة التوصيل (CDN) والأصول يوجد منفصلاً في CDN & Assets.

نظرة عامة

يقوم النظام كلّه على نموذج ذهني واحد: هناك جداول فهرس عامة (مشتركة، بلا مستأجر) وصفوف جسر مُنطَقة بـschoolId تربط المستأجر بذلك المحتوى المشترك. لا تستنسخ المدرسة الفهرس قطّ — بل تشير إليه.

prisma/models/— catalog data model
catalog.prisma— Subject — content root
chapter.prisma— Chapter
lesson.prisma— Lesson
curriculum.prisma— Curriculum
video.prisma— Video, VideoPurchase, Attachment
qbank.prisma— Question, ExamQuestion
exam.prisma— Exam, ExamTemplate
material.prisma— Material
assignment.prisma— Assignment
book.prisma— Book
textbook.prisma— Textbook
document.prisma— Document
proposal.prisma— Proposal
bridge.prisma— SubjectSelection, ContentOverride, InstructorPreference, BookSelection
enrollment.prisma— Enrollment, LessonProgress, SubjectCertificate
academic.prisma— AcademicLevel, AcademicGrade, AcademicStream
prisma/seeds/catalog/— load content into the DB
index.ts— orchestrator: seedCatalog (US + full SD) + seedFullCatalog
engine.ts— seedSubjectsOnly — the shallow nationals' shared seeder
registry.ts— all 12 Curriculum records + curriculumId backfill
us.ts— US — the US-curriculum source backbone
tree.ts— the catalog repo (../catalog) → sd · gb · cbse · ib-dp · caie-igcse: subjects, chapters, lessons, questions, exams — ids preserved
sa.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 S3
banners.ts— concept banners to S3 (owned, shared)
lesson-covers.ts— per-lesson/chapter covers to S3 (US, legacy)
books.ts— global catalog books
content.ts— materials, exams, questions
videos.ts— lesson videos
exam-templates.ts— exam blueprints + templates
demo.ts— demo-school structure + bridges
src/components/catalog/— shared core — every surface imports from here
concepts-data.ts— 23 concepts, colors, nearestConcept, exact-match maps + pools — single source
setup.ts— provisions a school onto the catalog
provision.ts— schedule/sections/timetable/library provisioning + the repair doctor
image.ts— Sharp pipeline + S3 upload
image-url.ts— resolve image URLs (CloudFront)
src/components/saas-dashboard/catalog/— platform authoring (DEVELOPER)
src/components/school-dashboard/listings/subjects/catalog/— school adoption + contribution
src/components/lumos/data/catalog/— LMS read queries
../catalog/— github.com/databayt/catalog — the curriculum repo; mirrors cdn.databayt.org/catalog/
sd/ · gb/ · us/ · cbse/ · ib-dp/ · caie-igcse/— <cur>/<grade>/<subject>/{structure,qbank,exams}.json, c<N>/l<N>/ — read by tree.ts (us is not adopted)
scripts/migrate/renames.json— pre-catalog slug → catalog id (tree.ts adopts rows by it)
public/subjects/concepts/— 23 source concept images
scripts/
us-curriculum/us-inventory.json— US source data
sudan-data/— Sudan source data + TOC + PDFs
  • الفهرس العام (جداول 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)المُوصِّليحمل
Subjectcatalog_subjectsdb.subjectجذر الهرم؛ slug (فريد)، curriculum، concept، أعداد مُزال تطبيعها
Chaptercatalog_chaptersdb.chapterمفتاح أجنبي subjectId، sequenceOrder
Lessoncatalog_lessonsdb.lessonمفتاح أجنبي chapterId، sequenceOrder، durationMinutes
Videolesson_videosdb.videovideoUrl، provider، visibility، price، الموافقة
Examcatalog_examsdb.examاختبار الفهرس (≠ SchoolExam المدرسي)
ExamQuestioncatalog_exam_questionsdb.examQuestionجدول وصل Exam ↔ Question مع order/points
Questioncatalog_questionsdb.questionبنك أسئلة الفهرس (≠ QuestionBank المدرسي)
Materialcatalog_materialsdb.materialكتب/مناهج/أوراق عمل على أي مستوى
Assignmentcatalog_assignmentsdb.assignmentقوالب واجبات/مشاريع قابلة لإعادة الاستخدام
Bookcatalog_booksdb.bookكتب المكتبة (≠ SchoolBook المدرسي)
Quizcatalog_quizzesdb.quizمُعرَّف؛ لا يُبذَر
Documentcatalog_documentsdb.documentسجلّ ملفات موحّد (PDF/PPTX/DOCX/صوت)
Imagecatalog_imagesdb.imageنسخ صور المفاهيم (راجع المفاهيم)
Textbookcatalog_textbooksdb.textbookبيانات الكتاب الوصفية (مفتاح S3 لملف PDF على Subject.pdf)
Curriculumcatalog_curriculadb.curriculumسجلّات المناهج الـ12؛ هدف مفتاح curriculumId

نماذج الجسر (تحمل schoolId)

النموذجالجدول (@@map)المُوصِّلالغرض
SubjectSelectionschool_subject_selectionsdb.subjectSelectionصفّ لكل مادة+صفّ مُتبنّى؛ isActive يُفعّل/يُعطّل المادة
ContentOverrideschool_content_overridesdb.contentOverrideتجاوز فصل/درس/فيديو لكل مدرسة؛ isHidden يُخفي المحتوى
InstructorPreferenceschool_instructor_preferencesdb.instructorPreferenceترتيب معلّم/فيديو لكل مدرسة
BookSelectionschool_book_selectionsdb.bookSelectionيجسر Book من الفهرس إلى مكتبة مدرسة

مرايا المدرسة (مفتاح أجنبي للفهرس → catalogXxxId)

نموذج الفهرس ونظيره المُنطَق للمدرسة هما نموذجان مختلفان. عند قراءة Exam راجع الجدول لتعرف أيّهما المقصود. مرآة المدرسة ترتبط بالفهرس عبر مفاتيح أجنبية اختيارية catalogSubjectId / catalogChapterId / catalogLessonId.

الفهرس (عام)مُنطَق للمدرسة (لكل مستأجر)
Exam → catalog_examsSchoolExam → exams
Book → catalog_booksSchoolBook → books
Assignment → catalog_assignmentsSchoolAssignment → assignments
Question → catalog_questionsQuestionBank → question_bank
ExamTemplate → catalog_exam_templatesSchoolExamTemplate → 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 / catalogLessonIdString?أحدها مضبوط (مفتاح أجنبي إلى هرم الفهرس)
title / langStringlang افتراضي "ar"
typeMaterialTypeTEXTBOOK، SYLLABUS، WORKSHEET، …
fileUrl / fileKey / fileSize / mimeType / pageCountمتنوّعمُخزَّن على S3
externalUrlString?للموارد غير المرفوعة
contributedBy / contributedSchoolIdString?النسب
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 خطوات، فتحصل المدرسة دوماً على شيء ما:

الخطوةالمُرشِّح
1country + curriculum + schoolType
2country + curriculum
3country="*" + curriculum (عالمي)
4US (تراجع أساسي)

المواد المخصّصة لصف بعينه تُنتج ربطاً 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+4Grade Nلا شيء
GBمراحل رئيسية (خريطة 6+3+3)Year Nلا شيء
CBSE5+3+4 (ابتدائي/متوسط/ثانوي)Class Nلا شيء
(غير معروف)إنجليزي عام 6+3+3Grade 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) (عنوان/متن ثنائي اللغة).
  • يُعاد استخدام NotificationType document_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 / currencyQuestion، 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
}

مستودع الكتالوج — databayt/catalog

لم تعد المناهج العميقة تعيش في هذا المستودع. مصدر الحقيقة هو github.com/databayt/catalog، يُستنسخ بجوار هذا المستودع في ../catalog، وهو مرآة مطابقة لشبكة التوزيع:

../catalog/sd/g6/math/structure.json      ==  cdn.databayt.org/catalog/sd/g6/math/structure.json
../catalog/sd/g6/math/c1/l3/qbank.json    ==  cdn.databayt.org/catalog/sd/g6/math/c1/l3/qbank.json

<curriculum>/<grade>/<subject>/
  structure.json                 # id، title {ar,en}، المصدر، الفصول c<N> ← الدروس l<N>
  qbank.json · exams.json        # مستوى المادة
  c<N>/qbank.json · exams.json   # الفصل N (بترتيب الكتاب)
  c<N>/l<N>/…                    # الدرس N داخل الفصل
  textbook.pdf · textbook.md · cover/thumbnail/banner.jpg · pages/<N>.webp

معرّف المادة هو <curriculum>-<grade>-<subject> (sd-g12-biology) — المقاطع الثلاثة نفسها التي في المسار، فلا جدول تحويل بين المجلّد والمعرّف. يعيش السؤال مرة واحدة في مجلّد نطاقه، وتحمل الامتحانات مفردات examType في هوغوورتس (final، midterm، chapter_test، practice، quiz، diagnostic). تعديلات المحتوى تذهب إلى مستودع الكتالوج (ويتحقّق منها هناك pnpm validate)، لا إلى هذا المستودع.

tree.ts — بذرة الكتالوج الوحيدة

prisma/seeds/catalog/tree.ts (seedCatalogTree) يقرأ النسخة المحلية لـsd وgb وcbse وib-dp وcaie-igcse (شجرة us في الكتالوج هي نسخة الدار الأمريكية ولا تُعتمد — يبقى us.ts لمنهج US K-12). لكل مادة يُحدِّث Subject ثم يتبنّى الفصول والدروس بدل إعادة بنائها:

  • تُوجد المادة بمعرّف الكتالوج، وإلا بمعرّفها السابق (scripts/migrate/renames.json في الكتالوج) فيُعاد تسميتها في مكانها — المعرّف الداخلي نفسه واختيارات المدارس نفسها؛ ويحوّل proxy.ts الروابط القديمة بـ308 (src/components/catalog/legacy-slugs.ts)؛
  • تُطابَق الفصول والدروس بمعرّفها السابق (أو c<N>/l<N> الحالي) وتُسمّى بموضعها — فتبقى المعرّفات، وتحتفظ الحصص المباشرة وتقدّم الطلاب بدروسها؛
  • ما لم يعد في الكتالوج يصبح ARCHIVED ببادئة archived-، ولا يُحذف أبداً.

مفاتيح الأصول تشير إلى المسار القانوني catalog/<cur>/<grade>/<subject>/… متى وُجد الملف في assets.lock.json بالكتالوج. تُعاد بناء الأسئلة والامتحانات لكل مادة من الملفات، والنطاق من المجلّد (أسئلة مستوى الفصل تُوزَّع على دروس ذلك الفصل لاختبار التدريب). بلا نسخة محلية لا تفعل البذرة شيئاً ولا تحذف شيئاً، فبذور وقت النشر آمنة. CATALOG_DIR / CATALOG_CURRICULA / CATALOG_CONCURRENCY تضبطها.

المناهج والرموز

المناهج الوطنية تستخدم رمز دولة 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وطني
بكالوريا IBIB-DPدولي
كامبريدج IGCSECAIE-IGCSEدولي
الهندي CBSECBSEدولي

التغطية

كل المناهج الاثني عشر لها مواد. ستة بعمق (فصول ودروس حقيقية) — US، والسودان، وإنجلترا (gb في الكتالوج)، وكامبريدج IGCSE، وIB-DP، والهندي CBSE. أما المناهج العربية الوطنية الستة فتبقى مواد فقط حتى تُؤلَّف بنية كتبها الوزارية.

المنهجالرمزالصفوفموادفصولدروسالمصدرالبذرة
الولايات المتحدةUS1–12~240~750~3,993US-inventoryus
السودانSD1–121358124,144الكتالوج sdsd
إنجلتراGB1–12184363730الكتالوج gbgb
كامبريدج IGCSECAIE-IGCSE1087676الكتالوج caie-igcsecaie-igcse
بكالوريا IBIB-DP1294949الكتالوج ib-dpib
الهندي CBSECBSE1075858الكتالوج cbsecbse
السعوديةSA1–12~132——sa.ts المضمّنsa
مصرEG1–12~140——eg.ts المضمّنeg
الإماراتAE1–12~144——ae.ts المضمّنae
قطرQA1–12~122——qa.ts المضمّنqa
الكويتKW1–12~139——kw.ts المضمّنkw
الأردنJO1–12~150——jo.ts المضمّنjo

المناهج العابرة للحدود العميقة (كامبريدج، IB، CBSE) تغطّي صفّاً تمثيلياً حتى الآن؛ وتوسعتها لكل صفّ آليّة (تأليف مزيد من structure.json في الكتالوج). أما شجرة us في الكتالوج (الدار الأمريكية، 128 مادة) فهي غير مُدمَجة — تتصادم على فضاء المعرّف us-g{N}-*؛ تبقى طبقة مرجعية.

قارئ الكتاب المدرسي — الكتاب صفحاتٍ (2026-09-07)

يفتح المسار /{lang}/subjects/{slug}/textbook الكتاب كما يفتحه تطبيق Books على iOS: الغلاف أولاً (خلفية مصبوغة من لون الغلاف، العنوان والطبعة وعدد الصفحات والوحدات والدروس، زرّا المحتويات وابدأ القراءة، وورقة عن الكتاب)، ثم صفحة محتويات مصمَّمة تحمل أرقام صفحات الكتاب المطبوعة، ثم النص شاشةً لكل صفحة. كل وحدة تدفّق أعمدة CSS عرضُ عموده عرضُ الشاشة، فتصبح أعمدة المتصفح صفحات؛ تنزلق الصفحات، ويقلّبها النقر على الحافة والسحب ومفاتيح الأسهم (معكوسة للعربية)، والنقر في الوسط يخفي الأدوات، والعدّاد يقرأ «N من T» (أو N وحده عند إخفاء الأدوات). الزر الدائري يفتح قائمة القراءة: المحتويات — ٪، البحث في الكتاب (يطوي التشكيل، مقتطفات برقم الصفحة المطبوع، وإبراز النتيجة في موضعها)، السمات والإعدادات (الأصلي / ورقي / هادئ / ليلي، ثمانية أو روبِك، ستة أحجام، ثلاثة تباعدات)، المشاركة، PDF، الصفحات الأصلية (صور pages/<N>.webp شاشةً لكل صفحة) والإشارات المرجعية. الأرقام المطبوعة تقوم على إزاحة «المطبوع → PDF» تؤخذ من أول مصدر يملكها: pageOffset الذي يكتبه المؤلف في structure.json، أو ترقيم الصفحات الذي تركه OCR (الفيزياء: PDF − 8)، أو تصويت عناوين البنية نفسها حين تُعثَر عليها في النص (ترقيم الأحياء حطام OCR، وعناوينه تصوّت 8). وصفحات الوحدات والدروس من ملف التأليف structure.json المنشور بجانب التوأم (catalog/textbooks/<slug>/structure.json — الفيزياء والأحياء حتى الآن؛ بقية الكتب تعود إلى مطابقة الأسماء). الصفحة الأولى من المسح هي شاشة الغلاف لا صفحة نص. الموضع والسمة وإعدادات النص تُحفظ في المتصفح. الملفات: school-dashboard/listings/subjects/textbook/ (spine.ts، engine.ts، book.tsx، sheets.tsx، cover.tsx، toc.tsx بجانب parse.ts وarticle.tsx)؛ الاختبارات في src/tests/school-dashboard/listings/subjects/.

المحتوى الحقيقي — السودان (كل الصفوف)

بنوك أسئلة السودان وامتحاناته تأتي مع بذرة الشجرة: 7,395 سؤالاً فريداً و1,533 امتحاناً عبر الصفوف 1–12، وكل سؤال محفوظ في مجلّد فصله/درسه في الكتالوج (قواعد تحليل المعرّفات التي كانت في sd-content.ts وضعته مرة واحدة أثناء ترحيل الكتالوج). وأُزيل هناك 3,754 سؤالاً قالبياً حشواً. الصفوف موسومة ["sd", "<grade>", "<subject>"]؛ ويتخطّى content.ts المحتوى الاصطناعي لأي مادة لها أسئلة موسومة "sd".

دروس السودان بلا صفوف 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–531 (89%)4 (11%)
6–98 (40%)12 (60%)
10–1214 (29%)34 (71%)

الأولويات: تمريرة OCR (Tesseract عربي) على الملفات الممسوحة، ثم إدخال يدوي للصفوف 10–12 عبر طبقة المحتوى / تدفّق الاقتراح.

خريطة الملفات

../catalog/                          # github.com/databayt/catalog — المحتوى (مرآة شبكة التوزيع)
prisma/
  models/catalog.prisma              # Subject, Chapter, Lesson + shared enums
  seeds/catalog/
    tree.ts                          # بذرة الكتالوج: sd · gb · cbse · ib-dp · caie-igcse
    engine.ts                        # seedSubjectsOnly (المناهج الوطنية الضحلة)
    registry.ts                      # 12 Curriculum records + curriculumId backfill
    index.ts                         # seedCatalog + seedFullCatalog
    us.ts                            # US (240, us-inventory.json)
    {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
src/components/catalog/
  catalog-key.ts                     # باني مفاتيح CDN (مطابق لـ @databayt/catalog)
  legacy-slugs.ts                    # معرّف المادة السابق ← معرّف الكتالوج (تحويل في proxy)
  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
المستوىموادمجموعاتمواضيعفيديوهاتمواردالصفوف
Elementary18493468,13017,233K-5
Middle21732765,49010,7546-8
High23793648,66915,0889-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 هو مفتاح الربط القانوني.

الصفالمستوىموادفصولدروسملاحظات
1Elementary417173العربية، الإنجليزية (SMILE Starter 1)، التربية الإسلامية، الرياضيات — سلسلة 2020
2Elementary422131المجموعة نفسها، SMILE Starter 2
3Elementary636176+ الجغرافيا، العلوم (سلسلة 2020، حُدِّث 2026-09)؛ الإنجليزية = كتاب التلميذ SMILE Book 1
4Elementary840232+ التربية الفنية، تكنولوجيا المعلومات، التاريخ، العلوم (أُضيف 2026-09)؛ ملف الرياضيات أُضيف 2026-09
5Elementary1466406+ ملبسنا، المورد، الأرض بيئة الحياة، الاقتصاد المنزلي، التقنية
6Elementary944176+ التربية الفنية، العلوم (أُضيفا 2026-09)؛ الإنجليزية ما زالت كتاباً خاطئاً (SMILE Book 4 غير متاح)
7Middle967309المجموعة كاملة بما فيها SMILE Book 7 (2024)
8Middle1066361المجموعة كاملة؛ الجغرافيا استُبدلت والتربية الوطنية أُضيفت 2026-09؛ التربية الفنية غير متاحة
9Middle9106668طبعات 2024 النهائية، SMILE Book 9
10Secondary18110488المنهج القومي الجديد (المركز القومي للمناهج 2025) — انظر أدناه
11Secondary19119382المنهج القديم، كل المسارات (الطبعات الجديدة متوقعة 2026/27 ولم تُنشر بعد)
12Secondary25192870سنة الشهادة — كل المسارات؛ 13 بنية أُعيد بناؤها من فهارس الكتب و5 مواد أُضيفت 2026-09

انتقل الصف الأول الثانوي إلى المنهج القومي السوداني الجديد في 2025/26 (طبعات أولى من المركز القومي للمناهج مؤرَّخة 2025). منذ 2026-09-04 يحمل sd/g10/ في الكتالوج تلك المجموعة: أربع مواد جديدة (التربية الوطنية، ريادة الأعمال، الفيزياء، وكتاب اللغة العربية الموحَّد الذي يحلّ محلّ كتب المطالعة/البلاغة/النحو القديمة)، وثماني مواد استُبدلت في مكانها (الأحياء، الكيمياء، التاريخ، الرياضيات ط3، العلوم الهندسية، التربية الإسلامية — كانت الدراسات الإسلامية، التربية الفنية — كانت الفنون والتصميم، وتكنولوجيا المعلومات والاتصالات — كانت علوم الحاسوب). الطبعات القديمة أُرشفت قبل الانتقال إلى مستودع الكتالوج (تبقى في الأرشيف المحلي السابق، لا في الكتالوج). صفوف قاعدة البيانات الخاصة بها (sd-g10-literature، sd-g10-rhetoric، sd-g10-arabic-advanced) تبقى منشورة حتى تُؤرشَف بقرار. البنى مأخوذة من جداول محتويات الكتب نفسها (structure.json، source.basis: official-toc-2026-09)؛ السجل الكامل في docs/history/sd-textbook-audit.md بالكتالوج.

ولحق الصف الحادي عشر في 2026-09-05: أُعيد بناء بناه القديمة الثماني عشرة (وحدات عامة بلا دروس تقريباً) من جداول محتويات الكتب نفسها (19 مادة، 122 وحدة، 511 درساً)، وصار لكل كتاب في الصفين الحادي عشر والثاني عشر توأمٌ بصيغة Markdown بجوار ملف PDF على شبكة التوزيع (catalog/sd/<grade>/<subject>/textbook.md، تُنتجه الكلمة المفتاحية textbook: MarkItDown ثم إصلاح اتجاه العربية ثم OCR احتياطي بـ tesseract، مع درجة جودة A–C في ترويسة الملف).

مسارات الثانوي (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)

ثغرات معروفة (2026-09-04): إنجليزية الصف 3 يُفترض أن تكون SMILE Starter 3 وإنجليزية الصف 6 SMILE Book 4 (كلاهما غير متاح على الشبكة)، التربية الفنية للصف 8 غير متاحة، كتب المنهج الجديد للصف الأول الثانوي في القرآن والفرنسية والعلوم الأسرية والعلوم العسكرية والإنجليزية والتربية المسيحية لم تُنشر بعد، والمواد الإحدى عشرة التي استُبدلت كتبها ما زالت تحمل بنوك أسئلة المنهج القديم على مستوى المادة (إعادة التأليف معلَّقة).

إضافة منهج وطني

المنهج الوطني الجديد (مثل مصر EG) يُؤلَّف في مستودع الكتالوج ثم يُعتمد هنا:

  1. في github.com/databayt/catalog: أضف eg إلى vocab/curricula.json وألِّف eg/curriculum.json وeg/g{N}/<subject>/structure.json (pnpm validate).
  2. أضف eg: { country: "EG", code: "EG", sortBase: … } إلى CURRICULA في prisma/seeds/catalog/tree.ts (ومدخلاً في single.ts إن أردت).
  3. أضف EG → "EG" إلى inferCurriculum() وCOUNTRY_CURRICULUM (src/lib/timetable-reference.ts).
  4. أضف الأسماء العربية إلى SUBJECT_CONCEPT_BY_NAME في concepts-data.ts.
  5. (اختياري) أضف مدخل EG إلى CURRICULUM_ACADEMIC_CONFIG إن اختلف عن الافتراضي العربي المشترك 6+3+3.
  6. شغّل 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. بذرة الكتالوج (tree.ts) ترث هذا؛ وللمناهج مواد-فقط، تأتي صور الفصل/الدرس مجاناً بمجرّد توفّر العمق.

النسخ (Variants)

النسخةالعرضالجودةالاستخدام
-sm.webp200px95مصغّرات الشبكة
-md.webp600px97صور البطاقات
-lg.webp1200px100بانرات البطل
-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)assets push بالكتالوجالسودان + علوم CBSE ص10 — مثبّتة في assets.lock.json بالكتالوج
مصغّرات الدروس / الفصولlesson-covers.tsUS فقط — تُسحَب من 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 …)المحتوى
fullseedFullCatalog — كل المناهج الـ12 → registry → صور المفاهيم (قاعدة جديدة)
catalogالافتراضي السريع: US (~240) + السودان الكامل + registry
us / sdUS (الجرد) / السودان من مستودع الكتالوج (tree.ts)
gb / cbse / caie-igcse / ib / catalog-treeالمناهج العميقة من مستودع الكتالوج (tree.ts؛ catalog-tree = الكل)
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            # British — deep, from the catalog repo
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
cd ../catalog && pnpm assets push --apply  # textbook PDFs / art → CDN (catalog repo)

التكرارية (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 يُنسَب إليه الفضل هنا مع الشكر؛ وهو لم يعد اعتمادية وقت تشغيل.

اطّلع أيضاً

  • CDN & Assets
  • Library
  • Multi-Tenancy
تعدّد المستأجرينالعمل دون اتصال

On This Page

نظرة عامةالحالة باختصارالمعماريةنماذج الفهرس العامنماذج الجسر (تحمل schoolId)مرايا المدرسة (مفتاح أجنبي للفهرس → catalogXxxId)التعدادات (Enums)تفصيل نماذج المحتوىالتهيئة (Provisioning)inferCurriculum + تراجع findSubjectsensureSubjectSelections — إصلاح ذاتي كسولطبيب التهيئة — repairProvisioningالبنية الأكاديمية لكل منهج (الثغرة المُصلَحة)الاقتراحات والموافقةالاقتراح (جانب المدرسة)طابور المُشغِّلالموافقة → الفهرس فقطمُثبَّت في مُنتقي المدرسةالرؤية والمحتوى المدفوعالموافقة تنشر الآن (الثغرة المُصلَحة)الفيديوهات المدفوعة (موصولة)الاختبارات / الأسئلة المدفوعة (مؤجّل)إدارة أعلام المُشغِّلضوابط جانب المدرسةهندسة المنهجمستودع الكتالوج — databayt/catalogtree.ts — بذرة الكتالوج الوحيدةالمناهج والرموزالتغطيةقارئ الكتاب المدرسي — الكتاب صفحاتٍ (2026-09-07)المحتوى الحقيقي — السودان (كل الصفوف)المحتوى المُولَّدجودة استخراج جدول المحتوياتخريطة الملفاتمنهج الولايات المتحدةمنهج السودانخطّ معالجة البياناتإضافة منهج وطنيالمفاهيم (Concepts)المفاهيم الـ23مصدر واحد للحقيقة — concepts-data.tsأحواض تدوير الفصولالنسخ (Variants)CDN والأصولجدول ثغرات الأصول الصادقالعملياتأوامر البذرالتكرارية (Idempotency)مزامنة قاعدة البيانات الحيّة المؤجّلة للنشرلا تشغّل البذرة الكاملة قطّالأصول (Origins)اطّلع أيضاً

من تطوير Databayt ·

مرحباً بك في بالقلم.

رحلة عظيمة على وشك أن تبدأ.