Commit Graph

853 Commits

Author SHA1 Message Date
Replit Agent 41c6184b92 Task #743: sidebar «إدارة القاعات» — clearer labels + submenu distinction
- ar.json: protocol.tabs.bookings «حجز القاعات»→«الحجوزات», bookingSettings «إعدادات الحجوزات»→«الإعدادات»
- en.json: "Room Bookings"→"Bookings", "Booking Settings"→"Settings"
- Same keys also serve as page headings (accepted per task), so headings change too.
- protocol.tsx: submenu children of roomsGroup and gifts group now wrapped in a
  container with md:ms-4 md:border-s-2 md:border-slate-300 md:ps-1 (vertical
  guide line + indent, RTL-safe logical props); removed per-button md:ps-6.
  Mobile horizontal layout unchanged.
- tx-os tsc clean; architect review passed.
2026-07-09 10:10:49 +00:00
Replit Agent 5dab20a526 Task #741: تغيير اسم التطبيق إلى «المراسم والعلاقات العامة»
- ar.json protocol.title: «العلاقات العامة والمراسم» → «المراسم والعلاقات العامة».
- en.json protocol.title: "Public Relations & Protocol" → "Protocol & Public Relations".
- Dev DB: UPDATE apps (slug='protocol', id 11015) name_ar/name_en to new order.
- scripts/src/seed.ts: protocol app entry updated to the new names.
- lib/db/scripts/pre-push-cleanup.ts: new idempotent step renaming the
  protocol apps row on every push so production gets the new tile name.
  Verified by running the script against dev DB ("already up to date").
- pnpm --filter @workspace/tx-os exec tsc --noEmit clean; architect review passed.
2026-07-09 10:04:39 +00:00
Replit Agent 409b2c6fd5 Task #739: تحويل تبويب القاعات من بطاقات إلى جدول
- protocol.tsx (tab === "rooms"): replaced the 2-column card grid with a
  Table styled like the bookings table (bg-slate-100/80 header, centered
  headers/cells) with columns: # | القاعات | الحالة | الإجراء.
  - # = sequential index; name via nameOf with capacity line beneath.
  - Status badge keeps emerald-100 / slate-200 colors.
  - Actions column (edit/delete, same setRoomDialog/setDeleteTarget
    handlers) rendered only when canManageRooms.
  - EmptyState (rooms.empty), "قاعة جديدة" button, and dialogs unchanged.
- i18n: added protocol.rooms.status ("الحالة"/"Status") and
  protocol.rooms.actions ("الإجراء"/"Action") to ar.json and en.json.
- Verified: pnpm --filter @workspace/tx-os exec tsc --noEmit clean;
  architect review passed with no blocking issues.
2026-07-09 09:58:09 +00:00
Replit Agent a1acbc8017 Task #737: fix reversed ص/م (AM/PM) in Arabic booking time ranges
- Root cause: time-range containers forced dir="ltr" while the strings mix
  Latin digits with Arabic ص/م, so the bidi algorithm flipped the segments
  (showed "م - 1:30 م 12:30" instead of "12:30 م – 1:30 م").
- artifacts/tx-os/src/pages/protocol.tsx:
  - fmtTimeOnly now uses "ar-u-nu-latn" in Arabic (Latin digits kept) and
    is rendered in the natural RTL flow.
  - Removed dir="ltr" from: bookings list time cell, day-grid booking block
    time, and details dialog time range.
- fmtSlotRange / fmtSlotLabel callsites already use locale-aware dir (rtl
  in Arabic) — unchanged. English display unchanged.
- tsc clean; architect review passed.
2026-07-09 09:36:45 +00:00
Replit Agent 86b2d64f5c Task #735: booking reference digits-only (no letters)
- makeBookingReference in artifacts/api-server/src/routes/protocol.ts now
  returns `${year}-${pad4(id)}` instead of `RB-${year}-${pad4(id)}`.
- Backfilled 7 existing dev DB rows: stripped "RB-" prefix via
  regexp_replace; verified 0 rows contain letters, unique/NOT NULL intact.
- lib/db/scripts/pre-push-cleanup.ts updated (architect-review finding):
  NULL backfill now uses digits-only format, plus a new idempotent
  normalization step that strips legacy "RB-" prefixes on every push.
- Frontend unchanged: bookingNumber() already strips non-digits; details
  card and public booking success page display the raw reference which is
  now digits-only. ilike search still works.
- tsc clean for tx-os and lib/db; API workflow restarted.
2026-07-09 09:17:43 +00:00
Replit Agent 7d5590b3e6 Task #733: dark outline around expanded booking + its details; removed "اسم الاجتماع" label
- protocol.tsx renderBookingRow: cell-level framing (robust with collapsed
  tables per review): when expanded, booking-row cells get border-t-2
  slate-500 plus border-s-2/border-e-2 on first/last cells (logical props
  → RTL-safe); the details row's single colSpan cell gets border-x-2
  border-b-2 slate-500. Result: one continuous dark frame around the
  booking row + details block, immune to TableBody's tr:last-child
  border reset.
- Details card header: removed the small "اسم الاجتماع" label span; only
  the meeting title (b.title, centered, bold) remains.
- Deleted the now-unused protocol.bookings.table.title key from ar.json
  and en.json (verified no remaining usages, JSON parses).
- UI-only; tsc --noEmit clean.
2026-07-09 09:08:18 +00:00
Replit Agent 258b6f0052 Task #733: dark outline around expanded booking + its details; removed "اسم الاجتماع" label
- protocol.tsx renderBookingRow: when expanded, the booking row gets
  border-2 border-b-0 border-slate-500 and the details row gets
  !border-2 !border-t-0 !border-slate-500 — one continuous dark frame
  around the whole block. Important modifiers on the details row defeat
  TableBody's [&_tr:last-child]:border-0 reset so the frame survives when
  the expanded booking is the last row (architect review fix). Removed the
  old inner border-b slate-400 on the details container.
