@potenlab/ui 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +361 -0
  2. package/dist/cli.js +756 -0
  3. package/package.json +8 -5
  4. package/template/admin/README.md +36 -0
  5. package/template/admin/_gitignore +41 -0
  6. package/template/admin/components.json +23 -0
  7. package/template/admin/docs/changes.json +295 -0
  8. package/template/admin/docs/dev-plan.md +822 -0
  9. package/template/admin/docs/frontend-plan.md +874 -0
  10. package/template/admin/docs/prd.md +408 -0
  11. package/template/admin/docs/progress.json +777 -0
  12. package/template/admin/docs/test-plan.md +790 -0
  13. package/template/admin/docs/ui-ux-plan.md +1664 -0
  14. package/template/admin/eslint.config.mjs +18 -0
  15. package/template/admin/next.config.ts +7 -0
  16. package/template/admin/package.json +43 -0
  17. package/template/admin/postcss.config.mjs +7 -0
  18. package/template/admin/public/avatars/user1.svg +4 -0
  19. package/template/admin/public/avatars/user2.svg +4 -0
  20. package/template/admin/public/avatars/user3.svg +4 -0
  21. package/template/admin/public/avatars/user4.svg +4 -0
  22. package/template/admin/public/avatars/user5.svg +4 -0
  23. package/template/admin/public/file.svg +1 -0
  24. package/template/admin/public/globe.svg +1 -0
  25. package/template/admin/public/next.svg +1 -0
  26. package/template/admin/public/profile/img1.svg +7 -0
  27. package/template/admin/public/profile/img2.svg +7 -0
  28. package/template/admin/public/profile/img3.svg +7 -0
  29. package/template/admin/public/vercel.svg +1 -0
  30. package/template/admin/public/window.svg +1 -0
  31. package/template/admin/src/app/favicon.ico +0 -0
  32. package/template/admin/src/app/layout.tsx +38 -0
  33. package/template/admin/src/app/page.tsx +5 -0
  34. package/template/admin/src/app/users/[id]/page.tsx +10 -0
  35. package/template/admin/src/components/layouts/app-sidebar.tsx +152 -0
  36. package/template/admin/src/components/user-management/profile-images.tsx +69 -0
  37. package/template/admin/src/components/user-management/user-detail-form.tsx +143 -0
  38. package/template/admin/src/features/user-management/components/user-columns.tsx +101 -0
  39. package/template/admin/src/features/user-management/components/user-detail.tsx +79 -0
  40. package/template/admin/src/features/user-management/components/user-list.tsx +74 -0
  41. package/template/admin/src/features/user-management/types/index.ts +113 -0
  42. package/template/admin/src/features/user-management/utils/format.ts +2 -0
  43. package/template/admin/src/lib/mock-data.ts +131 -0
  44. package/template/admin/src/styles/globals.css +26 -0
  45. package/template/admin/tsconfig.json +34 -0
