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

العمل دون اتصال

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

محرّك عمل دون اتصال بدرجة إنتاجية لـHogwarts — أساس بلا تنازلات (Dexie + OPFS + مزامنة Web Worker + إبطال WebSocket + بصمات محتوى + حزم مقسّمة بالكود)، تسليم مرحلي لكل دور، خارطة طريق أسبوع-بأسبوع، دليل تجريبي.

تستهدف Hogwarts المدارس التي يكون فيها التراجع إلى 2G/3G، والواي-فاي المتقطّع، والانقطاعات الكاملة هو القاعدة — تجارب السودان التجريبية، ومسارات النقل الريفية، وقاعات الامتحانات في الأقبية، والمتقدِّمون على بيانات الجوّال المدفوعة مسبقاً. هذه الصفحة هي الاستراتيجية على مستوى المنصّة لتقديم محرّك عمل دون اتصال بدرجة إنتاجية بأساس بلا تنازلات، ثم طبقات ميزات لكل دور على مراحل فوق هذا الأساس الصلب.

لا نُسلِّم أنصاف حلول. الأساس يُبنى مرّة واحدة ويُبنى بشكل صحيح؛ ميزات كل دور تُطبَّق فوقه على مراحل. لقيود تعدّد المستأجرين والمزامنة راجع تعدّد المستأجرين وقاعدة البيانات. لشكل الملحمة السابق الذي تتّبعه هذه الوثيقة راجع Document Intelligence. لاتفاقيات مفاتيح القاموس راجع الترجمة.

لماذا هذا مهم

تتعامل المنصّة اليوم مع فشل الشبكة بطرق متفرّقة خاصة بكل ميزة. تتراكم نقاط الألم في اللحظات الأكثر أهمية.

اللحظةاليومالتكلفة
تسجيل المعلّم للحضور دون اتصالPOST بمعاملة واحدة؛ الفشل يُظهر toast ويُلغي البياناتفقدان حضور اليوم؛ إعادة الإدخال يدوياً
تسليم الطالب للامتحان على 3G ضعيفالحفظ التلقائي يبقى؛ submitExamSession النهائي لا يبقىتطبيق غرامة التسليم المتأخّر على طلاب بإشارة سيئة
السائق في نفق أثناء المسارتذهب كتابات الصعود داخل التطبيق إلى الخادم مباشرة؛ تفشل بصمتلا يرى أولياء الأمور حدث الصعود؛ تتآكل الثقة
المتقدِّم يرفع نسخة دراسية على 2GS3 PUT، بلا تقسيم، بلا استئنافالتخلّي عن الطلب عند 90% من الرفع
تصحيح المعلّم بالسحب 50 ورقة على الهاتفإجراء خادم واحد لكل سحبة؛ أي فشل يُوقف الطابورإعادة بدء جلسة التصحيح؛ ~30 دقيقة ضائعة
قراءة أي شخص للجدول الدراسي دون اتصالذاكرة SW المؤقتة الديناميكية فقط إذا تمّت الزيارة سابقاًذاكرة مؤقتة باردة = شاشة فارغة + صفحة الفولباك دون اتصال

تخدم المنصّة اليوم ~12 ألف طالب عبر 8 مدارس نشطة؛ تستهدف تجارب السودان والمملكة دفع هذا الرقم إلى ~40 ألف خلال 12 شهراً. متوسّط شركات الجوّال في السودان 1.2 ميغابت/ثانية تنزيل مع انقطاعات متكرّرة؛ مدارس المملكة الداخلية ترى تشبّعاً في واي-فاي قاعة الامتحانات خلال الأسابيع الحرجة. محرّك عمل دون اتصال صلب هو ما يفتح الباب لكلا النشرين.

كيف يبدو النجاح

هذه هي المقاييس الرئيسية التي يلتزم بها الفريق عند "اجتياز بوّابة الأساس" + "تسليم كل ملاحم الأدوار".

  1. صفر فقدان للحضور عند الانقطاع — يُسجَّل 100% من دفعات حضور المعلّمين خلال 60 ثانية من إعادة الاتصال.
  2. صفر فقدان لتسليم الامتحان — تكتمل 100% من استدعاءات submitExamSession في النهاية، حتى بعد إعادة تشغيل المتصفّح.
  3. تحميل دون اتصال أقل من ثانية للجدول الدراسي والإعلانات بعد الزيارة الأولى (عرض فوري من Dexie live query).
  4. معدّل تثبيت PWA ≥ 35% على الهاتف للمعلّمين النشطين خلال 4 أسابيع من الإطلاق.
  5. معدّل نجاح المزامنة ≥ 99.9% عبر جميع التعديلات المُطابورة (نافذة متدحرجة 7 أيام).
  6. استخدام التخزين ≤ 25 ميغابايت لكل مستخدم نشط في المتوسّط (هامش حصّة Safari iOS).
  7. زيادة حجم الحزمة — حزمة الأساس ≤ 35 كيلوبايت مضغوطة gzip؛ حزم كل ملحمة تُحمَّل بكسل.
  8. ميزانية الخيط الرئيسي — تفريغ المزامنة لا يحجب الخيط الرئيسي > 16 مللي ثانية (العمل ينتقل إلى Web Worker).
  9. إبطال الذاكرة المؤقتة من أوّل بايت — push WebSocket من الخادم يُبطل ذاكرة العميل خلال 200 مللي ثانية من الكتابة البعيدة.

مبادئ هندسية (بلا تنازلات)

هذه غير قابلة للتفاوض. كل مبدأ يُقود خيارات معمارية ملموسة.

  1. المزامنة لا تحجب الخيط الرئيسي أبداً. كل إعادة التشغيل، التشفير، الضغط، فحص التعارض يعمل في Web Worker مخصّص (sync-worker.ts). الخيط الرئيسي محجوز للواجهة.
  2. الكتل الكبيرة تذهب إلى OPFS، ليس IDB. المرفقات، أوراق الامتحان، الصوت المُسجَّل، الصور كلّها تستخدم Origin Private File System. IDB يحفظ فقط الحمولات المُهيكَلة وmetadata الأجزاء.
  3. كل تعديل دليل-عبث. كل envelope يحمل بصمة محتوى SHA-256. يُتحقَّق من الخادم عند الاستقبال؛ يُتحقَّق من العميل عند إعادة التشغيل.
  4. إبطال ذاكرة في الوقت الحقيقي. WebSocket يدفع أحداث إبطال من الخادم لكل العملاء المتّصلين. لا قِدَم صامت؛ لا انتظار refetchOnReconnect.
  5. تقسيم الكود لكل ملحمة. حزمة الأساس ≤ 35 كيلوبايت. كل ملحمة دور تُسلِّم حزمتها المحمَّلة بكسل. المدارس الجديدة تُنزّل فقط الأساس؛ كود كل ميزة يُحمَّل عند أوّل استخدام.
  6. القياس عن بُعد من اليوم 1. كل انتقال حالة (enqueue، flush، conflict، error، retry) يُصدر حدثاً مُهيكَلاً إلى Sentry و OfflineSyncLog. لا إعادة تطبيق لاحقة.
  7. علم الميزة يُتحكَّم في الميزات الجديدة، ليس الأساس. School.offlineMode يُبدّل الميزات؛ مكتبة الأساس مُحمَّلة دائماً للـPWA المُثبَّتة. الأساس لا يمكن تبنّيه جزئياً.
  8. ميزانيات الأداء بوابات CI. size-limit، Lighthouse CI، ميزانيات Sentry مخصّصة. CI يفشل عند التراجع؛ لا "نُصلحه في متابعة".
  9. حلّ التعارضات صريح دائماً للمستخدم. الكتابات الفوقية الصامتة ممنوعة. كل تعديل رفضه الخادم يُظهر واجهة تعارض مُهيكَلة مع البيانات على كلا الجانبين + خيار قرار.
  10. مكتبة الأساس مُصدَّرة بإصدارات. ترحيلات المخطّط بين الإصدارات مُختبَرة. لا نكسر PWA قديمة في الحقل بدون مسار ترقية موثّق.
  11. شروط الاستخدام قابلة للملاحظة. useNetworkStatus، useStorageEstimate، usePendingSyncCount خطّافات عامة. أي سطح ميزة يمكنه إظهار ما يحدث للمستخدم.
  12. أمان متعدّد المستأجرين معماري، ليس إجرائياً. اسم IDB DB معزول بـschoolId؛ SW يقرأ المستأجر من كوكي الجلسة وقت الطلب، ليس من global؛ التسرّب عبر المستأجرين مستحيل بحكم البناء.

قرارات معمارية للأساس

كل تنازل اعتبرناه، ولماذا رفضناه.

