Wave 1 of replacing the fixed-data mock adapter. The mall now selects its API adapter per domain, with catalog and currency served live while auth, cart, orders, shipments and invoices stay on fixed data. Backend: - seed the 6 x 2 x 2 category tree as reference data (migration 0005). The API exposes no category write route, so this cannot come from the seed script - filter public product listing by the category subtree with a recursive CTE, matching the mock's existing behaviour instead of exact-match - add sort=price with order=asc|desc, validated by hand so an unsupported value returns the project's ApiError 400 shape rather than axum's own rejection Mall: - replace the all-or-nothing mockApi boolean with a liveDomains list composed through a typed per-domain pick map - source home floors, the category menu, search and product detail from the catalog API; banners, promos, quick links, store card and comment/coupon content stay local display-only content - drop the brand facet and the sales/comments sorts: no backend model backs them - fix salesOf/commentCountOf, which parsed digits out of the product id and so rendered "NaN sold" for live UUID ids; they now hash the id Seed: 24 products across 4 shops, idempotent on re-run. Note: the mall defaults to a live catalog, so pnpm dev:mall now expects the API to be running; set NUXT_PUBLIC_LIVE_DOMAINS to an empty array for all-mock work. OpenSpec change: openspec/changes/replace-mock-api-wave-1
1.7 KiB
1.7 KiB
Spec Delta
ADDED Requirements
Requirement: Public product browse
Public GET /api/products SHALL return only published products whose shop is active, and SHALL remain readable without authentication. When category_id is supplied, the filter SHALL match that category and every category beneath it, so requesting a parent category returns products assigned to its child and grandchild categories. The listing SHALL accept an optional sort of price together with an order of asc or desc, ordering by each product's lowest active SKU price; any other sort value SHALL be rejected with a 400 ApiError rather than silently ignored. An unsorted listing SHALL order newest first. Paging SHALL keep returning page and per_page alongside the filtered total.
Scenario: parent category includes descendant products
- WHEN a shopper requests products for a category that has child categories holding published products
- THEN the response contains the products assigned to those descendant categories, not only those assigned directly to the requested category
Scenario: sort by lowest active SKU price
- WHEN a shopper requests the product list with
sort=priceandorder=asc - THEN products come back ordered by their lowest active SKU price ascending
Scenario: unsupported sort is rejected
- WHEN a client requests a
sortvalue that is notprice - THEN the API responds 400 with an
ApiErrorbody instead of ignoring the parameter
Scenario: unpublished products never appear
- WHEN any public listing or filter is applied
- THEN products that are not
published, or whose shop is not active, are absent from bothitemsandtotal