Control Panel Specifications
Lead Capture (Download Modal)
Visitor-side gated download modal. The 5 lead fields + 2 consent checkboxes. DownloadLead schema (legal 7-year retention) + auto-captured UTM / language / source.
لمّا الزائر يضغط زرّ Download على Founder File مسوَّر، هذا المودال يفتح ويجمع البيانات اللي تحتاجها المنصّة لـ (أ) إرسال الـ PDF بالبريد، (ب) تأهيل العميل في CRM، (ج) احترام الموافقة على التسويق + المشاركة مع الراعي. كل حقل أدناه يُنتج عمود في صفّ `DownloadLead` اللي يُنشَأ عند الإرسال.
الحقول المعروضة للزائر (التقاط العميل)
الاسم الكامل
- الغرض — تخصيص إيميل التسليم وسجلّ الـ CRM. يُستخدم كاسم الـ `To:` لمّا الـ PDF يُرسَل وكاسم العرض في قوائم العملاء.
- نوع DB — `DownloadLead.FullName` · NVARCHAR(120) NOT NULL · 2–120 حرف.
- التحقّق — مقصوص، غير فاضي، بدون URLs (حماية spam: يُرفض لو يطابق `/https?:\/\//`). اختبارات QC: ادخل حرف واحد → يُرفض؛ الصق URL → يُرفض؛ ادخل " John Doe " → يُقصّ ويُقبَل كـ "John Doe".
البريد الإلكتروني
- الغرض — أهم حقل — يُستخدم لتسليم الـ PDF ولإزالة تكرار العملاء عبر الملفات. وهو المفتاح الأساسي للاشتراك في النشرة لاحقاً.
- نوع DB — `DownloadLead.Email` · NVARCHAR(254) NOT NULL · مفهرس لاستعلامات إزالة التكرار · الحد الأقصى لـ RFC 5321.
- التحقّق — Regex `^[^\s@]+@[^\s@]+\.[^\s@]+$` · يُحوَّل lowercase عند الحفظ · فحص MX-record على الدومين (async، غير حاجب). دومينات البريد المؤقّت تُرفض عبر blocklist يُحدَّث أسبوعياً. اختبارات QC: `test@test` → يُرفض (لا TLD)؛ `john@10minutemail.com` → يُرفض (قائمة مؤقّت).
الدولة
- الغرض — تقسيم جغرافي لتقارير التحرير + الراعي. يُغذّي meta GEO لمّا هذا العميل يزور صفحة profile لاحقاً (إثراء analytics).
- نوع DB — `DownloadLead.Country` · CHAR(2) NOT NULL · ISO-3166-1 alpha-2.
- التحقّق — dropdown اختيار واحد من قائمة `Country` ثابتة (~250 مدخل). الاختيار الافتراضي = يُستخرَج من الـ IP عبر header الـ CDN `cf-ipcountry` / `x-vercel-ip-country` (الزائر يقدر يبدّله). اختبار QC: افتح المودال من IP بالقاهرة → "Egypt" مُختار مسبقاً؛ يقدر يبدّل لأي دولة.
الدور
- الغرض — تأهيل العميل — يميّز المؤسس عن المستثمر عن الطالب. يُغذّي الـ onboarding المُجزَّأ اللي يستلمه العميل بعد التحميل.
- نوع DB — `DownloadLead.Role` · NVARCHAR(30) NOT NULL · CHECK Role IN ('founder','team-member','investor','accelerator','corporate','student','consultant','other').
- التحقّق — dropdown اختيار واحد. إلزامي. اختبار QC: اترك placeholder الافتراضي → الإرسال يفشل بـ "Role is required".
الاهتمام الأساسي
- الغرض — لماذا الزائر على المنصّة. يُغذّي محرّك التوصيات Cross-module — اختيار "Founder Profile" يوجّه الزائر لقمع إنشاء الملف بعد التحميل.
- نوع DB — `DownloadLead.PrimaryInterest` · NVARCHAR(30) NOT NULL · CHECK PrimaryInterest IN ('reading-files','founder-profile','startup-showcase','sponsorship','opportunities','other').
- التحقّق — dropdown اختيار واحد. إلزامي. اختبار QC: كل خيار يُطلق CTA مختلف على شاشة الشكر بعد التحميل.
مربعات الموافقة (قانوني)
موافقة النشرة
- الغرض — موافقة صريحة على استلام موارد المؤسسين + الفرص من StartupHub.today عبر البريد. قانون حماية البيانات (GDPR / المصري) يتطلّب أن يكون **غير محدّد** افتراضياً.
- نوع DB — `DownloadLead.ConsentNewsletter` · BIT NOT NULL · DEFAULT 0 (false).
- التحقّق — اختياري (التحميل ينجح في الحالتين). لمّا 1، العميل ينضم لقائمة النشرة ويُولَّد unsubscribe token. اختبار QC: ارسل بدون تأشير → العميل يستلم إيميل الـ PDF فقط، لا إيميلات تسويقية. ارسل بتأشير → العميل يستلم إيميل الـ PDF + أول سلسلة onboarding للنشرة.
موافقة الراعي (مشروطة)
- الغرض — لمّا الملف عنده راعي، هذا المربع يتحكّم فيما إذا كان العميل يُشارك مع الراعي. كتلة الراعي فوق مربعات الموافقة تُظهر مَن الراعي عشان الزائر يتّخذ قرار مستنير.
- نوع DB — `DownloadLead.ConsentSponsor` · BIT NOT NULL · DEFAULT 0 (false). NULL غير مسموح حتى لو الملف بدون راعي — يُخزَّن 0 في تلك الحالة للاتساق.
- التحقّق — المربع يظهر فقط لمّا `FounderFile.sponsorId IS NOT NULL`. اختياري. لمّا 1، صفّ العميل يُدفع إلى webhook الـ CRM للراعي خلال 5 دقائق. اختبارات QC: حمّل ملف بدون راعي → المربع غائب و ConsentSponsor=0؛ حمّل ملف بـ راعي بدون تأشير → ConsentSponsor=0 ولا webhook؛ حمّل ملف بـ راعي مع تأشير → ConsentSponsor=1 و webhook الراعي يُستدعى.
حقول تُلتقَط تلقائياً (بدون إدخال من الزائر)
- founderFileId
- UUID FK لـ FounderFile. يُملأ من سياق المودال (أي ملف ضغط عليه الزائر). غير قابل للتعديل.
- language
- CHAR(2) — اللغة اللي كان الزائر يتصفّح بيها (`en` / `ar` / `fr`). تُحدّد أي PDF يُرسَل.
- source
- NVARCHAR(20) · CHECK source IN ('listing','details','reader','direct'). من أي سطح ضغط الزائر Download. Listing = CTA spotlight على /founder-files؛ Details = CTA صفحة التفاصيل؛ Direct = وصل عبر deep link بـ `?download=1` (حملات CRM).
- utmSource / utmMedium / utmCampaign?
- NVARCHAR(80) NULL لكل واحد. تُلتقَط من query string الـ URL عند فتح المودال. تُستخدم في attribution التسويق.
- downloadedAt
- DATETIME2 NOT NULL · GETUTCDATE() عند الإدخال.
- deliveredAt?
- DATETIME2 NULL · يُختم لمّا إيميل الـ PDF يُرسَل فعلاً (webhook SendGrid / SES). NULL حتى ينجح التسليم.
- pdfVersionDelivered
- TINYINT NOT NULL · أي `vN.pdf` استلم العميل. يُلتقَط وقت التسليم. يسمح لسجلّ التدقيق بإعادة إنتاج البايتات اللي استلمها العميل بالظبط.
حقول اختيارية للمرحلة 2
هذه موجودة في الموديل لكن مودال المرحلة 1 لا يجمعها. الـ progressive profiling في المرحلة 2 سيكشفها تدريجياً حسب الـ Role (مثلاً "Founder" → يعرض companyName + startupStage عند التحميل الثاني).
- companyName?
- NVARCHAR(120) NULL.
- linkedinUrl?
- NVARCHAR(500) NULL · فحص regex.
- startupStage?
- NVARCHAR(30) NULL · enum (pre-seed/seed/series-a/…).
- industry?
- NVARCHAR(60) NULL · مشترك مع تصنيف الصناعات في Articles.
- websiteUrl?
- NVARCHAR(500) NULL.
معاينة الفورم — مودال التحميل (ملف بـ راعي)
هذا هو المودال الفعلي اللي يراه الزائر لمّا يضغط Download على Founder File مسوَّر وعنده راعي. أعرض النسخة بـ راعي لأنها تتضمّن مربعَي الموافقة — النسخة بدون راعي تخفي فقط صفّ موافقة الراعي.
