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

- الدور
- تصميم وتنفيذ، من البداية للنهاية
- النطاق
- موقع للمرضى + أربع شاشات للعيادة، ١٣ صفحة
- التقنيات
- Next.js 16 · TypeScript · Tailwind 4 · Postgres
- نوع المشروع
- عرض داخلي، عيادة متخيَّلة
المطلوب
كثير من العيادات الصغيرة تعمل بقائمة ورقية في الاستقبال وصالة انتظار ممتلئة. يأتي المرضى مبكرًا لأنهم لا يعرفون متى يحين دورهم، ويجيب الاستقبال عن السؤال نفسه طوال اليوم، وسرعة الطبيب الفعلية لا يراها أحد خارج الغرفة.
كان المطلوب نظامًا ترى فيه العيادة كلها نفسها: حجزًا من الهاتف بلا حساب، ومكتب استقبال يدير اليوم، وشاشة في صالة الانتظار تنادي الأرقام بصوت عربي، وورقة زيارة للطبيب، وأرقامًا للمالك — كلها بالعربية، وعلى قاعدة بيانات واحدة، وكل شاشة تلحق بالأخرى في ثوانٍ.
وهو عمل للعرض، وهذا أضاف قيدًا خاصًا به: يجب أن يستطيع غريب تجربة كل الأدوار في أقل من دقيقة دون أن يترك أثرًا. لذلك يُفتح كل دور بضغطة، وكل ما فيه متخيَّل، وتعيد العيادة بناء نفسها كل يوم. وقد بدأ المشروع صفحةً واحدة لصالة انتظار تحرّكها لوحة Arduino بلوحة مفاتيح وجرس؛ وهذا ما صارت إليه تلك الصفحة.
ما تم تسليمه
- حجز بلا حساب — الطبيب، وكشف أو إعادة، واليوم والساعة، والاسم والهاتف — حتى ١٤ يومًا مقدمًا، وقبل الموعد بعشرين دقيقة على الأقل، وبحد ثلاثة حجوزات قائمة لكل رقم هاتف
- صفحة تذكرة لكل حجز بمراحل الزيارة، وإضافة إلى التقويم، وتأجيل وإلغاء — وبعد تسجيل الوصول، الموضع في الطابور ووقت انتظار تقديري يتحدّث وحده
- الاستقبال: قائمة اليوم مع البحث، وتسجيل الوصول، والحالات بلا حجز، والطوارئ التي تتقدّم الصف، ومن لم يحضر، والدفع نقدًا أو عبر إنستاباي، وتقفيل لليوم يقارن ما عُدَّ في الدرج بما سُجِّل
- شاشة صالة انتظار بأرقام سباعية المقاطع لكل غرفة، ونغمة ثم الرقم مقروءًا بالعربية، ونغمة عاجلة للطوارئ، ورمز QR إلى صفحة الطابور عند المريض
- مكتب الطبيب: المريض الذي في الغرفة والطابور في الخارج، وورقة زيارة بالعلامات الحيوية والشكوى والتشخيص والملاحظات تحفظ نفسها، والزيارات السابقة، وروشتة A5 بالإنجليزية والعربية
- أرقام المالك على ٧ أيام أو ٣٠، وكلٌّ منها مقارَن بالفترة السابقة
- دخول بضغطة إلى كل دور، وأربعة أطباء — منهم طبيب أسنان — و١٤٨ مريضًا متخيَّلًا مبنيّين حول اليوم الحالي، وبداية جديدة كل يوم
- شاشات على قاعدة بيانات واحدة
- 5
- اختبارًا آليًا على المواعيد والطابور
- 41
- فحصًا في المتصفح على كل الأدوار
- 52
- ضغطة لدخول أي دور
- 1





