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

الاجتماعات

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

مؤتمرات الفيديو — كتلة واحدة قائمة بذاتها في src/components/school-dashboard/conference، منعكسة 1:1 على المسار /conference. الرابط الخارجي الملصوق فعّال؛ وخادم LiveKit SFU يبعد أربعة متغيرات بيئة عن التشغيل (والتسجيل بوابة منفصلة)؛ ومحوّلات Meet/Zoom/Teams الأصلية موصولة لكنها خاملة.

كتلة واحدة قائمة بذاتها في src/components/school-dashboard/conference/، منعكسة بنسبة 1:1 على المسار /conference. تشغّل ثلاثة خلفيات اجتماع خلف واجهة واحدة: مزوّد الرابط الخارجي الملصوق فعّال في كل مكان؛ وخادم LiveKit SFU (غرف داخل التطبيق) مكتوب بالكامل ولا يفصله عن التشغيل سوى أربعة متغيرات LIVEKIT_* — راجع RUNBOOK.md لمسار LiveKit Cloud السريع؛ والتسجيل بوابة منفصلة لم تعد تمنع فتح الغرفة؛ ومحوّلات Meet / Zoom / Teams الأصلية تنشئ اجتماعات حقيقية عبر واجهة كل مزوّد لكنها تبقى خاملة حتى تصل بيانات اعتماد OAuth.

البنية

src/components/school-dashboard/conference/— one block, mirrored 1:1 to /conference
content.tsx— server list (fetch + batched translate → table)
queries.ts— read-only Prisma builders
actions.ts— server-action barrel
list-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 actions
helpers.ts— requireContext · canAccessSession
slot-session.ts— day-qualified slot lookup + cron writer
materialize-day.ts— online policy → one school day of sessions
open-room.ts— loose mode — one standing room per section per day
sessions.ts— lifecycle state machine
join-core.ts— shared eligibility + token mint (action + route)
tokens.ts— join → 5-min JWT
recordings.ts— list / signed-URL / delete
notifications.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 link
moderation.ts— kickParticipant
recurring.ts— link carry-forward across terms
authorization.ts— PERMISSION_MATRIX (strict RBAC)
permissions.ts— UI gating (rich layer)
list-permissions.ts— UI gating + CRUD guards (list layer)
validation.ts— Zod (sessions)
list-validation.ts— Zod (table)
list-params.ts— nuqs URL cache
nav.tsx— heading + tab strip for the (app) surfaces
landing/— the /conference landing page (hero + bands)
content.tsx— composes the sections
hero.tsx— copy + room collage (student photo + tiles)
features-section.tsx— four value cards
now-section.tsx— live / coming-up strip, section-scoped
everything-section.tsx— full-bleed value band
how-to-section.tsx— three setup steps
welcome-section.tsx— closing band + settings CTA
types.ts— shared section props + LandingSession
table.tsx— DataTable + grid
columns.tsx— column defs + action menu
form.tsx— create/edit 5-step wizard (modal)
form-steps.tsx— wizard steps: basics · schedule · meeting · references · access
schedule-form.tsx— standalone scheduler
settings-form.tsx— admin school-policy form
section-recording-policy.tsx— per-section recording opt-out
section-online-policy.tsx— per-section online override (inherit/on/off)
detail.tsx— single-session detail
room.tsx— in-app LiveKit room
participants-panel.tsx— HOST/CO_HOST in-room kick UI
recordings.tsx— recordings list
recording-player.tsx— lazy signed-URL player
network-test.tsx— LiveKit RTT/ICE diagnostic
loading-skeleton.tsx— loading skeleton
types.ts— domain types + RoomJoinTicket
error-map.ts— error-code → string
network-protocol.ts— pure ICE-path classifier
livekit/— SFU server SDK wrappers
client.ts— singletons + readiness
token.ts— role → VideoGrant
rooms.ts— room lifecycle
egress.ts— recording egress
recording-urls.ts— signed S3 URLs
room-naming.ts— sch-{schoolId}-lc-{id}
webhook.ts— HMAC verify + idempotent router
providers/— link-provider adapters
types.ts— adapter contract
external.ts— pasted link (live)
google-meet.ts— wired, dark
zoom.ts— wired, dark
teams.ts— wired, dark
token-cache.ts— OAuth token cache
index.ts— registry
README.md— provisioning reference
RUNBOOK.md— LiveKit 6-gate provisioning
README.md— feature purpose, routes, status
ISSUE.md— open backlog and follow-ups
CLAUDE.md— block-level context

