Skip to main content

Phase 1 UI Diagnosis — 5 reported issues

Diagnosis (NOT fixes) for 5 issues reported during Phase 1 go-live manual testing, plus a short design-system drift audit. All file paths are absolute on the dev workstation. Line numbers are current as of 2026-04-23.


Issue 1 — Check-in Folio step: Balance column shows wrong value​

Root cause. Two compounding bugs in how the check-in wizard wires the shared FolioTable component.

  1. The caller passes subtotal as the sum of all positive-amount lines (room + tax). But FolioTable.tsx (@hmp/ui-kit) computes the per-line running balance as runningBalance = subtotal; for (i=0..idx) runningBalance += lines[i].amount, i.e. it double-counts the positive lines: the starting value already contains them, then they are added again. The Balance column therefore shows 2 × charges − payments instead of charges − payments.
  2. taxTotal={0} is hardcoded even though a tax line is pushed into folioLines (room-charge total vs. reservation.totalAmount diff). This gives a mismatched breakdown in the footer and hides the real tax figure from staff.

currentBalance itself is read directly from reservation.balanceDue (backend-owned) and is correct; the wrongness is in the per-line Balance column and the tax footer.

Files / lines.

  • Caller: C:\CLProjects\HMP\hmp-web-dashboard\src\app\(dashboard)\front-desk\check-in\[id]\keynova.tsx — the prop wiring at lines 1386–1397 (check-in), and the identical wiring at C:\CLProjects\HMP\hmp-web-dashboard\src\app\(dashboard)\reservations\[id]\keynova.tsx lines 1089–1100 (reservation detail, same component).
  • Shared component bug: C:\CLProjects\HMP\hmp-shared-packages\packages\ui-kit\src\components\FolioTable\FolioTable.tsx lines 158–162 (the let runningBalance = subtotal seed is wrong) and the props contract it implies.
  • Folio-line builder: keynova.tsx lines 863–901 (pushes room, tax diff, payment credit — correct per se).

Suggested fix. In FolioTable.tsx, change the running-balance seed to let runningBalance = 0 (or openingBalance prop) and let the per-line loop accumulate from zero. Separately, in both callers, compute subtotal = room lines only, taxTotal = tax lines, paymentsApplied = -payments so the footer breakdown matches the column.


Issue 2 — Tape chart right panel: Folio / Notes / Activity tabs are placeholders​

Root cause. The DetailPanel tab bodies for folio, notes and activity are still hardcoded "coming in Phase 2" strings. Overview is real; the other three tabs were stubbed in the original G-C-05 ticket and never wired.

Files / lines.

  • C:\CLProjects\HMP\hmp-web-dashboard\src\components\modules\front-desk\tape-chart\detail-panel.tsx
    • Line 303 — <div className="text-[11px]">Folio details coming in Phase 2</div>
    • Line 309 — <div className="text-[11px]">Guest notes coming in Phase 2</div>
    • Line 315 — <div className="text-[11px]">Audit trail coming in Phase 2</div>
    • Tab IDs defined at lines 18, 95–97. Folio render block 300–305, notes 306–311, activity 312–317.
  • Related: command-center-view.tsx:51 and timeline-view.tsx:49 toast "coming in Phase 2" for bulk-action placeholders.

Suggested fix. Replace the three placeholder divs with: (a) a FolioTable fed by frontDeskApi.getFolios(reservationId) (already exists), (b) a ReservationNotes list backed by the reservation /messages or /notes endpoint, (c) an AuditTrailList backed by /audit-logs?entityType=Reservation&entityId=…. All three endpoints ship; only the UI wiring is missing.


Issue 3 — Front Desk "Move Guest / Room Move" action does nothing​

Root cause. The "Room Move" dropdown item is a <Link> pointing to /reservations/:id?action=room-move, but the reservation detail page never reads the action query param. No dialog opens, no mutation fires — the user lands on the detail page and nothing visibly happens. The backend route (POST /api/room-move via frontDeskApi.moveRoom in lib/api/front-desk.ts:213) is implemented and reachable, it's just never called from this entry point.

