Task #704: أوقات حجز محددة يديرها الأدمن + مهلة حجز مسبقة

- DB: protocol_booking_slots (وقت "HH:mm" بتوقيت الرياض، مدة، تفعيل، ترتيب)
  + protocol_settings (booking.minLeadMinutes، الافتراضي 60 دقيقة).
- API: CRUD ‎/protocol/booking-slots (الكتابة تتطلب protocol.rooms.manage،
  تكرار الوقت → 409 duplicate_slot)، GET/PATCH ‎/protocol/settings (upsert + سجل
  تدقيق)، ونقطة عامة ‎/protocol/public/availability (محدّد معدل خاص، تعيد
  booked/tooSoon/available لكل وقت).
- الفرض في الخادم: الطلب العام يجب أن يطابق وقتاً مفعّلاً + يحترم المهلة
  (400 invalid_slot / lead_time برسائل عربية)؛ الموظفون ملزمون بالأوقات لكن
  معفيون من المهلة. بدون أوقات مفعّلة يعمل الوضع الحر القديم.
- الواجهة: نموذج الطلب العام يعرض شبكة أوقات حسب التوفر (مع fallback قديم)؛
  تبويب القاعات يعرض قسم إدارة الأوقات + المهلة لمن يملك canManageRooms؛
  حوار الحجز الداخلي يتحول لتاريخ + قائمة أوقات عند وجود أوقات مفعّلة.
- i18n: مفاتيح protocol.slots.* بالعربية والإنجليزية.
- اختبارات: tests/protocol-booking-slots.test.mjs — ‏10 اختبارات ناجحة
  (CRUD/تكرار، الإعدادات، التوفر، invalid_slot، lead_time، تعارض، وقت معطّل،
  إعفاء الموظفين، صلاحيات القراءة/الكتابة، الحذف).
- مراجعة المعماري: PASS؛ عولجت الملاحظات (توثيق قرار الصلاحيات + اختبار
  regression له، ومنع الإرسال في الواجهة قبل اختيار التاريخ والوقت).
This commit is contained in:
Replit Agent
2026-07-08 14:43:44 +00:00
parent d48e434400
commit f3fe741314
7 changed files with 1539 additions and 61 deletions
+17
View File
@@ -1894,6 +1894,23 @@
},
"audit": {
"empty": "لا يوجد نشاط."
},
"slots": {
"title": "أوقات الحجز",
"hint": "عند إضافة أوقات، يقتصر الحجز (الداخلي والعام) على هذه الأوقات فقط. اتركها فارغة للسماح بأي وقت.",
"startTime": "وقت البداية",
"duration": "المدة (دقيقة)",
"add": "إضافة",
"empty": "لا توجد أوقات محددة — الحجز متاح في أي وقت.",
"activate": "تفعيل",
"deactivate": "تعطيل",
"leadTitle": "مهلة الحجز المسبق",
"leadHint": "الحد الأدنى بالدقائق قبل بداية الموعد لقبول الطلبات العامة (0 لتعطيل الشرط). لا تنطبق المهلة على موظفي المراسم.",
"leadLabel": "المهلة (دقيقة)",
"leadSaved": "تم حفظ المهلة",
"dateLabel": "التاريخ",
"slotLabel": "الوقت",
"pickSlot": "اختر الوقت"
}
}
}