المسارات تعكس هذه الكتلة (انظر المسارات). نماذج Prisma الستة تقع في prisma/models/conference.prisma. الاختبارات تقع تحت src/tests/ (إعادة تنظيم بمحاكاة المسارات)، لا في مجلّد __tests__/ داخل الكتلة.

المكوّنات

تتعايش طبقتان للصلاحيات والتحقّق عن قصد: طبقة الجلسات الغنية (authorization.ts · permissions.ts · validation.ts) هي البوابة الصارمة وقت التشغيل لتدفّق غرفة LiveKit (الانضمام / الرمز / البدء / الإنهاء)، بينما طبقة القائمة (list-*.ts) هي بوابة الـ CRUD لجدول لوحة التحكّم وجلسات الروابط الخارجية.

الملفالغرض
content.tsxمكوّن خادمي — يجلب القائمة وخيارات النموذج في مسار متوازٍ واحد، ترجمة مُجمّعة، ثم يعرض الجدول.
actions.ts + actions/إجراءات خادمية: sessions (دورة الحياة)، tokens (الانضمام)، recordings، notifications، settings، moderation (الطرد)، recurring، helpers.
list-actions.ts · queries.tsCRUD لطبقة القائمة (create/update/delete/list) + بُناة استعلامات للقراءة فقط.
authorization.tsPERMISSION_MATRIX — 10 أفعال إجراء → UserRole[] المسموح؛ حُرّاس assert/check.
permissions.ts · 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صفحة الوصول لـ /conference (الواجهة الرئيسة · بطاقات القيمة · شريط المباشر/القادم · الأقسام الطويلة) والعنوان وشريط التبويبات فوق أسطح (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 (/conference)          landing/content.tsx (server) → hero · cards · live/upcoming strip · bands
   └─ عرض الجلسات
index (/conference/dashboard)  content.tsx (server) → table.tsx (client) → form.tsx (modal create)
   └─ row Join / detail
detail (/conference/[id])      detail.tsx → Join (live/scheduled) | Recordings (ended)
   └─ Join
room  (/conference/[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 → S3 me-central-1؛ الاحتفاظ لكل مدرسة تفرضه المهمة المجدولة اليومية.
  • الإشعارات (actions/notifications.ts): جميع أحداث live_class_* الخمسة تحدّد جمهورها (المعلّم + طلاب الشعبة + أولياء الأمور) وتُرسَل عبر الموزّع المشترك (dispatchNotificationsToAudience)، فتصل إلى جرس التطبيق والبريد الإلكتروني، وتحترم تفضيلات القنوات لكل مستخدم، وتحمل رابطاً مطلقاً. يُرسِل كلا المسارين: غرفة التطبيق (sessions.ts) ومسار الرابط الخارجي (list-actions.ts إنشاء / تعديل / إلغاء).
  • الحضور (actions/attendance-sync.ts، اختياري عبر School.conferenceAttendanceSync): عند انتهاء الجلسة (Webhook room_finished أو مهمة الجلسات المعلّقة)، يسجّل syncConferenceAttendance كل طالب في الشعبة حاضراً/متأخراً حسب وجوده في الغرفة، وغائباً لمن لم ينضمّ، بكتابة سجلات Attendance بالطريقة VIRTUAL. يتطلّب sectionId + timetableId؛ LiveKit فقط — الروابط الخارجية لا تحمل بيانات حضور. عديم التأثير عند التكرار (مفتاح الشعبة الفريد، إحياء عند التحديث).

المسارات

المسارالتخطيطيعرضالأدوار
/conferenceschool-dashboardlanding/content.tsx — صفحة الوصولكل أدوار المدرسة السبعة
/conference/dashboardschool-dashboard (app)فهرس DataTableكل أدوار المدرسة السبعة
/conference/scheduleschool-dashboard (app)schedule-form.tsxالمطوّر · المدير · المعلّم
/conference/settingsschool-dashboard (app)settings-form.tsx + section-recording-policy.tsxالمطوّر · المدير
/conference/network-testschool-dashboard (app)network-test.tsxالمطوّر · المدير (محكوم بالبيئة)
/conference/[id]school-dashboarddetail.tsxكل أدوار المدرسة السبعة
/conference/[id]/recordingsschool-dashboardrecordings.tsxالكل عدا المحاسب (أكاديمي لا مالي)
/conference/[id]/room(live-room)room.tsxأي مشارك جلسة مُصادَق
/live-classes/*—إعادة توجيه → /conferenceحشوة توافق قديمة (روابط ما قبل إعادة التسمية)

(app) مجموعة مسارات: لا تضيف مقطعاً إلى الرابط، بل تضيف العنوان وشريط التبويبات المشترك فقط (الجلسات · الجدولة · الإعدادات · فحص الشبكة، بحسب الدور). يقع /conference و/conference/[id] خارجها عمداً — صفحة الوصول تملك واجهتها الرئيسة، وتفاصيل الجلسة ورقة يُوصل إليها من صفٍّ لا من تبويب.

تقع غرفة المسار تحت مجموعة (live-room) منفصلة بتخطيط عارٍ بملء الشاشة (بلا شريط جانبي / زخارف لوحة التحكّم) كي يمتلك بثّ الـ SFU الشاشة؛ وهي force-dynamic لبناءات آمنة من نفاد الذاكرة. واجهات API (مُستدعاة خارجياً / عبر المجدول / بالاستطلاع → تبقى مسارات، لا إجراءات خادمية): api/webhooks/livekit (HMAC)، api/conference/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 + خيار الرابط الأسبوعي المتكرر). لا يتغيّر المزوّد بعد الإنشاء؛ وزر الانضمام واعٍ بالمزوّد في كل مكان (قائمة الجدول، صفحة التفاصيل، ومسار الغرفة يحوّل الجلسات الخارجية إلى رابط المزوّد).

التدريس عبر الإنترنت

أول إعداد هو كيف تُدرِّس المدرسة — حضوري أو عبر الإنترنت أو مختلط — من المدرسة ← الإعدادات ← الحصص المباشرة (وهو نفسه تبويب الإعدادات داخل الحصص المباشرة). حضوري يعني لا غرف مباشرة ولا أزرار انضمام، مهما قالت أيّ شعبة. عبر الإنترنت يعني أنّ كل حصة في الجدول حصة مباشرة. المختلط هو حيث تعيش الخيارات التالية: افتراضي للشُّعب، وتجاوزات لكل شعبة، ونافذة مؤقتة عبر الإنترنت. التحويل إلى حضوري يسري على الحصص الجديدة؛ وحصص اليوم المجدولة مسبقًا تبقى وتُغلق تلقائيًا.

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

المدرسة التي تقدّم حصصها عبر الإنترنت لا تجدول كل حصة على حدة، بل تعلن الأمر مرة واحدة فتتحول كل حصة في الجدول الدراسي إلى حصة مباشرة.

التحوّل إلى الإنترنت لا يُغلق المدرسة. فالتقديم عبر الإنترنت إضافة — قناة مباشرة تُفتح إلى جانب الحصة الحضورية — ومن ثم يمكن للمدرسة أن تكون مفتوحة وعلى الإنترنت في اليوم نفسه، أو على الإنترنت يوم الأحد وحضوريًا يوم الإثنين، أو على الإنترنت لشعبة دون أخرى. ولا شيء هنا يُلغي حصة.

هناك ثلاثة مفاتيح تجعل الحصة على الإنترنت، وهي تتكامل ولا تتنافس:

المفتاحالموضعالمعنى
التدريس عبر الإنترنت/conference/settingsالإعداد الدائم: «نحن مدرسة على الإنترنت».
التدريس عبر الإنترنت حسب الشعبةالصفحة نفسها، لكل شعبةيتجاوز إعداد المدرسة في الاتجاهين. و«إعداد المدرسة» يعني الاتّباع.
التحوّل إلى الأونلاين مؤقتاًالصفحة نفسها، بمدى زمنيمفتاح الطوارئ — أدناه. يرفع إعداد المدرسة لتلك الأيام.

وقرار الشعبة يتقدّم دائمًا على الإعدادين العامّين، حتى أثناء الطوارئ: فالفترة المؤقتة ترفع الإعداد الافتراضي للمدرسة ولا تُلغي قرارًا اتُّخذ بشأن شعبة بعينها. والشعبة المستثناة التي تريد إعادتها إلى الإنترنت تحتاج نقرة واحدة — أعِدها إلى «إعداد المدرسة».

التحوّل إلى الإنترنت في وقت قصير

الطقس، طريق مغلق، وباء، حرب. من /conference/settings → التحوّل إلى الأونلاين مؤقتاً:

  • من — أول يوم. مطلوب.
  • إلى — آخر يوم، شامل. اتركه فارغًا للبقاء على الإنترنت حتى إشعار آخر، وهو ما تعرفه عادةً في اليوم الأول.
  • السبب — نص حر يظهر للموظفين في هذه الصفحة.

يُقرأ التاريخان بتوقيت المدرسة نفسها ويغطيان أيامًا دراسية كاملة. ومسح من يُنهي الفترة — فلا يوجد زر «إلغاء» منفصل يمكن نسيانه. وعند انقضاء تاريخ النهاية تعود المدرسة إلى ما كانت عليه دون أي خطوة يدوية.

ويسري الحفظ فورًا: تُنشأ حصص اليوم وأنت ما زلت في الصفحة، لا عند ربع الساعة التالي.

مُقيَّد بالجدول أم بتوقيت حر

كيف تُقدَّم الحصص أونلاين خيار منفصل عن كونها تُقدَّم أصلًا:

  • حسب الجدول الدراسي — حصة مباشرة لكل حصة في الجدول، تبدأ وتنتهي مع الحصة. وهي الصيغة المُلزِمة، وهي التي تغذّي حضور الحصص.
  • غرفة مفتوحة في أي وقت — غرفة ثابتة واحدة لكل شعبة طوال اليوم الدراسي، بلا التزام بأوقات الحصص. فالمدرسة التي تحوّلت إلى الإنترنت بسبب عاصفة نادرًا ما تُعيد إنتاج جرس حصصها في اليوم الأول، والمدرسة التي هي على الإنترنت بالأساس قد لا تريد جرسًا إطلاقًا. يستضيفها مربّي الشعبة. ولأنها غير مرتبطة بحصة فهي لا تسجّل حضور الحصص، ولا تظهر في بطاقات الجدول كذلك — يُوصل إليها من الاجتماعات.
  • الجدول مع غرفة مفتوحة — كلاهما، فتتبع الشعبة جدولها ويبقى لها مكان تلتقي فيه بين الحصص.

الإعداد

  1. اختر المنصة. من /conference/settings (مدير المدرسة أو المطوّر) فعّل التدريس عبر الإنترنت ثم اختر منصة الاجتماع:

    • رابط خارجي — روابط Google Meet أو Zoom أو Teams. يعمل اليوم دون أي بنية تحتية.
    • غرفة داخل التطبيق — LiveKit. يمكن اختيارها فورًا، لكنها لا تصبح فاعلة إلا بعد تجهيز خادم البث (البوابات الست في conference/RUNBOOK.md). حتى ذلك الحين تعمل الحصص عبر الروابط الخارجية ثم تنتقل تلقائيًا يوم تجهيز البنية التحتية — دون ترحيل بيانات ودون إعادة إعداد.
  2. للروابط الخارجية: حدّد رابطًا لكل حصة. أنشئ جلسة من المعالج مع تفعيل الحفظ كافتراضي، فيُخزَّن رابط متكرر لهذه المادة والشعبة والفصل الدراسي. من هنا تأخذ الحصة المُنشأة تلقائيًا رابط اجتماعها — والحصة بلا رابط تُتخطّى بدلًا من نشر زر انضمام معطّل. وعند بداية فصل دراسي جديد يَنقل زر ترحيل الروابط المتكررة في الصفحة نفسها هذه الروابط إلى الفصل التالي.

    ويخبرك قسم تغطية روابط الاجتماعات في الصفحة نفسها بما أنجزته: كم زوجًا من (شعبة، مادة) في الفصل النشط له رابط خاص، وأيّها بلا رابط. راجعه قبل أن تحتاجه.

  3. حدّد رابطًا ثابتًا كحدّ أدنى. المدرسة التي تتحوّل إلى الإنترنت في وقت قصير لن يكون لديها رابط جاهز لكل حصة. ويُستخدم رابط اجتماع ثابت لأي حصة بلا رابط خاص بها، فيُنتج التحوّل حصصًا قابلة للانضمام بدل ألا يُنتج شيئًا. وهو غرفة واحدة مشتركة — كل حصة تعود إليه تصل إلى الاجتماع نفسه — فتعامَل معه كحدّ أدنى للطوارئ وأضِف روابط لكل حصة تباعًا. أما الغرفة المفتوحة فلا مصدر آخر لرابطها، لأنها لا تنتمي إلى مادة.

  4. اضبط الشعب إن كان التعليم مُدمجًا. تتجاوز قائمة التدريس عبر الإنترنت حسب الشعبة إعداد المدرسة في الاتجاهين: استثناء شعبة من مدرسة تحوّلت إلى الإنترنت، أو تحويل شعبة واحدة في مدرسة لم تتحوّل. ويعني «إعداد المدرسة» اتّباع الإعداد العام.

ما الذي يحدث بعد ذلك

كل خمس عشرة دقيقة تُنشئ مهمة التذكيرات حصص اليوم الدراسي الحالي — صفًا واحدًا لكل حصة في اليوم، بتوقيت المدرسة نفسها. وهي تطابق مرشِّح الجدول الدراسي نفسه (الفصل النشط، ذلك اليوم من الأسبوع، بلا فترات الفسحة، والحصص التي لها شعبة ومعلّم)، فلا يمكن أن تخترع حصة لا يعرضها الجدول. وتكرار التشغيل لا يُنشئ شيئًا جديدًا، والحصة التي ألغاها المدير لا تعود.

ولا يُنشأ شيء في يوم أعلنت المدرسة إغلاقه — فوجود استثناء «عطلة» أو «ملغى» في جدول الاستثناءات يوقف العملية لذلك اليوم، فلا يُذكَّر أحد بحصة في العيد. أما الجدول نفسه فيظل ظاهرًا مع تنبيه «المدرسة مغلقة اليوم» فوقه: فاستثناءات التقويم تُدخَل يدويًا، ولا ينبغي لإدخال خاطئ أن يُخفي جدولًا كاملًا دون تفسير. وتُتخطّى كذلك الحصص التي انتهى وقتها، فلا يؤدي تفعيل المفتاح وقت الغداء إلى نشر أزرار انضمام لحصص الصباح.

ولأن الصف موجود فعلًا قبل أن ينقر أحد، يستيقظ بقية النظام: يصل الطلاب إشعار بدء الحصة، وتظهر النقطة على الجدول الأسبوعي، وتُوسم الحصة بـأونلاين في كل بطاقة في الجدول — إلى جانب اسم القاعة لا بدلًا منه، فالقاعة تبقى مكان اللقاء لمن يستطيع الحضور — ويجد المعلّم زر البدء في بطاقة الحصة الحالية، ومع غرف التطبيق وتفعيل مزامنة الحضور يُسجَّل الحضور من الحضور الفعلي في الغرفة عند انتهاء الحصة. أما الحصص التي لم يبدأها أحد فتُغلق بحالة «ملغاة» بدل أن تبقى معلّقة.

وتذكر صفحة كل جلسة كيف يُعالَج الحضور فيها — أيُرصد تلقائيًا عند انتهائها أم يُرصد كالمعتاد، مع رابط إلى صفحة الحضور. فالاجتماعات الخارجية لا تنقل أي بيانات حضور، وهي الحالة الشائعة لمدرسة تحوّلت للتوّ إلى الإنترنت، فيُذكر ذلك صراحةً بدل أن يُكتشف من سجلّ فارغ.

ما الذي تقدّمه كل منصة

رابط خارجيغرفة داخل التطبيق
يعمل اليوم✅⛔ ست بوابات بنية تحتية
الانضمام من الجدول الدراسي✅✅
التذكيرات والإشعارات✅✅
مؤشر الحصة على الجدول الأسبوعي✅✅
التسجيل⛔ من جهة المزوّد فقط✅ إلى S3 مع مدة حفظ لكل مدرسة
الحضور من التواجد⛔ لا توجد بيانات تواجد✅ اختياري
إدارة المشاركين (الطرد)⛔✅

كيف ينضم الطالب

خمسة مداخل تصل إلى الحصة المباشرة، وتختلف في صرامة البوابة الزمنية:

المدخلالبوابة
صفوف اليوم في الجدول (طالب · معلّم · وليّ أمر)جارية الآن، أو تبدأ خلال ١٠ دقائق (isRowLiveJoinable)
بطاقة الحالية/التالية في الجدولالحصة الحالية، أو التالية خلال ١٠ دقائق (isLiveJoinable)
شريط "الآن" في صفحة /conferenceالمباشر الآن والقادم، مقيّد بالشعبة
جدول /conference/dashboardأي صف live أو scheduled يراه المستخدم
بوابة الوالدين ← الطفل ← الجدولشريط "اليوم" أعلى الجدول الأسبوعي
الإشعارstarted يفتح الغرفة مباشرة؛ والبقية تفتح تفاصيل الجلسة
الجوالحقل live_class في /api/mobile/timetable/:userId، والانضمام عبر GET /api/mobile/conference/:id/join

الشبكة الأسبوعية في الجدول تعرض نقطة مباشرة فقط — إشارة وعي، لا زر انضمام.

جلسة LiveKit تفتح الغرفة داخل التطبيق على /conference/[id]/room، والجلسة الخارجية تفتح رابط المزوّد مباشرة. مسار الغرفة يحوّل الجلسات الخارجية بعد التحقق من التسجيل في الشعبة، فيعمل كلا المزوّدين من كل المداخل أعلاه.

إشعار started وحده يفتح الغرفة مباشرة. إشعار startingSoon يصل قبل ٥–٢٠ دقيقة من الحصة، ومن ليس مضيفاً يُرفض انضمامه لجلسة ما زالت scheduled — فرابط غرفة مبكر يوصل الطالب إلى خطأ لا إلى غرفة انتظار.

داخل الغرفة

الغرفة من صنع التطبيق نفسه — كل زرّ يأخذ نصّه من القاموس، والتخطيط يتبع اتجاه الصفحة. ما تستطيع الحصة فعله فيها:

  • المعلّم: الميكروفون، الكاميرا، مشاركة الشاشة، السبورة، الشرائح (أي ملف PDF مرفق بالدرس، مع مزامنة الصفحة للجميع)، الاستطلاعات، قائمة الأسئلة مع «تعليم كمُجاب»، طابور الأيدي المرفوعة، والإشراف (إزالة مشارك).
  • الطالب: الميكروفون والكاميرا (مغلقان عند الدخول)، رفع اليد، طرح سؤال، التصويت، الدردشة. وليّ الأمر يشاهد فقط.
  • جودة البثّ تتكيّف مع اتصال الطالب: ضعفٌ مستمرّ يُنزل فيديو المعلّم درجةً (720 ← 360 ← 180) ثم إلى الصوت والشرائح — ويظهر شريط «تم تشغيل وضع الصوت والشرائح» — واتصالٌ جيّد مستمرّ يرفعه درجةً. الاختيار اليدوي من قائمة «الجودة» يتجاوز السلّم.
  • إعادة الاتصال تُظهر طبقة «جارٍ إعادة الاتصال…»؛ وانقطاع الاتصال يعرض «إعادة الانضمام» بدل الإخراج. الإزالة من قِبل المعلّم، وانتهاء الحصة، وفتح الحصة على جهاز آخر، كلٌّ منها يقول ذلك بوضوح.
  • الحضور يُسجَّل من وقت الوجود في الغرفة عبر إعادات الاتصال — لا شيء يُنقر.
  • الاستطلاعات المنتهية وكلّ الأسئلة تُحفظ على الحصة كأحداث.

بعد الحصة

عند تفعيل التسجيل، يُنشر التسجيل المكتمل فيديوًا للدرس في لوموس (ظاهر للمدرسة، قابل للتنزيل)، وتتنقّل صفحة الحصة بين «جاري تجهيز تسجيل الحصة…» ← «التسجيل متاح للمشاهدة» — أو تقول بصراحة إن التسجيل فشل.

حماية الفيديوهات والمواد

التسجيلات وفيديوهات الدروس والمواد تُشاهَد داخل التطبيق ولا تُنزَّل أبدًا: لا زرّ تنزيل، ولا صورة داخل صورة أو بثّ إلى شاشة أخرى، والمواد تُفتح في عارض داخل التطبيق لا كملفات. كلّ واجهة من هذه تحمل علامة مائية باهتة متحرّكة بهويّة المشاهد، فتُنسب أيّ لقطة شاشة أو تسجيل بالهاتف إلى صاحبها. أمّا عمل الطالب نفسه — موضع المشاهدة وإجابات الاختبار وتسليم الواجب — فما يزال يصطفّ دون اتصال ويُزامَن عند عودته؛ وصفحة /offline تعرض ما ينتظر.

التشغيل

  • الخادم (SFU): أربعة متغيرات بيئة (LIVEKIT_HOST وLIVEKIT_WS_URL وLIVEKIT_API_KEY وLIVEKIT_API_SECRET). التسجيل بوابة منفصلة (LIVEKIT_RECORDING_BUCKET مع بيانات اعتماد S3) ولم يعد يمنع عقد المكالمة. صفحة /conference/network-test تُبلّغ عن الاثنين.
  • الخادم المهيّأ لا يفعل شيئاً وحده — تبقى قيمة School.conferenceProviderDefault هي external حتى تختار المدرسة غير ذلك من /conference/settings.
  • مهمة توليد الجلسات تعمل على GitHub Actions لا على Vercel Cron ما دام المشروع على الخطة المجانية (.github/workflows/conference-crons.yml). بدونها لا تولّد المدرسة المتصلة أي جلسة بعد اليوم الذي حفظت فيه إعداداتها — فلا شيء للانضمام إليه ولا تذكيرات.

العرض التجريبي والمدرسة الهجينة

الأمر pnpm db:seed:single conference يحوّل المدرسة التجريبية إلى مدرسة هجينة عاملة: متصلة افتراضياً عبر LiveKit، شعبتان حضورياً، خمسة أيام من السجل بحضور حقيقي وغياب افتراضي (VIRTUAL)، جلسات اليوم الدراسي التالي التي ستجدها المهمة الدورية موجودة (exists)، بديل يستضيف إحداها، حصة مرتبطة بدرس واختبار وواجب ورابط، ثلاثة روابط خارجية متكررة، وعطلة معلنة. يُصلح أولاً — فترات الفسحة، خبرات المعلمين، ومعلماً لكل حصة بلا معلم — لأن الحصة بلا معلم بلا مضيف ولا تتصل أبداً.

معنى «الهجين» في المنتج: الاتصال إضافة لا بديل. تفاصيل الجلسة تُظهر القاعة الفعلية التي ما زالت الحصة تنعقد فيها؛ بطاقة الجدول تعرض القاعة وبجانبها علامة «مباشر»؛ البديل المؤكَّد يستضيف الجانب المتصل من الحصة التي يغطيها؛ درس 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/ (لأنها قائمة على الغرف لا الروابط).
  • إعادة التسمية شفّافة لقاعدة البيانات. @@map/@map تحفظ كل اسم جدول / عمود / نوع تعداد.
  • الـ Webhook والمهام المجدولة تبقى مسارات API — مُستدعاة خارجياً / عبر المجدول؛ لا يستطيع الإجراء الخادمي استقبالها.
  • مدة الرمز = 5 دقائق، مُحدَّث من العميل. رموز LiveKit عديمة الحالة، لذا يسري إلغاء الوصول عند حدّ التحديث التالي (≤5 دقائق) لا فوراً؛ ويُفرَض الطرد عبر بوابة status="removed" عند الانضمام.
  • قيد الانتظار (غير برمجي): تجهيز بنية LiveKit التحتية (الدليل جاهز)، وبيانات اعتماد OAuth الأصلية.

ذات صلة

  • المعمارية · النمط · الجدول الدراسي — Conference.timetableId يربط أزرار البدء / الانضمام في الجدول · الإشعارات — 5 أنواع live_class_*
الملف الشخصيإدارة الرسوم

On This Page

البنيةالمكوّناتتدفّق البياناتالمساراتالمخطّط — prisma/models/conference.prismaالتحكّم بالوصول — خاص أم على مستوى المدرسةالمراجع — درس، كويز، واجب، روابطمعالج الإنشاءالتدريس عبر الإنترنتالتحوّل إلى الإنترنت في وقت قصيرمُقيَّد بالجدول أم بتوقيت حرالإعدادما الذي يحدث بعد ذلكما الذي تقدّمه كل منصةكيف ينضم الطالبداخل الغرفةبعد الحصةحماية الفيديوهات والموادالتشغيلالعرض التجريبي والمدرسة الهجينةالمزوّدونالقراراتذات صلة

من تطوير Databayt ·

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

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