docs: add stage 1.8 shadcn ui standardization plan
This commit is contained in:
@@ -0,0 +1,267 @@
|
|||||||
|
# Stage 1.8 Shadcn Admin UI Standardization 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:** Rebuild the frontend UI foundation around shadcn/ui so future pages inherit a consistent enterprise admin layout, mobile navigation, form standard, select composition, and visual baseline.
|
||||||
|
|
||||||
|
**Architecture:** shadcn/ui remains the source component library. Project `App*` components become thin composition wrappers around shadcn primitives, while feature pages consume those wrappers instead of hand-rolled layout. The first implementation batch fixes shell navigation, field primitives, select composition, and high-frequency forms.
|
||||||
|
|
||||||
|
**Tech Stack:** React 19, Vite, TypeScript, Tailwind v3, shadcn/ui `radix-nova`, Radix primitives, lucide-react.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
- Modify: `frontend/src/app/layout/AppShell.tsx` - passes mobile menu state into topbar/sidebar sheet.
|
||||||
|
- Modify: `frontend/src/components/app/AppSidebar.tsx` - extracts reusable navigation content and adds sheet-friendly rendering.
|
||||||
|
- Modify: `frontend/src/components/app/AppTopbar.tsx` - adds mobile menu button and keeps desktop actions.
|
||||||
|
- Create: `frontend/src/components/ui/field.tsx` - shadcn field primitive from CLI, merged without losing local component changes.
|
||||||
|
- Modify: `frontend/src/components/app/AppField.tsx` - thin wrapper over shadcn field primitives.
|
||||||
|
- Modify: `frontend/src/features/**` select call sites - wrap `SelectItem` with `SelectGroup`.
|
||||||
|
- Modify: `frontend/src/features/auth/LoginPage.tsx` - migrate login form fields.
|
||||||
|
- Modify: `frontend/src/features/tickets/TicketCreatePage.tsx` - migrate main create form fields.
|
||||||
|
- Modify: `frontend/src/features/solutions/SolutionCreatePage.tsx` - migrate solution form fields.
|
||||||
|
- Modify: `frontend/src/features/system/SystemUsersPage.tsx`, `SystemRolesPage.tsx`, `SystemUserGroupsPage.tsx` - migrate quick-create forms where practical.
|
||||||
|
- Test/verify: `npm run typecheck`, `npm run build`, grep scans, desktop/mobile browser checks.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Mobile Shell Navigation
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `frontend/src/app/layout/AppShell.tsx`
|
||||||
|
- Modify: `frontend/src/components/app/AppSidebar.tsx`
|
||||||
|
- Modify: `frontend/src/components/app/AppTopbar.tsx`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Inspect current shell boundaries**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-Content frontend/src/app/layout/AppShell.tsx
|
||||||
|
Get-Content frontend/src/components/app/AppSidebar.tsx
|
||||||
|
Get-Content frontend/src/components/app/AppTopbar.tsx
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: `AppShell` renders fixed sidebar and topbar; sidebar is desktop-only.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Add mobile menu state in shell**
|
||||||
|
|
||||||
|
Implement `mobileNavOpen` state in `AppShell`, pass `onMobileMenuClick` into topbar, and pass `mobileOpen/onMobileOpenChange` into sidebar.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Extract sidebar navigation content**
|
||||||
|
|
||||||
|
In `AppSidebar`, extract grouped nav rendering into `SidebarNavContent({ onNavigate })`. Use the same component for desktop and mobile sheet.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Render mobile Sheet**
|
||||||
|
|
||||||
|
Use existing shadcn `Sheet`, `SheetContent`, `SheetHeader`, and `SheetTitle`. The sheet title must be present for accessibility. Menu item clicks call `onNavigate`.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Add topbar mobile trigger**
|
||||||
|
|
||||||
|
Add a `Button size="icon" variant="ghost"` visible below `lg`, with `Menu` icon using `data-icon` and `aria-label="打开导航"`.
|
||||||
|
|
||||||
|
- [ ] **Step 6: Verify**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
npm run typecheck
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: exit code 0.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2: Install and Merge Shadcn Field Primitive
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `frontend/src/components/ui/field.tsx`
|
||||||
|
- May modify: `frontend/src/components/ui/label.tsx`
|
||||||
|
- May modify: `frontend/src/components/ui/separator.tsx`
|
||||||
|
- Modify: `frontend/src/components/app/AppField.tsx`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Preview CLI changes**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$env:NODE_USE_ENV_PROXY = "1"; $env:HTTP_PROXY = "http://127.0.0.1:7897"; $env:HTTPS_PROXY = "http://127.0.0.1:7897"; cd frontend; npx shadcn@latest add field --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: creates `field.tsx`, previews updates to `label.tsx` and `separator.tsx`.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Inspect diffs before applying**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$env:NODE_USE_ENV_PROXY = "1"; $env:HTTP_PROXY = "http://127.0.0.1:7897"; $env:HTTPS_PROXY = "http://127.0.0.1:7897"; cd frontend; npx shadcn@latest add field --diff field.tsx
|
||||||
|
$env:NODE_USE_ENV_PROXY = "1"; $env:HTTP_PROXY = "http://127.0.0.1:7897"; $env:HTTPS_PROXY = "http://127.0.0.1:7897"; cd frontend; npx shadcn@latest add field --diff label.tsx
|
||||||
|
$env:NODE_USE_ENV_PROXY = "1"; $env:HTTP_PROXY = "http://127.0.0.1:7897"; $env:HTTPS_PROXY = "http://127.0.0.1:7897"; cd frontend; npx shadcn@latest add field --diff separator.tsx
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: understand local changes before applying.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Add the field component**
|
||||||
|
|
||||||
|
Apply the CLI result, then inspect changed files. If `label.tsx` or `separator.tsx` overwrite local customizations, restore needed local behavior manually.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Rebuild AppField**
|
||||||
|
|
||||||
|
Make `AppField` render shadcn `Field`, `FieldLabel`, and optional `FieldDescription`. Support `htmlFor`, `required`, `invalid`, and `description` props.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Verify**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
npm run typecheck
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: exit code 0.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3: Select Composition Sweep
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `frontend/src/features/tickets/TicketFilterBar.tsx`
|
||||||
|
- Modify: `frontend/src/features/tickets/TicketCreatePage.tsx`
|
||||||
|
- Modify: `frontend/src/features/tickets/TicketComments.tsx`
|
||||||
|
- Modify: `frontend/src/features/solutions/SolutionListPage.tsx`
|
||||||
|
- Modify: `frontend/src/features/solutions/SolutionCreatePage.tsx`
|
||||||
|
- Modify: `frontend/src/features/design-system/DesignSystemPreview.tsx`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Add SelectGroup imports**
|
||||||
|
|
||||||
|
For each file importing select primitives, add `SelectGroup`.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Wrap select items**
|
||||||
|
|
||||||
|
Change:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="all">全部状态</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
```
|
||||||
|
|
||||||
|
to:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<SelectContent>
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectItem value="all">全部状态</SelectItem>
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Scan**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
rg "SelectContent>[\\s\\S]*SelectItem|<SelectContent>\\s*<SelectItem" frontend/src -n
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: no direct `SelectItem` immediately under `SelectContent`.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Verify**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
npm run typecheck
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: exit code 0.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 4: High-Frequency Form Migration
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `frontend/src/features/auth/LoginPage.tsx`
|
||||||
|
- Modify: `frontend/src/features/tickets/TicketCreatePage.tsx`
|
||||||
|
- Modify: `frontend/src/features/solutions/SolutionCreatePage.tsx`
|
||||||
|
- Modify: `frontend/src/features/system/SystemUsersPage.tsx`
|
||||||
|
- Modify: `frontend/src/features/system/SystemRolesPage.tsx`
|
||||||
|
- Modify: `frontend/src/features/system/SystemUserGroupsPage.tsx`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Migrate login fields**
|
||||||
|
|
||||||
|
Use `AppField htmlFor="username"` and `AppField htmlFor="password"`. Inputs receive matching `id` and `aria-invalid={Boolean(error)}` only where field-specific invalid state is needed.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Migrate create ticket fields**
|
||||||
|
|
||||||
|
Use `AppField` for title, priority, impact scope, related system, environment, and description. Required fields use `required`.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Migrate create solution fields**
|
||||||
|
|
||||||
|
Use `AppField` for title, status, related system, environment scope, problem signature, root cause, solution, and risk note.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Migrate quick-create system forms**
|
||||||
|
|
||||||
|
Keep compact horizontal layout, but each input lives inside `AppField` so labels remain visible and shadcn field semantics are consistent.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Verify**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
npm run typecheck
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: both exit code 0.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 5: Full UI Verification and Commit
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- All modified frontend files
|
||||||
|
|
||||||
|
- [ ] **Step 1: Run shadcn context check**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$env:NODE_USE_ENV_PROXY = "1"; $env:HTTP_PROXY = "http://127.0.0.1:7897"; $env:HTTPS_PROXY = "http://127.0.0.1:7897"; cd frontend; npx shadcn@latest info
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: current project remains `Vite`, `Tailwind v3`, `radix-nova`, `lucide`.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Run source scans**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
rg "space-[xy]-|<SelectContent>\\s*<SelectItem|className=.*size-4" frontend/src/features frontend/src/components/app -n
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: no new violations in migrated surfaces. Remaining non-button decorative icon sizes are acceptable only if not inside shadcn components.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Browser checks**
|
||||||
|
|
||||||
|
Check desktop and mobile widths for:
|
||||||
|
- Login
|
||||||
|
- Workbench
|
||||||
|
- Ticket list
|
||||||
|
- Create ticket
|
||||||
|
- System users
|
||||||
|
|
||||||
|
Expected: mobile has navigation entry, no horizontal scroll, forms have visible labels.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git add frontend/src docs/superpowers/specs/2026-07-02-stage-1-8-shadcn-admin-ui-standardization-design.md docs/superpowers/plans/2026-07-02-stage-1-8-shadcn-admin-ui-standardization.md
|
||||||
|
git commit -m "feat: standardize shadcn admin ui foundation"
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: commit created on current branch.
|
||||||
|
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# Stage 1.8 Shadcn Admin UI Standardization Design
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Stage 1.8 upgrades the frontend from page-by-page polish into a stable enterprise admin UI foundation. All website UI must use shadcn/ui as the unified component library and visual source of truth.
|
||||||
|
|
||||||
|
## Principles
|
||||||
|
|
||||||
|
- Use shadcn/ui first. Business pages compose `@/components/ui/*` and the project App component layer; no second UI library.
|
||||||
|
- Keep frontend display text Chinese, concise, and operational.
|
||||||
|
- Use shadcn semantic tokens for color: `background`, `foreground`, `card`, `muted`, `primary`, `secondary`, `border`, `ring`, `destructive`.
|
||||||
|
- Do not introduce a separate theme system. `theme-factory` is used only as a quality checklist for contrast, responsive behavior, spacing, and perceived polish.
|
||||||
|
- Prefer dense but calm enterprise admin screens over marketing layouts, oversized hero blocks, decorative gradients, or AI-purple visual identity.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
### P1 Mobile Navigation
|
||||||
|
|
||||||
|
The current desktop sidebar is hidden on small screens, while the shell only reserves desktop left padding. Stage 1.8 adds a mobile navigation entry in the top bar and renders the same menu tree inside a shadcn `Sheet`.
|
||||||
|
|
||||||
|
Expected behavior:
|
||||||
|
- Desktop keeps fixed sidebar.
|
||||||
|
- Mobile shows a menu button in the top bar.
|
||||||
|
- Tapping the button opens a side sheet with the same grouped navigation.
|
||||||
|
- Tapping a menu item closes the sheet.
|
||||||
|
- Logged-in users can navigate to every visible enabled module on mobile.
|
||||||
|
|
||||||
|
### P2 Form Standardization
|
||||||
|
|
||||||
|
Forms must migrate toward shadcn `FieldGroup`, `Field`, `FieldLabel`, and `FieldDescription` primitives. The project `AppField` remains only as a thin wrapper around shadcn field primitives, not as a custom label/grid replacement.
|
||||||
|
|
||||||
|
Expected behavior:
|
||||||
|
- Required fields have visible labels.
|
||||||
|
- Invalid fields can express `data-invalid` and `aria-invalid`.
|
||||||
|
- Form layout uses `FieldGroup` and `Field` instead of raw `div` stacks.
|
||||||
|
- Login, create ticket, create solution, and system management quick-create forms become the first migrated surfaces.
|
||||||
|
|
||||||
|
### P2 Select Composition
|
||||||
|
|
||||||
|
Every shadcn select menu must use `SelectContent > SelectGroup > SelectItem`. Direct `SelectItem` children under `SelectContent` are not allowed.
|
||||||
|
|
||||||
|
First migrated surfaces:
|
||||||
|
- Ticket filter bar
|
||||||
|
- Ticket create priority
|
||||||
|
- Ticket comments visibility
|
||||||
|
- Solution list status filter
|
||||||
|
- Solution create status
|
||||||
|
- Design system preview select
|
||||||
|
|
||||||
|
### P3 Brand and Admin Visual Standard
|
||||||
|
|
||||||
|
Brand identity should come from consistent admin structure, not decorative theme divergence.
|
||||||
|
|
||||||
|
Visual baseline:
|
||||||
|
- Platform mark: compact text mark for `AI 工单平台`.
|
||||||
|
- Sidebar: grouped navigation, clear active state, consistent icon scale.
|
||||||
|
- Topbar: mobile menu, workspace breadcrumb, environment badge, user actions.
|
||||||
|
- Content: consistent page width, gutters, card/table density, and section spacing.
|
||||||
|
- No decorative gradients, emoji icons, or raw color palettes in business pages.
|
||||||
|
|
||||||
|
## Migration Order
|
||||||
|
|
||||||
|
1. Shell and navigation foundation.
|
||||||
|
2. shadcn field component integration.
|
||||||
|
3. Select composition sweep.
|
||||||
|
4. High-frequency forms migration.
|
||||||
|
5. Page visual pass for login, workbench, tickets, solutions, and system governance.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `npm run typecheck`
|
||||||
|
- `npm run build`
|
||||||
|
- `npx shadcn@latest info` with local proxy env
|
||||||
|
- Source scan for direct `SelectContent > SelectItem` patterns
|
||||||
|
- Source scan for form pages still using placeholder-only labels
|
||||||
|
- Browser checks at desktop width and mobile width for login, dashboard, tickets, create ticket, and system users
|
||||||
|
|
||||||
Reference in New Issue
Block a user