feat: customer-facing appointment notes (GRO-106) (#109)
* feat: add customer-facing appointment notes (GRO-106) - Migration 0014: add customer_notes column to appointments - Schema update: add customerNotes field to appointments table - Factory update: include customerNotes in buildAppointment - Portal route: PATCH /api/portal/appointments/:id/notes - Ownership validation via impersonation session - Future-only validation (no edits after start) - 500 character limit - Register portal router in index.ts Co-Authored-By: Paperclip <noreply@paperclip.ing> * Fix confirmationToken leak and add unit tests for portal notes endpoint - Return only id, customerNotes, updatedAt instead of full appointment row - Add comprehensive unit tests covering auth, ownership, time-gating, and validation - Fix: confirmationToken no longer returned to portal session Co-Authored-By: Paperclip <noreply@paperclip.ing> * feat: add customer notes UI to portal and staff views (GRO-178) - Add customerNotes field to Appointment type - Add read-only customer notes display in staff appointment detail modal - Add customer notes textarea with save, char counter (500 max), and disabled state - Wire up PATCH /api/portal/appointments/:id/notes in portal UI - Update mockData with customerNotes field Co-Authored-By: Paperclip <noreply@paperclip.ing> * fix: address QA review feedback - null check and portal route auth - Add null check after db.update().returning() in portal notes endpoint - Move portal router registration before auth middleware so clients can access it - Remove unused ENDED_SESSION variable from test file Co-Authored-By: Paperclip <noreply@paperclip.ing> * fix(portal): address QA review - isUpcoming time parsing and session header - Fixed parseTimeTo24Hour to handle 12-hour AM/PM format correctly - Added X-Impersonation-Session-Id header to CustomerNotesSection fetch - Added comprehensive tests for CustomerNotesSection and time parsing - Fixed TypeScript strict null checks for parseTimeTo24Hour Fixes QA review issues: - isUpcoming() now correctly parses 12-hour time format - CustomerNotesSection sends session ID header for auth - Added unit tests for new UI component Co-Authored-By: Paperclip <noreply@paperclip.ing> * fix: thread sessionId as prop instead of sessionStorage CustomerNotesSection was reading sessionStorage for the impersonation session ID, but CustomerPortal stores it in React state. Pass sessionId as a prop through AppointmentsSection and AppointmentCard instead. Also update tests to pass sessionId prop and add test for null sessionId case. Co-Authored-By: Paperclip <noreply@paperclip.ing> --------- Co-authored-by: Scrubs McBarkley <scrubs@groombook.app> Co-authored-by: Paperclip <noreply@paperclip.ing> Co-authored-by: groombook-cto[bot] <269737991+groombook-cto[bot]@users.noreply.github.com>
This commit was merged in pull request #109.
This commit is contained in:
committed by
GitHub
parent
553fa435ed
commit
d0b4baf5aa
@@ -6,6 +6,7 @@ import { clientsRouter } from "./routes/clients.js";
|
||||
import { petsRouter } from "./routes/pets.js";
|
||||
import { servicesRouter } from "./routes/services.js";
|
||||
import { appointmentsRouter } from "./routes/appointments.js";
|
||||
import { portalRouter } from "./routes/portal.js";
|
||||
import { staffRouter } from "./routes/staff.js";
|
||||
import { invoicesRouter } from "./routes/invoices.js";
|
||||
import { bookRouter } from "./routes/book.js";
|
||||
@@ -56,6 +57,9 @@ app.get("/api/branding", async (c) => {
|
||||
});
|
||||
});
|
||||
|
||||
// Portal routes — no staff auth required, uses impersonation session for client auth
|
||||
app.route("/api/portal", portalRouter);
|
||||
|
||||
// Protected API routes
|
||||
const api = app.basePath("/api");
|
||||
api.use("*", authMiddleware);
|
||||
|
||||
Reference in New Issue
Block a user