fix(GRO-2236): portal Book New service cards show price + duration #57

Merged
Scrubs McBarkley merged 1 commits from flea/gro-2236-portal-service-cards into dev 2026-06-08 23:30:31 +00:00
Member

GRO-2236 — Portal Book New: service cards show $undefined price / empty min

Root cause

The Book New "Select Services" cards read svc.price (dollars) and svc.duration, but GET /api/portal/services (api src/routes/portal.ts) projects the canonical DB columns basePriceCents and durationMinutes. Field-name + units mismatch → every card rendered $undefined and an empty min.

Fix (web-only)

  • Normalize the services payload at fetch time: basePriceCents → price (dollars), durationMinutes → duration. Tolerates an already-normalized shape.
  • Render price via a new formatServicePrice helper that returns null when neither priceRange nor numeric price is present, so the card hides the field instead of printing $undefined. Duration line is gated on typeof svc.duration === 'number'.
  • Integer prices render without trailing zeros ($45); fractional to cents ($45.50).

Tests

  • Added unit tests for normalizeService (cents→dollars, already-normalized passthrough, absent/null fields) and formatServicePrice (priceRange precedence, integer/fractional formatting, null fallback).
  • tsc --noEmit clean, eslint clean, vitest 74/74 pass.

Verification (UAT, after deploy)

Open Book New → Select Services → all cards show a real price + 60 min-style duration; no $undefined / empty min.

🤖 Generated with Claude Code

## GRO-2236 — Portal Book New: service cards show `$undefined` price / empty `min` ### Root cause The Book New "Select Services" cards read `svc.price` (dollars) and `svc.duration`, but `GET /api/portal/services` (`api` `src/routes/portal.ts`) projects the canonical DB columns `basePriceCents` and `durationMinutes`. Field-name + units mismatch → every card rendered `$undefined` and an empty `min`. ### Fix (web-only) - Normalize the services payload at fetch time: `basePriceCents → price` (dollars), `durationMinutes → duration`. Tolerates an already-normalized shape. - Render price via a new `formatServicePrice` helper that returns `null` when neither `priceRange` nor numeric `price` is present, so the card **hides** the field instead of printing `$undefined`. Duration line is gated on `typeof svc.duration === 'number'`. - Integer prices render without trailing zeros (`$45`); fractional to cents (`$45.50`). ### Tests - Added unit tests for `normalizeService` (cents→dollars, already-normalized passthrough, absent/null fields) and `formatServicePrice` (priceRange precedence, integer/fractional formatting, null fallback). - `tsc --noEmit` clean, `eslint` clean, `vitest` 74/74 pass. ### Verification (UAT, after deploy) Open Book New → Select Services → all cards show a real price + `60 min`-style duration; no `$undefined` / empty `min`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The Dogfather added 1 commit 2026-06-08 23:28:51 +00:00
fix(GRO-2236): map portal service basePriceCents/durationMinutes so Book New cards show price + duration
CI / Test (pull_request) Successful in 23s
CI / Lint & Typecheck (pull_request) Successful in 31s
CI / Build & Push Docker Image (pull_request) Successful in 46s
f1fe45fd6a
The Book New 'Select Services' cards read svc.price (dollars) and
svc.duration, but GET /api/portal/services projects the canonical DB
columns basePriceCents/durationMinutes. The mismatch rendered every card
as $undefined price and an empty 'min'.

Normalize the services payload at fetch time (basePriceCents -> price in
dollars, durationMinutes -> duration), tolerating an already-normalized
shape. Render price/duration via a formatServicePrice helper that returns
null when absent, so the card gracefully hides the field instead of
printing $undefined.

Adds unit tests for normalizeService and formatServicePrice.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Scrubs McBarkley merged commit 98c8a7bb83 into dev 2026-06-08 23:30:31 +00:00
Sign in to join this conversation.