انتقل إلى المحتوى
كل الأعمال

متاح للتجربةجدولة بيئة العملقراءة في دقيقة

RoomFlow

حجز قاعات الاجتماعات بقواعد واحدة للإتاحة والموافقات وتسجيل الحضور.

التقنيات
  • PHP 8.3
  • MySQL 8
  • Nginx
  • PWA
  • iCalendar
  • Vanilla JS
RoomFlow — employee dashboard with the availability check

المشكلة

يحجز شخصان القاعة نفسها في الساعة نفسها، ولا أحد يعرف أي القاعات تستخدم فعلا.

الحل

نظام حجز ثنائي اللغة يسأل ثلاثة أسئلة (متى، وكم شخصا، وما الذي تحتاجه القاعة) ثم يعرض قائمة قصيرة بالقاعات المناسبة مع سبب كل اقتراح. وخلفه تقويم يومي وأسبوعي وجدول أعمال، وموافقات، وتسجيل حضور برمز QR، وتقارير استخدام لمن يديرون المساحات.

مساهمتي

تصميم المنتج والتنفيذ الكامل: القرارات والواجهة والمحتوى بالعربية والإنجليزية والتطوير والاختبارات والخادم والنشر.

أبرز المزايا

  • رفض التعارض داخل معاملة في قاعدة البيانات
  • إلغاء الحجوزات غير المستخدمة تلقائيا لصالح قائمة الانتظار
  • ترتيب القاعات حسب ملاءمتها مع توضيح السبب
  • تطبيق ويب قابل للتثبيت مع تصدير للتقويم

النتيجة

ترفض قاعدة البيانات نفسها الحجز المزدوج، وتعود القاعة التي لم يسجل أحد حضوره فيها إلى قائمة الانتظار.

تم التحقق منه بمجموعة اختبارات تشمل محاولات حجز متزامنة دون أي حجز مزدوج. هو نسخة تجريبية ضمن الأعمال، لذلك لا توجد أرقام استخدام.

التفاصيل التقنية ودراسة الحالة كاملةالبنية والأمان والاختبار والقرارات، لمن يريد التفاصيل.

نظرة عامة

RoomFlow نظام ثنائي اللغة لحجز غرف الاجتماعات. من يحتاج قاعة يجيب عن ثلاثة أسئلة — متى، وكم عدد الأشخاص، وما الذي يحتاجه في القاعة — فيحصل على قائمة قصيرة بالقاعات المناسبة فعلا، مع سبب اقتراح كل واحدة. وخلف هذه الواجهة البسيطة يقع تقويم يومي وأسبوعي وجدولي، والموافقات، وتسجيل الحضور برمز QR، وتحرير القاعة عند عدم الحضور، وقائمة الانتظار، وفترات الصيانة، وتقارير الاستخدام لمن يديرون المكان.

يعمل كنسخة تجريبية عامة ببيانات وهمية. كل حساب وقاعة واجتماع وسجل تدقيق تراه فيه أنشئ خصيصا لهذه النسخة.

مشكلة حجز القاعات

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

ما الذي يعالجه RoomFlow

  • التعارضات ترفضها قاعدة البيانات داخل معاملة، لا JavaScript.
  • نموذج واحد يجيب عن سؤال «ما المتاح» في كل القاعات دفعة واحدة.
  • الحجز الذي لا يسجل أحد حضوره يحرر تلقائيا ويعرض على قائمة الانتظار.
  • القاعات ترتب حسب مدى ملاءمتها، والسبب معروض على البطاقة.
  • الاستخدام يحتسب من الحجوزات نفسها.

دوري

كل شيء: قرارات المنتج، وبنية المعلومات، وتصميم الواجهة والتفاعل، والمحتوى العربي والإنجليزي، والتنفيذ بـ PHP و MySQL، ومجموعة الاختبارات الآلية، وإعداد الخادم، والنشر.

قرارات المنتج وتجربة الاستخدام

الإصدار الأول كان مكتملا وصعب الاستخدام. كل ما قد يحتاجه مدير القاعات كان على الشاشات نفسها التي يحتاج فيها الموظف العادي شيئا واحدا. هذه هي القرارات التي عالجت ذلك.

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