@@ -0,0 +1,822 @@
1
+ # Development Plan
2
+
3
+ Single source of truth for the Potenlab Admin User Management UI.
4
+
5
+ - **Source PRD:** `template/docs/prd.md`
6
+ - **Source UI/UX:** `template/docs/ui-ux-plan.md`
7
+ - **Tech Stack:** Next.js 16 (App Router), TypeScript, shadcn/ui, Tailwind CSS 4, Lucide React, Bun
8
+ - **Fonts:** Pretendard Variable, Inter
9
+ - **Platform:** Desktop-only (1920px target)
10
+ - **Backend:** NONE -- all data is static/mock, hardcoded in the frontend
11
+
12
+ ---
13
+
14
+ ## Project Structure (Bulletproof React)
15
+
16
+ ```
17
+ src/
18
+ ├── app/ # Routes, providers, root layout
19
+ │ ├── layout.tsx # Root layout with sidebar + font loading
20
+ │ ├── page.tsx # User Management List (Dashboard) route
21
+ │ └── users/
22
+ │ └── [id]/
23
+ │ └── page.tsx # User Detail route
24
+ ├── components/ # SHARED + STYLED components
25
+ │ ├── ui/ # shadcn/ui base components (auto-generated, then customized)
26
+ │ │ ├── accordion.tsx
27
+ │ │ ├── avatar.tsx
28
+ │ │ ├── badge.tsx
29
+ │ │ ├── button.tsx
30
+ │ │ ├── card.tsx
31
+ │ │ ├── input.tsx
32
+ │ │ ├── label.tsx
33
+ │ │ ├── select.tsx
34
+ │ │ ├── separator.tsx
35
+ │ │ ├── sidebar.tsx
36
+ │ │ ├── switch.tsx
37
+ │ │ ├── table.tsx
38
+ │ │ ├── tabs.tsx
39
+ │ │ └── tooltip.tsx
40
+ │ ├── layouts/ # Page layout wrappers
41
+ │ │ └── content-layout.tsx # Content area wrapper (ml-[324px], padding)
42
+ │ ├── common/ # Generic reusable components
43
+ │ │ └── page-header.tsx # Title + badge + subtitle + action button
44
+ │ └── user-management/ # Feature-specific PRESENTATIONAL components
45
+ │ ├── user-table.tsx # Data table with columns and row rendering
46
+ │ ├── pagination-controls.tsx # Pagination UI above table
47
+ │ ├── search-bar.tsx # Search input for dashboard
48
+ │ ├── tab-navigation.tsx # Tab bar (All, Tab, Tab)
49
+ │ ├── user-detail-form.tsx # Detail page form (Basic Info + Other Settings)
50
+ │ └── profile-images.tsx # 3x profile image display
51
+ ├── features/ # BUSINESS LOGIC only
52
+ │ └── user-management/
53
+ │ ├── types/
54
+ │ │ └── index.ts # User interface, table column types
55
+ │ └── utils/
56
+ │ └── format.ts # Date formatting, number formatting (e.g., "100,000")
57
+ ├── lib/ # Library wrappers, utilities
58
+ │ ├── utils.ts # shadcn/ui cn() utility
59
+ │ └── mock-data.ts # Static mock user data (5 rows)
60
+ ├── types/ # Shared TypeScript types
61
+ │ └── index.ts # Global type definitions
62
+ └── styles/
63
+ └── globals.css # Tailwind CSS 4 + shadcn/ui theme overrides + font imports
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Phase 0: Foundation
69
+
70
+ Project setup, design tokens, fonts, Tailwind config, shadcn/ui initialization.
71
+
72
+ ### P0-T1: Initialize shadcn/ui
73
+
74
+ - **Output:** `src/components/ui/` directory scaffold, `components.json`, `src/lib/utils.ts`
75
+ - **Behavior:** Run `bunx --bun shadcn@latest init` in the template directory. Select "New York" style, CSS variables enabled. This generates the base config and the `cn()` utility.
76
+ - **Verify:**
77
+ - `components.json` exists at project root
78
+ - `src/lib/utils.ts` exists and exports `cn()`
79
+ - `bun run build` completes without errors
80
+
81
+ ### P0-T2: Configure design tokens in globals.css
82
+
83
+ - **Depends on:** P0-T1
84
+ - **Output:** `src/styles/globals.css` (or `src/app/globals.css` depending on shadcn init)
85
+ - **Behavior:** Replace/extend the generated globals.css with the full set of Tailwind CSS 4 `@theme` custom properties and shadcn CSS variable overrides from the ui-ux-plan. Include:
86
+ - `@theme` block with all custom colors: `--color-primary: #509594`, `--color-primary-hover: #3F7A79`, `--color-primary-active: #357070`, `--color-primary-light: #B9D5D4`, `--color-background: #FCFCFC`, `--color-surface: #FFFFFF`, `--color-border: #E2E8F0`, `--color-divider: #EFF1F4`, `--color-table-header: #F9FAFC`, `--color-sidebar-selected: #EEF2F6`, `--color-placeholder: #A0AEC0`, `--color-subtitle: #9DA0A8`, `--color-muted-fg: #5A5E6A`, `--color-table-cell: #3B3F4A`, `--color-inactive-tab: #EFF1F4`, `--color-inactive-tab-text: #1A202C`, `--color-badge-green-bg: #C6F6D5`, `--color-badge-green-text: #22543D`, `--color-toggle-on: #3B82F6`, `--color-toggle-off: #CBD5E0`, `--color-delete-text: #3F7A79`
87
+ - Font family tokens: `--font-pretendard`, `--font-inter`, `--font-mono`
88
+ - `:root` CSS variables for shadcn overrides: `--primary`, `--primary-foreground`, `--border`, `--background`, `--card`, `--muted`, `--muted-foreground`, `--accent`, `--accent-foreground`, `--radius`
89
+ - Base body styles: `background-color: #FCFCFC`, default font family Pretendard
90
+ - **Verify:**
91
+ - Open app in browser; page background is `#FCFCFC`
92
+ - Inspect `:root` in dev tools; all CSS variables are present
93
+ - Tailwind utility classes like `bg-primary`, `text-primary`, `bg-table-header`, `text-placeholder` resolve to correct hex values
94
+
95
+ ### P0-T3: Load fonts (Pretendard Variable + Inter)
96
+
97
+ - **Depends on:** P0-T2
98
+ - **Output:** `src/app/layout.tsx` (font loading section)
99
+ - **Behavior:**
100
+ - Use `next/font/google` to load Inter with `subsets: ['latin']`, `variable: '--font-inter'`, `display: 'swap'`
101
+ - Add Pretendard Variable via CDN `<link>` tag in layout `<head>`: `https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/variable/pretendardvariable.css`
102
+ - Apply font CSS variables to `<html>` or `<body>` element
103
+ - Set `<html lang="ko">` for Korean platform
104
+ - **Verify:**
105
+ - Add a test heading "ADMIN" and body text; inspect computed font-family in dev tools
106
+ - Pretendard Variable renders for heading text
107
+ - Inter renders for a test button/input element
108
+ - No FOUT (flash of unstyled text) -- fonts load with swap strategy
109
+
110
+ ### P0-T4: Create shared TypeScript types
111
+
112
+ - **Output:** `src/features/user-management/types/index.ts`, `src/types/index.ts`
113
+ - **Behavior:**
114
+ - Define `User` interface with all fields: `id`, `nickname`, `grade`, `avatar`, `phone`, `age`, `gender`, `region`, `joinDate`, `withdrawalDate`, `role`, `exerciseStyle`, `gymRelocation`, `bench`, `deadlift`, `squat`, `intro`, `profileImages: string[]`, `settings: { profilePublic: boolean; matchChatNotification: boolean; marketingNotification: boolean; }`
115
+ - Define `UserTableColumn` type for table column definitions
116
+ - Export shared navigation types if needed
117
+ - **Verify:**
118
+ - Import `User` type in a test file; TypeScript compiles without errors
119
+ - All fields from the PRD mock data structure are represented
120
+
121
+ ### P0-T5: Create mock data
122
+
123
+ - **Depends on:** P0-T4
124
+ - **Output:** `src/lib/mock-data.ts`
125
+ - **Behavior:**
126
+ - Export `mockUsers: User[]` with 5 user objects, all following the PRD sample data pattern (nickname "NicknameHere", grade "Mania", phone "010-1234-1234", age "Born 1999", gender "Male", region "Gangnam-gu", joinDate "Nov 1, 2022", withdrawalDate "Nov 1, 2022", etc.)
127
+ - Each user gets a unique `id` ("1" through "5")
128
+ - Export `totalUserCount = 100000`
129
+ - Profile images use placeholder paths: `/profile/img1.jpg`, `/profile/img2.jpg`, `/profile/img3.jpg`
130
+ - Avatar paths: `/avatars/user1.jpg` through `/avatars/user5.jpg`
131
+ - **Verify:**
132
+ - Import `mockUsers` and `totalUserCount` in a test; values are correct
133
+ - `mockUsers.length === 5`
134
+ - Each user has all required fields populated
135
+
136
+ ### P0-T6: Create utility functions
137
+
138
+ - **Depends on:** P0-T4
139
+ - **Output:** `src/features/user-management/utils/format.ts`
140
+ - **Behavior:**
141
+ - `formatNumber(n: number): string` -- formats numbers with comma separators (e.g., `100000` -> `"100,000"`)
142
+ - Any other string/date format helpers needed for display
143
+ - **Verify:**
144
+ - `formatNumber(100000)` returns `"100,000"`
145
+ - `formatNumber(0)` returns `"0"`
146
+
147
+ ### P0-T7: Add placeholder images to public directory
148
+
149
+ - **Output:** `public/avatars/user1.jpg` through `public/avatars/user5.jpg`, `public/profile/img1.jpg` through `public/profile/img3.jpg`
150
+ - **Behavior:**
151
+ - Create simple placeholder images (solid color squares or use a placeholder service URL in mock data instead)
152
+ - Avatar images: 44x44px source (renders at 22x22)
153
+ - Profile images: 232x232px source (renders at 116x116)
154
+ - Alternative: use placeholder URLs like `https://placehold.co/44x44/EEF2F6/5A5E6A?text=U1` in mock data and skip file creation
155
+ - **Verify:**
156
+ - Images load without 404 errors when referenced in components
157
+ - Avatar images render as small circles; profile images render at 116x116 with 8px radius
158
+
159
+ ---
160
+
161
+ ## Phase 1: Shared UI Components
162
+
163
+ Install and customize all shadcn/ui components under `src/components/ui/` and build reusable common components.
164
+
165
+ ### P1-T1: Install shadcn/ui components (batch)
166
+
167
+ - **Depends on:** P0-T1
168
+ - **Output:** `src/components/ui/button.tsx`, `card.tsx`, `table.tsx`, `input.tsx`, `select.tsx`, `badge.tsx`, `avatar.tsx`, `switch.tsx`, `tabs.tsx`, `separator.tsx`, `accordion.tsx`, `sidebar.tsx`, `label.tsx`, `tooltip.tsx`
169
+ - **Behavior:** Run:
170
+ ```bash
171
+ bunx --bun shadcn@latest add card button table input select badge avatar switch tabs separator accordion sidebar label tooltip
172
+ ```
173
+ This installs all 14 shadcn/ui components into `src/components/ui/`.
174
+ - **Verify:**
175
+ - All 14 `.tsx` files exist in `src/components/ui/`
176
+ - `bun run build` completes without errors
177
+ - Each component can be imported without TypeScript errors
178
+
179
+ ### P1-T2: Customize Button component with design variants
180
+
181
+ - **Depends on:** P1-T1, P0-T2
182
+ - **Output:** `src/components/ui/button.tsx` (modified)
183
+ - **Behavior:** Add custom variants to the Button component's `cva` config:
184
+ - `primary`: `bg-primary hover:bg-primary-hover active:bg-primary-active text-white font-inter text-[18px] font-semibold h-10 px-4 rounded-lg transition-colors duration-150 ease-out active:scale-[0.98]`. Disabled: `opacity-50 cursor-not-allowed pointer-events-none`
185
+ - `secondary`: `bg-sidebar-selected text-black font-inter text-[18px] font-semibold h-12 px-4 rounded-md`
186
+ - `pagination`: `bg-primary-light text-primary h-10 w-10 rounded-md hover:bg-[#A0C4C3] hover:text-primary-hover`. Disabled: `opacity-50 cursor-not-allowed`
187
+ - `ghost`: `bg-transparent text-delete-text hover:underline hover:text-primary-hover p-0 h-auto text-[14px]`
188
+ - Focus ring on all variants: `focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2`
189
+ - **Verify:**
190
+ - Render each variant in a test page; colors match design tokens
191
+ - Hover state changes background on primary buttons to `#3F7A79`
192
+ - Active state applies `scale(0.98)` on primary buttons
193
+ - Disabled state shows 50% opacity and `cursor: not-allowed`
194
+ - Focus ring appears on Tab focus
195
+
196
+ ### P1-T3: Customize Table component
197
+
198
+ - **Depends on:** P1-T1, P0-T2
199
+ - **Output:** `src/components/ui/table.tsx` (modified)
200
+ - **Behavior:** Override default shadcn/ui table styles:
201
+ - `Table` wrapper: `border border-border bg-white` (no border-radius per Figma)
202
+ - `TableHeader`: `bg-table-header`
203
+ - `TableHead`: `h-14 text-[14px] font-semibold text-table-cell px-4 align-middle`
204
+ - `TableRow`: `h-[60px] border-b border-border hover:bg-table-header cursor-pointer transition-colors duration-150`
205
+ - `TableCell`: `px-4 text-[14px] text-table-cell align-middle`
206
+ - **Verify:**
207
+ - Render a test table with 2 columns and 2 rows
208
+ - Table header background is `#F9FAFC`
209
+ - Row hover changes background
210
+ - Row height is 60px; header row height is 56px
211
+ - Cell text color is `#3B3F4A`
212
+
213
+ ### P1-T4: Customize Input component
214
+
215
+ - **Depends on:** P1-T1, P0-T2
216
+ - **Output:** `src/components/ui/input.tsx` (modified)
217
+ - **Behavior:** Override default input styles:
218
+ - Base: `border border-border rounded-md font-inter text-[18px] text-black placeholder:text-placeholder bg-white`
219
+ - Focus: `focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 transition-[border-color,box-shadow] duration-150`
220
+ - Hover: `hover:border-[#CBD5E0]`
221
+ - Disabled: `disabled:bg-table-header disabled:text-placeholder disabled:cursor-not-allowed`
222
+ - Two size presets used via className at call sites:
223
+ - Dashboard search: `h-12` (48px)
224
+ - Detail form: `h-[52px]` (52px)
225
+ - Default padding: `px-4`
226
+ - **Verify:**
227
+ - Render input with placeholder "Enter search keyword"; placeholder text is `#A0AEC0`
228
+ - Focus the input; border turns `#509594` with subtle ring shadow
229
+ - Hover border changes to `#CBD5E0`
230
+
231
+ ### P1-T5: Customize Select component
232
+
233
+ - **Depends on:** P1-T1, P0-T2
234
+ - **Output:** `src/components/ui/select.tsx` (modified)
235
+ - **Behavior:**
236
+ - `SelectTrigger`: `h-[52px] border border-border rounded-md px-4 pr-12 font-inter text-[18px] text-[#2D3748] bg-white hover:border-[#CBD5E0] focus:border-primary focus:ring-2 focus:ring-primary/20`
237
+ - Chevron icon area: 48px right padding, chevron `#A0AEC0` default, `#509594` on focus/open
238
+ - `SelectContent`: `bg-white border border-border rounded-md shadow-md`
239
+ - `SelectItem`: `hover:bg-sidebar-selected px-4 py-2 text-[18px]`
240
+ - Transition: chevron rotates 180deg on open (300ms ease-in-out)
241
+ - **Verify:**
242
+ - Render select with 3 options; trigger shows chevron down icon
243
+ - Click opens dropdown with white bg and shadow
244
+ - Hover on item shows `#EEF2F6` background
245
+ - Focus/open state border is primary color
246
+
247
+ ### P1-T6: Customize Badge component
248
+
249
+ - **Depends on:** P1-T1, P0-T2
250
+ - **Output:** `src/components/ui/badge.tsx` (modified)
251
+ - **Behavior:** Add a `green` variant:
252
+ - `bg-badge-green-bg text-badge-green-text font-inter text-[18px] font-bold px-3 py-1 rounded-full`
253
+ - Used for the count badge on the dashboard header ("100,000")
254
+ - **Verify:**
255
+ - Render `<Badge variant="green">100,000</Badge>`
256
+ - Background is `#C6F6D5`, text is `#22543D`, pill shape (fully rounded)
257
+
258
+ ### P1-T7: Customize Avatar component
259
+
260
+ - **Depends on:** P1-T1, P0-T2
261
+ - **Output:** `src/components/ui/avatar.tsx` (modified)
262
+ - **Behavior:**
263
+ - Table avatar: `w-[22px] h-[22px] rounded-full`
264
+ - `AvatarFallback`: First letter of nickname, `bg-sidebar-selected text-muted-fg text-[10px]`
265
+ - Ensure `AvatarImage` has `alt` text for accessibility
266
+ - **Verify:**
267
+ - Render avatar with an image src; displays 22px circle
268
+ - Render avatar without image; shows fallback letter in gray circle
269
+ - Inspect: `alt` attribute present on `<img>`
270
+
271
+ ### P1-T8: Customize Switch component
272
+
273
+ - **Depends on:** P1-T1, P0-T2
274
+ - **Output:** `src/components/ui/switch.tsx` (modified)
275
+ - **Behavior:**
276
+ - Track size: `w-[44px] h-[24px] rounded-full`
277
+ - OFF state: track `bg-toggle-off`, thumb white, positioned left
278
+ - ON state: track `bg-toggle-on` (blue `#3B82F6`), thumb white, positioned right
279
+ - Hover OFF: track `#B0BEC5`; hover ON: track `#2563EB`
280
+ - Thumb size: 20px with 2px inset from track edge
281
+ - Transition: `200ms ease-in-out` for thumb translation and track color
282
+ - Disabled: 50% opacity
283
+ - `aria-checked` attribute managed by Radix Switch
284
+ - **Verify:**
285
+ - Render switch in OFF state; track is gray, thumb is left
286
+ - Click switch; animates to ON -- track turns blue, thumb slides right
287
+ - Keyboard: Space toggles state
288
+ - Inspect: `aria-checked` toggles between `true`/`false`
289
+
290
+ ### P1-T9: Customize Tabs component
291
+
292
+ - **Depends on:** P1-T1, P0-T2
293
+ - **Output:** `src/components/ui/tabs.tsx` (modified)
294
+ - **Behavior:**
295
+ - `TabsList`: `gap-2 bg-transparent p-0` (no background on the list container itself)
296
+ - `TabsTrigger` active: `bg-primary text-white font-pretendard font-semibold h-[52px] px-4 rounded-md data-[state=active]:bg-primary data-[state=active]:text-white`
297
+ - `TabsTrigger` inactive: `bg-inactive-tab text-inactive-tab-text font-pretendard font-medium h-[52px] px-4 rounded-md`
298
+ - Hover (inactive): `hover:bg-border`
299
+ - Transition: `200ms ease-in-out` on background-color and color
300
+ - `aria-selected` on active tab (handled by Radix Tabs)
301
+ - **Verify:**
302
+ - Render 3 tabs: "All" (active), "Tab", "Tab"
303
+ - Active tab is teal with white text
304
+ - Inactive tabs are light gray with dark text
305
+ - Click inactive tab; it becomes active (color swap)
306
+ - Keyboard: Arrow Left/Right switches tabs
307
+
308
+ ### P1-T10: Customize Separator component
309
+
310
+ - **Depends on:** P1-T1, P0-T2
311
+ - **Output:** `src/components/ui/separator.tsx` (modified)
312
+ - **Behavior:**
313
+ - Default color: `bg-divider` (`#EFF1F4`)
314
+ - Height: 1px (horizontal)
315
+ - Full width of parent container
316
+ - **Verify:**
317
+ - Render separator; visible as a thin light gray line
318
+ - Inspect: background color is `#EFF1F4`
319
+
320
+ ### P1-T11: Customize Accordion component
321
+
322
+ - **Depends on:** P1-T1, P0-T2
323
+ - **Output:** `src/components/ui/accordion.tsx` (modified)
324
+ - **Behavior:**
325
+ - Trigger: chevron icon rotates 180deg on expand (300ms ease-in-out)
326
+ - Content: animated height expand/collapse (300ms ease-in-out)
327
+ - Trigger text: `font-inter text-[16px] text-muted-fg`
328
+ - `aria-expanded` on trigger (handled by Radix Accordion)
329
+ - **Verify:**
330
+ - Render accordion with one section; click trigger, content expands with animation
331
+ - Chevron rotates on expand
332
+ - Keyboard: Enter/Space toggles
333
+
334
+ ### P1-T12: Customize Label component
335
+
336
+ - **Depends on:** P1-T1, P0-T2
337
+ - **Output:** `src/components/ui/label.tsx` (modified)
338
+ - **Behavior:**
339
+ - Style: `font-pretendard text-[20px] font-semibold text-[#101010]`
340
+ - Margin bottom: `mb-2` (8px below label, above input)
341
+ - Uses `htmlFor` attribute to associate with input `id`
342
+ - **Verify:**
343
+ - Render label above an input; label text is 20px SemiBold, nearly black
344
+ - Click label; associated input receives focus (via `htmlFor`/`id` pairing)
345
+
346
+ ### P1-T13: Build PageHeader common component
347
+
348
+ - **Depends on:** P1-T2, P1-T6, P1-T10
349
+ - **Output:** `src/components/common/page-header.tsx`
350
+ - **Behavior:**
351
+ - Props: `title: string`, `subtitle: string`, `badgeCount?: number`, `actionLabel: string`, `actionIcon?: LucideIcon`, `onAction?: () => void`
352
+ - Layout: Flex row. Left side: title (32px SemiBold Pretendard, black) + Badge (only if `badgeCount` provided) on same line. Subtitle below (18px Medium Pretendard, `#9DA0A8`). Right side: action Button (primary variant) with optional icon.
353
+ - Below the header content, render a `<Separator />` with vertical margin
354
+ - **Verify:**
355
+ - Render `<PageHeader title="User Management" subtitle="User list management page" badgeCount={100000} actionLabel="Write" />`
356
+ - Title "User Management" in 32px SemiBold
357
+ - Badge "100,000" in green pill beside title
358
+ - Subtitle below in gray
359
+ - "Write" button top-right in teal
360
+ - Separator line beneath
361
+
362
+ ---
363
+
364
+ ## Phase 2: Layout & Navigation
365
+
366
+ Sidebar, root layout, content area structure.
367
+
368
+ ### P2-T1: Build AppSidebar component
369
+
370
+ - **Depends on:** P1-T1, P1-T11, P0-T2, P0-T3
371
+ - **Output:** `src/components/layouts/app-sidebar.tsx`
372
+ - **Behavior:**
373
+ - Uses shadcn/ui `Sidebar` primitives (or custom div-based implementation) + `Accordion`
374
+ - Fixed position left, 300px width, full viewport height, white background, right border `#E2E8F0`
375
+ - **Header section:**
376
+ - "ADMIN" title: Pretendard 24px SemiBold, `letter-spacing: 0.05em`
377
+ - Admin ID subtitle: 14px Regular `#9DA0A8`, e.g., "admin@potenlab.com"
378
+ - Bottom spacing 32px
379
+ - **Navigation section (Accordion):**
380
+ - "Home" item: `Home` icon (Lucide), no accordion, links to `/`
381
+ - "User" accordion: `Users` icon, expands to show "User Management" sub-item
382
+ - "Match" accordion: `Gamepad2` icon, expands to show "Match Management" sub-item
383
+ - "Admin" accordion: `Shield` icon, expands to show "Notice Management", "Report Management", "Terms Management" sub-items
384
+ - Menu item height: 48px, text: Inter 16px Regular `#5A5E6A`, icon: 20px `#5A5E6A`
385
+ - Menu item hover: `#F9FAFC` background (150ms ease-out)
386
+ - Sub-menu items: indented 40px, height 44px
387
+ - Active sub-menu: `#EEF2F6` background, `#509594` text, dot indicator
388
+ - Active state derived from `usePathname()` (Next.js)
389
+ - Sub-menu items use `next/link` for navigation
390
+ - **Footer section:**
391
+ - `Separator` above
392
+ - Logout button: `LogOut` icon + "Logout" text, centered, 16px Regular `#5A5E6A`
393
+ - Hover: text turns `#EF4444` (red hint for destructive action)
394
+ - Click: no-op (UI only)
395
+ - Keyboard: Tab navigates between items, Enter/Space toggles accordion, Arrow Up/Down navigates accordion items
396
+ - **Verify:**
397
+ - Sidebar renders at 300px width on left side of viewport
398
+ - "ADMIN" title visible with correct font
399
+ - Click "User" accordion; "User Management" sub-item appears with animation
400
+ - "User Management" has active highlight when on `/` route
401
+ - Logout button visible at bottom with separator above
402
+ - Hover logout; text turns red
403
+ - Keyboard: Tab through sidebar items; focus visible on each
404
+
405
+ ### P2-T2: Build ContentLayout wrapper component
406
+
407
+ - **Depends on:** P2-T1
408
+ - **Output:** `src/components/layouts/content-layout.tsx`
409
+ - **Behavior:**
410
+ - Wraps page content with correct offset: `ml-[324px] pt-5 pr-8 pb-8`
411
+ - Content area offset: 300px sidebar + 24px gap = 324px from left
412
+ - Top padding: 20px
413
+ - Receives `children` as prop
414
+ - Background: inherits from body (`#FCFCFC`)
415
+ - **Verify:**
416
+ - Wrap test content with `<ContentLayout>`; content appears to the right of the sidebar
417
+ - Inspect: margin-left is 324px, padding-top is 20px
418
+
419
+ ### P2-T3: Build root layout (app/layout.tsx)
420
+
421
+ - **Depends on:** P2-T1, P2-T2, P0-T3
422
+ - **Output:** `src/app/layout.tsx`
423
+ - **Behavior:**
424
+ - `<html lang="ko">` with font CSS variables applied
425
+ - Loads Inter via `next/font/google` with `variable: '--font-inter'`
426
+ - Loads Pretendard Variable via CDN link in `<head>`
427
+ - Renders `<AppSidebar />` persistent on all pages
428
+ - Renders `<ContentLayout>{children}</ContentLayout>` as main content area
429
+ - Skip link: `<a href="#main-content" className="sr-only focus:not-sr-only ...">Skip to main content</a>` as first element
430
+ - `<main id="main-content">` wraps content area for skip link target
431
+ - Imports `globals.css`
432
+ - **Verify:**
433
+ - Open app; sidebar visible on left, content on right
434
+ - Navigate between pages; sidebar persists
435
+ - Tab key first focuses "Skip to main content" link (visible on focus)
436
+ - Press Enter on skip link; focus moves to main content area
437
+ - Inspect `<html>`: `lang="ko"` attribute present
438
+ - Fonts loaded: Pretendard and Inter visible in Network tab
439
+
440
+ ---
441
+
442
+ ## Phase 3: Features -- User Management List Page (Dashboard)
443
+
444
+ ### P3-T1: Build TabNavigation component
445
+
446
+ - **Depends on:** P1-T9
447
+ - **Output:** `src/components/user-management/tab-navigation.tsx`
448
+ - **Behavior:**
449
+ - Uses shadcn/ui `Tabs`, `TabsList`, `TabsTrigger`
450
+ - 3 tabs: "All" (default active), "Tab", "Tab"
451
+ - Active tab: teal bg, white text. Inactive: light gray bg, dark text
452
+ - `defaultValue="all"`
453
+ - Tab switching updates local state (UI only, no filtering)
454
+ - Arrow Left/Right keyboard navigation between tabs
455
+ - **Verify:**
456
+ - Render component; "All" tab is active (teal)
457
+ - Click "Tab"; it becomes active, "All" becomes inactive
458
+ - Keyboard: Arrow Right moves focus and activates next tab
459
+
460
+ ### P3-T2: Build SearchBar component
461
+
462
+ - **Depends on:** P1-T4
463
+ - **Output:** `src/components/user-management/search-bar.tsx`
464
+ - **Behavior:**
465
+ - Full-width `Input` component
466
+ - Height: 48px (`h-12`), border radius 6px
467
+ - Placeholder: "Enter search keyword"
468
+ - Placeholder color: `#A0AEC0`
469
+ - Accepts text input (state managed locally, no actual search)
470
+ - Focus highlights border to primary
471
+ - **Verify:**
472
+ - Render search bar; full width with placeholder text in gray
473
+ - Click/focus; border changes to teal
474
+ - Type text; placeholder disappears, text visible
475
+
476
+ ### P3-T3: Build PaginationControls component
477
+
478
+ - **Depends on:** P1-T2, P1-T4, P1-T5
479
+ - **Output:** `src/components/user-management/pagination-controls.tsx`
480
+ - **Behavior:**
481
+ - Flex row with `justify-between`
482
+ - **Left group:**
483
+ - 4 pagination buttons: `<<` (ChevronsLeft), `<` (ChevronLeft), `>` (ChevronRight), `>>` (ChevronsRight)
484
+ - Button variant: `pagination` (40x40px, `#B9D5D4` bg, 6px radius)
485
+ - Each icon-only button has `aria-label` (e.g., "Go to first page", "Previous page", "Next page", "Go to last page")
486
+ - Page indicator between `<` and `>`: "1 / 1"
487
+ - Current page: 20px Bold
488
+ - Separator "/" and total: 16px Regular `#5A5E6A`
489
+ - All buttons visually present but disabled state (muted, since single page of mock data)
490
+ - **Right group:**
491
+ - Page jump `Input`: 100px width, 48px height, placeholder "Page", 6px radius
492
+ - "Go" button: `secondary` variant, 48px height
493
+ - Items per page `Select`: 96px width, 48px height, default "10 items", options: "10 items", "20 items", "50 items"
494
+ - State: current page, items per page managed locally (UI only)
495
+ - `aria-live="polite"` on page indicator region
496
+ - **Verify:**
497
+ - Render component; all 4 nav buttons visible in `#B9D5D4`
498
+ - "1 / 1" displayed between `<` and `>`
499
+ - Page jump input visible with "Page" placeholder
500
+ - "Go" button renders in gray
501
+ - Items per page dropdown shows "10 items"
502
+ - Click dropdown; opens with 3 options
503
+ - All icon buttons have `aria-label` (inspect DOM)
504
+
505
+ ### P3-T4: Build UserTable component
506
+
507
+ - **Depends on:** P1-T3, P1-T7, P1-T2, P0-T5
508
+ - **Output:** `src/components/user-management/user-table.tsx`
509
+ - **Behavior:**
510
+ - Uses shadcn/ui `Table`, `TableHeader`, `TableHead`, `TableBody`, `TableRow`, `TableCell`
511
+ - 10 columns per spec:
512
+ - Nickname (flex-1), Grade (flex-1), Avatar (80px fixed, centered), Phone Number (flex-1), Age (flex-1), Gender (flex-1), Region (flex-1), Join Date (flex-1, sort icon), Withdrawal Date (flex-1, sort icon), Delete (57px fixed)
513
+ - Renders 5 rows from `mockUsers` data
514
+ - Avatar column: `Avatar` component (22px circular)
515
+ - Delete column: ghost button with teal text "Delete" (`#3F7A79`), underline on hover
516
+ - Sort icons on Join Date and Withdrawal Date: `ChevronDown` icon (visual only, no actual sort)
517
+ - Row `onClick`: navigates to `/users/[id]` via `useRouter().push()`
518
+ - Delete `onClick`: calls `event.stopPropagation()` to prevent row navigation (UI only, no deletion)
519
+ - Row hover: `#F9FAFC` background, `cursor: pointer`
520
+ - Table container: white bg, `#E2E8F0` border, no border-radius
521
+ - Header row: `#F9FAFC` bg, 56px height, 14px SemiBold text
522
+ - Body row: 60px height, 14px Regular text, `#3B3F4A`
523
+ - Empty state: "No users found" centered (not needed for 5 mock rows, but define for completeness)
524
+ - **Verify:**
525
+ - Table renders with 10 column headers and 5 data rows
526
+ - Avatar column shows small circular image (or fallback initial)
527
+ - Hover over row; background changes, cursor is pointer
528
+ - Click row; navigates to `/users/1` (check URL bar)
529
+ - Click "Delete" on a row; does NOT navigate (stopPropagation works)
530
+ - Sort icons visible on Join Date and Withdrawal Date columns
531
+ - Table container has border but no rounded corners
532
+
533
+ ### P3-T5: Assemble Dashboard page (app/page.tsx)
534
+
535
+ - **Depends on:** P3-T1, P3-T2, P3-T3, P3-T4, P1-T13, P0-T5, P0-T6
536
+ - **Output:** `src/app/page.tsx`
537
+ - **Behavior:**
538
+ - Uses `Card` as the main container (white, 8px radius, `#E2E8F0` border, 32px padding)
539
+ - Composes inside Card, top to bottom:
540
+ 1. `PageHeader` with title "User Management", subtitle "User list management page", badgeCount `totalUserCount` (formatted to "100,000"), actionLabel "Write", actionIcon `Pencil`
541
+ 2. `TabNavigation` (3 tabs, "All" active)
542
+ 3. `SearchBar` (full-width input)
543
+ 4. `PaginationControls` (nav buttons + page jump + items per page)
544
+ 5. `UserTable` (5 rows of mock data)
545
+ - Spacing between sections: consistent gaps (use `space-y-6` or explicit margins per Figma)
546
+ - Route: `/` (app/page.tsx)
547
+ - **Verify:**
548
+ - Open `/` in browser; full dashboard visible inside white card
549
+ - Header shows "User Management" + green badge "100,000" + subtitle + "Write" button
550
+ - Separator line below header
551
+ - Tabs render below separator
552
+ - Search bar below tabs
553
+ - Pagination controls below search
554
+ - Data table with 5 rows below pagination
555
+ - All spacing looks correct against Figma wireframe
556
+ - "Write" button click: no-op (UI only)
557
+
558
+ ---
559
+
560
+ ## Phase 4: Features -- User Detail Page
561
+
562
+ ### P4-T1: Build ProfileImages component
563
+
564
+ - **Depends on:** P0-T5, P0-T7
565
+ - **Output:** `src/components/user-management/profile-images.tsx`
566
+ - **Behavior:**
567
+ - Props: `images: string[]` (array of 3 image URLs)
568
+ - Renders 3 images in a horizontal flex row with 16px gap
569
+ - Each image: 116x116px, 8px border-radius, `object-cover`
570
+ - Uses `<img>` or Next.js `<Image>` with appropriate alt text (e.g., "Profile photo 1", "Profile photo 2", "Profile photo 3")
571
+ - Fallback: if image fails to load, show gray placeholder block (116x116, `#EEF2F6` bg)
572
+ - **Verify:**
573
+ - Render with 3 placeholder images; 3 boxes appear horizontally
574
+ - Each image is 116x116 with rounded corners
575
+ - Alt text present on each `<img>` tag
576
+
577
+ ### P4-T2: Build UserDetailForm component
578
+
579
+ - **Depends on:** P1-T4, P1-T5, P1-T8, P1-T12, P1-T10, P4-T1, P0-T5
580
+ - **Output:** `src/components/user-management/user-detail-form.tsx`
581
+ - **Behavior:**
582
+ - Props: `user: User`
583
+ - **Basic Info section:**
584
+ - Section label: "Basic Info" (Pretendard 20px Bold, `#A0AEC0`)
585
+ - `ProfileImages` component with user's 3 profile images (margin-bottom 24px)
586
+ - "One-Line Intro" field: `Label` + full-width `Input` (52px height), pre-filled with `user.intro`
587
+ - **Row 1** (CSS Grid `grid-cols-4 gap-6`): Role (Select: "User", "Admin"), Nickname (Input), Phone (Input), Age (Input)
588
+ - **Row 2** (CSS Grid `grid-cols-4 gap-6`): Gender (Select: "Male", "Female"), Exercise Style (Select: "Bodybuilding", "Crossfit", "Cardio"), Gym Relocation (Select: "Available", "Not Available"), Region (Select: "Seoul Mapo-gu", "Gangnam-gu", "Songpa-gu")
589
+ - **Row 3** (CSS Grid `grid-cols-3 gap-6`): Bench (Input), Deadlift (Input), Squat (Input)
590
+ - All inputs/selects are 52px height, 6px radius, 16px horizontal padding
591
+ - All fields pre-filled with mock data values
592
+ - Labels above each field: 20px SemiBold `#101010`
593
+ - **Separator** between sections
594
+ - **Other Settings section:**
595
+ - Section label: "Other Settings" (Pretendard 20px Bold, `#A0AEC0`)
596
+ - Horizontal flex layout with 100px gap between toggle items
597
+ - 3 toggles: "Profile Public" (OFF), "Match & Chat Notification" (ON), "Marketing Notification" (OFF)
598
+ - Each toggle: label text left + `Switch` component right (or text above, switch beside)
599
+ - Switch state reflects `user.settings` values
600
+ - Toggles are interactive (local state change on click, UI only)
601
+ - All form fields are editable (inputs accept typing, selects open, toggles toggle)
602
+ - No form validation needed
603
+ - **Verify:**
604
+ - Render with mock user data; all fields pre-filled correctly
605
+ - "Basic Info" section label visible in gray
606
+ - 3 profile images displayed
607
+ - "One-Line Intro" input shows "This is the one-line intro content."
608
+ - Row 1: 4 fields in equal columns with 24px gap
609
+ - Row 2: 4 fields, dropdowns open on click
610
+ - Row 3: 3 fields in equal columns
611
+ - Separator between sections
612
+ - "Other Settings" label visible
613
+ - 3 toggles in a row with 100px gap
614
+ - "Match & Chat Notification" toggle is ON (blue); others are OFF (gray)
615
+ - Click a toggle; it switches state with animation
616
+
617
+ ### P4-T3: Assemble User Detail page (app/users/[id]/page.tsx)
618
+
619
+ - **Depends on:** P4-T2, P1-T13, P0-T5
620
+ - **Output:** `src/app/users/[id]/page.tsx`
621
+ - **Behavior:**
622
+ - Reads `params.id` from route (Next.js dynamic route)
623
+ - Looks up user from `mockUsers` by id (falls back to first user if not found)
624
+ - Uses `Card` as main container (same styling as dashboard card)
625
+ - Composes inside Card:
626
+ 1. `PageHeader` with title "User Management", subtitle "You can edit user information.", actionLabel "Save Changes", actionIcon `Pencil`
627
+ 2. `UserDetailForm` with the selected user
628
+ - "Save Changes" button click: no-op (UI only, no actual save)
629
+ - Route: `/users/[id]`
630
+ - **Verify:**
631
+ - Navigate to `/users/1`; detail page loads in white card
632
+ - Header shows "User Management" + subtitle "You can edit user information." + "Save Changes" button
633
+ - No badge on this page (badgeCount not passed)
634
+ - Form renders with all user fields pre-filled
635
+ - Sidebar shows "User Management" as active
636
+ - Click "Save Changes": no error (UI only)
637
+ - Navigate back via sidebar "User Management" link; returns to dashboard
638
+
639
+ ---
640
+
641
+ ## Phase 5: Polish
642
+
643
+ Accessibility, animations, micro-interactions, keyboard navigation, final QA.
644
+
645
+ ### P5-T1: Accessibility audit -- keyboard navigation
646
+
647
+ - **Depends on:** P3-T5, P4-T3
648
+ - **Output:** Updates to existing components as needed
649
+ - **Behavior:** Verify and fix keyboard navigation across all components:
650
+ - **Skip link:** Tab first focuses "Skip to main content"; Enter jumps to `#main-content`
651
+ - **Sidebar:** Tab/Shift+Tab navigates between items. Enter/Space toggles accordion. Arrow Up/Down navigates within accordion.
652
+ - **Tabs:** Arrow Left/Right switches tabs. Enter/Space activates.
653
+ - **Table rows:** Tab focuses row (or first interactive element). Enter navigates to detail.
654
+ - **Delete action:** Enter/Space triggers delete. Tab reaches it separately from row.
655
+ - **Pagination buttons:** Tab + Enter navigates. Each has `aria-label`.
656
+ - **Inputs:** Tab focuses. Standard text input behavior.
657
+ - **Selects:** Enter/Space opens. Arrow Up/Down navigates. Enter selects. Escape closes.
658
+ - **Toggles:** Space toggles ON/OFF.
659
+ - **Buttons:** Enter/Space activates.
660
+ - Focus ring: 2px `#509594` outline with 2px offset on ALL interactive elements
661
+ - **Verify:**
662
+ - Start at top of page, Tab through every interactive element without mouse
663
+ - Focus ring visible on each element
664
+ - No keyboard traps (can always Tab out or Escape out)
665
+ - All actions achievable via keyboard alone
666
+ - Skip link works on both pages
667
+
668
+ ### P5-T2: Accessibility audit -- ARIA and screen reader
669
+
670
+ - **Depends on:** P5-T1
671
+ - **Output:** Updates to existing components as needed
672
+ - **Behavior:** Verify and fix ARIA attributes:
673
+ - Badge: `aria-label="Total users: 100,000"` (screen reader reads meaningful text)
674
+ - Active tab: `aria-selected="true"` (Radix handles this)
675
+ - Table: ensure `<table>`, `<thead>`, `<th>`, `<tbody>`, `<tr>`, `<td>` semantic elements (shadcn/ui table uses these)
676
+ - Sort columns: `aria-label="Join Date, sortable column"` on header
677
+ - Delete buttons: `aria-label="Delete user NicknameHere"` with dynamic user name
678
+ - Toggle switches: `aria-checked` managed by Radix Switch
679
+ - Accordion: `aria-expanded` on triggers
680
+ - Pagination: `aria-live="polite"` on page indicator region
681
+ - All icon-only buttons: `aria-label` present
682
+ - Heading hierarchy: `<h1>` for page title, `<h2>` for section labels
683
+ - Form fields: `<label htmlFor="field-id">` paired with `<input id="field-id">`
684
+ - **Verify:**
685
+ - Use browser accessibility inspector (Chrome DevTools Accessibility tab)
686
+ - VoiceOver (macOS): navigate through page; all elements announced correctly
687
+ - Check: page title reads "User Management, heading level 1"
688
+ - Check: badge reads "Total users: 100,000"
689
+ - Check: toggle reads "Profile Public, switch, off"
690
+ - No accessibility warnings in browser dev tools
691
+
692
+ ### P5-T3: Micro-interactions and transitions
693
+
694
+ - **Depends on:** P3-T5, P4-T3
695
+ - **Output:** Updates to existing component styles
696
+ - **Behavior:** Ensure all transitions from the ui-ux-plan are implemented:
697
+ - **Button hover:** `background-color 150ms ease-out` (primary -> `#3F7A79`)
698
+ - **Button active:** `scale(0.98)` + `background-color 100ms ease-out` (primary -> `#357070`)
699
+ - **Table row hover:** `background-color 150ms ease-out` (-> `#F9FAFC`)
700
+ - **Toggle switch:** thumb `transform 200ms ease-in-out`, track `background-color 200ms ease-in-out`
701
+ - **Accordion expand/collapse:** `height 300ms ease-in-out`, `opacity 200ms ease-in-out`. Chevron `transform 300ms ease-in-out` (rotate 180deg)
702
+ - **Tab switch:** `background-color 200ms ease-in-out`, `color 200ms ease-in-out`
703
+ - **Input focus:** `border-color 150ms ease-out`, `box-shadow 150ms ease-out` (ring appears)
704
+ - **Delete text hover:** `color 150ms ease-out`, `text-decoration: underline`
705
+ - **Sidebar menu item hover:** `background-color 150ms ease-out`
706
+ - **Logout hover:** text color transition to red
707
+ - **Verify:**
708
+ - Visually test each interaction; animations are smooth, not janky
709
+ - No transitions are instant (all have appropriate duration)
710
+ - Toggle thumb slides smoothly
711
+ - Accordion content height animates (not instant appear/disappear)
712
+
713
+ ### P5-T4: Final visual QA against design specs
714
+
715
+ - **Depends on:** P5-T3
716
+ - **Output:** Bug fixes and adjustments to existing components/styles
717
+ - **Behavior:** Systematic check of every visual element against the Figma design specs and PRD:
718
+ - **Colors:** Verify every color token matches hex values in the PRD
719
+ - **Typography:** Verify font family, size, weight, line-height for every text style
720
+ - **Spacing:** Verify all paddings, margins, gaps match spec (card 32px padding, field gap 24px, etc.)
721
+ - **Sizing:** Verify all fixed dimensions (sidebar 300px, button heights, input heights, avatar 22px, profile images 116px, toggle 44x24px, table row heights)
722
+ - **Border radius:** Cards 8px, inputs/tabs/selects 6px, avatars full-round, table 0px
723
+ - **Borders:** Card `#E2E8F0`, dividers `#EFF1F4`, table `#E2E8F0`
724
+ - **Shadows:** Minimal/none on cards, `shadow-md` on dropdown menus only
725
+ - **Layout:** Sidebar 300px fixed, content offset 324px, 20px top padding
726
+ - **Dashboard page:** All elements match wireframe from Section 7.1
727
+ - **Detail page:** All elements match wireframe from Section 7.2
728
+ - **Empty/edge states:** Single page pagination shows disabled buttons
729
+ - **Verify:**
730
+ - Side-by-side comparison with Figma designs at 1920px viewport
731
+ - No visual regressions or misalignments
732
+ - All hover/focus states match spec
733
+ - Build succeeds: `bun run build` completes without errors
734
+
735
+ ---
736
+
737
+ ## Task Dependency Graph
738
+
739
+ ```
740
+ P0-T1 (shadcn init)
741
+ ├── P0-T2 (design tokens) ──> P0-T3 (fonts)
742
+ └── P1-T1 (install components)
743
+ ├── P1-T2 (Button)
744
+ ├── P1-T3 (Table)
745
+ ├── P1-T4 (Input)
746
+ ├── P1-T5 (Select)
747
+ ├── P1-T6 (Badge)
748
+ ├── P1-T7 (Avatar)
749
+ ├── P1-T8 (Switch)
750
+ ├── P1-T9 (Tabs)
751
+ ├── P1-T10 (Separator)
752
+ ├── P1-T11 (Accordion)
753
+ └── P1-T12 (Label)
754
+
755
+ P0-T4 (types) ──> P0-T5 (mock data)
756
+ P0-T4 (types) ──> P0-T6 (utils)
757
+
758
+ P1-T2 + P1-T6 + P1-T10 ──> P1-T13 (PageHeader)
759
+
760
+ P1-T11 + P0-T2 + P0-T3 ──> P2-T1 (AppSidebar)
761
+ P2-T1 ──> P2-T2 (ContentLayout)
762
+ P2-T1 + P2-T2 + P0-T3 ──> P2-T3 (Root Layout)
763
+
764
+ P1-T9 ──> P3-T1 (TabNavigation)
765
+ P1-T4 ──> P3-T2 (SearchBar)
766
+ P1-T2 + P1-T4 + P1-T5 ──> P3-T3 (PaginationControls)
767
+ P1-T3 + P1-T7 + P1-T2 + P0-T5 ──> P3-T4 (UserTable)
768
+ P3-T1 + P3-T2 + P3-T3 + P3-T4 + P1-T13 + P0-T5 + P0-T6 ──> P3-T5 (Dashboard page)
769
+
770
+ P0-T5 + P0-T7 ──> P4-T1 (ProfileImages)
771
+ P1-T4 + P1-T5 + P1-T8 + P1-T12 + P1-T10 + P4-T1 + P0-T5 ──> P4-T2 (UserDetailForm)
772
+ P4-T2 + P1-T13 + P0-T5 ──> P4-T3 (User Detail page)
773
+
774
+ P3-T5 + P4-T3 ──> P5-T1 (Keyboard a11y)
775
+ P5-T1 ──> P5-T2 (ARIA/SR a11y)
776
+ P3-T5 + P4-T3 ──> P5-T3 (Micro-interactions)
777
+ P5-T3 ──> P5-T4 (Visual QA)
778
+ ```
779
+
780
+ ---
781
+
782
+ ## Task Summary
783
+
784
+ | ID | Phase | Task | Key Output |
785
+ |----|-------|------|------------|
786
+ | P0-T1 | 0 | Initialize shadcn/ui | `components.json`, `src/lib/utils.ts` |
787
+ | P0-T2 | 0 | Configure design tokens in globals.css | `src/styles/globals.css` |
788
+ | P0-T3 | 0 | Load fonts (Pretendard + Inter) | `src/app/layout.tsx` (fonts) |
789
+ | P0-T4 | 0 | Create shared TypeScript types | `src/features/user-management/types/index.ts` |
790
+ | P0-T5 | 0 | Create mock data | `src/lib/mock-data.ts` |
791
+ | P0-T6 | 0 | Create utility functions | `src/features/user-management/utils/format.ts` |
792
+ | P0-T7 | 0 | Add placeholder images | `public/avatars/`, `public/profile/` |
793
+ | P1-T1 | 1 | Install shadcn/ui components (batch) | 14 files in `src/components/ui/` |
794
+ | P1-T2 | 1 | Customize Button variants | `src/components/ui/button.tsx` |
795
+ | P1-T3 | 1 | Customize Table component | `src/components/ui/table.tsx` |
796
+ | P1-T4 | 1 | Customize Input component | `src/components/ui/input.tsx` |
797
+ | P1-T5 | 1 | Customize Select component | `src/components/ui/select.tsx` |
798
+ | P1-T6 | 1 | Customize Badge component | `src/components/ui/badge.tsx` |
799
+ | P1-T7 | 1 | Customize Avatar component | `src/components/ui/avatar.tsx` |
800
+ | P1-T8 | 1 | Customize Switch component | `src/components/ui/switch.tsx` |
801
+ | P1-T9 | 1 | Customize Tabs component | `src/components/ui/tabs.tsx` |
802
+ | P1-T10 | 1 | Customize Separator component | `src/components/ui/separator.tsx` |
803
+ | P1-T11 | 1 | Customize Accordion component | `src/components/ui/accordion.tsx` |
804
+ | P1-T12 | 1 | Customize Label component | `src/components/ui/label.tsx` |
805
+ | P1-T13 | 1 | Build PageHeader common component | `src/components/common/page-header.tsx` |
806
+ | P2-T1 | 2 | Build AppSidebar component | `src/components/layouts/app-sidebar.tsx` |
807
+ | P2-T2 | 2 | Build ContentLayout wrapper | `src/components/layouts/content-layout.tsx` |
808
+ | P2-T3 | 2 | Build root layout | `src/app/layout.tsx` |
809
+ | P3-T1 | 3 | Build TabNavigation component | `src/components/user-management/tab-navigation.tsx` |
810
+ | P3-T2 | 3 | Build SearchBar component | `src/components/user-management/search-bar.tsx` |
811
+ | P3-T3 | 3 | Build PaginationControls component | `src/components/user-management/pagination-controls.tsx` |
812
+ | P3-T4 | 3 | Build UserTable component | `src/components/user-management/user-table.tsx` |
813
+ | P3-T5 | 3 | Assemble Dashboard page | `src/app/page.tsx` |
814
+ | P4-T1 | 4 | Build ProfileImages component | `src/components/user-management/profile-images.tsx` |
815
+ | P4-T2 | 4 | Build UserDetailForm component | `src/components/user-management/user-detail-form.tsx` |
816
+ | P4-T3 | 4 | Assemble User Detail page | `src/app/users/[id]/page.tsx` |
817
+ | P5-T1 | 5 | Accessibility -- keyboard navigation | Updates across components |
818
+ | P5-T2 | 5 | Accessibility -- ARIA and screen reader | Updates across components |
819
+ | P5-T3 | 5 | Micro-interactions and transitions | Style updates across components |
820
+ | P5-T4 | 5 | Final visual QA against design specs | Bug fixes, adjustments |
821
+
822
+ **Total: 31 tasks across 6 phases**