Wave 4 of replacing the fixed-data mock adapter, and the first capability the
mall never had a backend for: the home page's banners, promo tiles, quick links
and floor advert art move out of local arrays.
- four explicit tables (`banners`, `promos`, `quick_links`, `floor_adverts`)
rather than one JSONB payload table, so Postgres enforces each shape
- a migration seeds them from the assets the page already rendered, so the flip
is visually a no-op. Destinations are real routes now: the mock's promo links
pointed at dangling `?category=c1` ids and its first banner used `sort=sales`,
which the catalog API rejects
- `GET /api/content/home` is public and returns the four active, ordered lists,
always including a key so a page can render a missing block
- `GET /api/admin/content` and `PUT /api/admin/content/{kind}` let a platform
admin read everything and replace one kind transactionally, with positions
reindexed from the submitted order and a rejected list changing nothing
- the mall's fixed-data adapter learns `getHomeContent`, and a `content` domain
joins the per-domain switch so the rollback path still renders the page
Verified: 23 backend tests green including six new content tests; all three
frontends build; the home page renders the same four blocks as before, an admin
reorder and deactivation change the rendered carousel, and the fixed-data
rollback renders every block with the backend stopped.
OpenSpec change: openspec/changes/replace-mock-api-wave-4
33 lines
3.3 KiB
Markdown
33 lines
3.3 KiB
Markdown
# 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
|