رحلة الحجز

خمس خطوات، كل واحدة تسأل عما تحتاجه فقط.

  1. متىالتاريخ ووقت البدء والمدة. قيم افتراضية معقولة، ولم ينشأ شيء بعد.
  2. ما تحتاجهعدد الأشخاص، وأي تجهيزات. تبقى المرشحات الاختيارية مطوية.
  3. اختر القاعةالقاعات المناسبة، الأنسب أولا، مع سبب لكل واحدة، والسعة والموقع والطابق والتجهيزات والمدخل بلا عوائق ومدة بقائها متاحة.
  4. تفاصيل الاجتماععنوان، واختياريا غرض أو ملاحظات أو قاعدة تكرار.
  5. التأكيدإجراء واحد. ينشأ الحجز داخل معاملة، أو يرفض مع عرض بدائل.
Room results in RoomFlow: Cedar Room flagged Best match, showing type, capacity, wing, floor, step-free access, how long it is free, equipment tags and the reason it was suggested
كل اقتراح يذكر سببه. وقاعة الاجتماعات التنفيذية تعرض اشتراط الموافقة كتحفظ لا كميزة.

قائمة الجوال، ولماذا كانت غير مرئية

على الجوال كان زر القائمة موجودا، بالمقاس الصحيح والتسمية الصحيحة — ويعرض كصندوق فارغ. عيبان منفصلان، وكل منهما وحده كان كافيا لإخفاء القائمة.

  • الأيقونة بلا شكل. حين انتقل الشريط الجانبي إلى مجموعة أيقونات SVG مضمنة، فقدت القاعدة .nav-ic المربع المصمت الذي كان ينوب عن الأيقونة. وظل زر القائمة يستخدم الترميز القديم — عنصر <span> — and no #ic-menu فارغ — ولم يكن هناك رمز أصلا. زر بمقاس ٤٤×٤٤ بحد من بكسل واحد ولا شيء بداخله.
  • الدرج لم يكن يفتح بالعربية. html[dir="rtl"] .sidebar يسجل ٠،٢،١ في أولوية CSS؛ بينما .sidebar.is-open يسجل ٠،٢،٠. فتفوقت حالة الإغلاق على حالة الفتح، وبالعربية — وهي اللغة الافتراضية — بقيت اللوحة خارج الحافة اليمنى حتى حين أعلن الزر أنها مفتوحة.

كلاهما معالج، وأعيد بناء الدرج كحوار حقيقي: ينزلق من الحافة التي يقع عليها الزر، ويحصر التركيز أثناء فتحه، ويغلق بمفتاح Escape وبالنقر على الخلفية وبزر الإغلاق، ويعيد التركيز إلى زر القائمة في كل مرة، ويوقف تمرير الخلفية، ويخرج من ترتيب التنقل بالكامل عند إغلاقه.

RoomFlow Arabic mobile navigation drawer open from the right, showing Find a room, Available now and My bookings above a labelled Manage group, with a close button and a dimmed backdrop
الدرج العربي يفتح من اليمين، تحت الإبهام الذي فتحه.

معرفة القاعات المتاحة

لوحة التوفر تجيب عن السؤال دون بحث. القاعات المتاحة ترتب أولا، وكل قاعة تذكر حالتها بالكلمات إلى جانب أيقونة — لا باللون وحده، فاللون وحده غير مقروء لمن لديه عمى ألوان ولمن ينظر إلى شاشته في الشمس.

  • متاحة طوال اليوم، أو متاحة حتى وقت محدد
  • مشغولة حتى وقت محدد
  • يبدأ الحجز قريبا، مع ذكر وقته
  • Under maintenance

تفصيلة واحدة كلفتنا خللا: عبارة «متاحة حتى ٠٩:٠٠» كانت تشير إلى صباح الغد. صار كل استعلام عن «الحجز التالي» محصورا باليوم نفسه، فأي وقت يعرض للقارئ ينتمي إلى اليوم الذي ينظر إليه.

