feat(mall): serve catalog and currency from the live API
Wave 1 of replacing the fixed-data mock adapter. The mall now selects its API adapter per domain, with catalog and currency served live while auth, cart, orders, shipments and invoices stay on fixed data. Backend: - seed the 6 x 2 x 2 category tree as reference data (migration 0005). The API exposes no category write route, so this cannot come from the seed script - filter public product listing by the category subtree with a recursive CTE, matching the mock's existing behaviour instead of exact-match - add sort=price with order=asc|desc, validated by hand so an unsupported value returns the project's ApiError 400 shape rather than axum's own rejection Mall: - replace the all-or-nothing mockApi boolean with a liveDomains list composed through a typed per-domain pick map - source home floors, the category menu, search and product detail from the catalog API; banners, promos, quick links, store card and comment/coupon content stay local display-only content - drop the brand facet and the sales/comments sorts: no backend model backs them - fix salesOf/commentCountOf, which parsed digits out of the product id and so rendered "NaN sold" for live UUID ids; they now hash the id Seed: 24 products across 4 shops, idempotent on re-run. Note: the mall defaults to a live catalog, so pnpm dev:mall now expects the API to be running; set NUXT_PUBLIC_LIVE_DOMAINS to an empty array for all-mock work. OpenSpec change: openspec/changes/replace-mock-api-wave-1
This commit is contained in:
@@ -1,18 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
import { topCategories, childCategories } from "~/mock/data";
|
||||
import type { Category } from "@vmall/shared";
|
||||
import { t as pick } from "@vmall/shared";
|
||||
|
||||
const props = withDefaults(defineProps<{ pinned?: boolean }>(), { pinned: false });
|
||||
|
||||
const { locale } = useI18n();
|
||||
const { $api } = useNuxtApp();
|
||||
const open = ref(false);
|
||||
const hoverId = ref<string | null>(null);
|
||||
|
||||
// The catalog domain serves this live in Wave 1; the fixed-data adapter answers
|
||||
// the same call when it is rolled back. Errors are captured rather than thrown,
|
||||
// so a backend outage leaves the menu empty instead of breaking the shell.
|
||||
const { data: categories } = await useAsyncData("shell-categories", () => $api.listCategories());
|
||||
|
||||
const childrenOf = (parentId: string): Category[] =>
|
||||
(categories.value ?? [])
|
||||
.filter((category) => category.parent_id === parentId)
|
||||
.sort((a, b) => a.position - b.position);
|
||||
|
||||
const cats = computed(() =>
|
||||
topCategories().map((c) => ({
|
||||
cat: c,
|
||||
children: childCategories(c.id).map((cc) => ({ cat: cc, grandchildren: childCategories(cc.id) })),
|
||||
})),
|
||||
(categories.value ?? [])
|
||||
.filter((category) => category.parent_id === null)
|
||||
.sort((a, b) => a.position - b.position)
|
||||
.map((cat) => ({
|
||||
cat,
|
||||
children: childrenOf(cat.id).map((cc) => ({ cat: cc, grandchildren: childrenOf(cc.id) })),
|
||||
})),
|
||||
);
|
||||
|
||||
const active = computed(() => cats.value.find((g) => g.cat.id === hoverId.value) ?? null);
|
||||
|
||||
Reference in New Issue
Block a user