fix(portal): implement Customer Portal reschedule button and modal #144

Merged
groombook-engineer[bot] merged 14 commits from feature/gro-118-better-auth into main 2026-03-28 22:10:50 +00:00

14 Commits

Author SHA1 Message Date
groombook-cto[bot] cebf708058 Merge branch 'main' into feature/gro-118-better-auth 2026-03-28 21:08:41 +00:00
groombook-ci[bot] 14df08a6ab fix(auth): show login page instead of auto-redirecting to SSO
Replace the unconditional signIn.social() call on every render with a
LoginPage component that shows a "Sign in with SSO" button. This
prevents redirect loops when the OAuth flow fails or the session
cookie is not yet established.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-28 18:33:08 +00:00
groombook-ci[bot] d32c14504d fix(auth): support OIDC_INTERNAL_BASE for split-horizon OAuth endpoints
In-cluster pods can't reach the external gateway IP (hairpin NAT).
When OIDC_INTERNAL_BASE is set, use explicit OAuth endpoint URLs:
- authorizationUrl: external (browser redirect, no server fetch)
- tokenUrl/userInfoUrl: internal K8s service (server-to-server)

Falls back to discoveryUrl for local dev when not set.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 15:35:39 +00:00
groombook-ci[bot] 858cde6260 fix(auth): mount Better-Auth as sub-app via api.route()
Hono's basePath() + api.on("/auth/**") didn't match correctly.
Using api.route("/auth", authRouter) with a dedicated sub-app
ensures all /api/auth/* paths reach Better-Auth.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 15:23:57 +00:00
groombook-ci[bot] afc13b4a71 debug(auth): add test route to diagnose routing issue
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 15:17:24 +00:00
groombook-ci[bot] 04549adba5 fix(auth): register Better-Auth handler on api sub-app, not parent app
Hono's basePath() creates a sub-app that captures /api/* requests.
Route handlers on the parent app are not reachable for paths that
match the sub-app's middleware. Moving the handler to the api sub-app
(with path /auth/** instead of /api/auth/**) fixes the 404.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 15:07:53 +00:00
groombook-ci[bot] cc4833e831 debug(auth): add logging to Better-Auth handler for diagnosis
Temporary — will remove once auth routing is confirmed working.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 14:59:33 +00:00
groombook-ci[bot] 483ae373f5 fix(auth): use auth.handler instead of toNodeHandler for Hono
toNodeHandler writes directly to Node ServerResponse and returns void,
causing Hono to return 404 for all Better-Auth routes. auth.handler
uses web standard Request/Response which Hono expects.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 14:49:28 +00:00
groombook-ci[bot] 1eec37a192 fix(auth): skip resolveStaffMiddleware on Better-Auth routes
resolveStaffMiddleware crashed with "Cannot read properties of
undefined (reading 'sub')" on /api/auth/* requests because
authMiddleware skips those paths without setting jwtPayload.
Add the same skip so Better-Auth routes pass through cleanly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 14:40:28 +00:00
groombook-ci[bot] bec7f91566 fix(auth): skip auth middleware for Better-Auth's own routes
The auth middleware was intercepting /api/auth/** requests (OAuth
callbacks, session management) and returning 401 before Better-Auth
could process them. This prevented login when AUTH_DISABLED=false.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-28 14:27:56 +00:00
groombook-ci[bot] d85de0a7ff fix(web): use relative URL for Better-Auth client, fix lint errors
- auth-client.ts: default baseURL to empty string (relative URL via nginx
  proxy) instead of http://localhost:3000 which breaks deployed environments
- Dashboard.tsx: remove unused Appointment type import (lint error)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-28 14:15:35 +00:00
Barkley Trimsworth c201115a1f fix(portal): add missing drizzle-orm operators and fix staffId type
- Import lt, gt, ne from @groombook/db alongside existing eq/and
- Add non-null assertion on appt.staffId since appointment always has staffId

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-28 13:19:56 +00:00
Flea Flicker 96f1494126 fix(portal): implement reschedule button and modal for Customer Portal
- Add POST /api/portal/appointments/:id/reschedule endpoint with:
  - Session auth via X-Impersonation-Session-Id header
  - Ownership validation (clientId match)
  - Past/in-progress/cancelled/completed guard
  - Conflict detection for the target time slot
  - Duration-preserving reschedule (keeps original endTime offset)

- Add RescheduleFlow modal component in AppointmentsSection:
  - Date picker + time slot grid (same times as BookingFlow)
  - Shows current appointment summary
  - POSTs to /api/portal/appointments/:id/reschedule
  - Reloads page on success

- Wire Reschedule button in AppointmentCard (Appointments section)
- Wire Reschedule button in Dashboard next-appointment card
- Add showReschedule/rescheduleAppointment state in CustomerPortal

Fixes GRO-166

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 12:02:37 +00:00
groombook-ci[bot] b3a3f8023a fix(portal): wire up Edit Pet and Add New Pet buttons in customer portal
Enable the Edit Pet button on Manage Pets (Settings) and Pet Profiles,
and the Add New Pet button. Add PetForm component for editing. Remove
disabled/stub attributes from Reschedule, Cancel, and Add Notes buttons
in Dashboard and Appointments.

Resolves GRO-167.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-28 10:59:22 +00:00