RoomFlow live availability board showing seven rooms, free ones first, each with a written status badge such as Free all day, and one room marked Maintenance
الحالة مكتوبة بالكلمات مع أيقونة. واللون تعزيز فقط.

رؤية الجدول كاملا

التوفر يجيب عن سؤال «هل يوجد شيء متاح الآن». أما التقويم فيجيب عن السؤال الآخر: كيف يبدو اليوم فعلا، وأين الفجوات. ثلاثة عروض، اختيرت لأنها مهام مختلفة حقا لا تخطيطا واحدا بثلاثة عروض.

  • يوم — عمود لكل قاعة، لتقارن القاعات بنظرة واحدة وترى أيها متاح في الساعة ١٤:٠٠.
  • أسبوع — عمود لكل يوم عبر أسبوع عمل، لرصد النمط لا اللحظة.
  • جدول — قائمة مجمعة حسب اليوم، مع كتابة الفجوات المتاحة صراحة عند اختيار قاعة واحدة. وهذا هو العرض الافتراضي على الجوال.

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

كل حالة يمكن تمييزها دون الاعتماد على اللون: المؤكد، وبانتظار الموافقة، والمنتهي، وعدم الحضور، والملغى، والصيانة — لكل معالجته الخاصة، ومفتاح ألوان، وكلمات في الاسم المتاح للقارئ الآلي. وخط أحمر يحدد الوقت الحالي، ولا يظهر إلا على اليوم الحالي.

RoomFlow week calendar: seven day columns, bookings positioned by time, today tinted with a red current-time line, maintenance shown as a hatched block and one booking marked awaiting approval
عرض الأسبوع. اليوم الحالي مظلل، والخط الأحمر هو الوقت الحالي، والأعمدة المخططة صيانة.

الإشغال مشترك؛ والمحتوى ليس كذلك. يستطيع الموظف أن يرى أن قاعة محجوزة من ١٣:٠٠ إلى ١٤:٠٠ — وإلا لما أمكن للتقويم مساعدته في إيجاد فجوة — لكن اجتماع شخص آخر يظهر بوصفه Busyفقط، دون عنوان ولا منظم ولا رابط. أما المنسق فيرى التفاصيل التي يقتضيها دوره. والقاعدة مفروضة على الخادم، في الموضع نفسه الذي يحكم فتح الحجز مباشرة.

RoomFlow Arabic day calendar, right to left, with a column per room and the current-time indicator across all columns
عرض اليوم بالعربية. تنعكس الشبكة، وينتقل محور الوقت إلى اليمين.
RoomFlow mobile agenda listing bookings by day with times, room, organiser and status badges
على الجوال يحل الجدول محل الشبكة — لا الشبكة مضغوطة.

كل كتلة رابط حقيقي باسم متاح للقارئ الآلي يحمل العنوان والقاعة والحالة والوقتين — لأن شريطا ملونا في موضع ما لا يعني شيئا لقارئ الشاشة. والكتل التي لا يحق للمستخدم فتحها ليست روابط أصلا.

معلومات القاعات

تتبع القاعات نظام تسمية واحدا يتيح للقارئ تخمين الغرض من القاعة قبل فتحها: تسمية بالغرض في الطرفين الصغير والكبير، وأسماء محايدة في الوسط. وتحمل كل قاعة نوعا صريحا إلى جانب رمزها.

القاعةالعربيةالنوعالسعة
Focus Roomقاعة التركيزFocus room2
Jasmine Roomقاعة الياسمينMeeting room4
Cedar Roomقاعة الأرزMeeting room6
Collaboration Roomقاعة التعاونCollaboration room6
Olive Roomقاعة الزيتونMeeting room8
Horizon Roomقاعة الأفقCollaboration room12
Executive Boardroomقاعة الاجتماعات التنفيذيةBoardroom20

تعرض بطاقة النتيجة الاسم والرمز والنوع والسعة والجناح والطابق والمدخل بلا عوائق ومدة بقاء القاعة متاحة وتجهيزاتها. وما عدا ذلك يقع في صفحة القاعة بدل ازدحام البطاقة. وجميع القاعات السبع وهمية.