A second, working path does exist (tape-chart drag → use-tape-chart-actions.ts → actions.moveRoom → ConfirmMoveDialog), so the feature itself works — only the room-grid context menu entry point is dead.

Files / lines.

  • Dead Link: C:\CLProjects\HMP\hmp-web-dashboard\src\components\modules\front-desk\room-grid.tsx line 341 — <Link href={\/reservations/${…}?action=room-move`}>. Same pattern for room-swapandroom-upgrade` (lines 347, 353) — all three are dead.
  • Target page that ignores action: C:\CLProjects\HMP\hmp-web-dashboard\src\app\(dashboard)\reservations\[id]\keynova.tsx — no useSearchParams() reader for action.
  • Working reference: C:\CLProjects\HMP\hmp-web-dashboard\src\components\modules\front-desk\tape-chart\actions\confirm-move.tsx (dialog exists and works via tape-chart).

Suggested fix. Either (a) replace the <Link> with a <DropdownMenuItem onSelect> that opens the existing ConfirmMoveDialog in-place from room-grid.tsx, or (b) add a useSearchParams() hook to reservations/[id]/keynova.tsx that opens the dialog when action=room-move|room-swap|room-upgrade. Option (a) is cleaner.


Issue 4 — "Failed to load folio" when clicking Add Charge​

Root cause. ID type confusion. The "Add Charge to Folio" dropdown item links to /front-desk/folio/:id passing a reservation ID, but the target page calls frontDeskApi.getFolio(id) which hits GET /folios/:folioId expecting a folio UUID. Backend returns 404; the page's error branch renders "Failed to load folio".

The correct call for reservation-scoped access is frontDeskApi.getFolios(reservationId) which hits /folios/by-reservation/:reservationId (see lib/api/front-desk.ts:229).

Files / lines.

  • Dead link: C:\CLProjects\HMP\hmp-web-dashboard\src\components\modules\front-desk\room-grid.tsx line 366 — <Link href={\/front-desk/folio/${room.currentReservation?.reservationId || room.id}`}>`.
  • Page that assumes folio-ID: C:\CLProjects\HMP\hmp-web-dashboard\src\app\(dashboard)\front-desk\folio\[id]\page.tsx lines 199–212 (queryFn: () => frontDeskApi.getFolio(id)) and line 483 (error copy).
  • Also affected by the same confusion: auto-lock calls at lines 222–234.

Suggested fix. Make the folio page accept a reservation ID: in the page, first call getFolios(reservationId), pick the primary folio (folio.isMaster or the first), then load its details. Or, change the link to go to the specific folio ID once it's resolved client-side. Also fix the auto-lock useEffect to use the resolved folio ID.


Issue 5 — Housekeeping Room tile click → "Validations Failed" toasts​

Root cause. Payload key mismatch between frontend and the housekeeping-service PATCH /rooms/:id/status endpoint.

  • Frontend sends { housekeepingStatus: status } via roomApi.updateHousekeepingStatus at lib/api/rooms.ts:164-169.
  • Backend reads body.status (NOT body.housekeepingStatus) at C:\CLProjects\HMP\hmp-housekeeping-service\src\rooms\rooms.controller.ts line 57 (@Body() body: { status: string }) and passes body.status into roomsService.updateRoomStatus which then forwards undefined as the new status.
  • The service's transition check then throws BadRequestException("Invalid housekeeping status transition from X to undefined") at rooms.service.ts:187-190, and the safeguard at 194–207 also kicks in. Both surfaces can emit toast-sized messages, and with React Query retry or the safeguard record path, the user sees multiple "Validations Failed" toasts in quick succession.

The DTO on the property-service (UpdateRoomStatusDto, property-service/src/rooms/dto/room.dto.ts:144-164) does accept housekeepingStatus, but the gateway routes /rooms/status/:id to housekeeping-service first (services.config.ts:110–115), which uses its own minimal body type.

Files / lines.

  • Client payload: C:\CLProjects\HMP\hmp-web-dashboard\src\lib\api\rooms.ts lines 163–169.
  • Tile handler: C:\CLProjects\HMP\hmp-web-dashboard\src\components\modules\housekeeping\room-status-board.tsx lines 144–146 and 183–190.
  • Backend mismatch: C:\CLProjects\HMP\hmp-housekeeping-service\src\rooms\rooms.controller.ts lines 48–60 (especially line 57) and rooms.service.ts lines 176–207.

Suggested fix. Align the controller to accept housekeepingStatus (matching the platform DTO) AND fall back to status for backward compat — e.g. @Body() body: UpdateRoomStatusDto imported from a shared DTO, then const newStatus = body.housekeepingStatus ?? body.status. Do NOT change the frontend payload to status — status is the composite triple-state field on property-service, conflating it here would regress Issue tracked in CLAUDE.md room triple-status model.


Design-system drift audit​

The dashboard is largely on KeyNova (Satoshi + Ink & Brass + shadcn + HeaderZone). No @mui/* imports found. Drift is concentrated in (a) the (hospital) sub-app, (b) /guest kiosk pages, and (c) public marketing pages.

FileReason
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(hospital)\hospital\page.tsxNo HeaderZone; raw shadcn primitives; no KeyNova palette tokens.
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(hospital)\hospital\authorizations\page.tsxSame as above — hospital sub-app predates KeyNova.
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(hospital)\hospital\new-authorization\page.tsxSame — no navy header zone, no ivory content zone.
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(hospital)\hospital\login\page.tsxLegacy login form — not using KeyNova login style.
C:\CLProjects\HMP\hmp-web-dashboard\src\app\guest\kiosk\page.tsxFull-screen kiosk — no HeaderZone, no Satoshi class, no navy/gold tokens.
C:\CLProjects\HMP\hmp-web-dashboard\src\app\guest\guest-portal\[token]\page.tsxGuest-facing portal — not verified against KeyNova design system.
C:\CLProjects\HMP\hmp-web-dashboard\src\app\guest\self-checkin\[token]\page.tsxSame as above.
C:\CLProjects\HMP\hmp-web-dashboard\src\app\guest\self-checkout\[token]\page.tsxSame as above.
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(public)\welcome\page.tsxMarketing landing — no navy header zone; ad-hoc Tailwind without KeyNova tokens.
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(public)\pricing\page.tsxSame — pricing page.
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(public)\token-display\page.tsxKiosk-like screen, no KeyNova.
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(public)\food-order\page.tsxSame.

Inline style usage (style={{ background: '#695BF8', color: '#fff' }}) in check-in/[id]/keynova.tsx (lines 1410, 1447, 1479 etc.) uses the Iris purple from the deferred KeyNova revamp rather than the shipping Ink & Brass aged-gold token — worth flagging as minor drift even inside a keynova.tsx file.


Cross-cutting observations​

  1. FolioTable balance math is wrong at the shared-component level — both the check-in wizard and the reservation detail pass identical props, both render the same visual bug. Fixing FolioTable.tsx:158–162 and adding an openingBalance prop is the highest-leverage fix in this sweep.
  2. "Dead <Link> entry points" is a recurring pattern — Issue 3 and Issue 4 are both of this shape: a dropdown item in room-grid.tsx links to a page that either doesn't observe the query param or doesn't accept the ID type passed. A short-term lint or unit-test would catch this class of bug before it hits staff.
  3. Backend-body DTO drift between property-service (rich DTO) and housekeeping-service (inline type) — Issue 5 is a direct consequence. The @hmp/api-contracts package should own a single UpdateRoomStatusDto that both services import; the inline { status: string } on the controller is a structural smell.
  4. "Coming in Phase 2" is present in tape-chart AND in the bulk-action toasts — five call-sites across three files. A one-line greppable convention (e.g. a <ComingSoon /> component) would make it trivial to enumerate remaining stubs and gate-deploy.
  5. Design-system adoption is nearly complete inside (dashboard) — drift is isolated to the hospital sub-app, guest/kiosk pages, and public marketing pages. These are lower-priority for Phase 1 go-live (staff-facing is green) but should be scheduled into the KeyNova design-system Phase 2 work already tracked in MEMORY.md.