القرارات، ولماذا
سرد التقنيات المستخدمة لا يقول شيئًا. المفيد في دراسة الحالة هو القرارات التي كان لها بديل حقيقي — لذلك يذكر كل قرار هنا ثمنه، لا ما تم اختياره فحسب.
- ٠١
قاعدة البيانات ترفض الحجز المزدوج
الزيارات المتداخلة للطبيب نفسه يرفضها قيد استبعاد في Postgres، لا كود التطبيق.
قد يضغط هاتفان على الموعد نفسه في الثانية نفسها. والفحص في الكود يقرأ المواعيد الفارغة ثم يكتب، فيجتاز الطلبان الفحص معًا. أما القيد على الطبيب واليوم ومدى الوقت فيُفشل الكتابة الثانية داخل قاعدة البيانات نفسها، ويُخبَر ذلك المريض بكلام واضح أن الموعد حُجز للتو.
الثمن · يحتاج امتداد btree_gist ويربط المخطط بـPostgres. والحالات بلا حجز مستثناة من القاعدة عن قصد — فهي تنضم إلى الطابور كما هو ولم تملك موعدًا أصلًا.
- ٠٢
كل فعل جملة SQL واحدة
كل ما يفعله شخص في العيادة جملة UPDATE واحدة يحمل شرطُها WHERE شرطَه المسبق.
مشغّل Postgres الخاص بالخوادم المؤقتة يرسل جملة واحدة في كل طلب، بلا معاملات تفاعلية. لذلك يختار «نادي اللي بعده» أول مريض في الصف ويعلّمه مُنادى عليه في جملة واحدة تتحقق أيضًا ألا أحد في الغرفة بالفعل — فإذا ضغطه موظفان معًا نودي مريض واحد. والضغطة على شاشة متأخرة ثوانيَ لا تطابق شيئًا، ويُرد عليها بجملة وتحديث.
الثمن · جزء من المنطق يعيش في SQL لا في TypeScript، وهذا يجعله أصعب قراءة. وتغطيه اختبارات تعمل على Postgres حقيقي في الذاكرة لا على محاكاة.
- ٠٣
شاشات حية بالاستطلاع، بتكلفة زهيدة
الشاشات تسأل كل ثانيتين إلى ثلاث، والخادم يجيب «لم يتغير شيء» بعد استعلام صغير واحد.
كل كتابة ترفع رقم مراجعة. والشاشة ترسل رقم المراجعة الذي لديها؛ فإن ظل مطابقًا كان الرد 204 فارغًا ولا يُعاد رسم شيء. ويبطؤ الاستطلاع بعد خمس دقائق من الهدوء، ثم يبطؤ ثانيةً بعد ثلاثين، ويتوقف ما دام التبويب مخفيًا، فلا تكاد شاشة تُركت تعمل طوال الليل تكلّف شيئًا. ويعمل على أي مضيف خوادم مؤقتة، بلا خادم اتصالات يحتاج أن يبقى حيًّا.
الثمن · قد يستغرق التغيير فترة استطلاع كاملة ليظهر — ثانيتين على شاشة الانتظار. والأحداث المرسلة من الخادم تزيل هذا التأخير، وهي الخطوة التالية.
- ٠٤
تقدير الانتظار يتعلّم إيقاع الطبيب
«حوالي ٢٥ دقيقة» تأتي من الطابور ومن المدة التي تستغرقها زيارات اليوم فعلًا.
المدد المحجوزة خطة، والأطباء يتأخرون. وبعد ثلاث زيارات منتهية يقارن التقدير الوقت المخطط بالفعلي في آخر ست زيارات، فيمدّ بقية الطابور أو يقصّره — محصورًا بين ٠٫٧ و١٫٦، حتى لا تجعل حالة طويلة واحدة الطابور كله يبدو ميؤوسًا منه. والحساب نفسه يعمل على الخادم وفي المتصفح، فيتحرك العدّ التنازلي على الهاتف كل ثانية بين استطلاعين.
الثمن · هو تقدير، والتذكرة تقول ذلك صراحةً. وحتى تنتهي ثلاث زيارات يفترض الخطة.
- ٠٥
العيادة تعيش بتوقيت القاهرة
اليوم نص تاريخ والوقت دقائق منذ منتصف الليل، وكلاهما بتوقيت القاهرة، أينما كان الخادم.
المضيف يعمل بتوقيت UTC، وهاتف الزائر قد يكون مضبوطًا على أي شيء. وحفظ وقت العيادة كقيم محلية بسيطة يعني أن «الثلاثاء، ٥:٠٠ م» واحد عند الخادم والشاشات والمريض، عبر تغيير التوقيت الصيفي، ويبقى حساب المواعيد أعدادًا صحيحة.
الثمن · لا تعيش العيادة إلا في منطقة زمنية واحدة — وهذا حال أي عيادة. والموضع الوحيد الذي يحتاج لحظات حقيقية، ملف التقويم، يحوّل صراحةً.
- ٠٦
النداء ينطقه المتصفح
شاشة الانتظار تعزف نغمة، ثم تقرأ الرقم بصوت عالٍ بمحرك النطق العربي في المتصفح نفسه.
النداء الذي يظهر على الشاشة فقط يفوت المريض المنشغل بهاتفه. والصوت المسجَّل يحتاج ملفًا لكل رقم وكل غرفة؛ أما النطق الآلي فيقرأ «رقم 24، اتفضل عيادة 1» من نص. وتطلب الشاشة ضغطة واحدة في البداية لأن المتصفحات تقفل الصوت حتى ذلك الحين، وتختار أفضل صوت عربي على الجهاز، المصري أولًا.
الثمن · جودة الصوت تعتمد على الجهاز. فكروم على ويندوز بلا إضافات لا يملك صوتًا عربيًا؛ فتُعزف النغمة وتقول الشاشة لماذا لم يُقرأ الرقم. أما Edge على ويندوز ففيه صوت طبيعي.
- ٠٧
ضغطة لكل دور، وفحص مرتين
كل دور يُفتح بلا حساب، عبر ملف تعريف موقَّع يُفحص قبل عرض أي صفحة للعيادة، ثم مرة أخرى داخل كل فعل.
النسخة التجريبية التي تطلب كلمة مرور تخسر أغلب زوارها عند الباب، فكل دور زر. وملف التعريف موقَّع ويسمّي دوره. ومن لا يحمله يُعاد إلى صفحة اختيار الدور قبل عرض أي صفحة للعيادة، وكل فعل على الخادم يفحص الدور مرة أخرى، فلا يستطيع خطأ في التوجيه أن يفتح كتابة — ولا يستطيع طبيب أن يفتح زيارة طبيب آخر.
الثمن · أي أحد يستطيع أن يكون موظف الاستقبال. وهذا هو المقصود في نسخة تجريبية؛ أما عيادة تعمل فعلًا فتحتاج حسابات بأسماء الموظفين أولًا.
- ٠٨
الروشتة مكتوبة كما تُكتب في مصر
أسماء الأدوية بالإنجليزية تحت R/، والتعليمات بالعربية، واسم الطبيب باللغتين، على ورقة A5.
الصيدلي يقرأ أسماء الأدوية والمريض يقرأ التعليمات، فكل جزء بلغة قارئه. والعلامات الحيوية تحتفظ بوحداتها اللاتينية. وقائمة قصيرة بالأدوية الشائعة لكل تخصص تملأ سطرًا بضغطة، والورقة تحفظ نفسها بعد لحظة من توقف الطبيب عن الكتابة.
الثمن · كل اسم عليها متخيَّل، والورقة مطبوع عليها أنها غير صالحة للصرف.
- ٠٩
النسخة التجريبية تعيد نفسها
أول طلب في يوم جديد بتوقيت القاهرة يحجز ذلك اليوم ويعيد بناء العيادة من الصفر.
الزوار يحجزون وينادون ويلغون طوال اليوم. والبيانات مبنية بالنسبة إلى اليوم الحالي — ثلاثة أسابيع من التاريخ لأرقام المالك، وصالة انتظار في منتصف الوردية، وأسبوعان من الأيام نصف المحجوزة أمامها — فتبدو النسخة التجريبية واحدة في أي تاريخ. وحجز اليوم جملة إدخال واحدة، فلا يستطيع أول زائرَين معًا أن يعيدا بناءها كلاهما. وحين تكون الصالة فارغة، يُدخل أول دور يُفتح بضعة مرضى متخيَّلين بلا حجز، فيبقى دائمًا ما يُضغط عليه.
الثمن · كل ما يفعله الزائر يزول بحلول الصباح — وهذا الصواب لنسخة تجريبية، وأول ما يُزال لعيادة حقيقية.
خمس شاشات، لحظة واحدة
لم يكن أصعب ما في البناء شاشةً بعينها، بل أن خمسًا منها تصف اللحظة نفسها على أجهزة مختلفة، عبر مضيف لا يُبقي أي اتصال مفتوحًا — وأن مريضًا يقرر هل يغادر المقهى المجاور يعتمد على صحة ما في هاتفه.
كل شاشة تحتفظ بلقطة ورقم مراجعتها، وتعيد حساب ما يمكن حسابه محليًا — العدّ التنازلي والتقدير والساعة — كل ثانية. والشاشة التي فعلت شيئًا للتو تطلب لقطة جديدة فورًا بدل انتظار دورها. وشاشة الانتظار تحتفظ بطابور نداءاتها الخاص، فيُعلَن نداءان متتاليان كلاهما، واحدًا بعد الآخر، والنداء الذي كان على اللوحة حين شُغّلت الشاشة يُعامَل كماضٍ لا كخبر.
وفُحص ذلك من طرف إلى طرف لا بالعين. فسكربت يقود هاتف مريض ومكتب الاستقبال وشاشة الانتظار والطبيب في متصفحات منفصلة في آن واحد — حجز، وتسجيل وصول، ونداء، وتكرار نداء، وحالة بلا حجز، وإنهاء، وتقفيل اليوم — ويتحقق أن كل شاشة تتغير في ثوانٍ. وتنجح هذه الفحوص الاثنان والخمسون على قاعدة بيانات محلية وعلى النسخة المنشورة على Neon. وكشف التشغيل مشكلة في الاختبار نفسه: إبقاء دزينة من الصفحات الحية مفتوحة على خادم بعيد استنفد حدود الطلبات في المتصفح، فصار السكربت يغلق صفحات كل جزء حين ينتهي.
ما استُبعد عن قصد
ضبط النطاق جزء من العمل. كل بند هنا دُرس ثم استُبعد لسبب مذكور.
- الدفع الإلكتروني
- الدفع يُسجَّل في الاستقبال، نقدًا أو عبر إنستاباي. وبوابة الدفع عقد مع عيادة، لا شيء يُجرَّب في نسخة تجريبية.
- التحقق برسالة نصية
- الحجوزات مربوطة برقم هاتف ومحدودة بثلاثة قائمة. ورمز لكل حجز له تكلفة على كل رسالة، ومكانه يوم تبدأ عيادة حقيقية العمل.
- ملف طبي كامل
- الطبيب يكتب ورقة زيارة. أما الملف الكامل فيحتاج صلاحيات وقواعد حفظ وأمانًا على مستوى آخر.
- حسابات الموظفين وسجل التدقيق
- كل دور بضغطة عن قصد. والعيادة التي تستخدمه فعلًا تحتاج مستخدمين بأسمائهم وسجلًّا بمن فعل ماذا.
- الدفع من الخادم بدل الاستطلاع
- اختير الاستطلاع ليعمل على أي مضيف خوادم مؤقتة أولًا. والأحداث المرسلة من الخادم تزيل تأخير الثانيتين.
- زر النداء الفعلي
- لوحة مفاتيح Arduino والجرس الأصليان غير متصلين بهذا النظام. وربطهما عبر Web Serial هو الخطوة التالية.
كل ما في النسخة التجريبية متخيَّل: العيادة والأطباء والمرضى وأرقام الهواتف. والصور من Unsplash ولا تظهر فيها وجوه. والمواعيد والطابور والكتابة في قاعدة البيانات تغطيها ٤١ اختبارًا آليًا، وكل الأدوار تغطيها ٥٢ فحصًا في المتصفح؛ كما رُوجعت التخطيطات يدويًا على مقاسي الهاتف وسطح المكتب. والواجهة بالعربية فقط. والنداء المنطوق يعتمد على وجود صوت عربي في الجهاز. ولم تستخدمه عيادة حقيقية.
التقنيات
- Next.js 16
- React 19
- TypeScript
- Tailwind CSS 4
- Postgres
- Drizzle ORM
- Neon
- Vercel
الخدمات
- تصميم المنتج
- تصميم الواجهة
- هندسة الواجهة
- هندسة الخلفية
- تصميم البيانات
محتاج حاجة مبنية بنفس المستوى؟
نصمم ونبني أنظمة كاملة — مسار شراء يعمل، وصلاحيات، ولوحة تحكم تدير الشغل فعليًا. والعربية عندنا ليست ترجمة تُركَّب في الآخر.