منع التعارض

هذا هو الجزء الذي يجب أن يكون صحيحا. شخصان يضغطان «تأكيد» في اللحظة نفسها لا يجوز أن يحصل كلاهما على القاعة، ولا يمكن لأي تحقق في المتصفح أن يضمن ذلك.

ينشأ كل حجز داخل معاملة في قاعدة البيانات. وتقفل الصفوف المرشحة بـ SELECT … FOR UPDATE قبل تنفيذ اختبار التداخل، فيصطف الطلب الثاني خلف الأول بدل قراءة إجابة قديمة. واختبار التداخل نفسه يراعي المهلة الفاصلة: قاعة بمهلة ١٥ دقيقة ترفض حجزا يبدأ بعد ١٠ دقائق من انتهاء سابقه.

newStart < existingEnd + buffer newEnd   > existingStart - buffer

جرى التحقق منه بسباق اثنتي عشرة عملية تشغيل حقيقية على الموعد نفسه، خمس مرات. يفوز واحد فقط في كل جولة وتنتهي قاعدة البيانات بصف واحد بالضبط — ٦٠ محاولة متزامنة، دون أي حجز مزدوج. ولإثبات أن الاختبار ليس شكليا، وجهت المنظومة نفسها إلى تنفيذ ساذج يفحص ثم يدرج دون قفل: فأنتج اثني عشر حجزا متداخلا من اثني عشر.

كيف ترتب القاعات

لا يوجد هنا تعلم آلي ولا ادعاء به. الترتيب درجة صغيرة وحتمية يستطيع الإنسان قراءتها وتوقعها:

  • كفاءة السعة — القاعة التي تستوعب المجموعة بفارق مقعدين أو أقل تحصل على أفضل درجة، والقاعة الأكبر بكثير من الحاجة تتراجع.
  • توفر كل تجهيز مطلوب.
  • المدخل بلا عوائق حين يطلب.
  • نظام الاجتماع المرئي كأفضلية خفيفة.
  • اشتراط الموافقة يحتسب ضد القاعة، لأنه يؤخر التأكيد.

الأسباب المعروضة على كل بطاقة هي القيم نفسها التي حسبت منها الدرجة، فلا يمكن أن ينفصل التفسير عن الترتيب. والأسباب والتحفظات مفصولة عمدا: اشتراط الموافقة كلفة، وإدراجه تحت عنوان «لماذا هذه القاعة» يبيع عيبا على أنه ميزة.

وحين لا تناسب أي قاعة، ينتج المنطق نفسه بدائل — أقرب موعد متاح في كل قاعة مناسبة — بدل نتيجة فارغة.

الأدوار والصلاحيات

ثلاثة أدوار، لكل منها شاشة أولى مختلفة. ويفحص كل مسار وكل إجراء في المتحكم وكل رابط مباشر على الخادم؛ فإخفاء الرابط عرض، لا حماية.

موظف

البحث عن قاعة وحجزها ورؤية حجوزاته وإلغاؤها وتسجيل الحضور برمز QR. ولا يستطيع فتح حجز أي شخص آخر، لا برابط ولا بغيره.

منسق

كل ما يفعله الموظف، إضافة إلى جدول اليوم في كل القاعات، وتسجيلات الحضور المستحقة، وحالات عدم الحضور، والموافقات، وفترات الصيانة.

مدير النظام

كل ما يفعله المنسق، إضافة إلى المستخدمين والأدوار، وتقارير الاستخدام، وسجل التدقيق.

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

تسجيل الحضور وحالات عدم الحضور

