# Tasks ## 1. Backend: catalog browse - [x] 1.1 Add an append-only migration seeding child and grandchild categories under the existing `electronics`, `fashion` and `home-living` rows, matching the mock's 3-level shape; verify `GET /api/categories` returns more than 3 rows with populated `parent_id` - [x] 1.2 Replace the exact `p.category_id = $1` filter in `apps/api/src/routes/catalog.rs` with a `WITH RECURSIVE` subtree CTE shared by the count and page queries; verify a parent-category request returns its descendants' products - [x] 1.3 Add optional `sort=price` and `order=asc|desc` to `list_products`, ordering by each product's lowest active SKU price with `NULLS LAST`, and reject any other value with an `ApiError` 400 whose body is `{"error":{"code","message"}}`; verify the 400 shape with curl - [x] 1.4 Extend `apps/api/tests/catalog.rs` with cases for subtree listing, ascending/descending price order and the rejected-sort 400; verify `cargo test -p vmall-api` is green and repeatable ## 2. Shared contract - [x] 2.1 Add optional `sort` and `order` to `ProductListQuery` in `packages/shared/src/api.ts` and pass them through in `createApi.listProducts`; verify `pnpm --filter @vmall/mall build` still type-checks ## 3. Demo data - [x] 3.1 Grow `scripts/seed-demo.mjs` to roughly 24 bilingual products spread across the new category tree plus 4 shops, keeping the slug-lookup idempotency; verify a second run reports no duplicate creates and `GET /api/products?per_page=100` returns a full first page ## 4. Mall: per-domain adapter switch - [x] 4.1 Replace the `mockApi` boolean in `apps/mall/plugins/api.ts` with a `liveDomains` list defaulting to `["catalog", "currency"]`, composing the live client over the fixed-data base through a typed per-domain pick map (no `any`); verify catalog requests hit `:8080` while `getCart` still resolves from fixed data with the backend stopped ## 5. Mall: rewire browse surfaces - [x] 5.1 `apps/mall/pages/index.vue`: build floors from `listCategories` plus `listProducts` per category, leaving banners, promos, quick links and floor advert art local; verify every non-empty floor renders live products - [x] 5.2 `apps/mall/components/shell/CategoryMenu.vue`: source the tree from `listCategories`, keeping the header dropdown mode and the pinned home mode intact; verify the pinned sidebar shows children from the API - [x] 5.3 `apps/mall/pages/search.vue`: list through `listProducts` with the selected category's subtree and the new sort, and delete the brand facet plus the `sales`/`comments` sort options; verify filtering by a parent category lists descendant products - [x] 5.4 `apps/mall/pages/goods/[id].vue`: load the product and SKUs through `getProduct`, keeping the comment, coupon and sales-rail sections on local display-only content; verify an in-stock SKU selection updates price, stock and the cart target - [x] 5.5 Remove only the catalog helpers that lost their last consumer (`topCategories`, `childCategories`, `homeFloors`, `MOCK_BRANDS`, `brandOf`, `HomeFloor`, `MockBrand`) from `apps/mall/mock/data.ts`; amended from the original wording, which also removed `MOCK_PRODUCTS` and would have broken the rollback the `Mock API adapter` requirement promises, so `MOCK_PRODUCTS`/`searchMockProducts`/`productById` stay; verify `pnpm --filter @vmall/mall build` passes with no page importing a removed symbol ## 6. Verification - [x] 6.1 Run `pnpm --filter @vmall/mall build`, `pnpm --filter @vmall/shop-admin build` and `pnpm --filter @vmall/admin build`, since the shared contract changed; verify all three pass - [x] 6.2 With the live backend and seeds running, verify in a browser that home, search, product detail and the pinned category menu render live data, and that no console errors appear - [x] 6.3 Stop the backend and confirm the mall still starts and the unmigrated domains (auth, cart, orders, invoices) keep working from fixed data; verify the browse pages degrade without crashing