القرارنصف-الحلّالخيار بلا تنازلات
غلاف IDBindexedDB خام (~0 KB)dexie@^4 (~21 KB) مع dexie-react-hooks لـlive queries، نسخ المخطّط، المعاملات
موقع محرّك المزامنةالخيط الرئيسيsync-worker.ts Web Worker — إعادة التشغيل، التشفير، الضغط، فحوص النزاهة كلّها خارج الرئيسي
تخزين الكتلحقل blob في IDBOPFS لأي حمولة > 100 KB؛ IDB يحفظ فقط إشارة handle لملف OPFS
إبطال الذاكرة المؤقتةrefetchOnReconnectبثّ خادم WebSocket cache-invalidate؛ إبطال أقل من 200 مللي ثانية
نزاهة التعديللا شيءبصمة contentHash SHA-256 على كل envelope؛ يُتحقَّق منها عند الاستقبال وإعادة التشغيل
ضغط الحمولةلا شيءMessagePack + LZ4 (عبر @msgpack/msgpack + WASM lz4-wasm) للحمولات > 1 KB
طوبولوجيا استقبال المزامنةنقطة نهاية واحدةشظايا لكل مجال: /api/offline/sync/{attendance,exam,transport,messaging,application} بالتوازي
استراتيجية الحزمةحزمة أساس واحدةأساس (≤ 35 KB) + حزم كل ملحمة بكسل عبر حدود dynamic()
آلة الحالةenum statusآلة حالة منتهية بنمط XState: idle → pending → in_flight → synced ∣ conflicted ∣ failed
حلّ التعارضاتLWW من الخادمالخادم-مرجع + واجهة دمج مُهيكَلة (<ConflictResolver>) للتعارضات القابلة للحلّ
دور service workerذاكرة + stub مزامنةناقل رسائل بين التبويب وعامل المزامنة؛ يُشغّل إعادة التشغيل عند عدم وجود تبويب (background sync)
التسليم في الوقت الحقيقيلا شيء / استطلاع SSEإعادة استخدام socket.io-client@4.8.1 الحالي للإبطال منخفض الكمون + push notifications
أمان المستأجركوكي الجلسة في طبقة API+ Dexie معزول بـschoolId؛ SW يقرأ المستأجر لكل طلب من كوكي الجلسة
القياس عن بُعديُضاف لاحقاًOfflineSyncLog + breadcrumbs في Sentry + لوحة <OfflineDevtools> تُسلَّم مع EPIC-0

ما شُحن في 2026-08-29/30 — صندوق الصادر، وسياسة

أول شريحة من هذه الخطة صارت حيّة، محصورةً في دروس لوموس: صندوق صادر (src/lib/offline/outbox.ts، IndexedDB) يصفّ موضعَ المشاهدة والإكمال وإجابات الاختبار ونصّ الواجب إلى POST /api/offline/sync الذي يعمل بلا تكرار لكلّ عنصر ويردّ بحكم لكلّ عنصر؛ والمرفوضات تُركن ظاهرةً في /offline مع إعادة المحاولة أو التجاهل — فصفّ «ظهور المزامنة المعلّقة» أعلاه صار ✓ للدروس وما يزال ✗ لبقية الإجراءات. شريحة «التنزيل للمشاهدة دون اتصال» (الفيديو والمواد على الجهاز) شُحنت في 08-29 وسُحبت في 08-30 بقرار المدرسة: الفيديوهات والمواد تُشاهَد داخل التطبيق ولا تُنسخ إلى جهاز، والإصدار 2 من IndexedDB يحذف أيّ وسائط خزّنها جهاز. عامل الخدمة (public/service-worker.js، الإصدار 2) يُثبَّت فعلًا الآن.

النطاق

10 ملاحم، ~42 قصة، 231 نقطة قصّة. EPIC-0 هو الأساس الذي تعتمد عليه كل ملحمة أخرى ويجب أن يجتاز بوّابة الأساس (انظر أدناه) قبل بدء أي عمل لكل دور.

الملحمةالهدفالنقاط
EPIC-0 — الأساستخزين Dexie، كتل OPFS، محرّك مزامنة Web Worker، ناقل رسائل SW، إبطال WebSocket، بصمات محتوى، قياس، devtools62
EPIC-1 — قابلية تثبيت PWAتخزين مسبق لهيكل التطبيق، نافذة التثبيت، تدفّق التحديث، واجهة ترويج التثبيت15
EPIC-2 — ذاكرة قراءة ذكيةالجدول الدراسي، الإعلانات، الملف الشخصي، القائمة عبر Dexie live queries؛ إبطال ذاكرة WebSocket24
EPIC-3 — المعلّم دون اتصالالحضور بالجملة، مسح QR، طابور التصحيح بالسحب على الهاتف، واجهة تعارض مُهيكَلة32
EPIC-4 — الطالب دون اتصالطابور تسليم الامتحان، استمرارية سجلّ المراقَبة، تخزين الورقة مسبقاً، تكامل lockdown22
EPIC-5 — السائق دون اتصالصعود الرحلة للنقل بمحاكاة نمط geofence؛ تحميل مسبق للمسار + القائمة عبر OPFS15
EPIC-6 — المتقدِّم دون اتصالرفع مرفقات قابل للاستئناف عبر OPFS، مسوّدة طلب في Dexie، تسليم مُطابور18
EPIC-7 — طابور المراسلة الصادرةإرسال متفائل، طابور مرفقات، سياسة جسر واتساب، مزامنة استلام في الوقت الحقيقي16
EPIC-8 — قابلية المراقبةتحليلات OfflineSyncLog، breadcrumbs في Sentry، بلاطة لوحة المطوّر، لوحة SLI لكل مدرسة14
EPIC-9 — الوثائق، التعريب، إعداد المسؤولهذه الوثيقة + النسخة الإنجليزية، ~30 مفتاح قاموس، علم School.offlineMode، مجموعة Playwright E2E13

السياق المعماري

تمدّ الخطّة سقالة المرونة القائمة بدلاً من استبدالها. تطبيقان معياريان ذهبيان موجودان بالفعل في الكود — خطّاف الحفظ التلقائي للامتحان، ومتعقّب GPS لـgeofence — وتعمّم ملحمة الأساس كليهما في مكتبة قابلة لإعادة الاستخدام بدرجة إنتاجية.

