frontend phase 4: catalog browse (Home A5) & nurse service builder (B7)
Light up the two faces of the configurable service catalog over a new cached services/catalog domain (consumes the b5 contract; unlocks f6 search). - services/catalog: types/keys/constants/apis(client+mock+seam)/hooks/index + names.ts. Categories & option groups are session-cached reference data (Infinity staleTime, like geography); variant mutations invalidate myVariantsLists() and setQueryData the edited row. Mock-primary (USE_CATALOG_MOCK), one-line swap; mock reproduces the 400 missing-required and (nurse,category,option-set) 409 duplicate rules. - Customer Home (A5): greeting+avatar, search bar (navigates toward f6), data-driven category grid (loading/empty/error), patient nudge from the cached f2 query. Deferred /search placeholder stub. - Nurse Services & prices (B7) at /nurse/services: offerings list (active vs deactivated, edit, soft-deactivate w/ confirm, reactivate, no delete) and a 3-step variant builder (category -> required/optional options -> price+unit+ duration). Required-group gate; Toman->IRR digit-string at the field boundary (no float); live unit-aware estimated total (never from price alone); editable auto display_name; inline 409 duplicate warning; locked category edit form. - Shared, tested components: CategoryTile, PriceDisplay, VariantCard. Money util: tomanToRial + multiplyIrr (integer-safe) + tests. - i18n: catalog/services/search namespaces + home additions + nav.services (both locales, in sync). Icons, routes (SEARCH, NURSE_SERVICES), nurse nav. Gate: npm run check green; npm run test:ci green (147 tests, +18 across 4 suites); npm run build green with NEXT_PUBLIC_API_URL set. Docs: client/CLAUDE.md (Project Structure, caching note, namespaces), STATUS, for-backend REQ-010 (pagination param casing), phase report, mocks registry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,36 @@ for awareness.
|
||||
- **Requests filed:** frontend/requests/for-backend.md (yes/no)
|
||||
-->
|
||||
|
||||
## frontend-phase-4-b5 — Catalog browse (Home A5) & nurse service builder (B7) — 2026-07-05
|
||||
- **Shipped:** `services/catalog` domain (types/keys/constants/apis[client+mock+seam]/hooks/index) — the b5
|
||||
catalog skeleton + nurse pricing layer. Hooks: `useServiceCategories`, `useCategoryOptionGroups` (both
|
||||
**session-cached reference data**, Infinite `staleTime`), `useMyVariants` (paginated, self-scoped),
|
||||
`useCreateVariant`/`useUpdateVariant`/`useSetVariantActive` (mutations invalidate `catalogKeys.myVariantsLists()`;
|
||||
update `setQueryData`s the row). Shared composites (each tested): **`CategoryTile`** (data-driven Home tile +
|
||||
builder `selected` state), **`PriceDisplay`** (money-util Toman + i18n unit label + unit-aware estimated total),
|
||||
**`VariantCard`** (offering card, active/deactivated distinction, no delete). Money util gained `tomanToRial`
|
||||
(field-boundary Toman→IRR) + `multiplyIrr` (integer-safe estimate). Screens: **customer Home (A5)** — greeting +
|
||||
avatar, search bar (navigates toward f6 `/search`, results deferred), **data-driven category grid**
|
||||
(loading/empty/error), patient nudge (reuses cached f2 `usePatients`, no new fetch); **nurse Services & prices
|
||||
(B7)** at `/nurse/services` (new sidebar tab) — offerings list (active/inactive, edit, soft deactivate w/ confirm,
|
||||
reactivate, empty/skeleton) + **3-step variant builder** (category → required/optional options → price+unit+duration;
|
||||
required-group gate; Toman→IRR digit-string submit; live unit-aware total; editable auto `displayName`; inline
|
||||
`409` duplicate warning; locked-category edit form). Added `catalog`/`services`/`search` i18n namespaces + `home`
|
||||
additions + `nav.services` (both locales); 7 icons (`services`,`category`,`elderly`,`post_surgery`,`infant`,
|
||||
`chronic`,`companionship`); routes `SEARCH`,`NURSE_SERVICES`; deferred `/search` placeholder stub (→ f6).
|
||||
- **Consumes:** dev/contracts/domains/catalog.md (backend-phase-5). Routes `api/v1/catalog/{categories,option_groups}`,
|
||||
`api/v1/nurse_variants/{create,update/{id},set_active/{id},list,get/{id}}`. Wire camelCase; `price_unit` enum;
|
||||
IRR-string money; `409` duplicate listing / `400` missing required dimension.
|
||||
- **Mocked client-side:** `services/catalog` via `catalogMockApi` behind `USE_CATALOG_MOCK` (default `true`) — seeds
|
||||
the 5 real b5 categories + representative option groups (incl. a cross-category one) + the `409`/`400` rules;
|
||||
variant store seeded **empty** so the offerings empty-state demos. Real `catalogClientApi` wired for a one-line
|
||||
flip. See mocks-registry + the report (note: the mock seeds option groups the fresh backend does not — an admin
|
||||
authors them).
|
||||
- **Gate:** npm run check green · npm run test:ci green (147 tests, +18 across 4 suites: catalog components + money)
|
||||
· npm run build green with NEXT_PUBLIC_API_URL set (routes /nurse/services, /search generated; home prerenders).
|
||||
- **Requests filed:** frontend/requests/for-backend.md — yes (REQ-010 confirm the list pagination query-param name
|
||||
`pageSize` vs the doc's `page_size`).
|
||||
|
||||
## frontend-phase-3-b4 — Addresses, map picker & nurse coverage areas — 2026-07-03
|
||||
- **Shipped:** three domain services — `services/geography` (cached province→city→district reference lookups;
|
||||
**Infinity `staleTime`** + shared `geographyKeys`; `useProvinces`/`useCities`/`useDistricts`; seam+mock+client),
|
||||
|
||||
@@ -120,3 +120,15 @@ delivers fixes in its own change. **Frontend never edits backend code to "fix" a
|
||||
this is purely to prefill the client cascade.
|
||||
- **Proposed shape:** `CustomerAddressDto { …, provinceId: long }` (join from `cities.province_id`).
|
||||
- **Status:** open
|
||||
|
||||
## REQ-010 — Confirm/align the list pagination query-param name (catalog + all lists) — filed by frontend-phase-4-b5 — 2026-07-05
|
||||
- **Need:** Confirm the exact query-param name the paginated list endpoints bind for page size. The
|
||||
`catalog.md` route examples write `?page=&page_size=` (snake_case), but the **working** b4 `serviceAreas`
|
||||
client binds `pageSize` (camelCase, case-insensitive to the server's `PageSize` property) — the f3 report
|
||||
flagged this as the `page_size`→`pageSize` gotcha. The f4 catalog client follows the proven `pageSize` for
|
||||
`catalog/categories` and `nurse_variants/list`; the domain filter `category_id` stays snake_case per the doc.
|
||||
- **Why:** So the real-endpoint swap (f6 flips `USE_CATALOG_MOCK=false`) doesn't silently paginate wrong. If
|
||||
the server truly binds `pageSize`, please update the `page_size` occurrences in the contract docs to match;
|
||||
if it binds `page_size`, tell us and we'll switch the client (one line per list call).
|
||||
- **Proposed shape:** list query = `?page={1-based}&pageSize={≤100}`; response `data` = `{ items, total, page, pageSize }`.
|
||||
- **Status:** open
|
||||
|
||||
Reference in New Issue
Block a user