# Task: build apps/admin (platform console) — READ openspec/changes/frontend-apps/agent-conventions.md FIRST and follow it exactly. Your app = @vmall/admin, port 3002. Allowed roles: platform_admin. Replace the placeholder pages. app.vue (sidebar layout) exists — keep it. ## Pages to implement (apps/admin/pages/) 1. `login.vue` — per conventions; only platform_admin may proceed. (Seed account: admin@vmall.local / admin1234 — display this hint on the login page in a muted line for the dev MVP.) 2. `index.vue` (auth) — dashboard: stat cards (total users, shops, orders, enabled currencies) from `$api.admin.listUsers()`, `$api.admin.listShops()`, `$api.admin.listOrders()`, `$api.admin.listCurrencies()`. 3. `users.vue` (auth) — `$api.admin.listUsers(page)` table: email, display_name, role badge, assigned shop (resolve shop_id → shop name via `$api.admin.listShops()`, localized), created. Role editor per row: role select (platform_admin/shop_owner/shop_staff/customer) + shop select shown ONLY for shop roles + save button → `$api.admin.setUserRole(id, role, shopId|null)`; on validation error (e.g. shop role without shop → 400) show message. Paging. 4. `shops.vue` (auth) — `$api.admin.listShops()` table: localized name, slug, status badge, created; actions suspend/activate → `$api.admin.setShopStatus`. Create-shop card: name EN, name ZH, slug → `$api.admin.createShop({en, zh}, slug)`; refetch; show conflict error (duplicate slug → 409). 5. `orders.vue` (auth) — `$api.admin.listOrders(page)`: table (order_no, shop (resolve id→name), user_id short, total formatted in order.currency, status badge, created). Paging. 6. `currencies.vue` (auth) — `$api.admin.listCurrencies()` table: code, localized name, symbol, exponent, rate_to_base, base badge, enabled badge. Per-row rate editor (number input + save → `$api.admin.setRate(code, rate)`). New-currency card: code (3 letters), name EN + ZH, symbol, exponent (0–6), rate_to_base, enabled checkbox → `$api.admin.upsertCurrency({...})` with CurrencyUpsertBody (rate_to_base is a STRING). Toggle enabled via upsert with the row's other fields preserved. ## Middleware middleware/auth.ts per conventions; apply to everything except /login. ## Verify `pnpm --filter @vmall/admin build` MUST pass. Fix type errors properly (no `any`, no @ts-ignore). ## Report back Pages built, deviations, contract gaps found.