- ابدأ الآن
- البيئة المحلية
- البنية المعمارية
- الهيكل
- الأنماط
- الحزمة التقنية
- قاعدة البيانات
- تعدّد المستأجرين
- الفهرس (Catalog)
- العمل دون اتصال
- المصادقة
- التهيئة (Onboarding)
- تدفق التكامل (Integration Flow)
- التهيئة (Provision)
- القبول
- الحضور
- الامتثال التنظيمي
- الملف الشخصي
- الاجتماعات
- إدارة الرسوم
- الفاتورة
- التدويل
- الترجمة
- دليل الترجمة
- أمان قاعدة البيانات
- المساهمة
- الإلهام
- المالية
- النقل
كتلة واحدة قائمة بذاتها في src/components/school-dashboard/live/، منعكسة بنسبة 1:1 على المسار
/live. تشغّل ثلاثة خلفيات اجتماع خلف واجهة واحدة: مزوّد الرابط الخارجي الملصوق فعّال في كل
مكان؛ وخادم LiveKit SFU (غرف داخل التطبيق) مكتوب بالكامل ولا يفصله عن التشغيل سوى أربعة متغيرات
LIVEKIT_* — راجع RUNBOOK.md لمسار LiveKit Cloud السريع؛ والتسجيل بوابة منفصلة لم تعد تمنع فتح
الغرفة؛ ومحوّلات Meet / Zoom / Teams الأصلية تنشئ اجتماعات حقيقية عبر واجهة كل مزوّد لكنها تبقى
خاملة حتى تصل بيانات اعتماد OAuth.
كيف يعمل
صورة واحدة للأمر كلّه: ما تقرّره المدرسة مرّة واحدة، وما يفعله التطبيق وحده كلّ يوم دراسي، وما يراه الطالب فعليًا.
ثلاثة أمور تستحقّ قراءةً ثانية:
- الانتقال إلى الإنترنت لا يُغلق المبنى. الحصة المباشرة شيء تُضيفه إلى الحصة لا شيء يحلّ محلّها، فقد تكون المدرسة مفتوحة وعلى الإنترنت في اليوم نفسه.
- لا أحد يجدول الحصص واحدةً واحدة. المدرسة تقول كيف تُدرِّس، فيتحوّل الجدول الذي تحتفظ به أصلًا إلى حصص مباشرة، كل صباح، وحده.
- الحضور والتسجيلات تدبّر نفسها. لا أحد يرصد كشف حضور، ولا أحد يرفع فيديو.
دليل الاستخدام
إن كنت تدير المدرسة
- قل كيف تُدرِّس المدرسة. المدرسة ← الإعدادات ← الحصص المباشرة (واللوحة نفسها هي تبويب الإعدادات داخل الحصص المباشرة). اختر حضوري أو عبر الإنترنت أو مختلط. «حضوري» يعني لا غرف مباشرة إطلاقًا مهما قالت أي شعبة، وكل ما يلي لا يعني إلا النمطين الآخرين.
- اختر منصّة الاجتماع. غرفة داخل التطبيق تُبقي الحصة داخل التطبيق — بالتسجيل والحضور والسبورة وكل شيء. ورابط خارجي يستخدم روابط Google Meet أو Zoom أو Teams ويعمل بلا أي بنية تحتية. يمكنك اختيار الغرفة الداخلية قبل تجهيزها؛ تعمل الحصص عندئذٍ على الروابط الخارجية بهدوء ثم تنتقل وحدها.
- إن كنت على الروابط الخارجية، فامنح الحصص مكانًا تذهب إليه. اضبط رابط اجتماع دائمًا كأرضية — تهبط إليه كل حصة بلا رابط خاص — ثم أضف رابطًا لكل حصة تدريجيًا. ويخبرك تغطية روابط الاجتماع في الصفحة نفسها إلى أين وصلت.
- قرّر ما يُسمح للحصة أن تكونه. التسجيل مفعّل أو معطّل، ومدّة الاحتفاظ بالتسجيلات، والإشعار الذي يراه الطلاب عند تسجيل الحصة، وهل تُنشر التسجيلات إلى الدرس تلقائيًا. ثم: هل يشاهد أولياء الأمور، وهل يدخل الطلاب بصمت، وأي أدوات الغرفة مفعّلة — الدردشة ورفع اليد والاستطلاعات والسبورة ومشاركة الطلاب لشاشاتهم.
- اضبط التوقيت. كم دقيقة قبل الحصة يصل التذكير، وإن أردت رصد الحضور تلقائيًا: متى يُعدّ الطالب متأخرًا، ومتى يُعدّ حاضرًا، ومتى يُعدّ مغادرًا مبكرًا.
- اضبط الاستثناءات. في النمط المختلط يتجاوز إعداد الصف وإعداد الشعبة افتراضي المدرسة في الاتجاهين، و«افتراضي المدرسة» يعني الوراثة.
- وحين يُغلق شيءٌ المدرسة، استخدم الانتقال المؤقت إلى الإنترنت بتاريخ بداية، ونهاية إن كنت تعرفها. تُنشأ حصص اليوم وأنت ما زلت في الصفحة. ومسح تاريخ البداية يُنهي المدّة — فلا زرّ إلغاء منفصل تنساه.
إن كنت تُدرِّس
- لست أنت من ينشئ حصصك. تظهر لكل حصة في جدولك، كل صباح.
- ابدأ من حيث أنت أصلًا — زرّ الانضمام في بطاقة «اليوم» في جدولك، أو لوحة الحصص المباشرة. وبدؤك للحصة هو ما يفتح الغرفة لبقية الصف.
- تُفتح الحصة على بطاقتها أولًا — صورة المادة والصف واسم المعلّم وموعد الحصة، وزرّ انضمام واحد. لا شيء يبدأ ولا أحد يُستدعى إلى الغرفة قبل أن تضغطه، فيمكنك فتح الحصة لتطّلع عليها دون أن تبدأها. وفوق الصورة رجوع، وإضافة التي تحفظ الحصة في تقويمك، ومشاركة التي تُسلّم رابط الحصة لمن تختار.
- الصفحة تستمر تحت البطاقة. تتوقّف البطاقة قبل أسفل الشاشة عمدًا — عند أربعة أخماسها، على الهاتف كما على الحاسوب. وتحتها أربعة صفوف: بقية يوم الشعبة، تفتح كل حصة غرفتها إن كانت جارية وتعرض تسجيلها إن وُجد؛ والامتحانات والواجبات والروابط المرفقة بهذه الحصة؛ ودروس المادة للدراسة الذاتية؛ والمعلّم مع طلاب الشعبة.
- داخل الغرفة لديك الميكروفون والكاميرا ومشاركة الشاشة وسبورة، وأي ملف PDF مرفق بالدرس يُعرض شرائح بصفحة متزامنة مع الجميع، واستطلاعات، وقائمة أسئلة تستطيع تعليمها كمُجابة، وطابور الأيدي المرفوعة، وإمكانية إزالة أحدهم.
- تستطيع تغيير حصة واحدة دون تغيير المدرسة: في نموذج الحصة إعداد دخول الطلاب — افتراضي المدرسة، أو بصمت، أو بميكروفون وكاميرا مفعّلين.
- أنهِ الحصة حين تفرغ. يُكتب الحضور من مدّة وجود كل طالب في الغرفة عبر الانقطاعات وإعادات الاتصال. وإن كانت الحصة مسجَّلة، تُظهر صفحة الحصة تجهيز التسجيل ثم جاهزيته.
إن كنت طالبًا
- يظهر زرّ الانضمام حيث يومك أصلًا — صفوف «اليوم» في جدولك، وبطاقة الحصة الحالية أو التالية، وصفحة الحصص المباشرة، وإشعار التذكير. ويظهر حين تكون الحصة جارية أو على وشك البدء.
- تدخل بميكروفون مغلق وكاميرا معطّلة ما لم يفتح المعلّم الحصة على غير ذلك. شغّلهما حين يُطلب منك.
- ارفع يدك، أو اطرح سؤالًا، أو صوّت في استطلاع، أو استخدم الدردشة — ما فعّلته مدرستك منها.
- الاتصال الضعيف مُعالَج نيابةً عنك. تنخفض جودة الصورة قبل أن تنقطع، ثم إلى الصوت مع الشرائح. وإن انقطعت، يُعرض عليك إعادة الانضمام بدل إخراجك.
- وإن فاتتك الحصة وكانت مسجَّلة، تجدها فيديوَ الدرس. تعمل داخل التطبيق، ولا يمكن تنزيلها، ويحمل كلّ إطار منها علامة غير مرئية مرتبطة بحسابك.
إن كنت وليّ أمر
افتح بوابة أولياء الأمور ← ابنك أو ابنتك ← الجدول. تحمل حصص اليوم زرّ الانضمام نفسه، وتدخل بصفة مشاهد — ترى وتسمع، ولا تتحدّث ولا تستخدم الكاميرا ولا تطرح أسئلة. ويمكن لمدرستك تعطيل ذلك تمامًا.
البنية
src/components/school-dashboard/live/— one block, mirrored 1:1 to /livecontent.tsx— server list (fetch + batched translate → table)queries.ts— read-only Prisma buildersactions.ts— server-action barrellist-actions.ts— list-layer CRUD (create/update/delete/list)online-policy.ts— is this online, over which back-end, delivered how (one resolver)day-window.ts— school-calendar day math (pure, DST-safe)school-calendar.ts— holiday gate for the sweep (write side only)actions/— rich sessions-layer server actionshelpers.ts— requireContext · canAccessSessionslot-session.ts— day-qualified slot lookup + cron writermaterialize-day.ts— online policy → one school day of sessionsopen-room.ts— loose mode — one standing room per section per daysessions.ts— lifecycle state machinejoin-core.ts— shared eligibility + token mint (action + route)tokens.ts— join → 5-min JWTrecordings.ts— list / signed-URL / deletenotifications.ts— 5 events → notification hub (in-app + email)attendance-sync.ts— presence → attendance (opt-in, LiveKit-only)settings.ts— capacity · recording · attendance sync · online window + mode + standing linkmoderation.ts— kickParticipantrecurring.ts— link carry-forward across termsauthorization.ts— PERMISSION_MATRIX (strict RBAC)list-permissions.ts— UI gating + CRUD guards (list layer)validation.ts— Zod (sessions)list-validation.ts— Zod (table)list-params.ts— nuqs URL cachenav.tsx— heading + tab strip for the (app) surfaceslanding/— the /live landing pagecontent.tsx— composes the sections; gates on role + online statestatus-hero.tsx— the green banner: the headline beside a line marknow-strip.tsx— live / coming-up, with catalog subject artsession-row.tsx— the shared article row, drawn by both blockspast-shelf.tsx— classes already taught, beside their subject tilesreadiness-band.tsx— can the school teach online (admins only)role-guide.tsx— what THIS role can do hereget-started-band.tsx— pitch + 3 setup steps, only while not yet onlinereveal.tsx— the one motion wrapper (get-started band only)viewer.ts— role → what the page offers (single source)types.ts— section props · LandingSession · policy · readinesstable.tsx— DataTable + gridcolumns.tsx— column defs + action menuform.tsx— create/edit 5-step wizard (modal)form-steps.tsx— wizard steps: basics · schedule · meeting · references · accessschedule-form.tsx— standalone schedulersettings-form.tsx— admin school-policy formsection-recording-policy.tsx— per-section recording opt-outsection-online-policy.tsx— per-section online override (inherit/on/off)detail.tsx— single-session detailroom.tsx— pre-join title card, then the in-app LiveKit roomparticipants-panel.tsx— HOST/CO_HOST in-room kick UIrecordings.tsx— recordings listrecording-player.tsx— lazy signed-URL playernetwork-test.tsx— LiveKit RTT/ICE diagnosticloading-skeleton.tsx— loading skeletontypes.ts— domain types + RoomJoinTicketerror-map.ts— error-code → stringnetwork-protocol.ts— pure ICE-path classifierlivekit/— SFU server SDK wrappersclient.ts— singletons + readinesstoken.ts— role → VideoGrantrooms.ts— room lifecycleegress.ts— recording egressrecording-urls.ts— signed S3 URLsroom-naming.ts— sch-{schoolId}-lc-{id}webhook.ts— HMAC verify + idempotent routerproviders/— link-provider adapterstypes.ts— adapter contractexternal.ts— pasted link (live)google-meet.ts— wired, darkzoom.ts— wired, darkteams.ts— wired, darktoken-cache.ts— OAuth token cacheindex.ts— registryREADME.md— provisioning referenceRUNBOOK.md— LiveKit 6-gate provisioningREADME.md— feature purpose, routes, statusISSUE.md— open backlog and follow-upsCLAUDE.md— block-level contextالمسارات تعكس هذه الكتلة (انظر المسارات). نماذج Prisma الستة تقع في
prisma/models/conference.prisma. الاختبارات تقع تحت src/tests/ (إعادة تنظيم
بمحاكاة المسارات)، لا في مجلّد __tests__/ داخل الكتلة.
المكوّنات
تتعايش طبقتان للصلاحيات والتحقّق عن قصد: طبقة الجلسات الغنية (authorization.ts
· validation.ts) هي البوابة الصارمة وقت التشغيل لتدفّق غرفة LiveKit (الانضمام / الرمز / البدء /
الإنهاء)، بينما طبقة القائمة (list-*.ts) هي بوابة الـ CRUD لجدول لوحة التحكّم وجلسات الروابط
الخارجية.
| الملف | الغرض |
|---|---|
content.tsx | مكوّن خادمي — يجلب القائمة وخيارات النموذج في مسار متوازٍ واحد، ترجمة مُجمّعة، ثم يعرض الجدول. |
actions.ts + actions/ | إجراءات خادمية: sessions (دورة الحياة)، tokens (الانضمام)، recordings، notifications، settings، moderation (الطرد)، recurring، helpers. |
list-actions.ts · queries.ts | CRUD لطبقة القائمة (create/update/delete/list) + بُناة استعلامات للقراءة فقط. |
authorization.ts | PERMISSION_MATRIX — 10 أفعال إجراء → UserRole[] المسموح؛ حُرّاس assert/check. |
list-permissions.ts | التحكّم في الواجهة (التبويبات + الإظهار/الإخفاء حسب الدور) للطبقتين الغنية والقائمة. |
validation.ts · list-validation.ts | مخطّطات Zod والأنواع المستنتجة (جلسات غنية / نماذج الجدول). |
columns.tsx · table.tsx · form.tsx · schedule-form.tsx · list-params.ts | واجهة DataTable والشبكة على العميل ونموذجا الإنشاء (المنبثق + صفحة مستقلّة). |
landing/ · nav.tsx | صفحة /live (لافتة الواجهة · شريط المباشر/القادم · رفّ ما فاتك · تسجيلان مرتّبان · شريط الجاهزية · دليل الدور · شريط البدء) والعنوان وشريط التبويبات فوق أسطح (app). |
detail.tsx · room.tsx · participants-panel.tsx | تفاصيل الجلسة · غرفة LiveKit داخل التطبيق · طبقة إشراف المضيف/المضيف المساعد (الطرد). |
recordings.tsx · recording-player.tsx · section-recording-policy.tsx | قائمة التسجيلات · مشغّل برابط موقّع كسول · إلغاء التسجيل لكل شعبة. |
settings-form.tsx | نموذج سياسة المدرسة للمدير (الاحتفاظ / الحد الأقصى للتزامن / المدة القصوى / التسجيل الافتراضي) + ترحيل الفصول. |
network-test.tsx · network-protocol.ts | تشخيص اتصال LiveKit + مُصنِّف مسار ICE نقي (getLiveKitReadiness() / classifyFromStats). |
types.ts · error-map.ts | أنواع النطاق و RoomJoinTicket · ربط رموز الأخطاء بالنصوص المترجمة. |
livekit/ | أغلفة SDK لخادم SFU: client (الإعداد والجاهزية)، token، rooms، egress، recording-urls، room-naming، webhook. |
providers/ | محوّلات مزوّدي الروابط: types، external (فعّال)، google-meet/zoom/teams (خاملة)، token-cache، سجلّ index. |
تدفّق البيانات
landing (/live) landing/content.tsx (server) → لافتة الواجهة · live/upcoming strip · catch-up shelf · recordings pair · bands
└─ عرض الجلسات
index (/live/dashboard) content.tsx (server) → table.tsx (client) → form.tsx (modal create)
└─ row Join / detail
detail (/live/[id]) detail.tsx → Join (live/scheduled) | Recordings (ended)
└─ Join
room (/live/[id]/room) joinLiveClass → eligibility → 5-min JWT → room.tsx (LiveKit prebuilt)
↑ refreshLiveClassToken every ~60s re-runs eligibility
recordings (/[id]/recordings) recordings.tsx → recording-player.tsx (signed S3 URL, 4h; re-mints on expiry via onError)
- آلة حالة الجلسة (
actions/sessions.ts):scheduled → live → ended، إضافةً إلىcancelledوfailed. يجهّزstartLiveClassغرفة الـ SFU (يتحقّق منconferenceMaxConcurrent)؛ وendLiveClassعديم التأثير عند التكرار. الـ Webhook هو الكاتب الموثوق لحالتيlive(room_started) وended(room_finished). - الأهلية تُحَلّ بكسل عند كل انضمام (
actions/tokens.ts): المعلّم→مضيف (لجلسته) / مضيف مساعد، الطالب→مشارك (مطابقة الشعبة)، وليّ الأمر→مراقب (ابنه في الشعبة)، المدير→مضيف مساعد، المطوّر→مضيف. المستخدمون المطرودون (status="removed") يُحجبون عند تحديث الرمز التالي. - التسجيلات (
pending → processing → ready → expired): LiveKit Egress → S3me-central-1؛ الاحتفاظ لكل مدرسة تفرضه المهمة المجدولة اليومية. - الإشعارات (
actions/notifications.ts): جميع أحداثlive_class_*الخمسة تحدّد جمهورها (المعلّم + طلاب الشعبة + أولياء الأمور) وتُرسَل عبر الموزّع المشترك (dispatchNotificationsToAudience)، فتصل إلى جرس التطبيق والبريد الإلكتروني، وتحترم تفضيلات القنوات لكل مستخدم، وتحمل رابطاً مطلقاً. يُرسِل كلا المسارين: غرفة التطبيق (sessions.ts) ومسار الرابط الخارجي (list-actions.tsإنشاء / تعديل / إلغاء). - الحضور (
actions/attendance-sync.ts، اختياري عبرSchool.conferenceAttendanceSync): عند انتهاء الجلسة (Webhookroom_finishedأو مهمة الجلسات المعلّقة)، يسجّلsyncConferenceAttendanceكل طالب في الشعبة حاضراً/متأخراً حسب وجوده في الغرفة، وغائباً لمن لم ينضمّ، بكتابة سجلاتAttendanceبالطريقةVIRTUAL. يتطلّبsectionId+timetableId؛ LiveKit فقط — الروابط الخارجية لا تحمل بيانات حضور. عديم التأثير عند التكرار (مفتاح الشعبة الفريد، إحياء عند التحديث).
المسارات
| المسار | التخطيط | يعرض | الأدوار |
|---|---|---|---|
/live | school-dashboard | landing/content.tsx — صفحة الوصول | كل أدوار المدرسة السبعة |
/live/dashboard | school-dashboard (app) | فهرس DataTable | كل أدوار المدرسة السبعة |
/live/schedule | school-dashboard (app) | schedule-form.tsx | المطوّر · المدير · المعلّم |
/live/settings | school-dashboard (app) | settings-form.tsx + section-recording-policy.tsx | المطوّر · المدير |
/live/network-test | school-dashboard (app) | network-test.tsx | المطوّر · المدير (محكوم بالبيئة) |
/live/[id] | school-dashboard | detail.tsx | كل أدوار المدرسة السبعة |
/live/[id]/recordings | school-dashboard | recordings.tsx | الكل عدا المحاسب (أكاديمي لا مالي) |
/live/[id]/room | (live-room) | room.tsx | أي مشارك جلسة مُصادَق |
/live-classes/* | — | إعادة توجيه → /live | حشوة توافق قديمة (روابط ما قبل إعادة التسمية) |
(app) مجموعة مسارات: لا تضيف مقطعاً إلى الرابط، بل تضيف العنوان وشريط التبويبات المشترك فقط
(الجلسات · الجدولة · الإعدادات · فحص الشبكة، بحسب الدور). يقع /live و/live/[id]
خارجها عمداً — صفحة الوصول تملك واجهتها الرئيسة، وتفاصيل الجلسة ورقة يُوصل إليها من صفٍّ لا من تبويب.
تقع غرفة المسار تحت مجموعة (live-room) منفصلة بتخطيط عارٍ بملء الشاشة (بلا شريط جانبي / زخارف
لوحة التحكّم) كي يمتلك بثّ الـ SFU الشاشة؛ وهي force-dynamic لبناءات آمنة من نفاد الذاكرة. واجهات
API (مُستدعاة خارجياً / عبر المجدول / بالاستطلاع → تبقى مسارات، لا إجراءات خادمية): api/webhooks/livekit (HMAC)،
api/live/token (تجديد رمز الغرفة كل ~4 دقائق — مسار GET لأن استطلاعاً عبر إجراء خادمي يشحن
إعادة تصيير RSC كاملة مع كل استجابة)، api/cron/live-class-reminders (كل 15 دقيقة، بنافذة 5–20 دقيقة
غير مكرِّرة)، api/cron/end-stale-live-classes (كل 30 دقيقة — يُنهي الجلسات العالقة في حالة live
بعد وقتها ويشغّل مزامنة الحضور)، api/cron/expire-live-recordings (يومياً).
المخطّط — prisma/models/conference.prisma
LiveClass* → Conference*؛ مع الحفاظ على كل اسم جدول / عمود / نوع تعداد عبر @@map/@map →
ترحيل بلا تغيير (فرق migrate diff مطابق تماماً للأساس).
| النموذج | الغرض |
|---|---|
Conference | جلسة. provider (livekit|external)، meetingUrl?، visibility (section|school)، catalogLessonId?. |
ConferenceParticipant | صفّ لكل مستخدم مدعوّ + قياسات الاتصال (RTT، علامة الرجوع لـ TCP). |
ConferenceRecording | بيانات الـ Egress وموقع S3 + expiresAt للاحتفاظ. |
ConferenceEvent | سجلّ تدقيق الـ Webhook + eventId @unique لضمان عدم التكرار. |
ConferenceLink | رابط متكرّر يُضبط مرة واحدة [schoolId, subjectId, sectionId, termId]. |
ConferenceResource | مرجع مرفق — واحد فقط من schoolExamId / schoolAssignmentId / رابط http(s) في كل صف. |
التعدادات ConferenceProvider · ConferenceStatus · ConferenceRecordingStatus ·
ConferenceParticipantRole · ConferenceParticipantStatus · ConferenceVisibility؛ و5 أعمدة إعداد
School.conference* (conferenceRetentionDays 90 · conferenceMaxConcurrent 50 ·
conferenceMaxDuration 120 · conferenceRecordingDefault true · conferenceAttendanceSync false).
التحكّم بالوصول — خاص أم على مستوى المدرسة
visibility يحدّد من يرى الجلسة وينضم إليها، فوق حدود المستأجر المطلقة:
section(الافتراضي) — طلاب شعبة الجلسة ينضمون كـ PARTICIPANT وأولياء أمورهم كـ OBSERVER؛ الجلسة بلا شعبة تكون للمضيف فقط (اجتماع خاص).school— كل عضو في المدرسة: الطلاب PARTICIPANT، أولياء الأمور OBSERVER، الموظفون والمحاسب PARTICIPANT. للطوابير والندوات واللقاءات العامة. جدولتها تُشعر المدرسة كاملة عبر مركز الإشعارات.
لا يوجد عمداً أي مستوى عابر للمدارس أو مجهول — الأهلية وتقييد القوائم وقراءة التفاصيل والوصول
للتسجيلات كلها تفرض القاعدة نفسها (resolveParticipantRole، canAccessSession،
buildLiveClassWhere). روابط الاجتماع والمراجع مقفولة على http(s) — تُرفض javascript:/data:
عند التحقق.
المراجع — درس، كويز، واجب، روابط
مفتاح catalogLessonId واحد يربط الجلسة بدرس من المنهج؛ فتعرض صفحة الجلسة تلقائياً فيديوهات
الدرس ومرفقاته ومواده وعدد أسئلة التدريب. الاختبارات والكويزات (SchoolExam — الكويز هو
examType: QUIZ وليس نموذجاً منفصلاً) والواجبات والروابط الحرة تُرفق كصفوف ConferenceResource —
تُتحقق ملكيتها للمدرسة قبل الكتابة وتُستبدل كمجموعة عند التعديل. خطوة «المراجع» في المعالج تحمّل
خياراتها حسب المادة المختارة (getLiveClassReferenceOptions، لطاقم الإدارة فقط).
معالج الإنشاء
نموذج اللوحة معالج مضغوط من 5 خطوات (form.tsx + form-steps.tsx) على نمط النوافذ متعددة الخطوات
المعتمد — الأساسيات ← الجدولة ← الاجتماع ← المراجع ← الوصول، بضعة حقول في كل خطوة.
تبدأ خطوة «الأساسيات» بمنتقي الحصة: حصص الجدول الدراسي الحقيقية للفصل الدراسي النشط
(«الرياضيات · الصف 1-أ · الأحد 08:00»). هذا ما يجعل الجلسة تقديماً إلكترونياً لحصة واقعية لا
اجتماعاً منفصلاً — فاختيار حصة يملأ المعلّم والمادة والشعبة ونافذة وقت الحصة وأقرب تاريخ يقع فيه
ذلك اليوم، ويخزّن timetableId. هذا الارتباط جوهري: به تستطيع الجلسة رصد الحضور ممّن ينضم فعلاً
(تحتاج المزامنة إلى كشف الشعبة و الحصة)، وبه تظهر الحصة كنقطة «مباشر/مجدول» على شبكة الجدول
الأسبوعي. الخادم يعيد اشتقاق المعلّم والمادة والشعبة من صف الحصة، فالجدول — لا النموذج — هو مصدر
الحقيقة. اترك المنتقي على «غير مرتبطة بحصة» للطوابير الصباحية ولقاءات أولياء الأمور والدروس
المنفردة. ولا يمكن تغيير الارتباط لاحقاً (فإعادة الربط تُعيد ترقيم حضور مكتوب سلفاً).
ويتبع منتقيا المادة والدرس الكتالوج، مضبوطاً على صف الشعبة: المواد هي ما اعتمدته المدرسة لذلك الصف (بالاسم الذي تستخدمه المدرسة لها)، ودروس الكتالوج تأتي من فصول ذلك الصف. وتُنشأ كل جلسة بحالة «مجدولة»؛ أما البدء والإنهاء فخطوتان منفصلتان محروستان.
خطوة «الاجتماع» تختار الخلفية: غرفة داخل المنصة (LiveKit — الافتراضي متى كان isLiveKitConfigured()،
مع مفاتيح التسجيل والحد الأقصى للمشاركين) أو رابط خارجي (رابط Meet/Zoom/Teams + خيار الرابط
الأسبوعي المتكرر). لا يتغيّر المزوّد بعد الإنشاء؛ وزر الانضمام واعٍ بالمزوّد في كل مكان (قائمة
الجدول، صفحة التفاصيل، ومسار الغرفة يحوّل الجلسات الخارجية إلى رابط المزوّد).
التدريس عبر الإنترنت
أول إعداد هو كيف تُدرِّس المدرسة — حضوري أو عبر الإنترنت أو مختلط — من المدرسة ← الإعدادات ← الحصص المباشرة (وهو نفسه تبويب الإعدادات داخل الحصص المباشرة). حضوري يعني لا غرف مباشرة ولا أزرار انضمام، مهما قالت أيّ شعبة. عبر الإنترنت يعني أنّ كل حصة في الجدول حصة مباشرة. المختلط هو حيث تعيش الخيارات التالية: افتراضي للشُّعب، وتجاوزات لكل شعبة، ونافذة مؤقتة عبر الإنترنت. التحويل إلى حضوري يسري على الحصص الجديدة؛ وحصص اليوم المجدولة مسبقًا تبقى وتُغلق تلقائيًا.
إلى جانب النمط، تحوي اللوحة نفسها: منصّة الاجتماع والرابط الدائم؛ كيف تعمل الحصص عبر الإنترنت (الجدول، غرفة مفتوحة، كلاهما)؛ التسجيل ومدّة الاحتفاظ والإشعار الذي يراه الطلاب عند دخول حصة مسجَّلة ونشر التسجيلات إلى الدرس تلقائيًا؛ هل يشاهد أولياء الأمور؛ هل يدخل الطلاب بصمت (ويمكن للحصة تجاوز ذلك)؛ أدوات الغرفة المفعّلة — الدردشة ورفع اليد والاستطلاعات والسبورة ومشاركة شاشة الطلاب؛ كم دقيقة قبل الحصة يُرسَل التذكير؛ حدود الحضور؛ تجاوزات التدريس عبر الإنترنت لكل صف ولكل شعبة؛ وبيانًا للقراءة فقط بسياسة حماية المحتوى مع مساحة التسجيلات المستخدَمة.
المدرسة التي تقدّم حصصها عبر الإنترنت لا تجدول كل حصة على حدة، بل تعلن الأمر مرة واحدة فتتحول كل حصة في الجدول الدراسي إلى حصة مباشرة.
التحوّل إلى الإنترنت لا يُغلق المدرسة. فالتقديم عبر الإنترنت إضافة — قناة مباشرة تُفتح إلى جانب الحصة الحضورية — ومن ثم يمكن للمدرسة أن تكون مفتوحة وعلى الإنترنت في اليوم نفسه، أو على الإنترنت يوم الأحد وحضوريًا يوم الإثنين، أو على الإنترنت لشعبة دون أخرى. ولا شيء هنا يُلغي حصة.
هناك ثلاثة مفاتيح تجعل الحصة على الإنترنت، وهي تتكامل ولا تتنافس:
| المفتاح | الموضع | المعنى |
|---|---|---|
| التدريس عبر الإنترنت | /live/settings | الإعداد الدائم: «نحن مدرسة على الإنترنت». |
| التدريس عبر الإنترنت حسب الشعبة | الصفحة نفسها، لكل شعبة | يتجاوز إعداد المدرسة في الاتجاهين. و«إعداد المدرسة» يعني الاتّباع. |
| التحوّل إلى الأونلاين مؤقتاً | الصفحة نفسها، بمدى زمني | مفتاح الطوارئ — أدناه. يرفع إعداد المدرسة لتلك الأيام. |
وقرار الشعبة يتقدّم دائمًا على الإعدادين العامّين، حتى أثناء الطوارئ: فالفترة المؤقتة ترفع الإعداد الافتراضي للمدرسة ولا تُلغي قرارًا اتُّخذ بشأن شعبة بعينها. والشعبة المستثناة التي تريد إعادتها إلى الإنترنت تحتاج نقرة واحدة — أعِدها إلى «إعداد المدرسة».
التحوّل إلى الإنترنت في وقت قصير
الطقس، طريق مغلق، وباء، حرب. من /live/settings → التحوّل إلى
الأونلاين مؤقتاً:
- من — أول يوم. مطلوب.
- إلى — آخر يوم، شامل. اتركه فارغًا للبقاء على الإنترنت حتى إشعار آخر، وهو ما تعرفه عادةً في اليوم الأول.
- السبب — نص حر يظهر للموظفين في هذه الصفحة.
يُقرأ التاريخان بتوقيت المدرسة نفسها ويغطيان أيامًا دراسية كاملة. ومسح من يُنهي الفترة — فلا يوجد زر «إلغاء» منفصل يمكن نسيانه. وعند انقضاء تاريخ النهاية تعود المدرسة إلى ما كانت عليه دون أي خطوة يدوية.
ويسري الحفظ فورًا: تُنشأ حصص اليوم وأنت ما زلت في الصفحة، لا عند ربع الساعة التالي.
مُقيَّد بالجدول أم بتوقيت حر
كيف تُقدَّم الحصص أونلاين خيار منفصل عن كونها تُقدَّم أصلًا:
- حسب الجدول الدراسي — حصة مباشرة لكل حصة في الجدول، تبدأ وتنتهي مع الحصة. وهي الصيغة المُلزِمة، وهي التي تغذّي حضور الحصص.
- غرفة مفتوحة في أي وقت — غرفة ثابتة واحدة لكل شعبة طوال اليوم الدراسي، بلا التزام بأوقات الحصص. فالمدرسة التي تحوّلت إلى الإنترنت بسبب عاصفة نادرًا ما تُعيد إنتاج جرس حصصها في اليوم الأول، والمدرسة التي هي على الإنترنت بالأساس قد لا تريد جرسًا إطلاقًا. يستضيفها مربّي الشعبة. ولأنها غير مرتبطة بحصة فهي لا تسجّل حضور الحصص، ولا تظهر في بطاقات الجدول كذلك — يُوصل إليها من الاجتماعات.
- الجدول مع غرفة مفتوحة — كلاهما، فتتبع الشعبة جدولها ويبقى لها مكان تلتقي فيه بين الحصص.
الإعداد
-
اختر المنصة. من
/live/settings(مدير المدرسة أو المطوّر) فعّل التدريس عبر الإنترنت ثم اختر منصة الاجتماع:- رابط خارجي — روابط Google Meet أو Zoom أو Teams. يعمل اليوم دون أي بنية تحتية.
- غرفة داخل التطبيق — LiveKit. يمكن اختيارها فورًا، لكنها لا تصبح فاعلة
إلا بعد تجهيز خادم البث (البوابات الست في
conference/RUNBOOK.md). حتى ذلك الحين تعمل الحصص عبر الروابط الخارجية ثم تنتقل تلقائيًا يوم تجهيز البنية التحتية — دون ترحيل بيانات ودون إعادة إعداد.
-
للروابط الخارجية: حدّد رابطًا لكل حصة. أنشئ جلسة من المعالج مع تفعيل الحفظ كافتراضي، فيُخزَّن رابط متكرر لهذه المادة والشعبة والفصل الدراسي. من هنا تأخذ الحصة المُنشأة تلقائيًا رابط اجتماعها — والحصة بلا رابط تُتخطّى بدلًا من نشر زر انضمام معطّل. وعند بداية فصل دراسي جديد يَنقل زر ترحيل الروابط المتكررة في الصفحة نفسها هذه الروابط إلى الفصل التالي.
ويخبرك قسم تغطية روابط الاجتماعات في الصفحة نفسها بما أنجزته: كم زوجًا من (شعبة، مادة) في الفصل النشط له رابط خاص، وأيّها بلا رابط. راجعه قبل أن تحتاجه.
-
حدّد رابطًا ثابتًا كحدّ أدنى. المدرسة التي تتحوّل إلى الإنترنت في وقت قصير لن يكون لديها رابط جاهز لكل حصة. ويُستخدم رابط اجتماع ثابت لأي حصة بلا رابط خاص بها، فيُنتج التحوّل حصصًا قابلة للانضمام بدل ألا يُنتج شيئًا. وهو غرفة واحدة مشتركة — كل حصة تعود إليه تصل إلى الاجتماع نفسه — فتعامَل معه كحدّ أدنى للطوارئ وأضِف روابط لكل حصة تباعًا. أما الغرفة المفتوحة فلا مصدر آخر لرابطها، لأنها لا تنتمي إلى مادة.
-
اضبط الشعب إن كان التعليم مُدمجًا. تتجاوز قائمة التدريس عبر الإنترنت حسب الشعبة إعداد المدرسة في الاتجاهين: استثناء شعبة من مدرسة تحوّلت إلى الإنترنت، أو تحويل شعبة واحدة في مدرسة لم تتحوّل. ويعني «إعداد المدرسة» اتّباع الإعداد العام.
ما الذي يحدث بعد ذلك
كل خمس عشرة دقيقة تُنشئ مهمة التذكيرات حصص اليوم الدراسي الحالي — صفًا واحدًا لكل حصة في اليوم، بتوقيت المدرسة نفسها. وهي تطابق مرشِّح الجدول الدراسي نفسه (الفصل النشط، ذلك اليوم من الأسبوع، بلا فترات الفسحة، والحصص التي لها شعبة ومعلّم)، فلا يمكن أن تخترع حصة لا يعرضها الجدول. وتكرار التشغيل لا يُنشئ شيئًا جديدًا، والحصة التي ألغاها المدير لا تعود.
ولا يُنشأ شيء في يوم أعلنت المدرسة إغلاقه — فوجود استثناء «عطلة» أو «ملغى» في جدول الاستثناءات يوقف العملية لذلك اليوم، فلا يُذكَّر أحد بحصة في العيد. أما الجدول نفسه فيظل ظاهرًا مع تنبيه «المدرسة مغلقة اليوم» فوقه: فاستثناءات التقويم تُدخَل يدويًا، ولا ينبغي لإدخال خاطئ أن يُخفي جدولًا كاملًا دون تفسير. وتُتخطّى كذلك الحصص التي انتهى وقتها، فلا يؤدي تفعيل المفتاح وقت الغداء إلى نشر أزرار انضمام لحصص الصباح.
ولأن الصف موجود فعلًا قبل أن ينقر أحد، يستيقظ بقية النظام: يصل الطلاب إشعار بدء الحصة، وتظهر النقطة على الجدول الأسبوعي، وتُوسم الحصة بـأونلاين في كل بطاقة في الجدول — إلى جانب اسم القاعة لا بدلًا منه، فالقاعة تبقى مكان اللقاء لمن يستطيع الحضور — ويجد المعلّم زر البدء في بطاقة الحصة الحالية، ومع غرف التطبيق وتفعيل مزامنة الحضور يُسجَّل الحضور من الحضور الفعلي في الغرفة عند انتهاء الحصة. أما الحصص التي لم يبدأها أحد فتُغلق بحالة «ملغاة» بدل أن تبقى معلّقة.
وتذكر صفحة كل جلسة كيف يُعالَج الحضور فيها — أيُرصد تلقائيًا عند انتهائها أم يُرصد كالمعتاد، مع رابط إلى صفحة الحضور. فالاجتماعات الخارجية لا تنقل أي بيانات حضور، وهي الحالة الشائعة لمدرسة تحوّلت للتوّ إلى الإنترنت، فيُذكر ذلك صراحةً بدل أن يُكتشف من سجلّ فارغ.
ما الذي تقدّمه كل منصة
| رابط خارجي | غرفة داخل التطبيق | |
|---|---|---|
| يعمل اليوم | ✅ | ⛔ ست بوابات بنية تحتية |
| الانضمام من الجدول الدراسي | ✅ | ✅ |
| التذكيرات والإشعارات | ✅ | ✅ |
| مؤشر الحصة على الجدول الأسبوعي | ✅ | ✅ |
| التسجيل | ⛔ من جهة المزوّد فقط | ✅ إلى S3 مع مدة حفظ لكل مدرسة |
| الحضور من التواجد | ⛔ لا توجد بيانات تواجد | ✅ اختياري |
| إدارة المشاركين (الطرد) | ⛔ | ✅ |
كيف ينضم الطالب
خمسة مداخل تصل إلى الحصة المباشرة، وتختلف في صرامة البوابة الزمنية:
| المدخل | البوابة |
|---|---|
| صفوف اليوم في الجدول (طالب · معلّم · وليّ أمر) | جارية الآن، أو تبدأ خلال ١٠ دقائق (isRowLiveJoinable) |
| بطاقة الحالية/التالية في الجدول | الحصة الحالية، أو التالية خلال ١٠ دقائق (isLiveJoinable) |
شريط "الآن" في صفحة /live | المباشر الآن والقادم، مقيّد بالشعبة |
جدول /live/dashboard | أي صف live أو scheduled يراه المستخدم |
| بوابة الوالدين ← الطفل ← الجدول | شريط "اليوم" أعلى الجدول الأسبوعي |
| الإشعار | started يفتح الغرفة مباشرة؛ والبقية تفتح تفاصيل الجلسة |
| الجوال | حقل live_class في /api/mobile/timetable/:userId، والانضمام عبر GET /api/mobile/live/:id/join |
الشبكة الأسبوعية في الجدول تعرض نقطة مباشرة فقط — إشارة وعي، لا زر انضمام.
جلسة LiveKit تفتح الغرفة داخل التطبيق على /live/[id]/room، والجلسة الخارجية تفتح رابط
المزوّد مباشرة. مسار الغرفة يحوّل الجلسات الخارجية بعد التحقق من التسجيل في الشعبة، فيعمل كلا
المزوّدين من كل المداخل أعلاه.
إشعار started وحده يفتح الغرفة مباشرة. إشعار startingSoon يصل قبل ٥–٢٠ دقيقة من الحصة،
ومن ليس مضيفاً يُرفض انضمامه لجلسة ما زالت scheduled — فرابط غرفة مبكر يوصل الطالب إلى خطأ لا
إلى غرفة انتظار.
داخل الغرفة
الغرفة من صنع التطبيق نفسه — كل زرّ يأخذ نصّه من القاموس، والتخطيط يتبع اتجاه الصفحة. واجهتها هي تخطيط مشغّل الدروس على الهاتف — بالطريقة التي يرتّب بها مشغّل الفيديو في آبل عناصره — مع أدوات الحصة نفسها في مواضع المشغّل:
- الصف العلوي: المغادرة (✕) وحدها؛ ثم كبسولة زجاجية فيها قائمة المشاركين، والملء / الملاءمة، ومشاركة الحصة؛ وفي الطرف الآخر مكبّر الصوت الذي يكتم صوت الحصة على هذا الجهاز وحده.
- المنتصف: ثلاثة أقراص في موضع الإرجاع · التشغيل · التقديم في مشغّل الأفلام — الكاميرا، والميكروفون (القرص الكبير، أحمر أثناء الكتم)، ورفع اليد (للمعلّم: مشاركة الشاشة).
- الأسفل: اسم المادة وبجانبه «⋯» — جودة الاتصال، واختيار الميكروفون والكاميرا، وللمعلّم السبورة والشرائح — ثم ساعة الحصة (المنقضي، شريط للقراءة فقط، المتبقي)، ثم كبسولة النقاش (الدردشة، الأسئلة، الاستطلاع، مع العدد)، وللمعلّم الأيدي المرفوعة ما دامت هناك يد مرفوعة.
- تختفي الواجهة بعد ثلاث ثوانٍ من آخر لمسة وتعود بنقرة على الصورة؛ وأي قائمة أو لوحة مفتوحة تُبقيها ظاهرة.
ما تستطيع الحصة فعله فيها:
- المعلّم: الميكروفون، الكاميرا، مشاركة الشاشة، السبورة، الشرائح (أي ملف PDF مرفق بالدرس، مع مزامنة الصفحة للجميع)، الاستطلاعات، قائمة الأسئلة مع «تعليم كمُجاب»، طابور الأيدي المرفوعة، والإشراف (إزالة مشارك).
- الطالب: الميكروفون والكاميرا (مغلقان عند الدخول)، رفع اليد، طرح سؤال، التصويت، الدردشة. وليّ الأمر يشاهد فقط.
- جودة البثّ تتكيّف مع اتصال الطالب: ضعفٌ مستمرّ يُنزل فيديو المعلّم درجةً (720 ← 360 ← 180) ثم إلى الصوت والشرائح — ويظهر شريط «تم تشغيل وضع الصوت والشرائح» — واتصالٌ جيّد مستمرّ يرفعه درجةً. الاختيار اليدوي من قائمة «الجودة» يتجاوز السلّم.
- إعادة الاتصال تُظهر طبقة «جارٍ إعادة الاتصال…»؛ وانقطاع الاتصال يعرض «إعادة الانضمام» بدل الإخراج. الإزالة من قِبل المعلّم، وانتهاء الحصة، وفتح الحصة على جهاز آخر، كلٌّ منها يقول ذلك بوضوح.
- الحضور يُسجَّل من وقت الوجود في الغرفة عبر إعادات الاتصال — لا شيء يُنقر.
- الاستطلاعات المنتهية وكلّ الأسئلة تُحفظ على الحصة كأحداث.
بعد الحصة
عند تفعيل التسجيل، يُنشر التسجيل المكتمل فيديوًا للدرس في لوموس (ظاهر للمدرسة، قابل للتنزيل)، وتتنقّل صفحة الحصة بين «جاري تجهيز تسجيل الحصة…» ← «التسجيل متاح للمشاهدة» — أو تقول بصراحة إن التسجيل فشل.
حماية الفيديوهات والمواد
التسجيلات وفيديوهات الدروس والمواد تُشاهَد داخل التطبيق ولا تُنزَّل أبدًا: لا زرّ
تنزيل، ولا صورة داخل صورة أو بثّ إلى شاشة أخرى، والمواد تُفتح في عارض داخل
التطبيق لا كملفات. كلّ واجهة من هذه — وغرفة الحصة منها — تحمل علامة مائية من
طبقتين: رمز حساب المشاهد بشفافية ١٪، دائمة ولا تُلحَظ، تبقى في أيّ لقطة شاشة أو
تسجيل للشاشة؛ وبريد المشاهد مُقنَّعًا، لا يظهر إلا حين يُحتمَل بدء التقاط (مفاتيح
لقطة الشاشة، أو PrintScreen، أو الانتقال إلى نافذة أخرى). المتصفّح لا يستطيع منع
الالتقاط ولا اكتشافه، فالغاية أن يُنسَب التسريب إلى صاحبه لا أن يستحيل؛ ولا تشمل
الحماية كاميرا موجَّهة إلى الشاشة. أمّا عمل الطالب
نفسه — موضع المشاهدة وإجابات الاختبار وتسليم الواجب — فما يزال يصطفّ دون
اتصال ويُزامَن عند عودته؛ وصفحة /offline تعرض ما ينتظر.
التشغيل
-
الخادم (SFU): أربعة متغيرات بيئة (
LIVEKIT_HOSTوLIVEKIT_WS_URLوLIVEKIT_API_KEYوLIVEKIT_API_SECRET). التسجيل بوابة منفصلة (LIVEKIT_RECORDING_BUCKETمع بيانات اعتماد S3) ولم يعد يمنع عقد المكالمة. صفحة/live/network-testتُبلّغ عن الاثنين. -
الخادم المهيّأ لا يفعل شيئاً وحده — تبقى قيمة
School.conferenceProviderDefaultهيexternalحتى تختار المدرسة غير ذلك من/live/settings. -
مهمة توليد الجلسات تعمل على GitHub Actions لا على Vercel Cron ما دام المشروع على الخطة المجانية (
.github/workflows/conference-crons.yml). بدونها لا تولّد المدرسة المتصلة أي جلسة بعد اليوم الذي حفظت فيه إعداداتها — فلا شيء للانضمام إليه ولا تذكيرات. -
للبثّ كاتبٌ واحد. ثلاثة أشياء قد تنقل الحصة من «مجدولة» إلى «مباشرة» — زر «ابدأ» عند المعلّم، وضغطة المضيف على «انضم» في بطاقة العنوان، وويبهوك
room_startedمن خادم البث نفسه — وأيّها يصل أولًا هو الذي ينفّذ الآثار الجانبية الآن: إشعار «الحصة تُبثّ الآن» لكل الطلاب، وبدء التسجيل تلقائيًا. حتى 2026-09-04 كان الويبهوك وحده يحملهما خلف حارسٍ سبقته كتابةُ التطبيق غالبًا، فلم تُخطر أي حصة أحدًا ولم تُسجَّل على مسار الانضمام الحقيقي.actions/went-live.tsهو ذلك الكاتب. -
الجلسة الخارجية لا تُبدأ كغرفة. هي رابط اجتماعها؛ يرفضها «ابدأ» و«انضم» بدل فتح غرفة LiveKit باسمٍ لا يستطيع أي ويبهوك ردّه إلى الجلسة.
-
كل مصادر «عبر الإنترنت» تُمسح. مرشّح المرشّحين في الكرون يذكر الافتراضي على مستوى المدرسة، وتجاوز الشعبة، وتجاوز الصف، ونافذة التحوّل. وقلبُ شعبةٍ أو صفٍّ إلى «عبر الإنترنت» يولّد حصص اليوم فورًا، كما يفعل حفظ الإعداد على مستوى المدرسة.
-
افحص جسر الكرون؛ فهو لا يفحص نفسه. من 2026-08-31 إلى 09-03 فشلت كل دورة مجدولة — النشر المبنيّ مسبقًا كان يفتقر إلى محرّك Prisma لمعمارية linux-arm64، فأعادت وظائف الكرون الثلاث و
fee-dueالخطأ 500 بينما بقيت كل الصفحات خضراء. يحملbinaryTargetsالآن محرّكَي لينكس معًا، ويرفضdeploy-hobby.shأي حزمة تفتقر إلى المحرّك الذي تحتاجه وظائفها. سطرٌ واحد يخبرك بصحّة الجسر:gh run list --repo databayt/hogwarts --workflow=conference-crons.yml --limit 20
العرض التجريبي والمدرسة الهجينة
الأمر pnpm db:seed:single conference يحوّل المدرسة التجريبية إلى مدرسة هجينة عاملة: متصلة
افتراضياً عبر LiveKit، شعبتان حضورياً، خمسة أيام من السجل بحضور حقيقي وغياب افتراضي
(VIRTUAL)، جلسات اليوم الدراسي التالي التي ستجدها المهمة الدورية موجودة (exists)، بديل يستضيف
إحداها، حصة مرتبطة بدرس واختبار وواجب ورابط، ثلاثة روابط خارجية متكررة، وعطلة معلنة. يُصلح أولاً —
فترات الفسحة، خبرات المعلمين، ومعلماً لكل حصة بلا معلم — لأن الحصة بلا معلم بلا مضيف ولا تتصل أبداً.
كل جلسة يكتبها مؤرّخة بيوم تشغيله، لذلك يعيد البذر بناء نفسه متى تخلّفت أحدث جلسة عن اليوم — بتوقيت المدرسة نفسها. إعادة تشغيله تُبقي العرض التجريبي حيّاً بدل أن يترك الطالب أمام صفحة بلا جلسة جارية ولا قادمة ولا قريبة.
وثلاث من تلك الجلسات تتوسّط الساعة — واحدة في منتصف الحصة، وأخرى على وشك الانتهاء، وثالثة توشك أن تبدأ — كي يجد شريط الصفحة الرئيسية ما يعرضه في البطاقة الكبرى وفي البطاقتين تحتها. وتقع الثلاث في شعبة الطالب التجريبي نفسه، من ثلاث مواد مختلفة، لأن صفحته مقصورة على تلك الشعبة وتُستبعد الصفوف الأخرى قبل أن تصل إلى الشريط.
والصفوف المؤرّخة بـ«الآن» تتقادم خلال ساعة، والبذر يعمل مرة واحدة عند النشر على الأكثر، لذلك يُصلح
العرض التجريبي ساعته عند القراءة: على المستأجر التجريبي تفحص الصفحة تلك الصفوف الثلاثة قبل أن تقرأ،
ولا تعيد كتابتها إلا حين تكفّ عن قول ما كُتبت لتقوله. لهذا لا تكون /live فارغة هناك مهما طال
العهد بآخر بذر.
معنى «الهجين» في المنتج: الاتصال إضافة لا بديل. تفاصيل الجلسة تُظهر القاعة الفعلية التي ما زالت الحصة تنعقد فيها؛ بطاقة الجدول تعرض القاعة وبجانبها علامة «مباشر»؛ البديل المؤكَّد يستضيف الجانب المتصل من الحصة التي يغطيها؛ درس lumos يعرض جلسته المباشرة؛ والصفحات الرئيسية تحمل زر الانضمام. والتسجيل صادق — كل مفتاح يقول ذلك حين لا توجد حاوية تخزين.
المزوّدون
| المزوّد | الحالة | الناقص |
|---|---|---|
| الرابط الخارجي | ✅ فعّال | — (الافتراضي العام بلا بنية تحتية) |
| LiveKit SFU | 🟡 مكتوب، خامل | 6 بوابات بنية تحتية → conference/RUNBOOK.md |
| Google Meet | 🟡 createMeeting موصول، خامل | بيانات اعتماد OAuth من Google Cloud (GOOGLE_MEET_*) |
| Zoom | 🟡 createMeeting موصول، خامل | بيانات اعتماد Server-to-Server OAuth (ZOOM_*) |
| Microsoft Teams | 🟡 createMeeting موصول، خامل | بيانات اعتماد Azure AD (AZURE_*، Graph OnlineMeetings) |
ينشئ كل محوّل أصلي اجتماعاً حقيقياً عبر واجهة المزوّد (createMeeting) محكوماً بـ isConfigured()؛
أما getRecording/getAttendance فما زالا يطلقان ProviderNotImplementedError. تُحفظ المزوّدات
الأصلية كـ provider="external" + meetingProvider="<id>" → دون ترحيل تعداد.
القرارات
- كتلة واحدة، تجريد واحد للمزوّد. الرابط الخارجي هو الحدّ الأدنى المتاح لكل مدرسة؛ وLiveKit هو
السقف المتميّز الاختياري، محكوم بـ
isLiveKitConfigured(). تبقى دورة حياة SFU خارج طبقةproviders/(لأنها قائمة على الغرف لا الروابط). - شريط «الآن» بطاقة رئيسية وبطاقتان موجزتان. البطاقة الأولى هي الحصة الجارية، أو أقرب حصة إلى البدء، وتحمل كل شيء: المادة والوحدة والدرس والمعلّم بصورته وموضع الحصة من وقتها. أمّا البطاقتان تحتها فسطران فقط: المادة مع شارة الصف، ثم المعلّم والوقت في سطر واحد. الترتيب البصري مكسوب بتبسيط الجارتين لا بتزيين الأولى: ثلاث بطاقات متساوية كانت تجعل الحصة المباشرة سهلة الفوات كحصةٍ تبدأ بعد ساعتين.
- البطاقة تقول ما لا يعرفه الدور أصلاً. تُضيّق الصفحة شريط المعلّم على الحصص التي يدرّسها، فتذكر
بطاقاته الفصل وتُسقط اسمه؛ وصفوف الطالب كلها من فصله، فتذكر بطاقاته المعلّم وتضع الصف في الشارة.
أمّا المدير والموظف والمحاسب وولي الأمر فيقرؤون عبر عدة فصول ويرون الاثنين. وقاعدة المعلّم معلّقة
على وقوع التضييق فعلياً — فحساب معلّم بلا سجل
Teacherيرى المدرسة كلها، وهناك يبقى الاسم. - الرفّ الثاني صار «ما فاتك» — الحصص التي غبتَ عنها. يقرأ الحضور الفعلي لا سجلّ الغياب:
فصفّ
ConferenceParticipantلا يحملjoinedAtإلا بعد أن يدخل صاحبه الغرفة فعلاً، والصفوف تُنشأ عند الدخول لا مسبقاً — فغياب صفٍّ بـjoinedAtهو الإشارة نفسها، بلا أي تفعيل من المدرسة. ومَن يُحتسب حضوره ليس القارئ دائماً: وليّ الأمر يلحق بما فات أبناءه لا ما فاته هو. ومَن لا ينضم إلى شيء — وهذا حال كل إداري — يتحوّل عنده الرفّ إلى «ما دُرّس مؤخراً». والصفوف رفٌّ أفقي واحد قابل للتمرير، حتى اثنتي عشرة بطاقة، وتعرض البطاقة التسجيل متى وُجد جاهزاً وكان القارئ يملك مشاهدته. - تحت الرفّ تسجيلان مرتّبان بحسب القارئ. تسجيل حصةٍ غاب عنها القارئ يسبق تسجيل حصةٍ حضرها،
ولا يفصل بينهما التاريخ إلا عند التساوي — فطالبان في الفصل نفسه يريان زوجين مختلفين. ولا يُعرض
إلا تسجيل
ready، ولا يُعرض رابطه إلا لمن يملكview_recordings. - إعادة التسمية شفّافة لقاعدة البيانات.
@@map/@mapتحفظ كل اسم جدول / عمود / نوع تعداد. - الـ Webhook والمهام المجدولة تبقى مسارات API — مُستدعاة خارجياً / عبر المجدول؛ لا يستطيع الإجراء الخادمي استقبالها.
- مدة الرمز = 5 دقائق، مُحدَّث من العميل. رموز LiveKit عديمة الحالة، لذا يسري إلغاء الوصول عند حدّ
التحديث التالي (≤5 دقائق) لا فوراً؛ ويُفرَض الطرد عبر بوابة
status="removed"عند الانضمام. - قيد الانتظار (غير برمجي): تجهيز بنية LiveKit التحتية (الدليل جاهز)، وبيانات اعتماد OAuth الأصلية.
ذات صلة
- المعمارية · النمط · الجدول الدراسي —
Conference.timetableIdيربط أزرار البدء / الانضمام في الجدول · الإشعارات — 5 أنواعlive_class_*
On This Page
كيف يعملدليل الاستخدامإن كنت تدير المدرسةإن كنت تُدرِّسإن كنت طالبًاإن كنت وليّ أمرالبنيةالمكوّناتتدفّق البياناتالمساراتالمخطّط —prisma/models/conference.prismaالتحكّم بالوصول — خاص أم على مستوى المدرسةالمراجع — درس، كويز، واجب، روابطمعالج الإنشاءالتدريس عبر الإنترنتالتحوّل إلى الإنترنت في وقت قصيرمُقيَّد بالجدول أم بتوقيت حرالإعدادما الذي يحدث بعد ذلكما الذي تقدّمه كل منصةكيف ينضم الطالبداخل الغرفةبعد الحصةحماية الفيديوهات والموادالتشغيلالعرض التجريبي والمدرسة الهجينةالمزوّدونالقراراتذات صلة