Files
vmall/openspec/changes/replace-mock-api-wave-4/tasks.md
T
james 104737e4e1 feat(mall): serve home marketing content from the API
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
2026-09-17 16:48:10 +00:00

3.3 KiB

Tasks

1. Schema and seed

  • 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
  • 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

  • 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
  • 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

  • 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
  • 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

  • 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
  • 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
  • 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

  • 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

  • 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
  • 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
  • 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