يحمل كل حجز رمز حضور عشوائيا بطول ١٢٨ بت، يولد بمعزل عن معرف الحجز فلا يمكن تخمينه أو تتبعه. ويرسم رمز QR بمرمز Reed–Solomon كتب لهذا المشروع بدل خدمة خارجية، فلا تغادر أي بيانات حجز الخادم لرسم رمز.

  • يفتح تسجيل الحضور قبل الاجتماع بخمس عشرة دقيقة ويغلق بعد بدايته بخمس عشرة دقيقة.
  • المسح خارج هذه المهلة يرفض مع ذكر السبب، لا برسالة خطأ عامة.
  • الحجز الذي لا يسجل أحد حضوره يحرر تلقائيا ويتوقف عن حجز القاعة.
  • يعرض الموعد المحرر على صاحب أطول انتظار في القائمة ممن تناسبهم القاعة، وينتهي العرض إن لم يستخدم.
  • الحجز الذي حضره أصحابه يغلق عند انتهائه، فيفصل الاستخدام بين «المستخدم» و«المحجوز».

الحفاظ على أمان النسخة التجريبية

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

  • الحجز، وإلغاء حجزك أنت، والانضمام إلى قائمة الانتظار، وتسجيل الحضور — كلها تعمل فعلا، فهي الرحلة التي وجدت النسخة التجريبية لعرضها.
  • أما إلغاء حجز شخص آخر، أو إلغاء سلسلة كاملة، أو تعطيل مستخدمين، أو تعديل القاعات، فمحجوبة، وتسجل كل محاولة محجوبة في سجل التدقيق. demo_blocked.
  • حسابات النسخة التجريبية الثلاثة معروضة في صفحة الدخول مع زر تعبئة بنقرة. يملأ النموذج ويتوقف؛ لا يرسل شيء نيابة عن الزائر، ويبقى حقل كلمة المرور حقل كلمة مرور.
  • مهمة ليلية تعيد البيانات إلى حالتها الأصلية. تعمل بحساب MySQL منخفض الصلاحيات خاص بها لا يقرأ ويكتب إلا في هذا المخطط وحده — وتم التحقق من عجزه عن الحذف، أو الوصول إلى قاعدة أخرى، أو إنشاء قاعدة، أو منح نفسه أي صلاحية — وتحتفظ بقفل يمنع تداخل عمليتين.
  • ولا تقرأ تلك المهمة بيانات اعتماد التطبيق نفسه أبدا.

العربية والإنجليزية

العربية هي اللغة الافتراضية ولغة من الدرجة الأولى، لا طبقة ترجمة. وتنعكس الواجهة بخصائص CSS المنطقية، فينقلب الدرج والتنقل وتخطيطات البطاقات وشبكات النماذج دون ورقة أنماط ثانية.

  • أرقام عربية-هندية في كل الواجهة العربية.
  • أسماء الأشهر العربية، مركبة من أجزاء بدل تمريرها عبر صيغة تنسيق — إذ سرب إصدار سابق رمزا نائبا في كل تاريخ.
  • صيغ جمع عربية حقيقية، بما فيها المثنى: غرفة واحدة متاحة for one, غرفتان متاحتان للاثنين، وصيغة أخرى مختلفة فوق العشرة. وعبارة واحدة بصيغة «:n غرف» تخطئ في ثلاث من هذه الأربع.
  • شرطة متوسطة تفصل الحقائق المتتابعة بالعربية بدل النقطة الوسطى، لأن الصفر العربي-الهندي (٠) هو نفسه نقطة مرتفعة، ولا يمكن تمييز أحدهما عن الآخر بحجم القراءة.
  • يتحقق في كل تشغيل للاختبارات من تطابق مفاتيح ملفي اللغة واكتمال عائلات الجمع.
RoomFlow Arabic mobile dashboard, right-to-left, with the menu button top right and the availability form in a single column
Arabic, RTL.
RoomFlow English mobile dashboard, left-to-right, with the menu button top left and the same availability form
الإنجليزية، من اليسار إلى اليمين. التخطيط نفسه، معكوسا.

