Files
auto-virtual-tryon-frontend/docs/superpowers/plans/2026-03-27-auto-virtual-tryon-admin-frontend.md

20 KiB

Auto Virtual Tryon Admin Frontend Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Build a standalone Next.js admin console with a shared dashboard shell, a BFF data layer, four real-integration pages backed by auto-virtual-tryon, and honest placeholder modules for unsupported backend capabilities.

Architecture: Manually scaffold a Next.js App Router app in this existing non-empty directory instead of using create-next-app. Route all UI data access through a Next.js BFF layer in app/api/*, with adapters normalizing both proxied FastAPI responses and mock placeholder data into stable frontend view models. Keep operational pages focused: submit, review, order detail, and workflow detail do the real work; home, library, login, and settings pages preserve information architecture without pretending backend capabilities already exist.

Tech Stack: Next.js App Router, React, TypeScript, Tailwind CSS, Zod, Vitest, React Testing Library, jsdom, Lucide React


Task 1: Bootstrap The Next.js Workspace

Files:

  • Create: package.json

  • Create: tsconfig.json

  • Create: next.config.ts

  • Create: next-env.d.ts

  • Create: postcss.config.mjs

  • Create: eslint.config.mjs

  • Create: vitest.config.ts

  • Create: vitest.setup.ts

  • Create: app/layout.tsx

  • Create: app/page.tsx

  • Create: app/globals.css

  • Create: app/(dashboard)/layout.tsx

  • Create: src/components/layout/dashboard-shell.tsx

  • Create: src/components/layout/nav-config.ts

  • Test: tests/ui/dashboard-shell.test.tsx

  • Step 1: Create the package and toolchain files for a manual scaffold

{
  "name": "auto-virtual-tryon-frontend",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint .",
    "test": "vitest"
  }
}
  • Step 2: Install runtime and test dependencies

Run: npm install next@latest react@latest react-dom@latest zod clsx tailwind-merge lucide-react && npm install -D typescript @types/node @types/react @types/react-dom eslint eslint-config-next postcss tailwindcss @tailwindcss/postcss vitest vite @vitejs/plugin-react vite-tsconfig-paths jsdom @testing-library/react @testing-library/dom @testing-library/jest-dom Expected: install completes with added packages and no missing peer dependency errors

  • Step 3: Write the first failing shell smoke test
import { render, screen } from "@testing-library/react";
import { DashboardShell } from "@/components/layout/dashboard-shell";

test("renders the primary dashboard navigation", () => {
  render(<DashboardShell>content</DashboardShell>);

  expect(screen.getByText("订单总览")).toBeInTheDocument();
  expect(screen.getByText("提单工作台")).toBeInTheDocument();
  expect(screen.getByText("审核工作台")).toBeInTheDocument();
});
  • Step 4: Run the shell smoke test to verify it fails

Run: npm run test -- tests/ui/dashboard-shell.test.tsx Expected: FAIL with a module-not-found error for @/components/layout/dashboard-shell or a missing test environment configuration

  • Step 5: Add the minimal app shell and root redirect
// app/page.tsx
import { redirect } from "next/navigation";

export default function HomePage() {
  redirect("/orders");
}
  • Step 6: Re-run the shell smoke test to verify it passes

Run: npm run test -- tests/ui/dashboard-shell.test.tsx Expected: PASS

  • Step 7: Start the dev server for a compile smoke check

Run: npm run dev Expected: Next.js starts and serves http://localhost:3000

  • Step 8: Commit the bootstrap if this directory is a git repo
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  git add package.json tsconfig.json next.config.ts next-env.d.ts postcss.config.mjs eslint.config.mjs vitest.config.ts vitest.setup.ts app src tests
  git commit -m "feat: bootstrap next admin frontend"
fi

Task 2: Define Shared Types, Status Maps, And Adapters

Files:

  • Create: src/lib/types/backend.ts

  • Create: src/lib/types/view-models.ts

  • Create: src/lib/types/status.ts

  • Create: src/lib/adapters/orders.ts

  • Create: src/lib/adapters/reviews.ts

  • Create: src/lib/adapters/workflows.ts

  • Create: src/lib/mock/orders.ts

  • Create: src/lib/mock/libraries.ts

  • Create: src/lib/mock/workflows.ts

  • Test: tests/lib/adapters/orders.test.ts

  • Test: tests/lib/adapters/reviews.test.ts

  • Test: tests/lib/adapters/workflows.test.ts

  • Step 1: Write failing adapter tests for business-empty and mock states

import { adaptOrderDetail } from "@/lib/adapters/orders";

test("marks mock asset uris as mock previews", () => {
  const viewModel = adaptOrderDetail({
    order_id: 1,
    final_asset: { id: 10, uri: "mock://result-10", asset_type: "image", step_name: "export", metadata_json: null, created_at: "2026-03-27T00:00:00Z", order_id: 1, parent_asset_id: null, root_asset_id: null, version_no: 1, is_current_version: true },
  } as any);

  expect(viewModel.finalAsset?.isMock).toBe(true);
});
  • Step 2: Run the adapter tests to verify they fail

Run: npm run test -- tests/lib/adapters/orders.test.ts tests/lib/adapters/reviews.test.ts tests/lib/adapters/workflows.test.ts Expected: FAIL with missing adapter module exports

  • Step 3: Implement backend DTO types, frontend view models, and adapter functions
export type AssetViewModel = {
  id: number;
  uri: string;
  label: string;
  isMock: boolean;
};
  • Step 4: Add shared status labels and badge variants
export const ORDER_STATUS_META = {
  created: { label: "已创建", tone: "neutral" },
  running: { label: "处理中", tone: "info" },
  waiting_review: { label: "待审核", tone: "warning" },
  succeeded: { label: "已完成", tone: "success" },
  failed: { label: "失败", tone: "danger" },
} as const;
  • Step 5: Re-run the adapter tests to verify they pass

Run: npm run test -- tests/lib/adapters/orders.test.ts tests/lib/adapters/reviews.test.ts tests/lib/adapters/workflows.test.ts Expected: PASS

  • Step 6: Commit the shared model layer if git is available
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  git add src/lib tests/lib
  git commit -m "feat: add frontend view models and adapters"
fi

Task 3: Build The BFF Layer For Real And Placeholder Data

Files:

  • Create: src/lib/env.ts

  • Create: src/lib/http/backend-client.ts

  • Create: src/lib/http/response.ts

  • Create: src/lib/validation/create-order.ts

  • Create: src/lib/validation/review-action.ts

  • Create: app/api/orders/route.ts

  • Create: app/api/orders/[orderId]/route.ts

  • Create: app/api/orders/[orderId]/assets/route.ts

  • Create: app/api/reviews/pending/route.ts

  • Create: app/api/reviews/[orderId]/submit/route.ts

  • Create: app/api/workflows/[orderId]/route.ts

  • Create: app/api/dashboard/orders-overview/route.ts

  • Create: app/api/dashboard/workflow-lookup/route.ts

  • Create: app/api/libraries/[libraryType]/route.ts

  • Test: tests/app/api/orders-create.route.test.ts

  • Test: tests/app/api/reviews-pending.route.test.ts

  • Test: tests/app/api/libraries.route.test.ts

  • Step 1: Write failing BFF tests for one proxied endpoint and one mock endpoint

import { GET } from "@/app/api/libraries/[libraryType]/route";

test("returns mock library data for unsupported backend modules", async () => {
  const response = await GET(new Request("http://localhost/api/libraries/models"), {
    params: Promise.resolve({ libraryType: "models" }),
  } as any);

  expect(response.status).toBe(200);
});
  • Step 2: Run the BFF tests to verify they fail

Run: npm run test -- tests/app/api/orders-create.route.test.ts tests/app/api/reviews-pending.route.test.ts tests/app/api/libraries.route.test.ts Expected: FAIL with missing route handler modules

  • Step 3: Implement the backend fetch helper and environment parsing
export function getBackendBaseUrl() {
  return process.env.BACKEND_BASE_URL ?? "http://127.0.0.1:8000/api/v1";
}
  • Step 4: Implement the proxied route handlers and placeholder route handlers
return NextResponse.json({
  mode: "placeholder",
  items: MODEL_LIBRARY_FIXTURES,
});
  • Step 5: Normalize transport, validation, and system errors in one helper
return NextResponse.json(
  { error: "BACKEND_UNAVAILABLE", message: "后端暂时不可用,请稍后重试。" },
  { status: 502 },
);
  • Step 6: Re-run the BFF tests to verify they pass

Run: npm run test -- tests/app/api/orders-create.route.test.ts tests/app/api/reviews-pending.route.test.ts tests/app/api/libraries.route.test.ts Expected: PASS

  • Step 7: Commit the BFF layer if git is available
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  git add app/api src/lib/http src/lib/env.ts src/lib/validation tests/app/api
  git commit -m "feat: add bff routes for proxy and placeholder data"
fi

Task 4: Implement The Shared Dashboard UI System

Files:

  • Create: src/components/ui/button.tsx

  • Create: src/components/ui/card.tsx

  • Create: src/components/ui/status-badge.tsx

  • Create: src/components/ui/empty-state.tsx

  • Create: src/components/ui/page-header.tsx

  • Create: src/components/ui/section-title.tsx

  • Modify: app/globals.css

  • Modify: src/components/layout/dashboard-shell.tsx

  • Test: tests/ui/status-badge.test.tsx

  • Test: tests/ui/dashboard-shell.test.tsx

  • Step 1: Write a failing test for status badge labels and tones

import { render, screen } from "@testing-library/react";
import { StatusBadge } from "@/components/ui/status-badge";

test("renders the waiting review label", () => {
  render(<StatusBadge status="waiting_review" />);
  expect(screen.getByText("待审核")).toBeInTheDocument();
});
  • Step 2: Run the UI primitive tests to verify they fail

Run: npm run test -- tests/ui/status-badge.test.tsx tests/ui/dashboard-shell.test.tsx Expected: FAIL with missing StatusBadge export

  • Step 3: Implement the warm-console theme tokens and UI primitives
:root {
  --bg-canvas: #f6f1e8;
  --bg-surface: #fffdf8;
  --ink-strong: #23303a;
  --accent-primary: #6e7f52;
}
  • Step 4: Update the dashboard shell to use the approved navigation, type hierarchy, and side rail
<aside className="bg-[var(--ink-strong)] text-stone-100">
  {/* primary navigation */}
