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
This commit is contained in:
@@ -7,7 +7,15 @@ import { createMockApi } from "~/mock/api";
|
||||
* adapter, so a domain can be migrated - or rolled back - by editing this list
|
||||
* alone. See openspec/changes/replace-mock-api-wave-1/design.md.
|
||||
*/
|
||||
type LiveDomain = "auth" | "catalog" | "currency" | "cart" | "orders" | "shipments" | "invoices";
|
||||
type LiveDomain =
|
||||
| "auth"
|
||||
| "catalog"
|
||||
| "currency"
|
||||
| "content"
|
||||
| "cart"
|
||||
| "orders"
|
||||
| "shipments"
|
||||
| "invoices";
|
||||
|
||||
/**
|
||||
* Explicit per-domain method picks rather than a string allowlist: indexing
|
||||
@@ -22,6 +30,7 @@ const LIVE_PICKS = {
|
||||
listCategories: a.listCategories,
|
||||
}),
|
||||
currency: (a: ApiClient) => ({ listCurrencies: a.listCurrencies, convert: a.convert }),
|
||||
content: (a: ApiClient) => ({ getHomeContent: a.getHomeContent }),
|
||||
cart: (a: ApiClient) => ({
|
||||
getCart: a.getCart,
|
||||
addCartItem: a.addCartItem,
|
||||
@@ -47,8 +56,17 @@ const LIVE_PICKS = {
|
||||
|
||||
const KNOWN_DOMAINS = Object.keys(LIVE_PICKS) as LiveDomain[];
|
||||
|
||||
/** Wave 1: browse and price display come from the backend, everything else stays fixed-data. */
|
||||
const DEFAULT_LIVE_DOMAINS: LiveDomain[] = ["catalog", "currency"];
|
||||
/** Fallback when runtimeConfig supplies no list; keep in step with nuxt.config.ts. */
|
||||
const DEFAULT_LIVE_DOMAINS: LiveDomain[] = [
|
||||
"catalog",
|
||||
"currency",
|
||||
"content",
|
||||
"auth",
|
||||
"cart",
|
||||
"orders",
|
||||
"shipments",
|
||||
"invoices",
|
||||
];
|
||||
|
||||
export default defineNuxtPlugin(() => {
|
||||
const config = useRuntimeConfig();
|
||||
|
||||
Reference in New Issue
Block a user