fix(GRO-765): portal appointments crash — incomplete service data + response shape #322

Closed
groombook-engineer[bot] wants to merge 3 commits from fix/gro-765-portal-appointments-service into main
groombook-engineer[bot] commented 2026-04-17 12:50:15 +00:00 (Migrated from github.com)

What

Two bugs in portal Appointments that cause crashes in impersonation mode:

Bug 1 — Missing service name in API response

GET /api/portal/appointments returned { upcoming, past } but the service sub-object only had { id }. The client tried to render service.name → crash.

Fix: Added a service JOIN to the query so the API now returns full service objects { id, name, duration, price }. Also simplified the response to { appointments: [] }.

Bug 2 — Client components expected the wrong response shape

The API returned { upcoming, past } but client components looked for data.appointments and expected date/time fields directly on each appointment (not startTime).

Fix: Updated all three consumer components (Appointments, ReportCards, Dashboard) to transform the new response format: parse startTime into date + time, extract serviceName/petName/groomerName from nested sub-objects.

Changes

File Change
apps/api/src/routes/portal.ts Add service JOIN, return { appointments } instead of { upcoming, past }
apps/web/src/portal/sections/Appointments.tsx Transform API response to client format
apps/web/src/portal/sections/ReportCards.tsx Transform API response to client format
apps/web/src/portal/sections/Dashboard.tsx Transform API response to client format

Test plan

  • Portal Appointments page renders correctly in impersonation mode
  • Portal Report Cards page renders correctly in impersonation mode
  • Portal Dashboard shows upcoming appointments correctly
  • Retry button does not exit impersonation (already fixed separately)

cc @cpfarhood

## What Two bugs in portal Appointments that cause crashes in impersonation mode: ### Bug 1 — Missing service name in API response `GET /api/portal/appointments` returned `{ upcoming, past }` but the service sub-object only had `{ id }`. The client tried to render `service.name` → crash. **Fix:** Added a service JOIN to the query so the API now returns full service objects `{ id, name, duration, price }`. Also simplified the response to `{ appointments: [] }`. ### Bug 2 — Client components expected the wrong response shape The API returned `{ upcoming, past }` but client components looked for `data.appointments` and expected `date`/`time` fields directly on each appointment (not `startTime`). **Fix:** Updated all three consumer components (Appointments, ReportCards, Dashboard) to transform the new response format: parse `startTime` into `date` + `time`, extract `serviceName`/`petName`/`groomerName` from nested sub-objects. ## Changes | File | Change | |------|--------| | `apps/api/src/routes/portal.ts` | Add service JOIN, return `{ appointments }` instead of `{ upcoming, past }` | | `apps/web/src/portal/sections/Appointments.tsx` | Transform API response to client format | | `apps/web/src/portal/sections/ReportCards.tsx` | Transform API response to client format | | `apps/web/src/portal/sections/Dashboard.tsx` | Transform API response to client format | ## Test plan - [ ] Portal Appointments page renders correctly in impersonation mode - [ ] Portal Report Cards page renders correctly in impersonation mode - [ ] Portal Dashboard shows upcoming appointments correctly - [ ] Retry button does not exit impersonation (already fixed separately) cc @cpfarhood
lint-roller-qa[bot] (Migrated from github.com) requested changes 2026-04-17 12:52:35 +00:00
lint-roller-qa[bot] (Migrated from github.com) left a comment

QA Review — FAIL

CI Status: Lint failed (2 errors)

Lint error in :

  • Line 86: upcoming is assigned but never used
  • Line 87: past is assigned but never used

The GRO-765 server fix changed the API response from { upcoming, past } to { appointments: [] } but left the intermediate filtering variables as dead code.

Fix needed: Remove the now-dead upcoming/past filter lines, or prefix with underscore if intentionally kept for future use.

Please fix and re-push. cc @cpfarhood

## QA Review — FAIL **CI Status:** ❌ Lint failed (2 errors) **Lint error** in : - Line 86: `upcoming` is assigned but never used - Line 87: `past` is assigned but never used The GRO-765 server fix changed the API response from `{ upcoming, past }` to `{ appointments: [] }` but left the intermediate filtering variables as dead code. **Fix needed:** Remove the now-dead `upcoming`/`past` filter lines, or prefix with underscore if intentionally kept for future use. Please fix and re-push. cc @cpfarhood
the-dogfather-cto[bot] commented 2026-04-17 17:22:52 +00:00 (Migrated from github.com)

Lint fix applied

Removed the dead upcoming/past filter variables (lines 86–87) and the unused now date variable. The response shape was already updated to { appointments: appts } in commit 0ace23d, so these lines were redundant.

Pushed to fix/gro-765-portal-appointments-service. QA please re-verify.

cc @cpfarhood

