Task #649: complete Public Relations & Protocol module (fix 8 review gaps)

Addresses the 8 spec gaps that blocked prior completion, all additive and
scoped to the protocol_* module / /protocol routes. executive-meetings untouched.

Backend (api-server/routes/protocol.ts, lib/db schema, seed):
- External meetings create as "pending" (status dropped from create schema);
  added approve + reject endpoints with state-conditional WHERE status='pending'
  and 409 on contention; patch status enum restricted to completed/cancelled.
- Added approvedBy/approvedAt/rejectionReason columns + status index; default
  status now "pending".
- Booking reject and gift-issue reject made state-conditional (409 on 0 rows).
- Dashboard: roomsAvailableNow (NOT EXISTS), giftsIssuedThisMonth/Qty;
  upcomingExternal filtered to future pending|approved.
- Reports: added externalMeetings summary counts by status.
- Seed: room names corrected (AR + EN).

Frontend (tx-os/pages/protocol.tsx, App.tsx, locales):
- URL-synced tabs under /protocol/:tab (slug<->tab maps).
- Dedicated Rooms tab (moved out of Gifts); RoomDialog isActive toggle.
- External approve/reject buttons + rejectionReason display; ExternalDialog
  create omits status, edit limited to completed/cancelled.
- Dashboard cards clickable to navigate; reports external section.
- Bookings list/calendar (grouped-by-day) view toggle.
- New i18n keys in ar.json + en.json.

Verification: tx-os + protocol.ts typecheck clean; drizzle push + re-seed done;
both workflows restarted; /api/protocol/me returns 401 unauth; architect
re-review approved. Pre-existing typecheck errors in executive-meetings.ts and
push.ts are unrelated and were not touched.

Note: had to rebuild lib/db declarations (tsc --build --force) so api-server
project references picked up the new schema columns.
This commit is contained in:
Replit Agent
2026-07-06 09:46:12 +00:00
parent 2322b77b8d
commit 5e460920c7
7 changed files with 685 additions and 220 deletions
+14 -5
View File
@@ -1672,7 +1672,8 @@
"gifts": "الهدايا والدروع",
"issues": "طلبات الصرف",
"reports": "التقارير",
"audit": "سجل النشاط"
"audit": "سجل النشاط",
"rooms": "القاعات"
},
"status": {
"pending": "قيد الانتظار",
@@ -1702,7 +1703,9 @@
"pendingBookings": "حجوزات بانتظار الاعتماد",
"upcomingExternal": "لقاءات خارجية قادمة",
"giftStock": "إجمالي مخزون الهدايا",
"pendingIssues": "طلبات صرف معلّقة"
"pendingIssues": "طلبات صرف معلّقة",
"roomsAvailableNow": "القاعات المتاحة الآن",
"giftsIssuedThisMonth": "الدروع المصروفة هذا الشهر"
},
"bookings": {
"new": "حجز جديد",
@@ -1712,7 +1715,9 @@
"roomLabel": "القاعة",
"titleLabel": "عنوان الحجز",
"startsAt": "من",
"endsAt": "إلى"
"endsAt": "إلى",
"viewList": "قائمة",
"viewCalendar": "تقويم"
},
"external": {
"new": "لقاء جديد",
@@ -1740,7 +1745,10 @@
"nameAr": "الاسم (عربي)",
"nameEn": "الاسم (إنجليزي)",
"capacity": "السعة",
"location": "الموقع"
"location": "الموقع",
"active": "مفعّلة",
"isActive": "مفعّلة",
"empty": "لا توجد قاعات."
},
"issues": {
"new": "طلب صرف",
@@ -1759,7 +1767,8 @@
"requests": "الطلبات",
"issued": "المصروفة",
"issuedQty": "الكمية المصروفة",
"empty": "لا توجد بيانات."
"empty": "لا توجد بيانات.",
"byExternal": "اللقاءات الخارجية حسب الحالة"
},
"audit": {
"empty": "لا يوجد نشاط."