- Add tailwindcss v4 + @tailwindcss/vite to mall, shop-admin, admin
- Add @vmall/shared/theme.css tokens with html[data-accent] presets
- Add @vmall/ui kit (VBtn/VBadge/VField/VInput/VCard/VPanel/VTable/VPage,
VAccentSwatch, useAccent) as a Nuxt module
- Convert all three apps to kit + utilities; delete ui.css/mall.css and
every <style scoped>; consoles get accent presets, mall locked to red
- Fix VCard boolean prop default (padding) and PDP/store stale
useAsyncData keys on param navigation
- Archive adopt-tailwind-design-system; new frontend-ui capability spec
Wave 6, the last substantive piece of the mock-API migration. Wave 1 removed the
brand facet and the sales/comments sorts for want of a model; sales turn out to
be derivable from order_items and a brand model is a table plus a column.
- a `brands` table with a nullable `products.brand_id` and an ordered admin
replace, mirroring categories and storefront content; a public `GET /api/brands`
and a `brand_id` filter on the catalog, which the search page's facet uses
- `sold_count` per product, computed from `order_items` joined to orders that
reached payment, so an abandoned or cancelled checkout cannot count as a sale.
It is computed per read rather than stored, so it cannot drift from the orders
that produced it
- `sort=sales` alongside `sort=price`; anything else is still a 400
- merchants can set a product's brand through the existing product upsert
- the review UI is gone: the card's review figure and the product detail page's
reviews tab, summary and replies. There is no reviews model, and the mall
attributed invented comments to named shoppers and showed a "good rate". The
now-unreferenced fabrication helpers went with it (`salesOf`, `commentCountOf`,
`commentsFor`, `commentStats`, `salesRankFor`, `productDetail`, `storeDetail`)
Two bugs found by checking rather than trusting: the fixed-data `listProducts`
had silently ignored `brand_id`, `sort` and `order`, so the restored facet
rendered but filtered nothing until the rollback check caught it; and the seed's
brand lookup read back through the shared `r` variable the product loop
reassigns, working once and then throwing.
Verified: 29 backend tests green including a new brand-and-sales case; all three
frontends build; searching filters by brand (24 to 6) and sorts by sales with
counts matching the API; a product page offers detail and after-sale tabs only,
with a real sold count; the fixed-data rollback filters by brand too.
OpenSpec change: openspec/changes/replace-mock-api-wave-6