feat: customer appointment confirm/cancel in portal (GRO-47) #122
Reference in New Issue
Block a user
Delete Branch "%!s()"
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?
Summary
Customers receive email reminders with confirm/cancel links that work, but cannot confirm or cancel through the customer portal UI. This is P1 — reduces no-shows, gives groomers advance notice to fill slots.
Related Paperclip task: GRO-47
What Already Works
GET /api/book/confirm/:token— tokenized email link confirm (updatesconfirmationStatus = "confirmed")GET /api/book/cancel/:token— tokenized email link cancel (updatesconfirmationStatus = "cancelled", nullifies token, single-use)confirmationStatuswith ✓/✗ indicatorsWhat's Missing
1. Portal API endpoints (
apps/api/src/routes/portal.ts)Add two new routes following the exact same session auth pattern as
PATCH /appointments/:id/notes:POST /api/portal/appointments/:id/confirmclientIdstartTime <= now(past or in progress)confirmationStatus !== "pending"(already confirmed or cancelled)status === "cancelled"orstatus === "completed"confirmationStatus = "confirmed",confirmedAt = new Date(),updatedAt = new Date()POST /api/portal/appointments/:id/cancelclientIdstartTime <= now(past)status === "cancelled"orstatus === "completed"status = "cancelled",confirmationStatus = "cancelled",cancelledAt = new Date(),updatedAt = new Date()2. Portal UI (
apps/web/src/portal/sections/Appointments.tsx)In
AppointmentCardexpanded view:isUpcoming(appt) && !readOnly && appt.confirmationStatus === "pending". CallsPOST /api/portal/appointments/:id/confirmwith the impersonation session header. On success, update local state. Show loading/error state.POST /api/portal/appointments/:id/cancelwith session header. Show a confirmation dialog first. On success, update local state (remove from upcoming or mark cancelled). Show loading/error state.Follow the same fetch/state pattern as
CustomerNotesSection.Type Notes
The
Appointmenttype (frommockData.js) needsconfirmationStatus: "pending" | "confirmed" | "cancelled"added if not already present.Out of Scope for This PR
cc @cpfarhood
Closing — the confirm/cancel feature was merged to main via commit
9eb0c3d(included in the fix/gro66 squash). Both portal endpoints (POST /portal/appointments/:id/confirm and /cancel) are live.