إمكانية الوصول

  • كل عنصر تفاعلي يبلغ ٤٤×٤٤ بكسل CSS على المؤشر الخشن أو في شاشة حتى ٩٠٠ بكسل؛ ولا تستخدم المقاسات الأكثف إلا حيث يقود الفأرة.
  • حلقة تركيز واحدة ظاهرة على كل ما يقبل التركيز، باستخدام :focus-visible حتى لا تترك نقرة الفأرة حلقة خلفها.
  • درج الجوال حوار حقيقي: ينتقل التركيز إليه، ويحصر أثناء فتحه، ويعود إلى زر القائمة في كل مسار إغلاق.
  • والدرج المغلق يخرج من ترتيب التنقل بالكامل، بدل أن يبقى خارج الشاشة قابلا للتركيز.
  • الحالة دائما كلمات مع أيقونة، لا لون وحده.
  • رابط تخط، ومناطق معالم، و aria-current على عنصر التنقل النشط، وعناصر نماذج معنونة في كل مكان.
  • prefers-reduced-motion محترمة — فيتوقف الدرج وأعمدة الرسم البياني وانتقالات الأزرار.
  • جرى التحقق عند ٣٢٠ و٣٦٠ و٣٩٠ و٤٣٠ و٧٦٨ و١٠٢٤ و١٤٤٠ و١٩٢٠ بكسل باللغتين: لا تمرير أفقي ولا هدف لمس أصغر من المطلوب في أي موضع.

الأمان

  • كل استعلام مجهز مسبقا؛ ولا SQL مبني بالنصوص في أي مكان.
  • يهرب المخرج عند العرض، ودوال المساعدة التي ترجع ترميزا استثناء لا قاعدة.
  • رموز CSRF على كل طلب يغير حالة، ويتحقق منها على الخادم.
  • يجدد معرف الجلسة عند الدخول؛ والكوكيز HttpOnly, Secure and SameSite=Lax.
  • تحديد لمحاولات الدخول لكل بريد ولكل عنوان IP، مع قفل ينتهي بدل أن يبقى عالقا.
  • يفحص التخويل على كل مسار وإجراء متحكم ورابط مباشر — وفتح حجز شخص آخر، أو ملف تقويمه، يرجع ٤٠٣.
  • Content-Security-Policy و HSTS و X-Content-Type-Options و X-Frame-Options و Referrer-Policy و Permissions-Policy، كل منها يرسل مرة واحدة بالضبط.
  • لا يفصح عن إصدار الخادم، ولا X-Powered-By.
  • لا شيء خارج المجلد العام يمكن الوصول إليه عبر HTTP.
  • عامل الخدمة يخزن الهيكل الثابت فقط — لا صفحة مصادق عليها، ولا طلب POST، ولا رمز حجز.

البنية التقنية

PHP 8.3 و MySQL 8 دون إطار عمل. متحكم أمامي بجدول مسارات بتعابير نمطية، وتحميل تلقائي بنمط PSR، ومتحكمات نحيفة، وقواعد الحجز في مكان واحد بدل تشتتها في العروض.

  • ١٤ جدولا، و١٠ مفاتيح خارجية. القاعات، والمواقع، والتجهيزات، والحجوزات، والسلاسل، والحضور، وقائمة الانتظار، وفترات الصيانة، والإشعارات، وسجل التدقيق، ومحاولات الدخول، والإعدادات، والمستخدمون.
  • One booking engine. كشف التداخل، والمهل الفاصلة، والتحقق من السياسات، وتوسيع التكرار، والترتيب، والبحث عن مواعيد بديلة — كلها في صنف واحد تستدعيه المتحكمات.
  • المنطقة الزمنية مثبتة لكل اتصال. يضبط إزاحة جلسة MySQL على المنطقة الزمنية للتطبيق عند الاتصال، فلا يمكن أن يختلف NOW() and PHP's date() — وهو صنف الأخطاء الذي يكسر نوافذ التعارض وانتهاء القفل بصمت.
  • التكرار محدود بعدد التكرارات. وتحديد المشي اليومي بدلا من ذلك يجعل قاعدة يومية تنتج ٤٠٠ حجز بينما تحد الأسبوعية عند نحو ٥٧.
  • الأصول مصدرة بزمن تعديل الملف، فلا يمكن أن يظل إصلاح CSS غير مرئي خلف ذاكرة مؤقتة لساعة.
  • تصدير تقويم RFC 5545 مكتوب مباشرة، بطي أسطر صحيح على مستوى البايت، وتهريب لا يستطيع عنوان اجتماع عدائي الإفلات منه.

