# Tasks ## 1. Schema - [ ] 1.1 Add a migration creating `shop_profiles` keyed 1:1 to `shops` (`shop_id` primary key, `logo`, `banner`, `company`, `region`, `address` JSONB, `notice` JSONB, `after_sale` JSONB, four score columns, `updated_at`); verify the table exists after `cargo run -p vmall-api` ## 2. Shared contract - [ ] 2.1 Add `ShopProfile` (shop identity plus the optional profile fields) and a `ShopProfileInput` to `packages/shared/src/types.ts`; verify all three frontends build - [ ] 2.2 Add `listShops()` / `getShop(slug)` to the `ApiClient` and `createApi`, plus `admin.setShopProfile(id, body)`; give the fixed-data adapter matching implementations built from `MOCK_STORES` so the rollback path still renders; verify the mall build ## 3. Public shop read - [ ] 3.1 Add `GET /api/shops` returning active shops with their profile, and `GET /api/shops/{slug}` returning one, both unauthenticated and both tolerating a missing profile row; verify a suspended shop is absent from the list and an unknown slug is a 404 - [ ] 3.2 Add `PUT /api/admin/shops/{id}/profile` upserting the profile, validating non-empty `en`/`zh` on bilingual fields and gated to `platform_admin`; verify a bad payload is refused without changing the stored row ## 4. Demo profiles - [ ] 4.1 Teach `scripts/seed-demo.mjs` to set a profile for each demo shop using the existing `/mock/store-N.svg` assets and bilingual copy; verify a re-run is idempotent and `GET /api/shops` returns a profile for every shop ## 5. Mall store surfaces - [ ] 5.1 `pages/stores/index.vue`: list from `GET /api/shops` and drop the distance column and its sort; verify the directory renders every active shop and no "km" text remains - [ ] 5.2 `pages/stores/[id].vue`: load the shop by slug and its products from the catalog API by `shop_id`, drop the mock sales ranking in favour of the same shop-scoped product call; verify a store page renders its profile and products - [ ] 5.3 `pages/goods/[id].vue`: read the store card from the shop API instead of `storeById`, keeping the existing guards for a missing profile - [ ] 5.4 `pages/checkout/pay.vue` and `pages/user/orders/index.vue`: resolve shop names from the cached shop read, removing the generic "Shop" placeholder ## 6. Verification - [ ] 6.1 Run the mall, shop-admin and admin builds, since the shared contract changed; verify all three pass and `cargo test -p vmall-api` stays green - [ ] 6.2 With the backend seeded, verify in a browser that the store directory lists the demo shops, a store home renders its profile and products, the product page's store card shows a real name, and the cart/payment/order surfaces name the shop; confirm no console errors beyond deliberate failed responses - [ ] 6.3 Verify the rollback: with every domain on fixed data and the backend stopped, the store directory and store home still render