fix(GRO-2180): portal Appointments handles ISO startTime shape #49
Reference in New Issue
Block a user
Delete Branch "flea/gro-2180-appointments-starttime-shape"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
GRO-2180 — DEFECT: Customer portal
/appointmentscrashes onstartTimeshapeRoot cause
/api/portal/appointmentsreturns ISOstartTime/endTimeplus nestedpet/service/staffobjects, but the portal clientAppointmenttype expected flatdate/time/petName/serviceIdfields.isUpcoming()readappt.date/appt.time(bothundefined) →parseTimeTo24Hour(undefined)threwTypeError→ caught by theuseEffecttry/catch→ error state rendered → the success-path-only Book New button became unreachable.Fix
normalizeAppointment()at the fetch boundary maps the API shape → the flatAppointmentshape the UI renders: nestedpet.id/name,service.id,staff.id/name→petId/petName/serviceId/serviceName/groomerId/groomerName; derives displaydate/timefromstartTimeanddurationfrom the start/end delta. Tolerant of the legacy flat shape so existing fixtures/callers are unchanged.isUpcoming()now prefers the absolutestartTime, falling back todate/time.parseTimeTo24Hour()hardened against blank/undefinedinput (noNaN).Appointment.startTime/endTime(keptdate/timerequired — always populated bynormalizeAppointment, so no typecheck fallout elsewhere).This fixes every broken consumer (card display, upcoming/past split, and the Reschedule flow's
serviceId— previouslyundefined→/api/book/availability?serviceId=&…400), not justisUpcoming.Reconciliation note
This branch was force-updated from the earlier call-site-helper approach to the fetch-boundary normalization above. Reason: the earlier revision did not map the API's nested
pet/service/staffobjects (it assumed flatpetId/serviceId), so Reschedule would still send an emptyserviceId, and makingdate?/time?optional was failing CI typecheck. The new revision normalizes the nested shape and is fully green.Verification
tsc --noEmitclean, eslint 0 errors.normalizeAppointmentsuite (nested mapping, derived duration, null-tolerance, upcoming/past viastartTime) +parseTimeTo24Hourundefined/null/empty safety.UAT Playbook
Updated
UAT_PLAYBOOK.md§5.12.2 and added §5.12d — Appointment API Shape Normalization (GRO-2180) (TC-WEB-5.12.18..21).Unblocks GRO-1808 (booking funnel analytics UAT — portal flow). Closes GRO-2180.
9a30b4bd44tofa1fe00c51fa1fe00c51to3397767a01Progress — fix implemented, CI running
Root cause confirmed and fixed in groombook/web PR #49 (
dev←flea/gro-2180-appointments-starttime-shape).Fix:
/api/portal/appointmentsreturns ISOstartTime/endTime+ nestedpet/service/staffobjects, but the clientAppointmenttype expected flatdate/time/petName/serviceId. AddednormalizeAppointment()at the fetch boundary that maps the nested API shape → flat UI shape (derivesdate/timefromstartTime,durationfrom start/end delta), hardenedisUpcoming()to prefer absolutestartTime, and guardedparseTimeTo24Hour(undefined).This fixes every broken consumer — card display, upcoming/past split, and the Reschedule
serviceId(previouslyundefined→/api/book/availability?serviceId=&…400) — not just theisUpcomingcrash.Consolidation: a prior (timed-out) run had opened PR #49 with a call-site-helper approach that (a) was failing CI and (b) never mapped the nested
pet/service/staffobjects, so Reschedule would still send an emptyserviceId. I force-updated PR #49's branch to the complete fetch-boundary normalization and deleted the duplicate branch — single canonical PR preserved.Verification (local): 148 tests pass,
tsc --noEmitclean, eslint 0 errors. NewnormalizeAppointment+parseTimeTo24Hoursafety tests added.UAT Playbook: updated
UAT_PLAYBOOK.md§5.12.2 and added §5.12d (TC-WEB-5.12.18..21).Next
3397767. On green I self-merge todev(Phase 1 — CI only), then drivedev → uatto QA.