</aside>
  • Step 5: Re-run the UI primitive tests to verify they pass

Run: npm run test -- tests/ui/status-badge.test.tsx tests/ui/dashboard-shell.test.tsx Expected: PASS

  • Step 6: Commit the shared UI system if git is available
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  git add app/globals.css src/components tests/ui
  git commit -m "feat: add dashboard shell and shared ui primitives"
fi

Task 5: Implement The Submit Workbench

Files:

  • Create: src/features/orders/components/create-order-form.tsx

  • Create: src/features/orders/components/resource-picker-card.tsx

  • Create: src/features/orders/components/order-summary-card.tsx

  • Create: src/features/orders/submit-workbench.tsx

  • Modify: app/(dashboard)/submit-workbench/page.tsx

  • Test: tests/features/orders/submit-workbench.test.tsx

  • Step 1: Write a failing test for the customer-level/service-mode constraint

test("forces low customers to use auto_basic", async () => {
  render(<SubmitWorkbench />);

  await user.selectOptions(screen.getByLabelText("客户层级"), "low");

  expect(screen.getByDisplayValue("auto_basic")).toBeInTheDocument();
});
  • Step 2: Run the submit workbench test to verify it fails

Run: npm run test -- tests/features/orders/submit-workbench.test.tsx Expected: FAIL with missing SubmitWorkbench component

  • Step 3: Implement the mock-backed selectors, validation, and summary card
