The merge creates the store-directory capability (two requirements, six scenarios) and updates the mall's store-and-marketing requirement so the store directory and store home read live shops and order surfaces name their shop. openspec validate --all --strict stays green at 13 specs.
3.9 KiB
3.9 KiB
Tasks
1. Schema
- 1.1 Add a migration creating
shop_profileskeyed 1:1 toshops(shop_idprimary key,logo,banner,company,region,addressJSONB,noticeJSONB,after_saleJSONB, four score columns,updated_at); verified the table exists aftercargo run -p vmall-api
2. Shared contract
- 2.1 Add
ShopProfileandShopProfileInputtopackages/shared/src/types.ts, with every profile field nullable; verified all three frontends build - 2.2 Add
listShops()/getShop(slug),admin.setShopProfile(id, body)and fixed-data implementations built fromMOCK_STORES; also registered ashopsdomain in the per-domain switch. Unlike wave 4, where that registration was missed and only the browser caught it, here the domain was added alongside the methods
3. Public shop read
- 3.1 Add unauthenticated
GET /api/shopsandGET /api/shops/{slug}, both tolerating a missing profile row; verified a suspended shop is absent from the list and that a suspended or unknown slug is a 404 - 3.2 Add
PUT /api/admin/shops/{id}/profileupserting the profile, refusing bilingual fields without non-emptyen/zhand gated toplatform_admin; verified a rejected payload leaves the stored profile untouched
4. Demo profiles
- 4.1 Teach
scripts/seed-demo.mjsto upsert a profile per demo shop; verified a re-run is idempotent andGET /api/shopsreturns all four with logos, companies and scores
5. Mall store surfaces
- 5.1
pages/stores/index.vue: list fromGET /api/shops, dropping the distance column, its sort and the whole two-option sort control that existed only to offer it; verified four shops render with their logos and no "km" text. A shop with no logo renders an initial-letter placeholder rather than an invented image - 5.2
pages/stores/[id].vue: load the shop by slug and its products from the catalog API byshop_id; verified the profile, its score rows and six products render. Two removals were needed beyond the plan, both following the wave-1 precedent that a UI must not claim what no model backs: the sort is now default + price only (sales and comments have no model), and the "best sellers" rail is gone because nothing ranks by sales - 5.3
pages/goods/[id].vue: read the store card from the shared shop read instead ofstoreById, guarding every field and dropping the card entirely when a shop has no profile - 5.4
pages/checkout/pay.vueandpages/user/orders/index.vue: resolve shop names from the same cached shop read; verified order cards name Terra Grocery, Aurora Digital and Demo Store instead of the placeholder - 5.5 Moved the pure
lowestSkuhelper out of the fixed-data module intoapps/mall/utils/product.ts, re-exported for the mock-era pages, so the live store and product pages no longer import~/mock/datafor it. A first attempt usedexport { lowestSku } from "~/utils/product", which creates no local binding and broke every internal caller at runtime
6. Verification
- 6.1
cargo test -p vmall-apigreen at 28 tests (five new shop tests), and all three frontends build. Caveat worth recording:nuxt builddoes not typecheck — there is notypescript.typeCheckinnuxt.config.tsand novue-tscinstalled — so a build passing is not the type gate AGENTS.md describes. ThelowestSku is not definedbug above passed the build and only failed in the browser, which is how it was found - 6.2 Verified in a browser: the directory lists the four demo shops with real logos and no distance text, a store home shows its profile and six products with default/price sorts, the product page's store card names Aurora Digital and links to its store, and order cards name their shop. Console showed only the header's signed-out cart 401, once per page load
- 6.3 Verified the rollback: with every domain on fixed data and the backend stopped, the directory renders the four mock stores with their logos and a mock store home renders its profile and products