Files
vmall/openspec/changes/archive/2026-09-17-frontend-apps/agent-shop-admin.md
T

26 lines
3.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Task: build apps/shop-admin (merchant console) — READ openspec/changes/frontend-apps/agent-conventions.md FIRST and follow it exactly. Your app = @vmall/shop-admin, port 3001. Allowed roles: shop_owner, shop_staff.
Replace the placeholder pages. app.vue (sidebar layout) exists — keep it.
## Pages to implement (apps/shop-admin/pages/)
1. `login.vue` — per conventions; only shop_owner/shop_staff may proceed.
2. `index.vue` (auth) — dashboard: stat cards (total products, published count, orders by status counts, pending invoice requests) computed from `$api.shop.listMyProducts({ per_page: 100 })`, `$api.shop.listOrders({ per_page: 100 })`, `$api.shop.listInvoices()`. Also show shop profile from `$api.shop.getMyShop()` (localized name, slug, status).
3. `products/index.vue` (auth) — table: localized name, slug, SKU count, status badge, created; status filter select (all/draft/published/unpublished); actions: edit link, publish (`$api.shop.publish`) / unpublish (`$api.shop.unpublish`) buttons with immediate refetch; "New product" button → products/new. Paging if total > per_page.
4. `products/new.vue` + `products/[id].vue` (auth) — product form (shared component `components/ProductForm.vue`): slug, name EN + ZH, description EN + ZH (textareas), category select (`$api.listCategories()`, optional), images textarea (one URL per line → string[]). Save: create → `$api.shop.createProduct`, edit → `$api.shop.updateProduct`; body type ProductUpsertBody.
On the EDIT page additionally: SKU manager — table of existing SKUs (sku_code, price formatted, currency, stock, active) + form to add/update a SKU (`$api.shop.upsertSku(productId, body)` with SkuUpsertBody): sku_code, price in MAJOR units converted to minor ×100 (currencies USD/CNY/EUR all exponent 2; offer a select of those three), stock integer, active checkbox. After upsert, refetch the product via `$api.shop.getProduct(id)` (exists in the client).
Validation: slug required (lowercase alnum + dash), name EN required; show API errors (e.g. publish without priced SKU → 400) via .error-text.
5. `orders/index.vue` (auth) — `$api.shop.listOrders({ status, page })`: status filter, table (order_no, created, items count, total in order.currency formatted, status badge), row → orders/[id].
6. `orders/[id].vue` (auth) — order items, address, status. Create-shipment card (when status paid or fulfilling): carrier + tracking_no inputs + one qty input per order item (default = unshipped remainder = item.qty sum of that item's qty across this order's shipments from `$api.shop.listShipments()` filtered by order_id); submit → `$api.shop.createShipment(orderId, carrier, trackingNo, items)` with only qty>0 lines. Shipments of this order: table with status + "Mark shipped" button when pending (`$api.shop.markShipped`). Refetch after every action.
7. `shipments.vue` (auth) — `$api.shop.listShipments()`: full table (shipment_no, order_no, carrier, tracking_no, status badge, created) + mark-shipped action.
8. `invoices.vue` (auth) — `$api.shop.listInvoices()`: table (invoice_no or —, order_no, title, kind, tax_no, amount formatted, status badge) + "Issue" button when requested (`$api.shop.issueInvoice`).
## Middleware
middleware/auth.ts per conventions; apply to everything except /login.
## Verify
`pnpm --filter @vmall/shop-admin build` MUST pass. Fix type errors properly (no `any`, no @ts-ignore).
## Report back
Pages built, deviations, contract gaps found.