if (customerLevel === "low") {
  form.service_mode = "auto_basic";
}
  • Step 4: Add the submit action with loading, success, and inline error states
const [isPending, startTransition] = useTransition();
  • Step 5: Re-run the submit workbench test to verify it passes

Run: npm run test -- tests/features/orders/submit-workbench.test.tsx Expected: PASS

  • Step 6: Manually verify the happy path in the browser against a running backend

Run: npm run dev Expected: a created order redirects to /orders/<id> and shows the returned workflow_id

  • Step 7: Commit the submit workbench if git is available
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  git add app/(dashboard)/submit-workbench src/features/orders tests/features/orders
  git commit -m "feat: implement submit workbench"
fi

Task 6: Implement The Review Workbench

Files:

  • Create: src/features/reviews/components/review-queue.tsx

  • Create: src/features/reviews/components/review-image-panel.tsx

  • Create: src/features/reviews/components/review-action-panel.tsx

  • Create: src/features/reviews/components/review-workflow-summary.tsx

  • Create: src/features/reviews/review-workbench.tsx

  • Modify: app/(dashboard)/reviews/workbench/page.tsx

  • Test: tests/features/reviews/review-workbench.test.tsx

  • Step 1: Write a failing test that rerun_face requires a comment

test("requires a comment before rerun_face submission", async () => {
  render(<ReviewWorkbench />);

  await user.click(screen.getByRole("button", { name: "重跑 Face" }));

  expect(screen.getByText("请填写审核备注")).toBeInTheDocument();
});
  • Step 2: Run the review workbench test to verify it fails