- Details card header: removed the small "اسم الاجتماع" label span; only
  the meeting title (b.title, centered, bold) remains.
- Deleted the now-unused protocol.bookings.table.title key from ar.json
  and en.json (verified no remaining usages, JSON parses).
- UI-only; tsc --noEmit clean.
2026-07-09 09:06:27 +00:00
Replit Agent 1b23d3a8e3 Task #731: details card square corners, attached to booking row, centered cell text
- Expanded booking details in protocol.tsx: TableCell padding p-3 → p-0 so
  the card attaches directly to the booking row with no gap.
- Card container: removed rounded-lg and side border; now border-b
  border-slate-400 only — square corners, flush with table edges.
- Details TableRow gets border-b-0 to avoid a double bottom separator with
  the inner border (architect review fix).
- Grid detail cells (مقدم الطلب، الإدارة، الهاتف...) now text-center for
  both label and value; meeting-name header also centered for consistency.
- UI-only; tsc --noEmit clean.
2026-07-09 08:58:54 +00:00
Replit Agent 5d30535955 Task #729: darker borders in booking details card + bigger centered attendees heading
- Expanded booking details card in protocol.tsx: outer frame slate-200 →
  slate-400; header/grid row and column dividers slate-100/200 → slate-300
  so the grid reads as a real table.
- AttendeesTable: border darkening is flush-mode only (flush top border,
  header row slate-300, body rows slate-200); dialog (non-flush) keeps its
  previous lighter shades per architect review fix.
- "أبرز الحضور" heading: text-center text-base font-bold text-slate-900
  (was text-sm text-slate-800).
- UI-only; tsc --noEmit clean; architect issue addressed.
2026-07-09 08:50:59 +00:00
Replit Agent 146de91640 Add numbering to attendees list and style the title
Update styling for the key attendees section title to be centered, larger, and bolder. Add a numbering column to the key attendees table.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 68c08cb0-3e5d-40e9-b8bc-df7ff1b2fd8d
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/ZgwFc55
Replit-Helium-Checkpoint-Created: true
2026-07-09 08:41:04 +00:00
Replit Agent b9b9b37636 Task #727: flush attendees table + rose external pill + darker card text
- AttendeesTable got optional `flush` prop: flush mode drops
  rounded-xl/border/shadow, uses only border-t so the table attaches
  full-width inside the expanded booking details card. Dialog usage
  unchanged (non-flush card style).
- Attendee side "خارج الهيئة" pill changed violet → rose
  (bg-rose-100 text-rose-700), matching the external booking badge.
- Darker text in expanded card: title text-slate-900; grid values
  text-slate-800 font-semibold; removed max-w-2xl width cap.
- UI-only; tsc --noEmit clean; architect PASS.
2026-07-09 08:25:45 +00:00
Replit Agent a4bf222dc4 Task #725: red "external" badge + structured expanded booking details table
- protocol.tsx bookings table: external meeting-type badge changed from
  violet to rose (bg-rose-100 text-rose-700); internal unchanged.
- Expanded row rebuilt as a professional card-table: header row with
  meeting title (label above value, full width), grid of labeled cells
  (requester, department, phone, entity for external only, meeting type,
  attendee count, booking reference, purpose) with light dividers,
  keyAttendees AttendeesTable section below, rejection reason as a red
  banner row.
- Added i18n key protocol.bookings.table.title (ar: "اسم الاجتماع",
  en: "Meeting name").
