Task #710: Weekly availability intervals (Calendly-style) for protocol booking slots

- DB: protocol_booking_slots.days_of_week integer[] NOT NULL default {0..6} (0=Sun..6=Sat, Riyadh); pushed via drizzle-kit, legacy rows keep all-days behaviour.
- API (routes/protocol.ts): slot create/patch accept daysOfWeek (validated, deduped, sorted); new POST /protocol/booking-slots/generate (requireManageRooms) generates stepped slots in [from,to); duplicate rule is day-scoped: same start time on a different day merges the days into the existing slot (existing duration kept), fully covered times skipped; returns {created,merged,skipped,slots}; manual POST create also merges disjoint days instead of 409; matchesActiveSlot and public availability filter by Riyadh weekday.
- UI (protocol.tsx): interval-generation form (from/to/duration + day toggle pills + workdays/all-days presets), toast shows created/merged/skipped, slot list shows day names, booking dialog filters slots by chosen date weekday with stale-selection reset and empty-day hint. i18n keys added (ar/en).
- Tests: 13/13 pass incl. generation, day-scoped merge (same time different day is NOT a duplicate), duplicate skipping, validation, weekday filtering in availability + booking rejection, viewer 403 on generate.
This commit is contained in:
Replit Agent
2026-07-08 20:20:30 +00:00
parent 19e6693200
commit b56f1682a9
5 changed files with 157 additions and 37 deletions
+3 -3
View File
@@ -1930,10 +1930,10 @@
"intervalFrom": "من",
"intervalTo": "إلى",
"generate": "توليد الأوقات",
"generated": "تم توليد {{created}} وقتًا (تم تجاهل {{skipped}} مكررًا)",
"generatedNone": م يتولد أي وقت — كل الأوقات موجودة مسبقًا.",
"generated": "تم توليد {{created}} وقتًا، ودمج الأيام في {{merged}}، وتجاهل {{skipped}} مكررًا.",
"generatedNone": ا جديد — كل الأوقات والأيام موجودة مسبقًا.",
"pickDay": "اختر يومًا واحدًا على الأقل",
"noSlotsForDay": "لا توجد أوقات متاحة في هذا اليوم"
}
}
}
}