Run: npm run test -- tests/features/reviews/review-workbench.test.tsx Expected: FAIL with missing ReviewWorkbench component

  • Step 3: Implement the queue, current-order selection, and image inspection layout
const [selectedOrderId, setSelectedOrderId] = useState<number | null>(null);
  • Step 4: Implement approve and rerun actions with non-optimistic queue refresh
await submitReview(payload);
await refreshQueue();
  • Step 5: Re-run the review workbench test to verify it passes

Run: npm run test -- tests/features/reviews/review-workbench.test.tsx Expected: PASS

  • Step 6: Manually verify review actions against a running backend

Run: npm run dev Expected: pending items remain visible until the refreshed backend response says otherwise

  • Step 7: Commit the review workbench if git is available
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  git add app/(dashboard)/reviews src/features/reviews tests/features/reviews
  git commit -m "feat: implement review workbench"
fi

Task 7: Implement Order Detail And Workflow Detail

Files:

  • Create: src/features/orders/components/order-detail-header.tsx

  • Create: src/features/orders/components/order-assets-panel.tsx

  • Create: src/features/orders/components/order-workflow-card.tsx

  • Create: src/features/orders/order-detail.tsx

  • Create: src/features/workflows/components/workflow-status-card.tsx

  • Create: src/features/workflows/components/workflow-timeline.tsx

  • Create: src/features/workflows/workflow-detail.tsx

  • Modify: app/(dashboard)/orders/[orderId]/page.tsx

  • Modify: app/(dashboard)/workflows/[orderId]/page.tsx

  • Test: tests/features/orders/order-detail.test.tsx

  • Test: tests/features/workflows/workflow-detail.test.tsx

  • Step 1: Write failing tests for a mock asset banner and an empty final-result state

test("shows a mock asset banner when the final asset uses a mock uri", () => {
  render(<OrderDetail viewModel={mockOrderDetailVm} />);

  expect(screen.getByText("当前资产来自 mock 流程")).toBeInTheDocument();
});
  • Step 2: Run the detail page tests to verify they fail

Run: npm run test -- tests/features/orders/order-detail.test.tsx tests/features/workflows/workflow-detail.test.tsx Expected: FAIL with missing detail components

  • Step 3: Implement the order detail page with business-empty handling
{!viewModel.finalAsset ? <EmptyState title="最终图暂未生成" /> : <FinalAssetCard asset={viewModel.finalAsset} />}
  • Step 4: Implement the workflow detail page with step timeline and failure emphasis