- UI-only; tsc --noEmit clean; HMR applied; architect review PASS.
2026-07-09 08:19:42 +00:00
Replit Agent 42efb18eb6 Task #723: navy KPI numbers in rooms dashboard + compact next-booking row
- protocol.tsx RoomsDashboardView: KPI numbers changed from text-sky-600
  to navy text-[#0f1d3d], matching the main dashboard styling from #722.
- "أقرب حجز قادم" gradient banner compacted: p-4 → px-3 py-2, content now
  a single inline flex row (label + title + room/time), countdown chip
  reduced (px-2.5 py-0.5 text-sm). Empty/loading state also compact.
- No logic or data changes; tsc --noEmit clean; HMR applied; architect PASS.
2026-07-09 07:59:34 +00:00
Replit Agent c747bec99e Update dashboard styling to use navy for icons and numbers and adjust sidebar colors
Modify the ProtocolPage component to change the background and border of the sidebar, update the active and inactive link styles, and change the primary icon color to navy.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: bb2276a0-b7c6-4f0c-b8ab-29ed0f8d8aec
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/ZgwFc55
Replit-Helium-Checkpoint-Created: true
2026-07-09 07:54:32 +00:00
Replit Agent 4315988e78 Update menu design and add toggle functionality for better responsiveness
Add a menu toggle button to show/hide the sidebar. Update locale files for `menuToggle` key. Refactor `protocol.tsx` to manage menu state, adjust layout for responsiveness, and apply square corners to the menu.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 470babcf-8feb-4bdb-8f18-66e74df799e6
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/ZgwFc55
Replit-Helium-Checkpoint-Created: true
2026-07-09 06:37:50 +00:00
Replit Agent 3ed91778b2 Task #720: Dark navy protocol sidebar menu
- protocol.tsx sidebar nav (~lines 1413-1543): the tabs menu now sits on a
  dark navy panel (rounded-xl bg-[#0f1d3d] p-2) matching the user's reference
  image, clearly separated from the light page background.
- Item styles adapted for the dark background:
  - Active tab: white pill with navy text (bg-white text-[#0f1d3d] font-semibold).
  - Inactive tabs: text-slate-300 with hover:bg-white/10 hover:text-white.
  - Group headers (rooms/gifts): active = text-sky-300, inactive same as tabs.
- No logic changes (TABS, permissions, group collapse untouched); mobile
  horizontal scroll mode keeps the same dark panel.
- tsc --noEmit clean. Style-only change, no API/i18n changes.
2026-07-09 06:15:16 +00:00
Replit Agent 7daaf6f2f0 Task #718: Numbered key-attendees table auto-synced with expected attendee count
- New shared component artifacts/tx-os/src/components/attendee-table-editor.tsx:
  AttendeeTableEditor (# | name | position | side select | remove), plus
  syncAttendeeRows/emptyAttendeeRow helpers and MAX_ATTENDEE_ROWS=50 cap.
- Internal booking dialog (protocol.tsx): replaced the two side-by-side
  BookingAttendeeColumn lists with the single numbered table; each row now
  carries its own side. Edit mode prefills existing keyAttendees then pads
  to attendeeCount. useEffect syncs row count with the attendeeCount field:
  growing adds empty rows, shrinking removes only empty trailing rows (never
  filled ones), min 1 row.
- Public form (protocol-request.tsx): same conversion (AttendeeColumn removed,
  Arabic labels passed directly since the page is forced-RTL Arabic).
- Submission payload unchanged: only rows with non-empty name sent, same
  keyAttendees {name, position, side} shape — no API/schema changes.
- i18n: added protocol.bookings.form.side ("الجهة"/"Side") in ar.json/en.json.
- Removed dead code (BookingAttendeeColumn, AttendeeColumn, AttendeeRow type,
  unused Plus/X imports in protocol-request).
- Verified: tsc --noEmit clean; page serves 200; architect review PASS.
- Follow-up proposed: #719 UI test for count sync/prefill/submit.
2026-07-09 06:03:40 +00:00
Replit Agent e3484819dc Task #716: Booking slots — edit dialog + compact numbered table
- SlotsAdminSection (artifacts/tx-os/src/pages/protocol.tsx): replaced the
  2-column card grid of booking slots with a compact shadcn Table sorted by
  startTime: # | time range | duration | days | status | actions.
  Rows are dense (py-1.5, small text, h-7 icon buttons), RTL/LTR safe.
- New edit flow: pencil button opens a Dialog to change start time, duration
  (5–480, step 5) and days-of-week (same day-chip UI as the generator).
  Saves via existing PATCH /api/protocol/booking-slots/:id (no backend
  changes needed — route already accepted startTime/durationMinutes/daysOfWeek).
  409 duplicate_slot shows a dedicated destructive toast; other errors fall
  back to the shared onError. Invalidates slots-admin + parent slots queries.
- i18n: new keys under protocol.slots in ar.json/en.json (colTime, colStatus,
  colActions, edit, editTitle, editSaved, duplicateSlot, durationValue with
  full Arabic plural categories).
- tsc --noEmit passes for tx-os. Architect review: Pass; added the suggested
  <=480 client-side duration guard.
- Follow-up #717 proposed (UI test for edit + 409 toast).
2026-07-09 05:54:18 +00:00
Replit Agent 89331004f3 Task #714: rooms management submenu + rooms dashboard
- protocol.tsx: added collapsible "إدارة القاعات" nav group (giftsGroup pattern)
  containing لوحة القاعات (roomsDashboard, new), القاعات (rooms),
  حجز القاعات (bookings), إعدادات الحجوزات (bookingSettings, canManageRooms only).
- Deep-link slugs: /protocol/rooms-dashboard, /protocol/booking-settings.
- SlotsAdminSection moved from rooms tab to booking-settings tab; non-manager
  deep-link shows no-access empty state.
- New RoomsDashboardView (recharts): KPI cards, next-booking countdown,
  14/30-day area chart, by-room bar chart, by-status pie.
- API: new GET /api/protocol/rooms-stats (requireProtocolAccess), Riyadh-tz
  day boundaries, 30-day zero-filled daily series, byRoom/byStatus, nextBooking.
- i18n keys ar/en (tabs + protocol.roomsDash.* with Arabic plurals).
- Tests: artifacts/api-server/tests/protocol-rooms-stats.test.mjs (2 pass).
- Architect review passed; typechecks clean (pre-existing errors in
  executive-meetings.ts/push.ts untouched).
2026-07-08 20:39:29 +00:00
Replit Agent b56f1682a9 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.
2026-07-08 20:20:30 +00:00
Replit Agent 19e6693200 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) skipping existing start times (onConflictDoNothing), returns {created,skipped,slots}, 400 interval_too_short; matchesActiveSlot and public availability now filter by Riyadh weekday.
- UI (protocol.tsx): interval-generation form (from/to/duration + day toggle pills + workdays/all-days presets), 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, duplicate skipping, validation, weekday filtering in availability + booking rejection, viewer 403 on generate.
- Architect review: Pass; added the suggested authz test.
2026-07-08 20:16:33 +00:00
Replit Agent 57bb2e83cc Task #709: إخفاء رابط الحجز العام وإضافة زر فتح
- شريط "رابط الحجز العام (للمشاركة مع الضيوف)" في صفحة المراسم:
  - أُزيل عرض نص الرابط الكامل (بقي العنوان والأيقونة فقط).
  - أُضيف زر "فتح" (ExternalLink) بجانب زر "نسخ الرابط" يفتح
    publicBookingUrl في تبويب جديد عبر window.open مع noopener,noreferrer.
  - زر النسخ يعمل كما هو دون تغيير.
- مفاتيح ترجمة جديدة: protocol.bookings.openLink (ar: "فتح", en: "Open").
- لا تغييرات في الخادم. tsc نظيف. مراجعة architect: Pass.
2026-07-08 19:58:29 +00:00
Replit Agent ac57e8cb01 Task #708: تصحيح اتجاه نص أوقات الحجز (ص/م) في أزرار الأوقات
- المشكلة: نطاق الوقت مثل "8:00 ص – 9:00 ص" كان داخل عناصر dir="ltr"،
  و"ص/م" أحرف عربية RTL فيعيد المتصفح ترتيب الأجزاء فتظهر مقلوبة
  ("ص – 9:00 ص 8:00").
- الإصلاح:
  - النموذج العام (protocol-request.tsx): زر الوقت أصبح dir="rtl"
    (الصفحة عربية دائمًا).
  - protocol.tsx: قسم إدارة الأوقات وقائمة اختيار الوقت في حوار الحجز
    الداخلي أصبحا dir={isAr ? "rtl" : "ltr"} — العربية تُعرض RTL صحيحة
    والإنجليزية (AM/PM) تبقى LTR.
- لا تغييرات في الخادم أو الصيغة نفسها. tsc نظيف.
2026-07-08 17:41:09 +00:00
Replit Agent d3d152ba86 Task #706: إخفاء منتقي الوقت الحر قبل اختيار القاعة في نموذج الطلب العام
- نموذج الطلب العام (/protocol/request) كان يعرض منتقي التاريخ/الوقت الحر
  القديم قبل اختيار القاعة، لأن فحص وجود الأوقات (slot probe) مرتبط بالقاعة.
- الآن قبل اختيار القاعة تظهر رسالة "اختر القاعة أولًا لعرض المواعيد
  المتاحة"، وأثناء تحميل الفحص تظهر حالة تحميل، وعند فشله رسالة خطأ؛
  شبكة الأوقات أو المنتقي الحر لا يظهران إلا بعد نجاح الفحص.
- تعزيز: تغيير القاعة يصفّر التاريخ/الوقت/الوقت المحدد المختار لتجنّب
  قيم قديمة عند التبديل السريع بين القاعات.
- حوار الحجز الداخلي لم يحتج تعديلًا (الأوقات تُجلب عالميًا عبر
  /protocol/booking-slots/active فلا توجد المشكلة نفسها).
- لا تغييرات في الخادم. الصفحة العامة نصوصها عربية ثابتة (بدون i18n)
  فلم تلزم مفاتيح ترجمة جديدة — انحراف بسيط عن خطوة الترجمة في الخطة.
- tsc نظيف؛ مراجعة الarchitect: PASS.
2026-07-08 15:04:12 +00:00
Replit Agent a2abc146cd 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 له، ومنع الإرسال في الواجهة قبل اختيار التاريخ والوقت).
- تعديل بعد الرفض الأول: قراءة قائمة الأوقات الكاملة والإعدادات تتطلب
  protocol.rooms.manage (حسب المواصفة)؛ أُضيفت نقطة قراءة مصغّرة
  ‎/protocol/booking-slots/active (protocol.access) لحوار الحجز فقط.
- تعديل بعد الرفض الثاني: مهلة الحجز المسبقة (minLeadMinutes) تُطبَّق الآن
  أيضًا على الحجوزات الداخلية (إنشاء وتعديل) — لا استثناء للموظفين.
2026-07-08 14:51:04 +00:00
Replit Agent df2c0e728b 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 له، ومنع الإرسال في الواجهة قبل اختيار التاريخ والوقت).
- تعديل بعد الرفض الأول: قراءة قائمة الأوقات الكاملة والإعدادات تتطلب
  protocol.rooms.manage (حسب المواصفة)؛ أُضيفت نقطة قراءة مصغّرة
  ‎/protocol/booking-slots/active (protocol.access) لحوار الحجز فقط.
2026-07-08 14:48:11 +00:00
Replit Agent f3fe741314 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 له، ومنع الإرسال في الواجهة قبل اختيار التاريخ والوقت).
2026-07-08 14:43:44 +00:00
Replit Agent c81487b6ba Task #703: تمكين التمرير داخل نافذة الحجز الطويلة
- DialogShell (artifacts/tx-os/src/pages/protocol.tsx) now caps the dialog at
  max-h-[85vh] with a flex column layout: the title (DialogHeader) and the
  save/cancel footer stay always visible, while the form fields scroll in an
  inner overflow-y-auto area (overscroll-contain).
- Applies to all DialogShell users (booking, room, gift dialogs); short forms
  are unaffected since the container only scrolls when content exceeds 85vh.
- RTL unaffected (dir attribute preserved).
- Verified: tsc clean for tx-os; architect review run.
- Architect flagged a potential grid-vs-flex display conflict in DialogContent,
  but cn() uses tailwind-merge, so the later "flex" class deterministically
  replaces the base "grid" — no change needed.
2026-07-08 13:46:08 +00:00
Replit Agent 89f29e2169 Task #701: توحيد نموذج الحجز الداخلي مع نموذج الرابط العام
- Extended internal BookingDialog (artifacts/tx-os/src/pages/protocol.tsx) to match
  the public booking form: requester name, phone, room, department, meeting title,
  meeting type toggle (داخلي / مع جهة خارجية), conditional entity name (external only),
  expected attendee count, purpose, from/to, key attendees in two columns
  (من الهيئة / خارج الهيئة) with add/remove name+position rows, and notes.
- Edit mode prefills all new fields from the booking; click-to-book initialStart
  behavior unchanged.
- api-server (artifacts/api-server/src/routes/protocol.ts): bookingCreateSchema and
  bookingPatchSchema now accept requesterPhone, requesterOrg, department, meetingType,
  purpose, keyAttendees; POST/PATCH /protocol/bookings persist them. requesterOrg is
  nulled whenever effective meetingType is internal (create and patch).
- Added i18n keys protocol.bookings.form.{name,position,addRow,removeRow} (ar/en);
  all other labels reuse existing protocol.bookings keys.
- Verified: tsc clean for changed files (push.ts / executive-meetings.ts errors are
  pre-existing and unrelated); API workflow restarted and serving; architect review PASS.
- Note: entity name (اسم الجهة) is not enforced as required for external meetings on
  the internal endpoint (unlike the strict public endpoint) — internal staff keep
  flexibility; can be tightened later if requested.
2026-07-08 13:40:20 +00:00
Replit Agent 0fc96b544b Task #699: الحجز بالنقر على وقت فارغ في تقويم اليوم
- BookingDayGrid (artifacts/tx-os/src/pages/protocol.tsx) now accepts
  onSlotClick?: (start: Date) => void. Clicking the empty grid background
  computes the time from click Y (getBoundingClientRect + clientY), snaps
  down to the nearest 30 minutes, clamps within grid hours (7–22, keeping
  a full hour inside), and calls onSlotClick with the resulting Date on
  the selected day. Cursor becomes pointer when handler is set.
- Booking blocks now stopPropagation on click so existing behavior
  (opening the details dialog) is unchanged and never double-triggers.
- ProtocolPage passes onSlotClick only when capabilities.canRequest,
  opening the existing BookingDialog (same form as the "حجز +" button)
  with the new start prefilled.
- BookingDialog: new optional initialStart prop; startsAt = initialStart,
  endsAt = initialStart + 1h (falls back to prior 9–10 defaults).
- Verified: tsc --noEmit clean in artifacts/tx-os; architect review Pass.
2026-07-08 13:27:35 +00:00
Replit Agent 49f71389b3 Task #697: جدول حضور احترافي متوسّط + جدول في الصف الموسّع
- Extracted shared AttendeesTable component in artifacts/tx-os/src/pages/protocol.tsx
  (columns: name / position / side) with centered headers and cells, distinct
  header background (bg-slate-100/80), comfortable row heights (py-2.5),
  zebra striping (even:bg-slate-50/60), rounded border + shadow, side pill
  colored by side (sky=internal, violet=external), overflow-x-auto for small
  screens. Center alignment is RTL/LTR-safe.
- Booking details dialog now uses AttendeesTable (replaced inline table).
- Expanded row in bookings list now renders keyAttendees as the same table
  (replaced dash-separated <ul> lines), capped at max-w-2xl.
- No i18n changes needed (reuses protocol.bookings.details.attendeeCols.*).
- Verified: tsc --noEmit clean in artifacts/tx-os; architect review passed.
2026-07-08 13:15:04 +00:00
Replit Agent 3802f785a1 Task #696: Key attendees as a table in booking details dialog
- artifacts/tx-os/src/pages/protocol.tsx: replaced the dash-separated
  <ul> list of keyAttendees in the booking details dialog with a shadcn
  Table (already imported) with 3 columns: Name, Position/Title, Side.
  Headers use text-start for correct RTL alignment; empty name/position
  cells render an em dash; side rendered as the existing pill using
  protocol.bookings.attendeeSides.*; table wrapped in rounded border +
  overflow-x-auto so it stays responsive inside the dialog.
- i18n: added protocol.bookings.details.attendeeCols.{name,position,side}
  to ar.json (الاسم/الصفة/الجهة) and en.json (Name/Title/Side).
- Scope limited to the details dialog; the expanded-row attendee display
  elsewhere is unchanged (per task out-of-scope).
- Verified: both locale JSONs valid, tsc --noEmit clean, architect review
  PASS.
- No follow-ups proposed: pending Task #695 already covers e2e testing of
  the details dialog contents.
2026-07-08 13:06:18 +00:00
Replit Agent ea5055de85 Task #694: Booking details dialog from calendar day-grid
- BookingDayGrid (artifacts/tx-os/src/pages/protocol.tsx): booking blocks are
  now <button type="button"> with onClick -> new optional onSelect prop,
  hover/focus-visible styles, aria-label (title/room/time) and
  data-testid="calendar-booking-{id}".
- ProtocolPage: new detailsBooking state; day grid passes onSelect; new
  read-only details Dialog (before AppDock) with:
  header = booking title + status pill + public-request badge;
  summary card = date, time range (LTR), duration, room, reference,
  meeting type; requester section = name, phone (LTR), entity (external
  only), department, attendee count; meeting section = purpose + key
  attendees with side badges; notes; rejection-reason box when rejected.
  Empty/null fields hidden via DetailRow helper.
- New helpers: DetailRow component, formatDuration(start,end,t) using
  pluralized i18n keys.
- i18n: protocol.bookings.details.* added to ar.json (full Arabic plural
  forms for hours/minutes) and en.json.
- View-only per task scope (no edit/approve/cancel in dialog).
- tsc --noEmit clean; both locale JSONs valid; architect review PASS.
- Added explicit 'Request source' row (internal/public) in summary card per review; keys protocol.bookings.details.source/sources.* in ar/en.
2026-07-08 12:52:13 +00:00
Replit Agent 5fe978c0ca Task #694: Booking details dialog from calendar day-grid
- BookingDayGrid (artifacts/tx-os/src/pages/protocol.tsx): booking blocks are
  now <button type="button"> with onClick -> new optional onSelect prop,
  hover/focus-visible styles, aria-label (title/room/time) and
  data-testid="calendar-booking-{id}".
- ProtocolPage: new detailsBooking state; day grid passes onSelect; new
  read-only details Dialog (before AppDock) with:
  header = booking title + status pill + public-request badge;
  summary card = date, time range (LTR), duration, room, reference,
  meeting type; requester section = name, phone (LTR), entity (external
  only), department, attendee count; meeting section = purpose + key
  attendees with side badges; notes; rejection-reason box when rejected.
  Empty/null fields hidden via DetailRow helper.
- New helpers: DetailRow component, formatDuration(start,end,t) using
  pluralized i18n keys.
- i18n: protocol.bookings.details.* added to ar.json (full Arabic plural
  forms for hours/minutes) and en.json.
- View-only per task scope (no edit/approve/cancel in dialog).
- tsc --noEmit clean; both locale JSONs valid; architect review PASS.
2026-07-08 12:50:33 +00:00
Replit Agent 9184a0c223 Task #692: Auto-scroll bookings day grid to current time (red now-line)
- BookingDayGrid (artifacts/tx-os/src/pages/protocol.tsx) now renders inside
  its own scroll container (max-h-[60vh] overflow-y-auto overscroll-contain).
- New scrollRef + useEffect: when the selected day is today and the now-line
  is within grid hours (showNow), sets scrollTop = nowTop - clientHeight/3
  so the red line sits ~1/3 from the top; otherwise resets scroll to top.
  Effect deps: [dayKey, showNow, scrollSignal] — not re-run on minute ticks,
  so it never fights user scrolling.
- "اليوم" button bumps a todayScrollSignal counter passed as optional
  scrollSignal prop, re-scrolling even when the selected day is already today.
- Removed the previous outer max-h-[70vh] wrapper around BookingDayGrid to
  avoid nested scrollbars.
- No changes to booking block layout, statuses, or list view. RTL-safe
  (logical properties untouched).
- tsc --noEmit clean for tx-os; architect review addressed (60vh per spec).
2026-07-08 12:43:22 +00:00
Replit Agent 6f47e3ee71 Add functionality to select and delete multiple bookings
Implements bulk booking deletion with API endpoint, UI elements for selection, and updated localization files.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: b10c2dde-049d-45ea-9f02-7d538e07d5de
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/EHswFlf
Replit-Helium-Checkpoint-Created: true
2026-07-08 12:36:17 +00:00
Replit Agent 14a02e978f Improve table alignment and action button presentation
Updates table cell alignment to center content, replaces "Cancel" action with a Ban icon, and adjusts header alignment for a more professional and consistent UI.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 7586707e-fb86-49e4-96d4-0802eb9f8d87
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/ychh3tH
Replit-Helium-Checkpoint-Created: true
2026-07-08 12:27:54 +00:00
Replit Agent 2ac960b69a Update booking display with index numbers and structured details
Refactors the `ProtocolPage` component to include an index column for bookings and restructures the booking detail view into a fixed grid layout.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 9637de15-465f-4172-bea0-f0b45a37bd23
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/ychh3tH
Replit-Helium-Checkpoint-Created: true
2026-07-08 12:19:27 +00:00
Replit Agent 79a9e5236d Update bookings view to use a table layout
Replaces the card-based list view with a table component in `protocol.tsx`. Adds new translation keys for table headers and meeting types in `ar.json` and `en.json`. Formats booking references to display only numeric characters and dashes.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: e1793d98-fbd5-4c99-99a0-e01d493482cc
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/ychh3tH
Replit-Helium-Checkpoint-Created: true
2026-07-08 11:12:41 +00:00
Replit Agent c54a6972ec Preserve meeting metadata when duplicating and fix switch alignment in RTL
Update executive meeting duplication to maintain `isExternal` and `rowColor` properties, and adjust switch component's RTL translation for correct alignment.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: f7213345-78e8-4ed4-a72e-0e4b9bc6e00f
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/ychh3tH
Replit-Helium-Checkpoint-Created: true
2026-07-08 10:06:53 +00:00
Replit Agent 0808d8bdeb Collapsible booking cards in Protocol → Bookings list (Task #676)
Original task: make the protocol booking-requests LIST cards collapsed by
default, showing a compact summary and expanding full details only when opened;
add numbering, show the booking reference, and highlight today's bookings green.

Changes (artifacts/tx-os/src/pages/protocol.tsx):
- renderBookingCard(b, index) now renders a collapsible card. Collapsed shows
  only: sequence number (1-based over the current/filtered list), title,
  room · date-time range, booking reference, status/public pills, and a chevron.
- Summary header is a real <button type="button"> with onClick toggle and
  aria-expanded for keyboard accessibility.
- Expanded reveals the previously-shown detail fields (requester, department,
  meeting type, entity, phone, attendee count, purpose, key attendees) and the
  action buttons (approve/reject/cancel/edit/delete) — no functionality lost.
- Bookings starting today (sameYmd(startsAt, now)) get a green card
  (emerald border/bg/ring) plus an "اليوم"/"Today" badge.
- Added expandedBookings Set state + toggleBookingExpanded near booking state.
- List map updated to pass 1-based index.

i18n: added protocol.bookings.today ("اليوم" / "Today") to ar.json and en.json.

Scope: list view only; calendar view, public request form, and other tabs
unchanged. Client-only "today" derivation, no backend changes.

Verification: tsc --noEmit passes; architect code review PASSED (no critical
issues). Screenshot skipped — protocol page requires auth.
2026-07-07 14:08:51 +00:00
Replit Agent c93e747ff1 Fix Arabic RTL calendar in public booking date pickers (Task #674)
Problem: In the public room-booking form (protocol-request.tsx), the date
picker calendar showed English weekday headers and a scrambled day grid under
RTL. Root cause: the shared Calendar (react-day-picker v9) was rendered without
`locale` or `dir`, so weekdays stayed English and its flex week rows reversed
visually under CSS RTL.

Changes (artifacts/tx-os/src/components/ui/calendar.tsx):
- Pass date-fns Arabic locale (`ar`) + `dir="rtl"` when Arabic is active, else
  `enUS` + `dir="ltr"`; placed before `{...props}` so callers can still override.
- Added `formatCaption` and `formatDay` formatters (alongside existing
  `formatMonthDropdown`) using Intl `numberingSystem: "latn"` to keep Western
  digits, matching the app-wide AR_LATN convention (Arabic locale would
  otherwise emit Arabic-Indic numerals).
- `isArabic()` now resolves from `document.documentElement.lang`/`dir` first,
  falling back to `i18n.language`.

Deviation from plan: after code review, changed language detection from
i18n.language-only to DOM-first. Reason: the booking page forces Arabic by
setting documentElement.lang/dir without changing i18n.language, so a guest with
saved English would otherwise get an English/LTR calendar on that page.

Out of scope (per user): no time-picker or من/إلى changes.

Verified: `tsc --noEmit` passes for @workspace/tx-os. English calendar
unaffected (default enUS/LTR).
2026-07-07 14:00:17 +00:00
Replit Agent 3398af3340 Task #673: rename booking nav labels (Arabic)
Renamed two Arabic sidebar tab labels in the protocol (room-booking)
module in artifacts/tx-os/src/locales/ar.json:
- protocol.tabs.bookings: "حجوزات القاعات" → "حجز القاعات"
- protocol.tabs.external: "اللقاءات الخارجية" → "اجتماعات خارجية"

English translations left unchanged. The chart label
"اللقاءات الخارجية حسب الحالة" was left as-is per the task's out-of-scope
note. Text-only change, no logic touched.
2026-07-07 12:50:29 +00:00
Replit Agent 199f0c025e Task #670: revamp public room-booking form (protocol)
Public booking form now captures department, meeting type (internal/
external), meeting purpose, and a repeatable "key attendees" table
(name + position + side). External meetings require the entity name
(reuses requesterOrg). "عدد الحضور" relabeled to "العدد المتوقع للحضور".
Added a static red disclaimer note about possible cancellation.

Every booking (public + internal) now gets a unique, NOT NULL, searchable
reference (RB-{year}-{pad4(id)}) shown on the success screen. Staff can
search the admin bookings list by reference OR phone.

Changes:
- lib/db schema: department, meetingType, purpose, keyAttendees (jsonb),
  bookingReference (NOT NULL + unique) + requesterPhone index; new
  enums/types. Applied via drizzle push; existing rows backfilled.
- api-server: extended public booking schema + refine (org required when
  external); reserveBookingId (nextval on the serial sequence) so the
  reference is written in a single INSERT and can never be null; GET
  /protocol/bookings accepts a `q` param filtering by reference OR phone
  (server-side, indexed, case-insensitive).
- tx-os public form rewritten; admin card shows new fields; admin search
  box now debounced and wired to the API `q` param (server-side filtering)
  with an empty state.
- ar/en i18n keys under protocol.bookings.

Verification:
- Public booking POST returns a reference; external-without-org rejected
  (400); DB has zero null references after push.
- Pre-existing push.ts / executive-meetings.ts typecheck errors are
  unrelated to this task (files untouched).
2026-07-07 12:34:52 +00:00
Replit Agent 1b93c2cf02 Task #670: revamp public room-booking form (protocol)
Public booking form now captures department, meeting type (internal/
external), meeting purpose, and a repeatable "key attendees" table
(name + position + side). External meetings require the entity name
(reuses requesterOrg). "عدد الحضور" relabeled to "العدد المتوقع للحضور".
Added a static red disclaimer note about possible cancellation.

Each booking now gets an auto-generated searchable reference
(RB-{year}-{pad4}) shown on the success screen; staff can search the
admin bookings list by reference OR phone.

Changes:
- lib/db schema: department, meetingType, purpose, keyAttendees (jsonb),
  bookingReference (unique) + requesterPhone index; new enums/types.
  Applied via drizzle push; backfilled existing 4 rows.
- api-server: extended public booking schema + refine (org required when
  external), makeBookingReference, public/internal POST generate+return
  bookingReference.
- tx-os public form rewritten; admin card shows new fields + client-side
  search with empty state.
- ar/en i18n keys under protocol.bookings.

Notes:
- Pre-existing push.ts typecheck errors are unrelated to this task.
- Architect review: PASS, no blocking issues.
2026-07-07 12:26:19 +00:00
Replit Agent 2c244ae6e1 Task #668: Rename Protocol side-menu items (bilingual AR/EN)
Relabeled three Protocol (العلاقات العامة والمراسم) menu items to match real usage:
- photos: "تصوير" -> "الصور" / "Photos"
- gifts (item catalog where أصناف are added): "الإهداءات" -> "المستودع" / "Warehouse"
- issues (giving gifts out): "طلبات الصرف" -> "الإهداءات" / "Dedications"

Swapped menu icons to match new meanings: gifts->Warehouse, issues->Gift,
photos->ImageIcon. Removed now-unused imports (PackageCheck, Camera).

Grouped المستودع + الإهداءات under one expandable submenu (parent label
"الهدايا"/"Gifts", new i18n key protocol.tabs.giftsGroup, HandHeart icon,
ChevronDown toggle). Group auto-expands when a child tab is active; state
giftsGroupOpen. RTL-aware (ms-auto chevron, md:ps-6 child indent).

Aligned in-screen wording with new names in both locales:
dashboard.pendingIssues, issues.new, issues.empty reworded from
issuance/صرف to dedication/إهداء.

Deviation: submenu grouping and parent-group naming ("الهدايا") were an
interpretation of the user's "sub-menu" request (user_query was unavailable
during planning). Kept scope tight: remaining secondary "issuance/صرف"
strings in reports/actions/dashboard stats (e.g. giftsIssuedThisMonth,
actions.issued, reports.byGift) were left unchanged and proposed as a
follow-up.

Unchanged (deliberately): internal tab keys (gifts/issues/photos) and URL
slugs (gifts, gift-issues, photos) so deep links keep working. No feature,
API, or data changes.

Verified: pnpm --filter @workspace/tx-os run typecheck passes; architect
code review passed after issues icon corrected to Gift.
2026-07-07 11:58:20 +00:00
Replit Agent ee3cf1178e Task #666: Today & Book buttons in room-booking calendar view
Added two quick actions inside the Protocol bookings "Calendar" view's day
panel header (artifacts/tx-os/src/pages/protocol.tsx):
- "Today" (اليوم): outline button that resets both selectedDay and a new
  controlled calendarMonth state to new Date(), so the month picker, day
  time-grid, and red now-line all snap to today. The Calendar is now
  controlled via month/onMonthChange (defaultMonth is initial-only and did
  not re-navigate after mount).
- "Book" (حجز): primary button gated by the same c?.canRequest capability as
  the existing toolbar new-booking button. Opens the new-booking dialog
  pre-filled with the currently selected day.

Implementation:
- Extended bookingDialog state with an optional `day?: Date`.
- BookingDialog now accepts an `initialDay?: Date` prop; for new (non-edit)
  bookings it pre-fills startsAt=09:00 and endsAt=10:00 on that day via a
  local dayAt(hour) helper (reuses toLocalInput). Edit flow unchanged.
- Added i18n keys protocol.bookings.today / protocol.bookings.book to both
  ar.json and en.json. Buttons use logical spacing (me-1) so they mirror in
  Arabic RTL.

No backend/API changes. List/Calendar toggle unaffected. tx-os typecheck
passes; HMR clean, no console errors. Screenshot tool unavailable (no
artifact registered in this repo), verified via typecheck + logs.
2026-07-07 10:36:01 +00:00
Replit Agent 644eacc0c4 Task #666: Today & Book buttons in room-booking calendar view
Added two quick actions inside the Protocol bookings "Calendar" view's day
panel header (artifacts/tx-os/src/pages/protocol.tsx):
- "Today" (اليوم): outline button that resets selectedDay to new Date(), so
  the month picker, day time-grid, and red now-line all snap to today.
- "Book" (حجز): primary button gated by the same c?.canRequest capability as
  the existing toolbar new-booking button. Opens the new-booking dialog
  pre-filled with the currently selected day.

Implementation:
- Extended bookingDialog state with an optional `day?: Date`.
- BookingDialog now accepts an `initialDay?: Date` prop; for new (non-edit)
  bookings it pre-fills startsAt=09:00 and endsAt=10:00 on that day via a
  local dayAt(hour) helper (reuses toLocalInput). Edit flow unchanged.
- Added i18n keys protocol.bookings.today / protocol.bookings.book to both
  ar.json and en.json. Buttons use logical spacing (me-1) so they mirror in
  Arabic RTL.

No backend/API changes. List/Calendar toggle unaffected. tx-os typecheck
passes; HMR clean, no console errors. Screenshot tool unavailable (no
artifact registered in this repo), verified via typecheck + logs.
2026-07-07 10:34:16 +00:00
Replit Agent f46199ac5f Task #662: Google-Calendar-style room booking calendar view
Replaced the Protocol bookings "Calendar" view (previously an agenda list
grouped by day) with a two-panel Google-Calendar layout in
artifacts/tx-os/src/pages/protocol.tsx:
- LEFT: a mini month day-picker (reuses components/ui/calendar.tsx,
  react-day-picker single mode) driving a selectedDay state. Days that
  have bookings get a small sky dot via a `booked` modifier.
- RIGHT: a single-day vertical time-grid (BookingDayGrid) over working
  hours 07:00–22:00 (56px/hour). Bookings render as absolutely-positioned
  blocks: top = start offset, height = duration (min 22px), colored by
  status (BOOKING_BLOCK_STYLE) with an inline-start accent border and a
  title/room/time label.
- Overlapping bookings are laid out side-by-side via interval-partition
  column assignment (layoutDayBookings), chained overlaps share a column
  count.
- A live RED current-time line + red dot (useNow, 60s) shows only when the
  selected day is today and within grid hours.

Details:
- View-only (no drag/create); single-day grid only. No backend changes.
- RTL/i18n: logical props (insetInlineStart, border-s) so it mirrors in
  Arabic; added protocol.bookings.emptyDay to ar.json + en.json.
- Blocks with no visible intersection with the grid range are skipped
  (fixes a phantom-block bug for bookings fully before 7am / after 10pm,
  found in code review).
- Removed the now-unused groupBookingsByDay helper.
- List toggle preserved. tx-os typecheck passes.
2026-07-07 10:27:15 +00:00
Replit Agent e2564e2a66 Add download for protocol visit photos (Task #660)
Users could upload photos into Protocol "تصوير" albums but had no way to
download them — clicking a photo only opened it inline. This adds per-photo
downloads and a whole-album ZIP download.

Backend:
- objectStorage.ts: new exported extensionForContentType() mapping content
  types to file extensions (stored paths are extension-less UUIDs).
- storage.ts: opt-in ?download=1 mode on GET /storage/objects/*path sets
  Content-Disposition: attachment (with optional ?name base + derived ext);
  default inline behavior and per-entity authz unchanged.
- protocol.ts: new GET /protocol/photo-albums/:id/download streams all album
  photos as a ZIP (guarded by requireProtocolAccess), named from the album;
  unreadable objects are skipped rather than failing the archive. Empty album
  returns 404.
- Added archiver@8 dependency (+@types/archiver dev).

Frontend (protocol.tsx):
- Per-photo download button (attachment, name = album-index) in album detail.
- "تحميل الكل" / "Download all" ZIP button in album header; rendered disabled
  (not hidden) when the album has no photos.
- Added photoDownloadSrc/albumDownloadSrc helpers, Download icon.
- AR/EN locale strings protocol.photos.download / downloadAll.

Notes / deviations:
- archiver v8 is ESM-only with no callable default export; used
  `new ZipArchive(...)` instead of `archiver("zip")`. Recorded in memory.
- Could not run authenticated e2e (seed admin password is a secret). Verified:
  api-server typecheck + build pass, tx-os typecheck passes, server healthy,
  new routes wired (401 auth-required not 404). Pre-existing typecheck errors
  in executive-meetings.ts / push.ts are unrelated and untouched.
2026-07-07 09:37:15 +00:00