# Tasks ## 1. Schema and seed - [x] 1.1 Add a migration creating `banners`, `promos`, `quick_links` and `floor_adverts` with explicit columns (`image`, `url`, `position`, `active`, plus `label`/`glyph` on quick links); verified the tables exist after `cargo run -p vmall-api` - [x] 1.2 Seed the same migration from the assets the page uses today, matching `pages/index.vue`'s arrays; verified `GET /api/content/home` returns 3 banners, 3 promos, 6 quick links and 6 floor adverts. The seeded destinations are valid routes (`/seckill`, `/collective`, `/integral`, `/search?sort=price&order=desc`) rather than the mock's dangling `?category=c1` ids and its `sort=sales`, which the catalog API rejects ## 2. Shared contract - [x] 2.1 Add `HomeBanner`, `HomePromo`, `HomeQuickLink`, `FloorAdvert`, `HomeContent`, `ContentKind` and `ContentInputByKind` to `packages/shared/src/types.ts`, with the quick-link label as `LocalizedText`; verified all three frontends build - [x] 2.2 Add `getHomeContent()` and `admin.getContent()` / `admin.replaceContent(kind, list)` to the `ApiClient` interface and `createApi`, and give the fixed-data client matching implementations so the rollback path keeps serving the current arrays. Also registered a `content` domain in the per-domain switch (`plugins/api.ts` and `nuxt.config.ts`) — missed on the first pass and caught by the browser check, where the promos still rendered the mock hrefs because the new method had no live pick ## 3. Public content read - [x] 3.1 Add `GET /api/content/home` returning the four active, position-ordered lists, with no authentication; verified it answers with the seeded counts and omits a row once it is marked inactive - [x] 3.2 Return an empty list for any kind with no active entries rather than omitting the key; asserted by `home_content_is_public_and_ordered` ## 4. Admin content management - [x] 4.1 Add `GET /api/admin/content` returning every kind including inactive rows, gated to `platform_admin`; verified a customer and a shop owner are refused with 403 - [x] 4.2 Add `PUT /api/admin/content/{kind}` replacing one kind transactionally, validating each entry and reindexing positions from the submitted order; verified a reordered submission reads back in the new order, and that the rendered page follows - [x] 4.3 Verify an invalid entry (missing `image`, or a quick-link label without `zh`) fails without changing the stored list ## 5. Mall home page - [x] 5.1 Point `pages/index.vue` at `getHomeContent()`, delete the three mock arrays from the page, and render the floor advert pool by floor order; verified the page still renders its sidebar and floors, and that a floor renders no advert rather than a broken image when the pool is empty ## 6. Verification - [x] 6.1 Run the mall, shop-admin and admin builds, since the shared contract changed; all three pass, and `cargo test -p vmall-api` is green at 23 tests - [x] 6.2 Compared the home page against the pre-change render: 3 banners, 6 quick links, 3 promo tiles and floor adverts 1-6 across 6 floors, visually unchanged. Reordering banners and marking one inactive through the admin API changed the rendered carousel accordingly, then the seed was restored - [x] 6.3 Verified the rollback: with every domain on fixed data and the backend stopped, the home page still renders all four blocks with no console errors