<ol>{viewModel.steps.map((step) => <WorkflowStepRow key={step.name} step={step} />)}</ol>
  • Step 5: Re-run the detail page tests to verify they pass

Run: npm run test -- tests/features/orders/order-detail.test.tsx tests/features/workflows/workflow-detail.test.tsx Expected: PASS

  • Step 6: Commit the detail pages if git is available
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  git add app/(dashboard)/orders app/(dashboard)/workflows src/features/orders src/features/workflows tests/features/orders tests/features/workflows
  git commit -m "feat: implement order and workflow detail pages"
fi

Task 8: Implement Placeholder Home, Workflow Lookup, Libraries, Login, And Settings

Files:

  • Create: src/features/orders/orders-home.tsx

  • Create: src/features/workflows/workflow-lookup.tsx

  • Create: src/features/libraries/library-page.tsx

  • Create: src/features/settings/settings-placeholder.tsx

  • Create: src/features/auth/login-placeholder.tsx

  • Modify: app/(dashboard)/orders/page.tsx

  • Modify: app/(dashboard)/workflows/page.tsx

  • Create: app/(dashboard)/libraries/models/page.tsx

  • Create: app/(dashboard)/libraries/scenes/page.tsx

  • Create: app/(dashboard)/libraries/garments/page.tsx

  • Create: app/(dashboard)/settings/page.tsx

  • Create: app/login/page.tsx

  • Test: tests/features/orders/orders-home.test.tsx

  • Test: tests/features/libraries/library-page.test.tsx

  • Step 1: Write failing tests for the honest placeholder messaging

test("explains that the orders list depends on a future backend api", () => {
  render(<OrdersHome />);

  expect(screen.getByText("当前未接入真实订单列表接口")).toBeInTheDocument();
});
  • Step 2: Run the placeholder page tests to verify they fail

Run: npm run test -- tests/features/orders/orders-home.test.tsx tests/features/libraries/library-page.test.tsx Expected: FAIL with missing placeholder components

  • Step 3: Implement the /orders home page with direct lookup, recent visits, and transition copy
<EmptyState title="当前未接入真实订单列表接口" description="可通过订单号直达详情,或从最近访问记录继续处理。" />
  • Step 4: Implement the workflow lookup, library placeholders, settings, and login placeholders
<LibraryPage title="模特库" placeholderMode />
  • Step 5: Re-run the placeholder page tests to verify they pass

Run: npm run test -- tests/features/orders/orders-home.test.tsx tests/features/libraries/library-page.test.tsx Expected: PASS

  • Step 6: Commit the placeholder pages if git is available
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  git add app/(dashboard)/orders app/(dashboard)/workflows app/(dashboard)/libraries app/(dashboard)/settings app/login src/features/orders src/features/workflows src/features/libraries src/features/settings src/features/auth tests/features
  git commit -m "feat: add placeholder dashboard modules"
fi

Task 9: Verify The Full Build And Document Local Setup

Files:

  • Create: README.md

  • Modify: package.json

  • Modify: docs/superpowers/specs/2026-03-27-auto-virtual-tryon-admin-frontend-design.md

  • Step 1: Add a README with local run instructions and backend environment notes

BACKEND_BASE_URL=http://127.0.0.1:8000/api/v1
npm install
npm run dev
  • Step 2: Run the full automated test suite

Run: npm run test Expected: PASS

  • Step 3: Run lint and production build verification

Run: npm run lint && npm run build Expected: PASS

  • Step 4: Perform a final manual flow check against the FastAPI backend

Run: npm run dev Expected: submit, review, order detail, and workflow detail all work against /Volumes/DockCase/codes/auto-virtual-tryon

  • Step 5: Commit the verified frontend if git is available
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  git add README.md package.json docs/superpowers/specs/2026-03-27-auto-virtual-tryon-admin-frontend-design.md
  git commit -m "docs: add frontend runbook and verification notes"
fi