diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index b1c06a3..4acba68 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -280,7 +280,7 @@ Plans: Plans: - [x] 38-01-PLAN.md — Schema migration (parentId), service layer (CRUD + cycle detection), API routes, tests -- [ ] 38-02-PLAN.md — Client hooks, tag list page (tree view + quick-add + search), edit page (rename/reparent/delete), sidebar activation +- [x] 38-02-PLAN.md — Client hooks, tag list page (tree view + quick-add + search), edit page (rename/reparent/delete), sidebar activation **UI hint**: yes diff --git a/.planning/STATE.md b/.planning/STATE.md index d4ede64..4a60c8a 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,8 +3,8 @@ gsd_state_version: 1.0 milestone: v2.4 milestone_name: Admin Foundation status: executing -stopped_at: Completed 35-03-PLAN.md — FIX-05 (cursor-pointer audit) resolved -last_updated: "2026-04-19T20:29:07.741Z" +stopped_at: Completed 38-02-PLAN.md — admin tag management client UI +last_updated: "2026-04-19T20:32:22Z" last_activity: 2026-04-19 progress: total_phases: 20 @@ -30,7 +30,7 @@ Plan: 2 of 2 Status: Ready to execute Last activity: 2026-04-19 -Progress: [████████░░] 94% +Progress: [█████████░] 97% ## Performance Metrics @@ -110,6 +110,6 @@ Items carried forward from v2.3: ## Session Continuity -Last session: 2026-04-19T20:29:07.738Z -Stopped at: Completed 35-03-PLAN.md — FIX-05 (cursor-pointer audit) resolved +Last session: 2026-04-19T20:32:22Z +Stopped at: Completed 38-02-PLAN.md — admin tag management client UI Resume file: None diff --git a/.planning/phases/38-admin-tag-management/38-02-SUMMARY.md b/.planning/phases/38-admin-tag-management/38-02-SUMMARY.md new file mode 100644 index 0000000..36c5970 --- /dev/null +++ b/.planning/phases/38-admin-tag-management/38-02-SUMMARY.md @@ -0,0 +1,94 @@ +--- +phase: 38-admin-tag-management +plan: "02" +subsystem: client +tags: [admin, tags, react-query, tree-view, crud] +dependency_graph: + requires: ["38-01"] + provides: ["admin-tag-ui"] + affects: ["src/client/routes/admin/", "src/client/hooks/"] +tech_stack: + added: [] + patterns: + - collapsible tree view with buildTree/flattenTree/filterTree utilities + - dual React Query key invalidation (admin-tags + tags) + - cycle-safe parent picker via getDescendantIds client-side filtering + - impact-aware delete confirmation (item count + child count) +key_files: + created: + - src/client/hooks/useAdminTags.ts + - src/client/routes/admin/tags.tsx + - src/client/routes/admin/tags.$tagId.tsx + modified: + - src/client/routes/admin.tsx +decisions: + - "buildTree/flattenTree/filterTree co-located in tags.tsx — pure JS utilities, no separate lib file needed" + - "Dual query key invalidation on all tag mutations keeps public tags cache fresh alongside admin cache" + - "getDescendantIds used client-side for parent picker filtering — server validates cycles authoritatively" +metrics: + duration: "2m" + completed: "2026-04-19" + tasks_completed: 2 + tasks_total: 2 + files_changed: 4 +--- + +# Phase 38 Plan 02: Admin Tag Management Client UI Summary + +React Query hooks, collapsible tree list page, edit page with cycle-safe reparent and impact-aware delete confirmation, and Tags sidebar link activated in the admin panel. + +## Tasks Completed + +| # | Name | Commit | Files | +|---|------|--------|-------| +| 1 | Client hooks + tag list page with tree view and quick-add | 1f8b85d | useAdminTags.ts, admin/tags.tsx | +| 2 | Tag edit page with rename, reparent, delete + sidebar link activation | 0571ee4 | admin/tags.$tagId.tsx, admin.tsx | + +## What Was Built + +### `src/client/hooks/useAdminTags.ts` + +Five React Query hooks: `useAdminTags` (list), `useAdminTag` (single with 404 retry suppression), `useCreateAdminTag`, `useUpdateAdminTag`, `useDeleteAdminTag`. All mutations invalidate both `["admin-tags"]` and `["tags"]` to keep the public tag cache fresh. + +### `src/client/routes/admin/tags.tsx` + +Tag list page at `/admin/tags` with: +- `buildTree` — builds hierarchical tree from flat array with self-referential parent links +- `flattenTree` — depth-first flatten respecting the `expanded` Set for show/hide +- `filterTree` — recursive filter that preserves parent rows when descendants match +- Collapsible chevron-based tree rows with 20px-per-depth indentation +- Quick-add inline form with name input, parent picker (`No parent (top-level)` default), and "Add Tag" button +- Search input filtering tree in-place +- Skeleton loading, error state, two empty states (no tags / no search results) + +### `src/client/routes/admin/tags.$tagId.tsx` + +Tag edit page at `/admin/tags/$tagId` with: +- `getDescendantIds` — recursively collects all descendant IDs to exclude from parent picker (client-side cycle prevention) +- `getDeleteConfirmText` — builds contextual confirmation: item count warning + child count warning + "This cannot be undone." +- Name rename field and cycle-safe parent picker (`parentOptions` excludes self + all descendants) +- Delete confirmation modal with impact-aware body text per D-13/D-14 +- Actions row: Delete Tag (left, destructive) / Save Changes (right, primary) + +### `src/client/routes/admin.tsx` + +Replaced the disabled `