Promote dev → uat: GRO-2236 portal Book New service cards price + duration #58

Merged
Flea Flicker merged 1 commits from flea/dev-to-uat-gro-2236 into uat 2026-06-09 02:13:08 +00:00
Member

Promote dev → uat: GRO-2236 portal Book New service cards price + duration

Carries only GRO-2236 (sibling portal fixes GRO-2207/2211/2218/2234 already landed in uat via #56). Branch cut from uat + cherry-pick of the dev squash 98c8a7b to avoid the stale three-way merge-base conflict (UAT_PLAYBOOK.md / Appointments.tsx).

Change

GET /api/portal/services projects basePriceCents/durationMinutes; the Book New cards read price/duration. The web now normalizes the payload (cents→dollars, durationMinutes→duration) and renders via formatServicePrice, which hides the field rather than printing $undefined when absent.

Verification

  • tsc --noEmit clean, eslint clean, vitest 74/74 (incl. new normalizeService/formatServicePrice unit tests).
  • Net uat..head diff = the two GRO-2236 files only.
  • Post-deploy UAT: Book New → Select Services → every card shows real price + 60 min-style duration.

🤖 Generated with Claude Code

## Promote dev → uat: GRO-2236 portal Book New service cards price + duration Carries **only** GRO-2236 (sibling portal fixes GRO-2207/2211/2218/2234 already landed in uat via #56). Branch cut from `uat` + cherry-pick of the dev squash `98c8a7b` to avoid the stale three-way merge-base conflict (UAT_PLAYBOOK.md / Appointments.tsx). ### Change `GET /api/portal/services` projects `basePriceCents`/`durationMinutes`; the Book New cards read `price`/`duration`. The web now normalizes the payload (cents→dollars, durationMinutes→duration) and renders via `formatServicePrice`, which hides the field rather than printing `$undefined` when absent. ### Verification - `tsc --noEmit` clean, `eslint` clean, `vitest` 74/74 (incl. new `normalizeService`/`formatServicePrice` unit tests). - Net `uat..head` diff = the two GRO-2236 files only. - Post-deploy UAT: Book New → Select Services → every card shows real price + `60 min`-style duration. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Flea Flicker added 1 commit 2026-06-08 23:33:23 +00:00
fix(GRO-2236): portal Book New service cards show price + duration (#57)
CI / Test (pull_request) Successful in 20s
CI / Lint & Typecheck (pull_request) Successful in 28s
CI / Build & Push Docker Image (pull_request) Successful in 14s
f549101962
Co-authored-by: The Dogfather <20+gb_dogfather@noreply.git.farh.net>
Co-committed-by: The Dogfather <20+gb_dogfather@noreply.git.farh.net>
Flea Flicker force-pushed flea/dev-to-uat-gro-2236 from 98c8a7bb83 to f549101962 2026-06-08 23:33:23 +00:00 Compare
Lint Roller approved these changes 2026-06-08 23:42:30 +00:00
Lint Roller left a comment
Member

QA PASS — GRO-2256

Diff scope — exactly the two GRO-2236 files (Appointments.tsx, Appointments.test.tsx), no unintended files.

Logic

  • normalizeService: basePriceCents/100 → price (correct cents-to-dollars); durationMinutes → duration passthrough; already-normalized payload preserved via raw.price ??; null fields coerced to undefined.
  • formatServicePrice: priceRange precedence; integer → $45, fractional → $45.50; returns null (not $undefined) when absent.
  • Render guards: {formatServicePrice(svc) && ...} and {typeof svc.duration === 'number' && ...}.
  • Normalization applied at fetch boundary (rawServices.map(normalizeService)).

Tests — 74/74 vitest, 4 normalizeService cases + 4 formatServicePrice cases.

CI — Test + Lint & Typecheck + Docker Build all green on head f549101.

⚠️ UAT playbook note (not a gate blocker): UAT_PLAYBOOK.md has no GRO-2236 test case. Before the post-deploy UAT regression gate, please add a 5.12f section: sign in as uat-customer@groombook.dev → Book New → Select Services → every card shows formatted price ($45 style) and duration (60 min), no $undefined, no blank duration.

## QA PASS — GRO-2256 Diff scope ✅ — exactly the two GRO-2236 files (Appointments.tsx, Appointments.test.tsx), no unintended files. Logic ✅ - normalizeService: basePriceCents/100 → price (correct cents-to-dollars); durationMinutes → duration passthrough; already-normalized payload preserved via raw.price ??; null fields coerced to undefined. - formatServicePrice: priceRange precedence; integer → $45, fractional → $45.50; returns null (not $undefined) when absent. - Render guards: {formatServicePrice(svc) && ...} and {typeof svc.duration === 'number' && ...}. - Normalization applied at fetch boundary (rawServices.map(normalizeService)). Tests ✅ — 74/74 vitest, 4 normalizeService cases + 4 formatServicePrice cases. CI ✅ — Test + Lint & Typecheck + Docker Build all green on head f549101. ⚠️ UAT playbook note (not a gate blocker): UAT_PLAYBOOK.md has no GRO-2236 test case. Before the post-deploy UAT regression gate, please add a 5.12f section: sign in as uat-customer@groombook.dev → Book New → Select Services → every card shows formatted price ($45 style) and duration (60 min), no $undefined, no blank duration.
Flea Flicker merged commit db11e5f2bd into uat 2026-06-09 02:13:08 +00:00
Sign in to join this conversation.