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:
@@ -131,11 +131,18 @@ export const protocolExternalMeetingsTable = pgTable(
|
||||
location: varchar("location", { length: 300 }),
|
||||
startsAt: timestamp("starts_at", { withTimezone: true }).notNull(),
|
||||
endsAt: timestamp("ends_at", { withTimezone: true }),
|
||||
status: varchar("status", { length: 20 }).notNull().default("scheduled"),
|
||||
// Lifecycle: pending -> approved | rejected, then optionally
|
||||
// completed | cancelled. Mirrors the booking / gift-issue approval flow.
|
||||
status: varchar("status", { length: 20 }).notNull().default("pending"),
|
||||
notes: text("notes"),
|
||||
createdBy: integer("created_by").references(() => usersTable.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
approvedBy: integer("approved_by").references(() => usersTable.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
approvedAt: timestamp("approved_at", { withTimezone: true }),
|
||||
rejectionReason: text("rejection_reason"),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
@@ -146,6 +153,7 @@ export const protocolExternalMeetingsTable = pgTable(
|
||||
},
|
||||
(t) => ({
|
||||
startIdx: index("protocol_ext_meetings_start_idx").on(t.startsAt),
|
||||
statusIdx: index("protocol_ext_meetings_status_idx").on(t.status),
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user