Date picker now shows a clear error when the value doesn't match
YYYY-MM-DD, instead of silently failing with a browser console warning.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The "Rebook Now" button on the Report Card detail view had no click
handler. Now navigates to /admin/book with pet info pre-filled via URL
params (petName, serviceName). Button text changed from "Book Now" to
"Rebook Now" per the bug report.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add a useEffect in SetupWizard that checks GET /api/setup/status
on mount. If needsSetup === false, redirect to /admin immediately
instead of showing the wizard. Shows a "Checking setup status..."
loading state while the check is in flight.
Fixes GRO-254
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add useSearchParams to read URL parameters (e.g., ?clientName=Jane)
and sync them to the BookingBody state on mount via useEffect.
This ensures validation checks React state, not empty initial state.
Fixes GRO-255
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Export Appointment interface from Appointments.tsx for use elsewhere
- CustomerPortal: use { id: string } for reschedule state instead of
Record<string, unknown>; cast to Appointment via unknown intermediate
- AccountSettings: remove unused eslint-disable directive
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The transaction callback returns [guardError, deleted] but only
guardError is consumed. Destructure only what we need.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Frontend:
- Super users now see a "Revoke" button (disabled when last super user)
alongside the ★ badge on super-user rows in the Staff table.
Non-super-user rows show the existing "+ Grant" button.
Backend (race condition fix):
- PATCH /api/staff/:id (isSuperUser=false or active=false): count check +
update now wrapped in a db.transaction() with FOR UPDATE lock on the
target row, preventing a race where two concurrent revokes could both
pass the guard and leave zero super users.
- DELETE /api/staff/🆔 same transaction + FOR UPDATE guard applied.
GRO-206 CTO review feedback
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Backend:
- GET /api/staff/me — returns current staff record (includes isSuperUser)
- PATCH /api/staff/:id — accepts optional isSuperUser boolean; only super users
can change this field; last-super-user guardrail prevents revoking the only
active super user
- PATCH /api/staff/:id (active=false) — guardrail prevents deactivating the
last active super user
- DELETE /api/staff/:id — guardrail prevents deleting the last active super user
Frontend (Staff.tsx):
- Fetches current user via GET /api/staff/me to determine isSuperUser
- Shows ★ Super User badge for super user staff rows
- Super user sees "+ Grant" button on non-super-user rows
- Super user sees "Revoke" toggle on super-user rows (disabled if last one)
- Deactivate button disabled for the last active super user
- Error message displayed when backend guardrail triggers
GRO-206
Co-Authored-By: Paperclip <noreply@paperclip.ing>