النشر

  • مجلد ومستخدم نظام ومجمع PHP-FPM خاص به، مع open_basedir confining it.
  • قاعدة بيانات وحساب تطبيق خاصان به، لا يملكان إلا SELECT و INSERT و UPDATE و DELETE على ذلك المخطط وحده — وتم التحقق من رفض الوصول العابر للقواعد.
  • حساب منفصل وأضيق صلاحية لإعادة تهيئة النسخة التجريبية ليليا.
  • مضيف افتراضي في nginx بـ HTTPS، وإعادة توجيه دائمة من HTTP، وتخزين مؤقت للأصول الثابتة.
  • يتحقق من الإعدادات قبل كل إعادة تحميل؛ وفحصت التطبيقات المستضافة على الخادم نفسه قبل كل تغيير وبعده ولم تتأثر.
  • تدوير للسجلات، وإعادة تهيئة ليلية للبيانات، ومهمة صيانة كل خمس دقائق تحرر حالات عدم الحضور وتنهي عروض قائمة الانتظار القديمة.

الاختبار

ست مجموعات اختبار تعمل على التطبيق المنشور، لا على محاكاة له.

المجموعةما تغطيهعدد الاختبارات
Unit and integrationمنطق التداخل والمهل، والتحقق من السياسات، والموافقات، والصيانة، والتكرار، والترتيب، والبدائل، و ICS، و QR، والتغطية ثنائية اللغة وصيغ الجمع، وتوافق المنطقة الزمنية، ومهام الصيانة143
Concurrency١٢ عملية حقيقية تتسابق على موعد واحد، ٥ جولات٦٠ محاولة، ٠ حجز مزدوج
HTTP مباشرالمصادقة، وفرض الأدوار، و CSRF، والوصول المباشر للكائنات، وترويسات الأمان، والنقل، وضوابط النسخة التجريبية، وعقد التنقل، وصياغة التوفر124
Calendarثلاثة عروض، ووضع الكتل، ومسارات التداخل، والمرشحات، والحالات، ومؤشر الوقت الحالي، والحالات الفارغة، والأسماء للقارئ الآلي، والخصوصية حسب الدور، والتهريب، وثنائية اللغة56
End-to-end journeyالدخول، والبحث، والاختيار، والتأكيد، والعرض، والتعارض، والتصدير، وتسجيل الحضور بـ QR، والإلغاء23
Login throttlingالقفل، وانتهاؤه، والعزل بين الحسابات5

اثنان من هذه الاختبارات موجودان لأنهما أمسكا بي. فحصت منظومة التزامن أمام تنفيذ غير مقفل عمدا لإثبات قدرتها على كشف العطل الذي تدعي منعه. وكان اختبار يتحقق من أن كل إجراء يغير حالة محمي في النسخة التجريبية ينجح شكليا — إذ كان يقسم الدوال على public function بينما تعلن المتحكمات public static function، فلم يحلل دالة واحدة قط. وبعد إعادة كتابته اعتمادا على جدول المسارات، كشف ثغرة حقيقية فورا.

ما تم قياسه

هذه أرقام من البناء نفسه. لا توجد هنا أعداد مستخدمين ولا معدلات تبن ولا ادعاءات توفير، لأن RoomFlow نسخة عرض لمعرض أعمال وليست لديه أي منها.

  • 351اختبار آلي ناجح
  • 60محاولة حجز متزامنة، دون أي حجز مزدوج
  • 236فحص صفحة × عرض دون تمرير أفقي
  • 0عنصر لمس أصغر من المطلوب على الجوال واللوحي
  • 8عرض شاشة تم التحقق منه باللغتين
  • 14جدولا، و١٠ مفاتيح خارجية

التقنيات

  • PHP 8.3
  • MySQL 8
  • nginx
  • PHP-FPM
  • No framework
  • Vanilla JavaScript
  • خصائص CSS المنطقية
  • تطبيق ويب تقدمي
  • Service worker
  • RFC 5545 iCalendar
  • مرمز QR بخوارزمية Reed–Solomon
  • Let’s Encrypt
  • Bilingual AR / EN