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.
- The caller passes
subtotalas the sum of all positive-amount lines (room + tax). ButFolioTable.tsx(@hmp/ui-kit) computes the per-line running balance asrunningBalance = 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 shows2 × charges − paymentsinstead ofcharges − payments. taxTotal={0}is hardcoded even though a tax line is pushed intofolioLines(room-charge total vs.reservation.totalAmountdiff). 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 atC:\CLProjects\HMP\hmp-web-dashboard\src\app\(dashboard)\reservations\[id]\keynova.tsxlines 1089–1100 (reservation detail, same component). - Shared component bug:
C:\CLProjects\HMP\hmp-shared-packages\packages\ui-kit\src\components\FolioTable\FolioTable.tsxlines 158–162 (thelet runningBalance = subtotalseed is wrong) and the props contract it implies. - Folio-line builder:
keynova.tsxlines 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.
- Line 303 —
- Related:
command-center-view.tsx:51andtimeline-view.tsx:49toast"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.tsxline 341 —<Link href={\/reservations/${…}?action=room-move`}>. Same pattern forroom-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— nouseSearchParams()reader foraction. - 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.tsxline 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.tsxlines 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 }viaroomApi.updateHousekeepingStatusatlib/api/rooms.ts:164-169. - Backend reads
body.status(NOTbody.housekeepingStatus) atC:\CLProjects\HMP\hmp-housekeeping-service\src\rooms\rooms.controller.tsline 57 (@Body() body: { status: string }) and passesbody.statusintoroomsService.updateRoomStatuswhich then forwardsundefinedas the new status. - The service's transition check then throws
BadRequestException("Invalid housekeeping status transition from X to undefined")atrooms.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.tslines 163–169. - Tile handler:
C:\CLProjects\HMP\hmp-web-dashboard\src\components\modules\housekeeping\room-status-board.tsxlines 144–146 and 183–190. - Backend mismatch:
C:\CLProjects\HMP\hmp-housekeeping-service\src\rooms\rooms.controller.tslines 48–60 (especially line 57) androoms.service.tslines 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.
| File | Reason |
|---|---|
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(hospital)\hospital\page.tsx | No HeaderZone; raw shadcn primitives; no KeyNova palette tokens. |
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(hospital)\hospital\authorizations\page.tsx | Same as above — hospital sub-app predates KeyNova. |
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(hospital)\hospital\new-authorization\page.tsx | Same — no navy header zone, no ivory content zone. |
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(hospital)\hospital\login\page.tsx | Legacy login form — not using KeyNova login style. |
C:\CLProjects\HMP\hmp-web-dashboard\src\app\guest\kiosk\page.tsx | Full-screen kiosk — no HeaderZone, no Satoshi class, no navy/gold tokens. |
C:\CLProjects\HMP\hmp-web-dashboard\src\app\guest\guest-portal\[token]\page.tsx | Guest-facing portal — not verified against KeyNova design system. |
C:\CLProjects\HMP\hmp-web-dashboard\src\app\guest\self-checkin\[token]\page.tsx | Same as above. |
C:\CLProjects\HMP\hmp-web-dashboard\src\app\guest\self-checkout\[token]\page.tsx | Same as above. |
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(public)\welcome\page.tsx | Marketing landing — no navy header zone; ad-hoc Tailwind without KeyNova tokens. |
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(public)\pricing\page.tsx | Same — pricing page. |
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(public)\token-display\page.tsx | Kiosk-like screen, no KeyNova. |
C:\CLProjects\HMP\hmp-web-dashboard\src\app\(public)\food-order\page.tsx | Same. |
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
- 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–162and adding anopeningBalanceprop is the highest-leverage fix in this sweep. - "Dead
<Link>entry points" is a recurring pattern — Issue 3 and Issue 4 are both of this shape: a dropdown item inroom-grid.tsxlinks 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. - Backend-body DTO drift between property-service (rich DTO) and housekeeping-service (inline type) — Issue 5 is a direct consequence. The
@hmp/api-contractspackage should own a singleUpdateRoomStatusDtothat both services import; the inline{ status: string }on the controller is a structural smell. - "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. - 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.