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
4.8 KiB
TBD — migrate the mall off the mock API (waves 2+)
Wave 1 is planned in the OpenSpec change openspec/changes/replace-mock-api-wave-1/
(catalog + currency). This file tracks everything after it.
How to use: check a box only once the behaviour is implemented and verified against
the live backend (cargo run -p vmall-api, node scripts/seed-demo.mjs). Wave 1 leaves a
per-domain switch in apps/mall/plugins/api.ts, so most of these are adding a domain name
to the live list — each still needs its own verification below.
Delete this file once every box in Waves 2 and 3 is checked. Wave 4 is optional: if you decide against it, delete this file anyway and record that decision wherever you like.
Wave 2 — auth + cart
- Flip
authto live and verifylogin/register/meagainst:8080using the seededcustomer@vmall.local/customer123. - Confirm bad credentials now produce a real 401 — the mock accepted any input (
apps/mall/mock/api.ts:125), so this is a deliberate UX change. - Confirm the JWT round-trips through the
vmall.tokenlocalStorage key, shared with shop-admin and admin, and that logout clears it. - Flip
cartto live. Cart requires a token (AuthUseron all four handlers inapps/api/src/routes/cart.rs) and real SKU ids from Wave 1 — both are prerequisites, not optional. - Remove cart's mock display coupling:
apps/mall/pages/cart.vue:4,34,41,56,60usesproductById/storeByIdfor shop name, image and stock. The liveCartViewalready returnsproduct_name,imageandunit_price_minor(apps/api/src/cart.rs:44-53). - Verify: add / qty update / remove / empty cart for a logged-out user, and the out-of-stock 409 path.
Wave 3 — orders + shipments + invoices
- Flip
ordersto live and verify checkout splits one cart into per-shop orders with a price snapshot and stock decrement. - Checkout still sources
shipping_addressfromMOCK_ADDRESSES(apps/mall/pages/checkout/index.vue:4,23,37,127) — that is intentional; see the out-of-scope note below. - Verify cancel restores stock and
payOrderonly acceptspending_payment(both are enforced live; the mock only mimicked them). - Flip
shipmentsto live and verifyconfirmDeliveredmoves the order tocompleted. - Flip
invoicesto live and verify a company invoice requires a tax number and that one order can hold only one active invoice. - Fix contract debt so the TS types stop lying:
Shipment.itemsis required inpackages/shared/src/types.tsbut the live struct has noitemsfield (apps/api/src/models.rs:172-182), andInvoice.invoice_nois nullable live (models.rs:194) but non-nullstringin TS. - Remove the remaining
storeByIdmock usage on the order pages (apps/mall/pages/user/orders/index.vue:4). - Confirm the mall still renders when the live API is down (mock mode remains the fallback for local UI work).
Wave 4 — optional new backend capabilities
Only if you want more of the storefront backed by real data. Each is a new capability, not a flip.
- Storefront content — banners, promos, quick links, home floors and floor advert art. Needs real tables, admin CRUD and i18n JSONB. Do this first if you want the home page fully live; it is the most visible remaining mock surface.
- Public store read — a buyer-facing shop endpoint so
stores/indexandstores/[id]leave mock. Small: products already carryshop_id, and the public catalog already joins shops for the active check. - Brand model + sales/comments sorts — restores the brand facet and the sorts removed in Wave 1. Needs a
brandstable (products.brand_id+ i18n) plus sales and comments data, neither of which exists today. - Extend the
ORDER BYwhitelist if more sorts are wanted beyond thesort=priceadded in Wave 1.
Deliberately out of scope — not tracked here, and they do not block deleting this file
These have no API contract and no backend model. Leaving them on ~/mock/data is a decision, not a backlog item.
- Addresses — never a blocker:
Addressis embedded in the order and live checkout takes it in the request body, so no addresses table is needed.MOCK_ADDRESSEScan stay behind checkout indefinitely. - Favorites, coupons, account stats — pure presentation, no transactional impact.
- seckill / collective / integral marketing pages — display-only mock content.
Decisions already made (do not relitigate)
- Category filtering is by subtree; the backend exact-match filter was the bug (fixed in Wave 1).
- A migration wave must not change what the UI claims: facets without a backing model are removed rather than left matching nothing.
- The mall is the last mock holdout;
shop-adminandadminalready run live against the same backend.