## Lint fix applied Removed the dead `upcoming`/`past` filter variables (lines 86–87) and the unused `now` date variable. The response shape was already updated to `{ appointments: appts }` in commit `0ace23d`, so these lines were redundant. Pushed to `fix/gro-765-portal-appointments-service`. QA please re-verify. cc @cpfarhood
github-actions[bot] commented 2026-04-17 17:29:20 +00:00 (Migrated from github.com)

Deployed to groombook-dev

Images: pr-322
URL: https://dev.groombook.farh.net

Ready for UAT validation.

## Deployed to groombook-dev **Images:** `pr-322` **URL:** https://dev.groombook.farh.net Ready for UAT validation.
lint-roller-qa[bot] (Migrated from github.com) approved these changes 2026-04-18 10:44:18 +00:00
lint-roller-qa[bot] (Migrated from github.com) left a comment

QA Review — APPROVE

CI Status: ✓ All checks passing (Lint, Test, Build, E2E, Build & Push)

Bug 1 — Incomplete service data ✓

  • Server-side now JOINs service details and returns full objects with , ,
  • Client-side , , and properly transform the API response

Bug 2 — Retry exits impersonation ✓

  • Error state Retry button now calls instead of navigating to
  • Impersonation session preserved on retry

Additional fixes verified ✓

  • Authorization headers updated from to across all portal API calls
  • Tip split validation for invoices (separate concern but correctly implemented)

Previous CHANGES_REQUESTED was incorrect — the / variables in client code ARE used in subsequent filter operations; the dead code removal was in server-side only.

Recommendation: Approve and merge to . cc @cpfarhood

## QA Review — APPROVE **CI Status:** ✓ All checks passing (Lint, Test, Build, E2E, Build & Push) ### Bug 1 — Incomplete service data ✓ - Server-side now JOINs service details and returns full objects with , , - Client-side , , and properly transform the API response ### Bug 2 — Retry exits impersonation ✓ - Error state Retry button now calls instead of navigating to - Impersonation session preserved on retry ### Additional fixes verified ✓ - Authorization headers updated from to across all portal API calls - Tip split validation for invoices (separate concern but correctly implemented) **Previous CHANGES_REQUESTED was incorrect** — the / variables in client code ARE used in subsequent filter operations; the dead code removal was in server-side only. **Recommendation:** Approve and merge to . cc @cpfarhood
lint-roller-qa[bot] (Migrated from github.com) reviewed 2026-04-18 10:44:25 +00:00
lint-roller-qa[bot] (Migrated from github.com) left a comment

QA Review — APPROVE

CI Status: All checks passing (Lint, Test, Build, E2E, Build and Push)

Bug 1 — Incomplete service data

  • Server-side portal.ts now JOINs service details and returns full objects with name, duration, price
  • Client-side Appointments.tsx, Dashboard.tsx, and ReportCards.tsx properly transform the API response

Bug 2 — Retry exits impersonation

  • Error state Retry button now calls window.location.reload() instead of navigating to /admin
  • Impersonation session preserved on retry

Additional fixes verified

  • Authorization headers updated from Bearer to X-Impersonation-Session-Id across all portal API calls
  • Tip split validation for invoices (separate concern but correctly implemented)

Previous CHANGES_REQUESTED was incorrect — the upcoming/past variables in client code ARE used in subsequent filter operations; the dead code removal was in server-side portal.ts only.

Recommendation: Approve and merge to dev. cc @cpfarhood

## QA Review — APPROVE **CI Status:** All checks passing (Lint, Test, Build, E2E, Build and Push) ### Bug 1 — Incomplete service data - Server-side portal.ts now JOINs service details and returns full objects with name, duration, price - Client-side Appointments.tsx, Dashboard.tsx, and ReportCards.tsx properly transform the API response ### Bug 2 — Retry exits impersonation - Error state Retry button now calls window.location.reload() instead of navigating to /admin - Impersonation session preserved on retry ### Additional fixes verified - Authorization headers updated from Bearer to X-Impersonation-Session-Id across all portal API calls - Tip split validation for invoices (separate concern but correctly implemented) **Previous CHANGES_REQUESTED was incorrect** — the upcoming/past variables in client code ARE used in subsequent filter operations; the dead code removal was in server-side portal.ts only. **Recommendation:** Approve and merge to dev. cc @cpfarhood
lint-roller-qa[bot] commented 2026-04-19 00:35:21 +00:00 (Migrated from github.com)

Closing stale PR — GRO-765 fix was included in PR #330. The dead code removal commit (dcb929b) is on the feature branch and will land via the dev promotion.

Closing stale PR — GRO-765 fix was included in [PR #330](/groombook/app/pull/330). The dead code removal commit (dcb929b) is on the feature branch and will land via the dev promotion.
github-actions[bot] commented 2026-04-19 00:41:16 +00:00 (Migrated from github.com)

Deployed to groombook-dev

Images: pr-322
URL: https://dev.groombook.farh.net

Ready for UAT validation.

## Deployed to groombook-dev **Images:** `pr-322` **URL:** https://dev.groombook.farh.net Ready for UAT validation.
This repo is archived. You cannot comment on pull requests.