تخطَّ إلى المحتوى الرئيسي

Control Panel Specifications

Data Model (form)

File-wide vs per-language fields. Trilingual publish gate. Every field with type, validation range, source (auto vs manual).

مكتمل

مرجع مختصر لكل حقل على سجلّ Founder File، مأخوذ مباشرة من الموديل الإنتاجي (`core/models/founder-file.model.ts`). ثلاث مجموعات: ميتاداتا الملف ككل (صفّ في `FounderFile`)، محتوى لكل لغة (1–3 صفوف في `FounderFileLang`)، والجداول الوسيطة / المرجعية. للمواصفات الكاملة بأنواع SQL وقواعد التحقّق ورسم الفورم، شوف Create spec.

حقول الملف ككل (صفّ واحد في `FounderFile`)

id
مفتاح أساسي للنظام (UUID). لا يُعدَّل من المستخدم أبداً.
fileNumber
رقم تسلسلي تحريري (INT IDENTITY). يظهر كـ "File #04" على الأغلفة + الكروت. مقفول بعد أول حفظ.
slug
يُولَّد تلقائياً من العنوان EN (kebab-case، ASCII-safe). فريد. يُقفل بعد خروج الحالة من `draft`.
الحالة
Enum (6 حالات): draft · ready-for-review · under-review · published · needs-updates · archived. شوف status-workflow.
الفئة
Enum اختيار واحد (إلزامي). 9 سلاسل تصنيفية: `business-fundamentals` · `funding-investment` · `startup-storytelling` · `growth-gtm` · `financial-literacy` · `legal-compliance` · `product-operations` · `leadership-teams` · `mena-market-insights`. مشترك مع تصنيف Articles.
الموضوعات[]
علاقة N:M مع جدول `Topic` (سلاسل slugs). الحد الأدنى 1، الأقصى 8. الكرت يعرض أول 3، صفحة التفاصيل تعرض كل الموضوعات. مشترك مع Articles.
اللغات المتاحة[]
مصفوفة رموز لغات (`en` / `ar` / `fr`). مُشتقّة من صفوف `FounderFileLang` الموجودة والمكتملة. تُغذّي شارات EN/AR/FR على الكروت + مبدّل اللغة العام.
رابط صورة الغلاف
رابط Azure Blob · 3:4 portrait · حد أدنى 600×800 · حد أقصى 5 MB · JPG/PNG/WebP. ترجع لـ SVG تحريري عند NULL/404.
ogImageUrl؟
صورة اختيارية 1.91:1 للمشاركة الاجتماعية. ترجع لـ `coverImageUrl` عند NULL.
sponsorId؟
FK اختياري لـ `FounderFileSponsor`. ON DELETE SET NULL. إن وُجد، عقد الراعي يجب أن يكون ساري عند الحفظ.
isFeatured
BIT (الافتراضي 0). يتحكّم في شارة "FEATURED" + spotlight section على الـ landing. ملف واحد فقط Featured لكل فئة في الوقت ذاته.
isGated
BIT (الافتراضي 1). لمّا 1، الضغط على Download يفتح مودال الموافقة (التقاط عميل). لمّا 0، PDF stream مباشر.
isDownloadable
BIT (الافتراضي 1). kill-switch رئيسي. لمّا 0، زرّ Download يختفي بالكامل من الصفحة العامة.
وقت القراءة بالدقائق
TINYINT، النطاق 1–120. تقدير تحريري يدوي. يظهر كشارة "{n} دقيقة قراءة" على الكروت + صفّ meta في التفاصيل.
publishedAt
DATETIME2 NULL. يُختم تلقائياً UTC عند أول انتقال لـ `published`. إعادة النشر لا تستبدله.
lastUpdatedAt
DATETIME2 NOT NULL. يقفز لـ GETUTCDATE() مع كل حفظ ناجح (أي حقل).
archivedAt؟ / archivedBy؟
ختم الحذف الناعم + الفاعل. NULL طالما live. يُملآن ذرّياً عند الأرشفة. قابل للاستعادة لمدة 90 يوم.

محتوى لكل لغة (1–3 صفوف في `FounderFileLang`)

PK مركّب = (`FounderFileId`, `Lang`). الملف يملك 1–3 صفوف (EN / AR / FR). بوّابة النشر الثلاثية تتطلّب اكتمال 3 صفوف قبل خروج الحالة من `draft`.