المكوّنالموقعالحالة الحالية
Service workerpublic/service-worker.jsذاكرة الشبكة-أولاً للـHTML/API؛ syncOfflineForms() في السطر 160 هو stub
تسجيل SWsrc/components/providers/service-worker-provider.tsxتسجيل window.load للإنتاج فقط
صفحة الفولباك دون اتصالsrc/components/offline/content.tsxساكنة؛ لا مؤشّر آخر مزامنة
Manifestsrc/app/manifest.tsقابل للتثبيت؛ اختصارات لـ/attendance + /attendance/qr-code
useOfflineQueue<T>src/components/school-dashboard/attendance/shared/hooks.ts:541في الذاكرة فقط؛ يُفقَد عند إعادة التحميل — للتقاعد
LocationQueue (IDB)src/components/school-dashboard/attendance/geofencee/geo-tracker.tsx:59طابور IndexedDB كامل، تفريغ دفعي — مُعمَّم في عنصر أوّلي مشترك
الحفظ التلقائي للامتحانsrc/components/school-dashboard/exams/take/hooks/use-auto-save.tsdebounce + localStorage + exp-backoff — مُعمَّم في عنصر أوّلي مشترك
مسوّدة الطلبsrc/components/school-marketing/application/application-context.tsx:412جلسة خادم + localStorage لكل حملة + لكل مستخدم — مُرحَّل إلى Dexie
مسوّدة الإعدادsrc/components/onboarding/util.ts:291DRAFT_STORAGE_KEY = "onboarding_draft_" — مُرحَّل إلى Dexie
استراتيجيات الشبكةsrc/lib/performance-optimization.tsslow-2g/2g/3g/4g/saveData/rtt — مُوصَّل في useNetworkStatus
ذاكرة LRU للخادمsrc/lib/cache/exam-cache.tsذاكرة TTL لحدود الدرجات، علامة المدرسة
Circuit breakersrc/lib/circuit-breaker.ts5 إخفاقات → 30 ثانية تهدئة لـDB
@tanstack/react-querypackage.jsonمثبّت 5.90.12 — مُوصَّل في EPIC-0
socket.io-clientpackage.jsonمثبّت 4.8.1 — يُستخدم لإبطال ذاكرة WebSocket في EPIC-0
Mobile APIsrc/app/api/mobile/**30+ نقطة نهاية بمصادقة JWT (JSON بحالة snake_case)
خطّاف صعود geofencesrc/app/api/transportation/geofence-boarding/route.tswebhook بمصادقة Bearer-token؛ idempotent؛ ack-and-ignore لرموز عدم التطابق

حقائق المخطّط

  • كل نموذج تجاري لديه schoolId مع @@index([schoolId]). كل كتابة دون اتصال يجب أن تحفظ هذا.
  • KioskLog.syncedToAttendance موجود بالفعل في prisma/models/attendance.prisma:404 — يُضبَط حالياً بشكل متزامن؛ سيُعاد استخدامه كعلم مزامنة مؤجَّلة.
  • نماذج جديدة مطلوبة: OfflineMutation (طابور خادم دائم)، OfflineSyncLog (ذاكرة idempotency، TTL 30 يوماً)، CacheInvalidation (سجلّ بثّ موضوع، TTL 7 أيام).
  • حقل تعدادي جديد School.offlineMode: disabled / limited / full. الافتراضي limited للمدارس الجديدة، full للنشر التجريبي.
  • سلاسل الترجمة تعيش في src/components/internationalization/{school-en,school-ar}.json تحت dictionary.offline.*.

الأنماط

كل ملحمة تتّبع نفس الاتفاقيات.

  • Dexie DB لكل مستأجر. قاعدة Dexie واحدة لكل أصل تُسمّى hogwarts-${schoolId} مع مخطّط بإصدارات. live queries عبر useLiveQuery. الترحيلات مُختبَرة في CI.
  • كتل OPFS. الملفّات > 100 KB تذهب إلى OPFS. IDB يحفظ { opfsPath, size, sha256, mimeType } فقط.
  • عامل المزامنة. sync-worker.ts يُشغّل استعلامات Dexie، ضغط الحمولة، التحقّق من بصمة المحتوى، إعادة تشغيل HTTP. الخيط الرئيسي يُرسل رسائل إليه؛ العامل يبثّ التقدّم.
  • مظروف تعديل من نوع discriminated. { id, schoolId, userId, action, payload, idempotencyKey, contentHash, attempts, lastError, state, createdAt } حيث action هو سلسلة حرفية تُحلّ عبر actionRegistry وقت التفريغ.
  • مفاتيح idempotency. cuid v2 مولَّد من جانب العميل؛ يبقى في الحمولة؛ يفحص الخادم OfflineSyncLog قبل إعادة التطبيق.
  • شظايا مزامنة لكل مجال. /api/offline/sync/{attendance|exam|transport|messaging|application} للمعالجة المتوازية. كل نقطة نهاية تُرسل إلى معالجات الإجراءات لمجالها.
  • إبطال WebSocket. الخادم يبثّ رسائل cache-invalidate على الكتابات. العميل يُطابق ضدّ جداول Dexie النشطة ويُطلق تحديث live-query.
  • خطّافات react رقيقة. كود الميزة يستخدم useOfflineMutation / useLiveQuery / useNetworkStatus / usePendingSyncCount / useStorageEstimate. لا يستورد dexie أو @tanstack/* مباشرة أبداً.
  • التعريب أوّلاً. كل سلسلة دون اتصال تعيش في dictionary.offline.*. لا إنجليزية مدمجة في JSX أو toasts أو إرجاعات الأخطاء.
  • تقسيم الكود لكل ملحمة. كود عدم الاتصال لكل ميزة يعيش في حزم بكسل. حدود import("./feature/offline.ts").

الحالة الحالية مقابل الحالة المستهدفة

القدرةاليومبعد الأساس + ملاحم الأدوار
الحضور بالجملة دون اتصال✗ ضائع✓ مُطابور في Dexie، يُعاد تشغيله بواسطة عامل المزامنة، بصمة محتوى مُتحقَّقة
تسليم الامتحان النهائي دون اتصال✗ يرى المستخدم toast خطأ✓ شاشة كاملة "التسليم مُطابور"، تكتمل عند إعادة الاتصال خلال 30 ثانية
تصحيح بالسحب على الهاتف✗ RPC لكل سحبة✓ دفعة من 25 سحبة، تعديل واحد، واجهة متفائلة مع تراجع
صعود السائق داخل التطبيق دون اتصال✗ فشل صامت✓ طابور Dexie + شارة تفريغ يدوي + ذاكرة geofence دون اتصال
مرفق المتقدِّم على 2G✗ إعادة البدء من البايت 0✓ نمط tus قابل للاستئناف عبر OPFS، الأجزاء المكتملة محفوظة
المراسلة الصادرة دون اتصال✗ واجهة متفائلة لكن بلا طابور✓ حالات فقاعة queued/sending/sent/failed + استلامات في الوقت الحقيقي
قراءة الجدول الدراسي دون اتصال△ ذاكرة الشبكة-أولاً، فارغة في الذاكرة الباردة✓ Dexie live query، عرض فوري، إبطال WebSocket
تثبيت PWA△ manifest قابل للتثبيت، بلا واجهة نافذة✓ شريط تثبيت في الزيارة الثالثة + تثبيت يدوي في الإعدادات
تدفّق تحديث SW△ تحديثات SW صامتة✓ toast يطلب إعادة التحميل عند انتظار SW جديد؛ إطلاق مرحلي
رؤية المزامنة المعلّقة✗ لا شيء✓ درج في الرأس + إعادة المحاولة/التجاهل + واجهة تعارض
أمان المستأجر△ كوكي الجلسة + middleware✓ + Dexie معزول؛ SW يقرأ المستأجر من كوكي الجلسة لكل طلب
القياس عن بُعد✗ لا شيء على أحداث عدم الاتصال✓ OfflineSyncLog + breadcrumbs في Sentry + لوحة المطوّر + devtools
ميزانية الخيط الرئيسي△ المزامنة تعمل على الخيط الرئيسي✓ Web Worker؛ الخيط الرئيسي لا يُحجَب > 16 مللي ثانية أثناء المزامنة
إبطال الذاكرة المؤقتة△ refetchOnReconnect✓ push WebSocket؛ إبطال أقل من 200 مللي ثانية
حلّ التعارضات✗ كتابة فوقية صامتة أو toast✓ واجهة <ConflictResolver> مُهيكَلة مع كلا الجانبين + خيار قرار
تقسيم الكود△ حزمة واحدة✓ أساس < 35 KB؛ حزم كل ملحمة بكسل

سياسة حلّ التعارضات لكل إجراء

الافتراضي هو الخادم-هو-المرجع مع واجهة دمج مُهيكَلة. التجاوزات لكل إجراء أدناه.

الإجراءالاستراتيجيةالواجهة عند التعارض
attendance.bulkUploadآخر كتابة تفوز بـmarkedAtToast "كان حفظك أقدم من تحرير {الزميل}. القيم الأحدث محفوظة." مع رابط diff
attendance.qrScanأول كتابة تفوز بـscannedAtToast "الطالب مُحدَّد حاضراً بالفعل في {الوقت}."
exam.submitرفض عند عدم تطابق paperVersionشاشة كاملة <PaperVersionConflict> مع خيار إعادة الجلب
exam.autoSaveآخر كتابة تفوز بـsavedAtصامت (المسوّدات تقبل دائماً الأحدث)
exam.proctorEventإلحاق فقطصامت (سجلّ مراجعة؛ إزالة التكرار من جانب الخادم)
grading.batchApplyرفض عند عدم تطابق gradeVersionلوحة <GradingConflict> تُظهر تغييراتك مقابل الحالة الحالية، احتفاظ/تجاهل لكل صف
transportation.boardingEventخادم-مرجع idempotentصامت (بالفعل upsert على (schoolId, tripId, studentId))
application.submitأول كتابة تفوز بـsubmittedAtToast "الطلب مُسلَّم بالفعل في {التاريخ}."
application.draftSaveآخر كتابة تفوز بـsavedAtصامت
messaging.sendإلحاق فقطصامت (idempotency على clientMessageId)
messaging.markReadآخر كتابة تفوز (أقصى حالة قراءة)صامت (زيادة أحادية)
notification.acknowledgeإلحاق فقط idempotentصامت

مصفوفة توافق المتصفحات

تستهدف الاستراتيجية المتصفحات التي تستخدمها المدارس فعلياً. كشف القدرات في وقت التشغيل في useNetworkStatus و useOPFSSupport.

المتصفّح / النظامDexieOPFSWeb WorkerSWBackground SyncbeforeinstallpromptNetwork Infoملاحظات
Chrome Android 110+✓✓✓✓✓✓✓دعم كامل؛ الهدف الأساسي
Safari iOS 16+✓✓ (16.4+)✓✓✗✗ (أضف للشاشة)✗OPFS في 16.4؛ SW مقيَّد؛ الرجوع إلى online
Samsung Internet 19+✓✓✓✓✓✓✓دعم كامل
Chrome Desktop 110+✓✓✓✓✓✓✓دعم كامل
Safari macOS 16+✓✓ (16.4+)✓✓✗✗✗نفس Safari iOS
Firefox 110+✓✓ (111+)✓✓✗✗جزئيOPFS في 111؛ عامِله كـSafari للمزامنة
Edge 110+✓✓✓✓✓✓✓مبني على Chromium، دعم كامل
متصفّحات داخل التطبيق (واتساب)✓✗✓△✗✗✗OPFS غير متاح؛ الرجوع إلى تخزين blob في IDB

حيث OPFS مفقود، طبقة التخزين ترجع إلى حقول blob في IDB مع تحذير حصّة. حيث Background Sync مفقود، المزامنة تعمل على حدث online + تغيّر visibility أثناء فتح التبويب؛ عند إعادة فتح PWA، العامل يُفرّغ فوراً.

ميزانية التخزين لكل دور

مراقب navigator.storage.estimate() يُحذّر عند 80% من الاستخدام. OPFS مُضمَّن في نفس حصّة الأصل؛ نُقسِّم عمداً.

الدورIDB (Dexie)OPFSالمجموع النموذجيالأسوأسياسة الإجلاء
المعلّم3 ميغابايت2 ميغابايت5 ميغابايت15 ميغابايتLRU 7 أيام للقائمة؛ طابور الحضور لا يُجلى قبل الإرسال
الطالب2 ميغابايت3 ميغابايت5 ميغابايت12 ميغابايتPDF ورقة الامتحان يُجلى عند نهاية الجلسة؛ الحفظ التلقائي لا يُجلى قبل الإرسال
السائق4 ميغابايت1 ميغابايت5 ميغابايت18 ميغابايتقائمة الرحلة تُجلى عند نهاية الرحلة؛ طابور الصعود لا يُجلى قبل الإرسال
المتقدِّم2 ميغابايت8 ميغابايت10 ميغابايت25 ميغابايتأجزاء المرفقات في OPFS حتى اكتمال الرفع؛ المسوّدات تبقى حتى التسليم
ولي الأمر1 ميغابايت0 ميغابايت1 ميغابايت4 ميغابايتللقراءة فقط؛ الإعلانات المخزّنة LRU 14 يوماً
المسؤول3 ميغابايت1 ميغابايت4 ميغابايت12 ميغابايتنفس المعلّم

ميزانيات الأداء

هذه هي الميزانيات التي يفرضها CI.

المقياسالميزانيةكيف يُقاس
حزمة الأساس مضغوطة gzip≤ 35 KBCI size-limit
كل حزمة ملحمة مضغوطة gzip≤ 15 KBCI size-limit
وقت التفاعل دون اتصال (IDB بارد)≤ 1.5 ثانية على 3GLighthouse CI
كتابة Dexie p95≤ 50 msمعاملة Sentry
قراءة Dexie p95≤ 20 msمعاملة Sentry
كتابة OPFS p95 (لكل جزء 1 ميغابايت)≤ 200 msمعاملة Sentry
تفريغ عامل المزامنة لكل تعديل≤ 300 msOfflineSyncLog.replayedAt - createdAt
كمون إبطال WebSocket≤ 200 mstimestamp الخادم → تحديث live-query العميل
وقت تنشيط SW≤ 800 msتوقيت navigator.serviceWorker.ready
إعادة الاتصال إلى طابور مُفرَّغ (10 عناصر)≤ 3 ثوانٍاختبار Playwright يدوي + Sentry
نسبة إصابة الذاكرة للجدول الدراسي≥ 95%قياس مخصّص
حجب الخيط الرئيسي أثناء المزامنة (p99)≤ 16 msPerformance API long-task observer

المرحلة التمهيدية (الأسبوع 0): استكشافات وأبحاث

عشرة استكشافات تُسلَّم قبل بدء EPIC-0. كل واحد محدّد بـ0.5-1 يوم وينتج Decision Record. كلّها يجب أن تُدمَج قبل بدء EPIC-0.

الاستكشافالهدفالنتيجةالمسؤول
S-0.1قياس أداء Dexie مقابل idb لدفعات 1000 كتابة، كمون live-query، ترحيل المخطّطاختير Dexie؛ أرقام قياس في DRالمطوّر أ
S-0.2حصص OPFS + أداء الكتابة على Safari iOS 16.4، Chrome Android 110، Firefox 111مصفوفة القدرات + الفولباكالمطوّر ب
S-0.3نموذج أوّلي Web Worker + Comlink RPC لمحرّك المزامنةPOC يعمل؛ ميزانية الخيط الرئيسي مُتحقَّق منهاالمطوّر أ
S-0.4قياس LZ4-wasm مقابل Snappy على حمولات نموذجية (دفعة حضور، إجابات امتحان)اختير الضغط؛ أرقام نسبة الضغطالمطوّر ب
S-0.5أداء SHA-256 عبر SubtleCrypto مقابل WASM على حمولات 1 ميغابايتاختير التجزئة؛ تكلفة لكل بايتالمطوّر أ
S-0.6طوبولوجيا cache-invalidate لـWebSocket — قناة واحدة مقابل غرف لكل مدرسةالطوبولوجيا + خطة التوسعةالمطوّر ب
S-0.7نمط ناقل رسائل service-worker → sync-workerمخطّط معماري + كود مرجعيالمطوّر أ
S-0.8XState مقابل آلة حالة يدوية لدورة حياة التعديلقرار + تحليل تكلفة الحزمةالمطوّر ب
S-0.9سلوك Background Sync في iOS PWA — متى يطلق فعلياًخطة الفولباك iOS موثّقةالمطوّر أ
S-0.10خط أساس لأنماط الاتصال الحالية من Sentry / Vercel Analyticsخط أساس "X% من الجلسات ترى ≥1 فشل شبكة"المطوّر ب

القصص لكل ملحمة

الملحمة-0 — الأساس: محرّك عمل دون اتصال بدرجة إنتاجية (62 نقطة)

الأساس يُبنى مرّة، يُبنى بشكل صحيح، ويُسلَّم قبل بدء أي عمل لكل دور. خمس ملاحم فرعية، كلّها مُدمَجة قبل بوّابة الأساس (انظر أدناه).

0.A — طبقة التخزين (18 نقطة)

القصةالعنوانالنقاطالنوع
0.A.1إضافة Dexie + dexie-react-hooks؛ مصنع DB لكل مستأجر hogwarts-${schoolId}5INFRA
0.A.2إصدارات المخطّط + إطار الترحيل + اختبارات ترحيل CI5INFRA
0.A.3تجريد OPFS (putBlob/getBlob/deleteBlob) مع فولباك IDB5INFRA
0.A.4نماذج Prisma OfflineMutation + OfflineSyncLog + CacheInvalidation3DB

0.B — عامل المزامنة (18 نقطة)

القصةالعنوانالنقاطالنوع
0.B.1سقالة sync-worker.ts Web Worker + Comlink RPC5INFRA
0.B.2أنواع envelope discriminated-union في actionRegistry + مخططات Zod3API
0.B.3آلة حالة دورة حياة التعديل بنمط XState (idle → pending → in_flight → ...)5API
0.B.4خطّ ضغط LZ4 + MessagePack3INFRA
0.B.5بصمة محتوى SHA-256 + تحقّق إعادة التشغيل2API

0.C — أوّليات الشبكة والتعارض (12 نقطة)

القصةالعنوانالنقاطالنوع
0.C.1خطّاف useNetworkStatus المعياري (navigator.onLine + Network Info + heartbeat)3UI
0.C.2خطّافات react رقيقة useMutationQueue<T> / useOfflineMutation / useLiveQuery5UI
0.C.3ذرّة <ConflictResolver> + عنصر أوّلي لواجهة الدمج المُهيكَلة4UI

0.D — service worker كمنسّق (8 نقاط)

القصةالعنوانالنقاطالنوع
0.D.1استبدال stub syncOfflineForms() في public/service-worker.js:1603INFRA
0.D.2نقاط نهاية شظايا المزامنة لكل مجال /api/offline/sync/{domain}3API
0.D.3بثّ خادم cache-invalidate عبر WebSocket + مُستقبِل عميل عبر socket.io2API

0.E — القياس عن بُعد + devtools (6 نقاط)

القصةالعنوانالنقاطالنوع
0.E.1breadcrumbs في Sentry + قياس OfflineSyncLog على كل انتقال3INFRA
0.E.2لوحة <OfflineDevtools> عائمة (DEVELOPER فقط) تُظهر الطابور + الذاكرات3UI

تُسلِّم ملحمة الأساس محرّك العمل دون اتصال بدرجة إنتاجية الذي تستهلكه كل ملحمة دور. ملفّات جديدة تهبط تحت src/lib/offline/{db,opfs,registry,types,state-machine,compression,integrity}.ts، عامل sync-worker.ts، و src/components/offline/{hooks,provider,devtools}/. يحجب كل ملحمة أخرى حتى تمرير بوّابة الأساس.

الملحمة-1 — قابلية تثبيت PWA + تخزين هيكل التطبيق مسبقاً (15 نقطة)

القصةالعنوانالنقاطالنوع
1.1قائمة التخزين المسبق لهيكل التطبيق + ختم الإصدار وقت البناء5INFRA
1.2ذرّة <InstallPromptBanner> + التقاط beforeinstallprompt3UI
1.3تدفّق تحديث SW + <UpdateAvailableToast> + إطلاق مرحلي3UI
1.4اختصارات manifest لـ /exams، /messages، /timetable1INFRA
1.5تلميع صفحة عدم الاتصال (تعريب + عدد التعديلات المعلّقة + واجهة تخزين)3UI

تُرقّي manifest القابل للتثبيت الحالي إلى PWA حقيقي. اسم الذاكرة المؤقتة يستخدم __BUILD_ID__ للإبطال عند النشر. شريط التثبيت يظهر في الزيارة الثالثة للوحة التحكّم، التجاهل محفوظ في Dexie. تدفّق التحديث يستمع لـcontrollerchange ويطلب من المستخدم إعادة التحميل. الإطلاق المرحلي يُمكّن تحديثات SW لـ10% → 50% → 100% من الأجهزة عبر 48 ساعة.

الملحمة-2 — ذاكرة قراءة ذكية (24 نقطة)

القصةالعنوانالنقاطالنوع
2.1جداول Dexie للقراءات المخزّنة + ربط live-query5API
2.2قراءة مخزّنة للجدول الدراسي (getTimetableForOffline) مع إبطال WebSocket5API
2.3قراءة مخزّنة للإعلانات + مزامنة حالة القراءة عند الاتصال3API
2.4قراءات مخزّنة للملف الشخصي + قائمة الفصل3API
2.5ذرّة <CachedDataBanner> (تُعرَض عندما يكون عمر الذاكرة > 5 دقائق ودون اتصال)3UI
2.6إجلاء الذاكرة عند تبديل المستأجر2INFRA
2.7مراقب حصّة التخزين + فرض سياسة إجلاء LRU3INFRA

تُعرَض الأسطح غالباً-للقراءة فورياً دون اتصال من Dexie live queries. مفاتيح الذاكرة تتّبع [schoolId, resource, id]. staleTime المُدرك للشبكة يُشتقّ من عتبات performance-optimization.ts — 5min على 4G، 30min على slow-2g. WebSocket يُبطل المُدخلات المخزّنة خلال 200 مللي ثانية من الكتابات البعيدة.

الملحمة-3 — المعلّم دون اتصال (الحضور + QR + التصحيح) (32 نقطة)

القصةالعنوانالنقاطالنوع
3.1تسجيل bulkUploadAttendance كمعالج إجراء "attendance.bulkUpload"5API
3.2تخزين مسبق لقائمة الحضور (Dexie + OPFS لصور الطلاب) عند فتح الفصل5API
3.3طابور مسح QR (يُعيد استخدام KioskLog.syncedToAttendance)5UI
3.4دفعة تصحيح بالسحب على الهاتف (تجميع 25 سحبة في تعديل واحد)5UI
3.5<PendingMutationsDrawer> مع أدوات إعادة المحاولة/التجاهل + مُجمَّعة حسب الإجراء5UI
3.6واجهة تعارض مُهيكَلة لـlast-write-wins (409 ATTENDANCE_NEWER_RECORD) عبر <ConflictResolver>5UI
3.7واجهة متفائلة في جدول الحضور عبر useOptimistic (React 19) مع تراجع2UI

تُغلّف bulkUploadAttendance في src/components/school-dashboard/attendance/actions/bulk.ts:33 كإجراء مسجَّل. التخزين المسبق يملأ Dexie + OPFS (صور الطلاب) عندما يفتح المعلّم فصلاً. طابور QR يستبدل toast بمحاولة واحدة في qr-scanner.tsx:209. التصحيح بالسحب يُدفِّع 25 سحبة في تعديل واحد.

الملحمة-4 — الطالب دون اتصال (تسليم الامتحان + سجلّ المراقَبة) (22 نقطة)

القصةالعنوانالنقاطالنوع
4.1تسجيل submitExamSession كمعالج إجراء "exam.submit"5API
4.2ترحيل use-auto-save إلى أوّليات الأساس (إسقاط إعادة المحاولة المخصّصة)3API
4.3حفظ أحداث securityFlags للمراقَبة دون اتصال3API
4.4شاشة كاملة "التسليم مُطابور" عند الانقطاع أثناء التسليم5UI
4.5تخزين مسبق للامتحان (الأسئلة + الخيارات + الأصول) في OPFS3INFRA
4.6حلّ تعارض paperVersion (شاشة كاملة <PaperVersionConflict>)3UI

يُغلق الفجوة الحرجة: اليوم الحفظ التلقائي يصمد لكن submitExamSession النهائي في src/components/school-dashboard/exams/take/actions.ts:369 لا يفعل. مفتاح idempotency = sessionId. الخادم يرفض التسليم عند عدم تطابق paperVersion مع واجهة تعارض مُهيكَلة. الترحيل يُسقط حوالي 120 سطراً من إعادة المحاولة/backoff المخصّصين.

الملحمة-5 — السائق دون اتصال (صعود الرحلة) (15 نقطة)

القصةالعنوانالنقاطالنوع
5.1تسجيل recordBoardingFromGeofenceInternal كمعالج إجراء3API
5.2تخزين مسبق لوضع رحلة السائق (المسار + القائمة + الصور في OPFS)5API
5.3<TripQueueBadge> مع تفريغ يدوي + واجهة إعادة محاولة4UI
5.4تقاعد LocationQueue المخصّص؛ ترحيل إلى أوّليات الأساس3INFRA

يحاكي نمط geofence المعياري الذهبي في تجربة السائق داخل التطبيق. recordBoardingFromGeofenceInternal هو بالفعل idempotent على (schoolId, tripId, studentId). التخزين المسبق يملأ Dexie + OPFS عند بدء الرحلة. القصة 5.4 تتقاعد عن صنف LocationQueue في geo-tracker.tsx:59.

الملحمة-6 — المتقدِّم دون اتصال (مرفقات قابلة للاستئناف + تسليم) (18 نقطة)

القصةالعنوانالنقاطالنوع
6.1رفع S3 متعدّد الأجزاء بنمط tus قابل للاستئناف مدعوم بمتجر أجزاء OPFS8API
6.2ترحيل مسوّدة الطلب من localStorage → Dexie (مع BroadcastChannel عبر التبويبات)5API
6.3تسليم نهائي مُطابور ("application.submit")3API
6.4واجهة تقدّم رفع مُدركة للشبكة (تُظهر الوقت المُقدَّر لكل effectiveType)2UI

المتقدِّمون على 3G ضعيف يمكنهم استئناف رفع نسخة دراسية 5 ميغابايت — الأجزاء تعيش في OPFS. نقطة نهاية جديدة /api/uploads/resumable/[uploadId]/[partNumber] تتعقّب الأجزاء المكتملة. سياق الطلب في application-context.tsx:412 يترقّى من localStorage إلى Dexie.

الملحمة-7 — طابور المراسلة الصادرة (16 نقطة)

القصةالعنوانالنقاطالنوع
7.1إجراء مسجَّل "messaging.send" مع idempotency3API
7.2واجهة متفائلة: فقاعة الدردشة تُظهر queued/sending/sent/failed عبر useOptimistic5UI
7.3طابور المرفقات بإعادة استخدام رفع EPIC-6 القابل للاستئناف عبر OPFS3API
7.4سياسة جسر واتساب دون اتصال (تجاوز أثناء عدم الاتصال؛ إرسال idempotent بـclientMessageId)3API
7.5استلامات تسليم في الوقت الحقيقي عبر WebSocket2UI

تُغلق فجوة المراسلة. حالات الفقاعة تتطابق مع دورة حياة XState من EPIC-0. الجسر يستقبل clientMessageId لمنع التكرار-على-الإعادة.

الملحمة-8 — قابلية المراقبة + تحليلات عدم الاتصال (14 نقطة)

القصةالعنوانالنقاطالنوع
8.1استعلامات تحليلات OfflineSyncLog + getOfflineSyncStats(schoolId)3DB
8.2breadcrumbs في Sentry + معاملات مخصّصة لحالة الطابور3INFRA
8.3بلاطة لوحة المطوّر: تعديلات مُطابورة، معدّل نجاح المزامنة، p95 وقت المزامنة5UI
8.4لوحة SLI لكل مدرسة (معدّل نجاح المزامنة، معدّل إصابة الذاكرة، معدّل التثبيت)3UI

تُجيب "هل يساعد عدم الاتصال المستخدمين" بأرقام صلبة. مقاييس لكل مدرسة تظهر في لوحة SaaS. لوحة SLI مكشوفة لدور ADMIN ليتمكّن كل مدرسة من المراقبة الذاتية.

الملحمة-9 — الوثائق والتعريب وإعداد المسؤول (13 نقطة)

القصةالعنوانالنقاطالنوع
9.1content/docs-en/offline.mdx + content/docs-ar/offline.mdx3DEVX
9.2~30 مفتاح قاموس تحت dictionary.offline.* (en + ar)5UI
9.3واجهة تبديل المسؤول لـSchool.offlineMode3UI
9.4Playwright offline E2E + مجموعة وحدات (≥ 95% تغطية على الأساس)2DEVX

ملحمة تسليم قائمة بذاتها. القصة 9.4 تغطّي مسارات الأدوار الأربعة عبر context.setOffline(true). مفاتيح القاموس: online، offline، slowConnection، backOnline، syncing، pendingCount، pendingDrawerTitle، retry، discard، discardConfirm، lastSynced، cached، submissionQueued، submissionInFlight، conflictTitle، conflictBody، conflictResolveKeep، conflictResolveDiscard، conflictResolveMerge، installAvailable، installCta، updateAvailable، updateCta، offlinePageTitle، offlinePageBody، dataSaver، featureUnavailableOffline، storageNearFull، storageFullError، paperVersionConflict، gradingConflict.

استراتيجية الانتقال

الانتقال من حالة اليوم إلى عمل دون اتصال بدرجة إنتاجية يحدث على ثلاث مراحل لكل ميزة. لا تبديل مفاجئ.

  1. مرحلة الكتابة المزدوجة. تكتب الميزة عبر الإنترنت مباشرة وتُطابور أيضاً لإعادة التشغيل عبر الأساس الجديد. OfflineSyncLog يُزيل التكرار عبر مفتاح idempotency. اكتشف الاختلاف في القياس عن بُعد؛ نبّه على أي.
  2. التحويل. بدّل الميزة إلى الطابور-أولاً. التفريغ عبر الإنترنت تلقائي وفوري. علم School.offlineMode='full' يُمكّن مسار الطابور؛ limited يبقي عبر الإنترنت فقط.
  3. التنظيف. أزل إعادة المحاولة/localStorage/طابور الذاكرة المخصّص. احذف الكود الميت في use-auto-save.ts، useOfflineQueue (attendance/shared/hooks.ts:541)، LocationQueue (geo-tracker.tsx:59).

ترتيب التحويل: الحضور → تسليم الامتحان → صعود السائق → مرفقات الطلب → المراسلة. كل تحويل يعيش خلف علم الميزة ويُسلَّم بشكل مستقلّ.

متعدّد الجوانب

إضافات المخطّط

طبّق عبر بروتوكول Neon branch-before-touch.

  • OfflineMutation — طابور خادم دائم: id، schoolId @@index، userId @@index، action، idempotencyKey @unique، contentHash، payload Json، status enum(PENDING|SUCCESS|FAILED|CONFLICT)، attempts، lastError، createdAt، syncedAt.
  • OfflineSyncLog — ذاكرة idempotency: idempotencyKey @id، schoolId @@index، userId، action، responseJson Json، replayedAt. وظيفة TTL 30 يوماً.
  • CacheInvalidation — سجلّ بثّ موضوع: id، schoolId @@index، topic، keyHash، emittedAt، propagatedAt. وظيفة TTL 7 أيام.
  • School.offlineMode — حقل تعدادي disabled / limited / full. الافتراضي limited.
  • KioskLog.syncedToAttendance — موجود بالفعل. أعد الاستخدام، لا تكرّر.

التحكّم في الوصول حسب الدور

  • درج المزامنة المعلّقة: مرئي لجميع الأدوار المُصادَق عليها.
  • إعادة المحاولة / التجاهل: محدّد لدور المالك لكل ميزة.
  • بلاطة لوحة المطوّر: تجميع عبر المدارس.
  • لوحة SLI لكل مدرسة: دور ADMIN.
  • تبديل وضع عدم الاتصال للمسؤول: دور ADMIN فقط.
  • لوحة <OfflineDevtools>: دور DEVELOPER فقط (محمي بفحص جلسة).

التعريب

30 مفتاح قاموس جديد تحت dictionary.offline.*. صفر سلاسل مدمجة في كود واجهة عدم الاتصال. السلاسل العربية يراجعها متحدّث أصلي قبل الإطلاق التجريبي.

استراتيجية الاختبار

طبقة الاختبارالتغطيةالأداة
وحدةتوقيت useMutationQueue، عزل Dexie لكل مستأجر، انتقالات XState، تحقّق بصمة المحتوىVitest
تكاملإرسال actionRegistry، شظايا مزامنة لكل مجال، سياسات التعارض، إبطال WebSocketVitest + msw
E2Eمسارات الأدوار الأربعة مع context.setOffline(true) + إعادة الاتصال؛ استمرارية إغلاق التبويب؛ تبديل النطاقPlaywright
بصري<PendingMutationsDrawer>، <CachedDataBanner>، <ConflictResolver>، <OfflineDevtools> في الحالاتStorybook + Chromatic
يدويفحص جهاز حقيقي على Safari iOS 16.4، Chrome Android 110، Samsung Internet 19داخلي بنمط TestFlight
أداءكل ميزانيات الأداء أعلاه + Lighthouse CIsize-limit + Sentry + LH
إمكانية الوصولكل واجهة عدم اتصال قابلة للتنقّل بلوحة المفاتيح + قارئ الشاشة يُعلن "أنت دون اتصال"axe + يدوي VoiceOver/TB
ترحيلترحيل مخطّط Dexie للأمام + توافق خلفيحاوية اختبار Dexie

القياس عن بُعد والمقاييس

كل انتقال حالة يُصدر حدثاً مُهيكَلاً. أسماء الأحداث تستخدم namespace offline.*.

الحدثالحمولةالوجهة
offline.mutation.enqueued{ schoolId, userId, action, idempotencyKey, contentHash }Sentry + OfflineSyncLog
offline.mutation.flushed{ ..., durationMs, attempts, compressionRatio }Sentry + OfflineSyncLog
offline.mutation.failed{ ..., error, willRetry }Sentry + OfflineSyncLog
offline.mutation.conflict{ ..., conflictType, resolution }Sentry + OfflineSyncLog
offline.network.changed{ from, to, effectiveType, saveData }Sentry
offline.cache.hit{ schoolId, queryKey, ageMs, source }تحليلات مخصّصة
offline.cache.miss{ schoolId, queryKey }تحليلات مخصّصة
offline.cache.invalidated{ schoolId, topic, keyHash, latencyMs }تحليلات مخصّصة
offline.install.prompted{ schoolId, userId, role }تحليلات مخصّصة
offline.install.accepted{ schoolId, userId, role }تحليلات مخصّصة
offline.install.dismissed{ schoolId, userId, role, dismissCount }تحليلات مخصّصة
offline.sw.updated{ schoolId, fromVersion, toVersion, rolloutPercent }Sentry
offline.storage.warning{ schoolId, usagePercent, quota }Sentry
offline.worker.long_task{ schoolId, taskMs, action }Sentry

الإطلاق التدريجي (علم الميزة)

School.offlineModeSW مُسجَّلذاكرة قراءةطابور كتابةنافذة تثبيت
disabled✗✗✗✗
limited (الافتراضي)✓✓✗✗
full✓✓✓✓

المدارس التجريبية (السودان، الملك فهد) تختار full. SaaS Marketing يبقى على الافتراضي.

إعداد المطوّر

يضيف مساهم جديد دعم عدم الاتصال لميزته في خمس خطوات.

  1. سجّل معالج إجراء جديد في src/lib/offline/registry.ts مع مخطّط حمولة Zod وسياسة تعارض.
  2. استدعِ useOfflineMutation("yourFeature.actionName", payload) من العميل؛ للقراءات استخدم useLiveQuery(() => db.yourFeature.where(...).toArray()).
  3. أضف دخول Storybook لواجهة التعارض إن كانت التعارضات محتملة.
  4. أضف اختبار Playwright في playwright/tests/offline.spec.ts.
  5. أضف مفاتيح قاموس لأي سلاسل عدم اتصال جديدة تحت dictionary.offline.<feature>.*.

مثال عمل كامل يعيش في src/components/school-dashboard/attendance/ بعد تسليم EPIC-3.

بوّابة الأساس

EPIC-0 يجب أن يجتاز كل ما يلي قبل بدء أي ملحمة دور. عمل الأساس ليس تسليماً جزئياً.

  • مصنع DB Dexie لكل مستأجر + إصدارات المخطّط + اختبارات الترحيل مُدمَجة
  • تجريد OPFS مع فولباك IDB مُدمَج + اختبار Safari iOS 16.4 اليدوي ناجح
  • sync-worker.ts يُشغّل كل إعادة تشغيل التعديلات؛ مراقب long-task للخيط الرئيسي يُبلِّغ عن صفر حجب > 16 مللي ثانية أثناء المزامنة
  • actionRegistry + دورة حياة XState مُدمَجة بتغطية ≥ 95% اختبارات وحدة
  • ضغط LZ4 + MessagePack مقيس بنسبة ≥ 3× على الحمولات النموذجية
  • بصمة محتوى SHA-256 تتحقّق في كل إعادة تشغيل؛ اختبار العبث يفشل مغلقاً
  • كمون cache-invalidate عبر WebSocket من طرف إلى طرف ≤ 200 مللي ثانية في staging
  • service worker يُفرّغ الطابور على حدث Background Sync (Chrome) وعلى إعادة فتح التبويب (iOS)
  • ذرّة <ConflictResolver> مُسلَّمة مع تغطية Storybook لكل سياسة تعارض
  • OfflineSyncLog يكتب من كل انتقال؛ breadcrumbs في Sentry مرئية في اللوحة
  • لوحة <OfflineDevtools> تُعرَض لدور DEVELOPER وتُظهر حالة الطابور الحيّة
  • حجم حزمة الأساس ≤ 35 KB مضغوطة gzip (بوّابة CI size-limit)
  • اختبار Playwright لتسرّب المستأجر أخضر (طابور على school-a → تبديل إلى school-b → الطابور غير مرئي)

عمل الأساس يستمرّ حتى تُحدَّد كل المربّعات. لا ملحمة دور تبدأ قبل ذلك.

المخاطر والمقايضات

  1. حصص IndexedDB + OPFS. Safari iOS يُحدّد حوالي 50 ميغابايت؛ Chrome Android يسمح حتى 60% من القرص. التخفيف: OPFS للكتل؛ حمولات فقط في IDB؛ مراقب navigator.storage.estimate() يُحذّر عند 80%؛ يُجلي ذاكرات القراءة قبل الكتابات المُطابورة.
  2. تسرّب عبر المستأجرين عند تبديل النطاق الفرعي. التخفيف: اسم DB معزول بـschoolId؛ SW يقرأ schoolId من كوكي الجلسة لكل طلب؛ اختبار Playwright في بوّابة الأساس.
  3. أسئلة امتحان بائتة مقابل النزاهة. التخفيف: التخزين المسبق يحفظ paperVersion؛ الخادم يرفض التسليم عند عدم التطابق مع <PaperVersionConflict> مُهيكَل.
  4. تكرارات جسر واتساب. التخفيف: clientMessageId يُمرَّر إلى الجسر كمفتاح idempotency.
  5. نطاق SW متعدّد النطاقات الفرعية. كل school-X.databayt.org يحصل على نطاق SW خاص (صحيح).
  6. غياب Background Sync في Safari iOS. التخفيف: حدث online + مستمع visibility-change في التبويب؛ عند إعادة فتح PWA، عامل المزامنة يُفرّغ الطابور فوراً.
  7. OPFS غير متاح على Safari قديم / متصفّحات داخل التطبيق. التخفيف: طبقة التخزين تكتشف القدرة عند التهيئة؛ ترجع إلى حقول blob في IDB مع تحذير تخزين.
  8. صفحة عدم اتصال باردة. التخفيف: تخزين مسبق عدواني للهيكل عند التثبيت؛ صفحة عدم الاتصال مفيدة حتى في أوّل زيارة (تُظهر CTA التثبيت).
  9. تقييد Background Sync. التخفيف: التفريغ الأمامي هو المسار الأساسي؛ المزامنة الخلفية بذل أفضل.
  10. انحراف الوقت على التعديلات المحفوظة. التخفيف: الخادم يسجّل serverAcceptedAt كمرجع؛ markedAt العميل يبقى كـmetadata مراجعة فقط.
  11. حدود اتصال WebSocket لكل أصل. التخفيف: socket واحد لكل تبويب؛ تنسيق بنمط SharedWorker عبر التبويبات لنفس الأصل.
  12. إجلاء التخزين على مستوى نظام التشغيل. التخفيف: تثبيت PWA يُرقّي إلى "تخزين مُدار" بضمانات إجلاء؛ المستخدمون غير المُثبَّتين يرون toast تحذير.

الجدول الزمني التفصيلي (14 أسبوعاً، أسبوع-بأسبوع)

مطوّران، مراحل مُنسَّقة. أرقام الأسابيع تقويمية بدءاً من انطلاق المشروع.

الأسبوعالمرحلةتركيز المطوّر أتركيز المطوّر بالمعلم
0استكشافاتS-0.1، S-0.3، S-0.5، S-0.7، S-0.9S-0.2، S-0.4، S-0.6، S-0.8، S-0.10كل Decision Records مُدمَجة
1الأساس 0.ADexie tenant DB + إصدارات المخطّطOfflineMutation + OfflineSyncLog + CacheInvalidationنماذج Prisma تعمل على Neon
2الأساس 0.A+Bتجريد OPFS + فولباك IDBسقالة sync-worker.ts + Comlink RPCDexie + OPFS مُتحقَّق منهما على iOS 16.4
3الأساس 0.B+Cدورة حياة XState + الضغط + بصمات المحتوىuseNetworkStatus + useOfflineMutation + useLiveQueryميزانية الخيط الرئيسي مُتحقَّق منها
4الأساس 0.D+Eناقل رسائل SW + شظايا مزامنة لكل مجالإبطال WebSocket + Sentry + <OfflineDevtools>بوّابة الأساس — كل المربّعات خضراء
5PWA + ذاكرة قراءةEPIC-1.1، 1.2، 1.4EPIC-2.1، 2.2، 2.6نافذة التثبيت مرئية في staging
6PWA + ذاكرة قراءةEPIC-1.3، 1.5EPIC-2.3، 2.4، 2.5، 2.7EPIC-1 منجَز؛ EPIC-2 منجَز
7المعلّم دون اتصالEPIC-3.1، 3.2، 3.7EPIC-4.1، 4.5حضور المعلّم دون اتصال يعمل في staging
8المعلّم دون اتصالEPIC-3.3، 3.4EPIC-4.2، 4.3الحفظ التلقائي للامتحان مُرحَّل إلى الأساس
9المعلّم + الطالبEPIC-3.5، 3.6EPIC-4.4، 4.6EPIC-3 منجَز؛ EPIC-4 منجَز
10السائق + المتقدِّمEPIC-5.1، 5.2EPIC-6.1، 6.2صعود السائق دون اتصال + رفع OPFS قابل للاستئناف
11السائق + المتقدِّمEPIC-5.3، 5.4EPIC-6.3، 6.4EPIC-5 منجَز؛ EPIC-6 منجَز
12المراسلةEPIC-7.1، 7.2، 7.5EPIC-7.3، 7.4EPIC-7 منجَز
13قابلية المراقبةEPIC-8.1، 8.2EPIC-8.3، 8.4لوحة المطوّر + لوحة SLI لكل مدرسة تعمل
14الوثائق + التجريبيEPIC-9.1، 9.2EPIC-9.3، 9.4 + تمكين التجريبيالتجريبي مُمكَّن في مدرسة سودانية واحدة

احتياطي: أسبوع واحد محجوز لـsoak في staging قبل تمكين التجريبي.

P0 — EPIC-0، 1، 3، 4 (الأساس + المعلّم اليومي + الطالب عالي المخاطر). P1 — EPIC-2، 5، 6، 9 (ذاكرة قراءة، سائق، متقدِّم، وثائق). P2 — EPIC-7، 8 (طابور المراسلة + تحليلات).

دليل الإطلاق التجريبي

تمكين School.offlineMode='full' لمدرسة تجريبية تغيير مدروس وقابل للملاحظة. خمس خطوات.

  1. ما قبل الإطلاق (T-7 أيام). تأكّد أن المدرسة على أحدث إصدار؛ فحص مسارات الإنترنت؛ خط أساس لمعدّل فشل الاتصال؛ إحاطة المسؤول.
  2. تمكين ناعم (T-0). يُبدّل ADMIN offlineMode='full' في إعدادات المدرسة. service worker يُسجَّل في الزيارة التالية.
  3. مراقبة اليوم 1. راقب OfflineSyncLog لأول ~50 تعديل مُطابور؛ تحقّق من نجاح كل تفريغ؛ افحص Sentry.
  4. مراجعة اليوم 7. قارن المقاييس الرئيسية. إذا أي شيء تحت 95%، جذر المشكلة أو ارجع إلى limited.
  5. استرجاع اليوم 30. قرّر: الإبقاء على full، الترقية إلى الافتراضي، أو التحسين.

الرجوع فوري: يضبط ADMIN offlineMode='limited'.

قائمة اختبار التحقّق السريع

فحوصات يدوية قبل إعلان "ميزة منجَزة دون اتصال". شغّل على Safari iOS 16.4، Chrome Android 110، Chrome Desktop 110 كلّ منها.

  • service worker مُسجَّل (chrome://serviceworker-internals يُظهره نشطاً)
  • manifest قابل للتثبيت
  • هيكل التطبيق يُحمَّل مع Network: Offline بعد زيارة سابقة عبر الإنترنت
  • صفحة عدم الاتصال تُعرَض مع وقت آخر مزامنة + عدد التعديلات المعلّقة + واجهة تخزين
  • حضور المعلّم بالجملة: حدّد 20 طالباً دون اتصال → أعد التحميل → كلّهم مرئيون كمعلّقين → أعد الاتصال → كلّهم مُفرَّغون → لا تكرارات
  • امتحان الطالب: أدخل الإجابات دون اتصال → سلّم دون اتصال → شاشة كاملة "التسليم مُطابور" → أعد الاتصال → مُسلَّم
  • صعود السائق: امسح 5 طلاب في عدم اتصال محاكٍ للنفق → أعد الاتصال → كلّ الخمسة مُسجَّلون
  • المتقدِّم: ارفع مرفق 5 ميغابايت، اقتل الاتصال منتصف الرفع → أعد الاتصال → الرفع يستأنف من الأجزاء المكتملة في OPFS
  • المراسلة: أرسل 3 رسائل دون اتصال → الفقاعة تُظهر queued → أعد الاتصال → كلّها تُظهر sent
  • تبديل النطاق الفرعي: عناصر طابور على school-a → انتقل إلى school-b → الطابور غير مرئي → عُد إلى school-a → الطابور سليم
  • نافذة تثبيت PWA: زر لوحة التحكّم 3 مرّات → يظهر الشريط → قبول → يطلق كـstandalone
  • تحديث SW: انشر SW جديد → يظهر toast → إعادة التحميل تكتمل
  • كل الواجهة بالعربية عند ?lang=ar → السلاسل تُعرَض من اليمين إلى اليسار
  • الدرج المعلّق: زرّ إعادة المحاولة يعمل؛ زرّ التجاهل يعمل؛ كلاهما يؤكّد قبل الإتلاف
  • التخزين شبه ممتلئ: املأ IDB+OPFS إلى 80% → يظهر toast تحذير
  • offlineMode='disabled' → لا SW مُسجَّل
  • إبطال WebSocket: اكتب من جهاز أ → live query لجهاز ب يُحدَّث خلال ثانية واحدة
  • واجهة التعارض: حاكِ تعارض نسخة → <ConflictResolver> يُظهر كلا الجانبين → المستخدم يختار احتفاظ/تجاهل
  • مُحلّل الخيط الرئيسي لا يُظهر مهام طويلة > 16 مللي ثانية أثناء تفريغ مزامنة 50 عنصراً

معايير القبول — "عمل دون اتصال بدرجة إنتاجية مُسلَّم"

  1. مع School.offlineMode='full'، معلّم على chrome://devtools network: Offline يُسجّل الحضور، يُعيد التحميل، يُغلق التبويب، يعيد الفتح — كل العلامات معلّقة؛ إعادة الاتصال تُعيد التشغيل بصفر تكرار وصفر حجب للخيط الرئيسي.
  2. طالب يفقد الاتصال لـ10+ دقائق منتصف الامتحان، يستمرّ في الإجابة، يضغط تسليم، يرى "التسليم مُطابور"؛ إعادة الاتصال تكتمل خلال 30 ثانية بصفر فقدان بيانات.
  3. حدث SW sync-forms يطلق عند إعادة الاتصال؛ يُفرّغ طابور Dexie دون أن تكون الصفحة مفتوحة.
  4. إغلاق/إعادة فتح المتصفّح تحافظ على الطابور (Dexie، ليس في الذاكرة).
  5. تبديل النطاق الفرعي school-a → school-b لا يُعيد تشغيل طابور school-a ضدّ school-b.
  6. Playwright tests/offline.spec.ts أخضر عبر مسارات الأدوار الأربعة + أمان المستأجر + حلّ التعارضات.
  7. كل سلاسل واجهة عدم الاتصال تُعرَض في en و ar من مفاتيح القاموس؛ صفر إنجليزية مدمجة في grep.
  8. offlineMode='disabled' يُعطّل تسجيل SW كلّياً.
  9. حزمة الأساس ≤ 35 KB مضغوطة gzip (بوّابة CI). حزم كل ملحمة ≤ 15 KB لكلٍّ منها.
  10. القياس في Sentry يُظهر ≥ 99.9% معدّل نجاح مزامنة على نافذة متدحرجة 7 أيام عبر كل مدارس full.
  11. مراقب long-task للخيط الرئيسي يُظهر صفر حجب > 16 مللي ثانية أثناء تفريغ المزامنة في قياسات p99.
  12. كمون إبطال ذاكرة WebSocket p95 ≤ 200 مللي ثانية في الإنتاج.
  13. استئناف مرفقات OPFS يعمل بعد قتل الرفع منتصف الطيران على iOS 16.4.

الملفّات الرئيسية

المسارالدور
src/lib/offline/db.ts (جديد)مصنع Dexie tenant DB + إصدارات المخطّط
src/lib/offline/opfs.ts (جديد)تجريد OPFS (putBlob/getBlob/deleteBlob)
src/lib/offline/registry.ts (جديد)خريطة actionRegistry المكتوبة
src/lib/offline/types.ts (جديد)أنواع envelope التعديل discriminated-union
src/lib/offline/state-machine.ts (جديد)دورة حياة التعديل XState
src/lib/offline/compression.ts (جديد)خطّ ضغط LZ4 + MessagePack
src/lib/offline/integrity.ts (جديد)تحقّق بصمة محتوى SHA-256
src/lib/offline/sync-worker.ts (جديد)محرّك مزامنة Web Worker
src/components/offline/use-mutation-queue.ts (جديد)خطّاف الطابور الدائم المعياري
src/components/offline/use-network-status.ts (جديد)خطّاف حالة الشبكة المركَّب
src/components/offline/use-offline-mutation.ts (جديد)غلاف react-query للتعديلات
src/components/offline/use-live-query.ts (جديد)غلاف Dexie live query
src/components/offline/use-storage-estimate.ts (جديد)خطّاف تفاعلي لـnavigator.storage.estimate()
src/components/offline/provider.tsx (جديد)<OfflineProvider>
src/components/offline/pending-mutations-drawer.tsx (جديد)واجهة الطابور قيد التشغيل
src/components/offline/conflict-resolver.tsx (جديد)عنصر أوّلي لواجهة الدمج المُهيكَلة
src/components/offline/install-prompt-banner.tsx (جديد)واجهة التقاط beforeinstallprompt
src/components/offline/update-available-toast.tsx (جديد)واجهة تدفّق تحديث SW
src/components/offline/cached-data-banner.tsx (جديد)مؤشّر "البيانات من ذاكرة مؤقتة"
src/components/offline/devtools.tsx (جديد)لوحة <OfflineDevtools> (DEVELOPER فقط)
src/app/api/offline/sync/[domain]/route.ts (جديد)نقاط نهاية شظايا مزامنة لكل مجال
src/app/api/cache-invalidate/route.ts (جديد)بثّ إبطال ذاكرة WebSocket
src/app/api/uploads/resumable/[uploadId]/[partNumber]/route.ts (جديد)رفع مقسّم بنمط tus
public/service-worker.jsاستبدل stub syncOfflineForms() في السطر 160
prisma/models/offline-sync.prisma (جديد)OfflineMutation + OfflineSyncLog + CacheInvalidation
src/components/school-dashboard/attendance/actions/bulk.ts:33سجّل كـ"attendance.bulkUpload"
src/components/school-dashboard/exams/take/actions.ts:369سجّل كـ"exam.submit"
src/components/school-dashboard/attendance/geofencee/geo-tracker.tsx:59تقاعد LocationQueue؛ استخدم العناصر الأوّلية المشتركة
src/components/school-dashboard/attendance/shared/hooks.ts:541استبدل useOfflineQueue في الذاكرة
src/lib/performance-optimization.tsاربط كتبعية useNetworkStatus
src/app/manifest.tsأضف اختصارات /exams، /messages، /timetable
src/components/internationalization/{school-en,school-ar}.jsonأضف مفاتيح dictionary.offline.*
playwright/tests/offline.spec.ts (جديد)E2E يغطّي مسارات الأدوار الأربعة + المستأجر + التعارضات

المسرد

  • معالج إجراء (Action handler). إجراء خادم مسجَّل في actionRegistry بسلسلة اسم. نفس الدالة المُستدعاة عبر الإنترنت أو عبر إعادة تشغيل دون اتصال.
  • هيكل التطبيق (App shell). الحدّ الأدنى من HTML و CSS و JS اللازمة لعرض كروم التنقّل. يُخزَّن مسبقاً وقت التثبيت.
  • Background Sync. واجهة برمجة متصفّح تطلق حدث sync عندما يكون الجهاز متّصلاً. دعم متصفّح محدود.
  • Comlink. مكتبة لـRPC مريح مع Web Worker.
  • حلّ التعارض. سياسة تُطبَّق عندما لا يمكن تطبيق تعديل مُطابور بنظافة. الافتراضي خادم-مرجع مع واجهة دمج مُهيكَلة.
  • بصمة محتوى (Content hash). SHA-256 لحمولة التعديل، محفوظة على envelope. يُتحقَّق منها عند الاستقبال وإعادة التشغيل. دليل عبث.
  • Dexie. مكتبة غلاف فوق IndexedDB مع دلالات استعلام غنية، معاملات، live queries، إصدارات مخطّط.
  • مفتاح idempotency. cuid v2 مولَّد من جانب العميل مرفق بكل تعديل. الخادم يستخدم OfflineSyncLog لإزالة التكرار.
  • IDB. IndexedDB، متجر مفتاح-قيمة مفهرس أصلي للمتصفّح.
  • LZ4. خوارزمية ضغط سريعة؛ مستخدمة هنا عبر WASM لضغط الحمولة > 1 KB.
  • MessagePack. صيغة تسلسل ثنائية. أكثر إحكاماً من JSON لترميز envelope.
  • مظروف التعديل. الغلاف المكتوب حول كتابة مُطابورة. يحمل metadata لإعادة التشغيل بما في ذلك مفتاح idempotency وبصمة المحتوى.
  • Network Information API. navigator.connection.effectiveType / saveData / rtt. مستخدم لتكييف الواجهة مع جودة الاتصال.
  • OPFS. Origin Private File System. نظام ملفّات أصلي للمتصفّح محدود بالأصل؛ أفضل من IDB للكتل الكبيرة.
  • PWA. تطبيق ويب تقدّمي. تطبيق ويب قابل للتثبيت مع قدرة دون اتصال.
  • التخزين المسبق. تخزين الأصول أو البيانات قبل أن يحتاج إليها المستخدم دون اتصال.
  • استقبال المزامنة. POST /api/offline/sync/{domain}، نقاط النهاية لكل مجال التي تستقبل التعديلات المُطابورة.
  • عامل المزامنة. sync-worker.ts، Web Worker الذي يُعالج كل إعادة تشغيل دون اتصال خارج الخيط الرئيسي.
  • معزول لكل مستأجر. تخزين مفهرس بـschoolId بحيث لا تتسرّب البيانات عبر المدارس.
  • XState. مكتبة لنمذجة آلة الحالة المنتهية. مستخدمة لدورة حياة التعديل.

انظر أيضاً

  • تعدّد المستأجرين — قواعد تحديد نطاق schoolId التي يجب أن تحترمها كل كتابة دون اتصال
  • قاعدة البيانات — بروتوكول أمان Prisma + Neon للجداول الجديدة
  • Notifications — أنماط ذاكرة جانب القراءة تشارك البنية التحتية
  • الترجمة — اتفاقيات مفاتيح القاموس لسلاسل عدم الاتصال
  • Document Intelligence — قالب وثيقة الملحمة السابق الذي تتّبعه هذه الوثيقة
  • البنية المعمارية — نمط المرآة وحلّ سياق المستأجر
  • MVP — اتفاقيات شارات النضج لتتبّع تقدّم الملحمة
  • خارطة الطريق — نظرة ربعية لإطلاق عدم الاتصال
الفهرس (Catalog)المصادقة

On This Page

لماذا هذا مهمكيف يبدو النجاحمبادئ هندسية (بلا تنازلات)قرارات معمارية للأساسما شُحن في 2026-08-29/30 — صندوق الصادر، وسياسةالنطاقالسياق المعماريحقائق المخطّطالأنماطالحالة الحالية مقابل الحالة المستهدفةسياسة حلّ التعارضات لكل إجراءمصفوفة توافق المتصفحاتميزانية التخزين لكل دورميزانيات الأداءالمرحلة التمهيدية (الأسبوع 0): استكشافات وأبحاثالقصص لكل ملحمةالملحمة-0 — الأساس: محرّك عمل دون اتصال بدرجة إنتاجية (62 نقطة)الملحمة-1 — قابلية تثبيت PWA + تخزين هيكل التطبيق مسبقاً (15 نقطة)الملحمة-2 — ذاكرة قراءة ذكية (24 نقطة)الملحمة-3 — المعلّم دون اتصال (الحضور + QR + التصحيح) (32 نقطة)الملحمة-4 — الطالب دون اتصال (تسليم الامتحان + سجلّ المراقَبة) (22 نقطة)الملحمة-5 — السائق دون اتصال (صعود الرحلة) (15 نقطة)الملحمة-6 — المتقدِّم دون اتصال (مرفقات قابلة للاستئناف + تسليم) (18 نقطة)الملحمة-7 — طابور المراسلة الصادرة (16 نقطة)الملحمة-8 — قابلية المراقبة + تحليلات عدم الاتصال (14 نقطة)الملحمة-9 — الوثائق والتعريب وإعداد المسؤول (13 نقطة)استراتيجية الانتقالمتعدّد الجوانبإضافات المخطّطالتحكّم في الوصول حسب الدورالتعريباستراتيجية الاختبارالقياس عن بُعد والمقاييسالإطلاق التدريجي (علم الميزة)إعداد المطوّربوّابة الأساسالمخاطر والمقايضاتالجدول الزمني التفصيلي (14 أسبوعاً، أسبوع-بأسبوع)دليل الإطلاق التجريبيقائمة اختبار التحقّق السريعمعايير القبول — "عمل دون اتصال بدرجة إنتاجية مُسلَّم"الملفّات الرئيسيةالمسردانظر أيضاً

من تطوير Databayt ·

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

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