feat(mall): serve catalog and currency from the live API
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
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
# 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 `auth` to live and verify `login` / `register` / `me` against `:8080` using the seeded `customer@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.token` localStorage key, shared with shop-admin and admin, and that logout clears it.
|
||||
- [ ] Flip `cart` to live. Cart requires a token (`AuthUser` on all four handlers in `apps/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,60` uses `productById` / `storeById` for shop name, image and stock. The live `CartView` already returns `product_name`, `image` and `unit_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 `orders` to live and verify checkout splits one cart into per-shop orders with a price snapshot and stock decrement.
|
||||
- [ ] Checkout still sources `shipping_address` from `MOCK_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 `payOrder` only accepts `pending_payment` (both are enforced live; the mock only mimicked them).
|
||||
- [ ] Flip `shipments` to live and verify `confirmDelivered` moves the order to `completed`.
|
||||
- [ ] Flip `invoices` to 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.items` is required in `packages/shared/src/types.ts` but the live struct has no `items` field (`apps/api/src/models.rs:172-182`), and `Invoice.invoice_no` is nullable live (`models.rs:194`) but non-null `string` in TS.
|
||||
- [ ] Remove the remaining `storeById` mock 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/index` and `stores/[id]` leave mock. Small: products already carry `shop_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 `brands` table (`products.brand_id` + i18n) plus sales and comments data, neither of which exists today.
|
||||
- [ ] Extend the `ORDER BY` whitelist if more sorts are wanted beyond the `sort=price` added 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: `Address` is embedded in the order and live checkout takes it in the request body, so no addresses table is needed. `MOCK_ADDRESSES` can 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-admin` and `admin` already run live against the same backend.
|
||||
Reference in New Issue
Block a user