title
NVARCHAR(80) NOT NULL · 10–80 حرف · h1 على صفحة التفاصيل + عنوان كرت القائمة + ذيل breadcrumb + تبويب المتصفّح.
subtitle
NVARCHAR(160) NOT NULL · 30–160 حرف · الـ tagline اللي يظهر مباشرة تحت h1 على hero صفحة التفاصيل (و spotlight section على landing القائمة).
shortDescription
NVARCHAR(300) NOT NULL · 60–300 حرف · ملخّص 1–2 جملة يظهر كفقرة الجسم على كروت القائمة + يُستخدم احتياطياً كـ meta description لمّا `seoDescription` فاضي. ملاحظة: مختلف عن `subtitle` — `subtitle` هو الـ tagline الداخلي، `shortDescription` ملخّص مستوى الكرت.
fullDescription
NVARCHAR(MAX) NOT NULL · 100–600 حرف · نصّ فقط (HTML يُحذف عند الحفظ). يظهر كفقرة "لماذا هذا الملف مهم" على صفحة التفاصيل.
bestForTagline؟
NVARCHAR(120) NULL · 15–120 حرف · tagline تحريري قصير يظهر في كتلة "Best for" على كروت القائمة (مثل "Pre-raise self-audit"). كان hardcoded لكل ملف في i18n dictionary؛ الآن حقل CP حقيقي لكل لغة. الكرت يرجع للـ key القديم `ff.bestFor.<file.id>` لمّا الحقل فاضي.
pdfUrl
NVARCHAR(500) NOT NULL · مسار Azure Blob `founder-files/{slug}/{lang}/v{N}.pdf` · حد أقصى 50 MB · `application/pdf` فقط · مفحوص للفيروسات. آخر 5 نسخ محفوظة لكل لغة. ملاحظة: الموديل حالياً عنده `pdfUrl` واحد على الملف؛ هدف الـ migration per-language. شوف pdf-storage.
fileSizeBytes
BIGINT NOT NULL · يُحسب تلقائياً من الـ PDF المرفوع (على السيرفر). يظهر كشارة "{X} MB" في الـ CP بعد الرفع + (مستقبلاً) زرّ التحميل العام.
pageCount
INT NOT NULL · يُحسب تلقائياً من خطوة فحص الـ PDF. عرض فقط.
seoTitle؟ / seoDescription؟
NVARCHAR(120) NULL / NVARCHAR(300) NULL · override اختياري لـ SEO لكل لغة. لمّا NULL، الصفحة تستخدم `title` + `subtitle` لـ `<title>` + meta description.
ogTitle؟ / ogDescription؟ / ogImageUrl؟ / canonicalUrl؟
overrides Open Graph لكل لغة. كلها اختيارية. ترجع للقيم على مستوى الملف / `seoTitle`.

الجداول المرتبطة

FounderFileTopic (وسيط N:M)
وسيط بين `FounderFile` وجدول `Topic` المشترك. PK مركّب = (FounderFileId, TopicId). الكرت يعرض أول 3 موضوعات؛ التفاصيل تعرض الكل (حد أقصى 8 لكل ملف).
FounderFileLearningPoint (1:N)
صفّ لكل bullet في "What you'll learn" على صفحة التفاصيل. 3–5 صفوف لكل ملف. كل صفّ عنده `TextEn` + `TextAr` + `TextFr` (كل واحد NVARCHAR(100)، كلهم إلزامي). يظهر بالكامل على التفاصيل؛ أول 5 مكرَّرة على spotlight في الـ landing.
FounderFileSponsor (مرجعي)
سجلّ مستقلّ. الحقول: id، name، name_ar، tier (`sponsored-by` / `presented-by` / `in-partnership-with` / `powered-by`)، websiteUrl، logoUrl، shortDescription، shortDescription_ar، contractStart، contractEnd، isActive. مربوط من `FounderFile.sponsorId`.
DownloadLead (1:N · سجلّ قانوني)
صفّ لكل تحميل مسوَّر بالموافقة. يخزّن founderFileId (FK ON DELETE NO ACTION)، fullName، email، country، role (enum بـ 8 قيم)، primaryInterest (enum بـ 6 قيم)، companyName?، linkedinUrl?، startupStage?، industry?، websiteUrl?، language (CHAR(2))، consentNewsletter (BIT)، consentSponsor (BIT)، source (`listing`/`details`/`reader`/`direct`)، utmSource?/utmMedium?/utmCampaign?، downloadedAt (DATETIME2). احتفاظ 7 سنوات؛ لا يُحذَف نهائياً من الـ CP.
FounderFileAuditLog (1:N)
صفّ لكل انتقال حالة أو تغيير حقل قابل للتعديل. الحقول: id، founderFileId، occurredAt، actorId (FK→AdminUser)، eventType (`status-transition` / `field-change` / `review-picked-up` / `restored`)، fromValue، toValue، reason؟. تستخدمه شاشة Edit → تبويب History.
FounderFileAnalyticsDay (1:N)
عدّادات يومية مُجمَّعة لكل (ملف، يوم، لغة): views، reads، downloads، shares + 4 عدّادات نقرات CTA. تُغذّي خيارات الترتيب العام "الأكثر تحميلاً" / "الأكثر قراءة" + تقارير قمع العملاء في الـ admin.