diff --git a/docs/plans/2026-02-24-tree-fork-ui-design.md b/docs/plans/2026-02-24-tree-fork-ui-design.md new file mode 100644 index 00000000..2313fcdb --- /dev/null +++ b/docs/plans/2026-02-24-tree-fork-ui-design.md @@ -0,0 +1,108 @@ +# Tree Forking UI Design + +> **Date:** 2026-02-24 +> **Feature:** Personal tree forking — explicit modal, reason capture, fork badge + +--- + +## Overview + +Add a proper fork UX to the flow library. The backend is fully complete (POST `/trees/:id/fork`, fork fields in API responses, tests passing). The frontend needs: a `ForkModal` component with a "Reason for Forking" field, updated fork handlers in `TreeLibraryPage` and `MyTreesPage`, fork field types on `Tree`, and a "Fork" chip on tree cards. + +--- + +## What's Being Built + +### 1. Types — `frontend/src/types/tree.ts` + +Add `ForkInfo` interface and fork fields to `Tree`: + +```ts +export interface ForkInfo { + parent_tree_id: string + parent_tree_name: string | null + fork_depth: number + fork_reason: string | null + has_parent_updates: boolean +} +``` + +Add to `Tree`: +```ts +fork_info?: ForkInfo | null +parent_tree_id?: string | null +fork_depth?: number +``` + +Add to `TreeCreate`: +```ts +fork_reason?: string +``` + +### 2. `ForkModal` Component — `frontend/src/components/library/ForkModal.tsx` + +A focused dialog with: +- **Name field** — pre-filled with `"Copy of "` +- **"Reason for Forking"** — optional textarea (placeholder: "e.g. customizing for a specific client…") +- **Cancel** (secondary) + **Fork** (gradient) buttons +- Calls `treesApi.fork(treeId, { name, fork_reason })` on submit +- Success: shows toast, navigates to `/my-trees` +- Error: shows inline error, stays open + +### 3. Update Fork Handlers + +In `TreeLibraryPage` and `MyTreesPage`, replace the current silent `handleForkTree` (which calls `treesApi.fork()` directly) with a handler that: +1. Sets the selected tree to fork +2. Opens `ForkModal` + +The modal handles the actual API call and navigation. + +### 4. "Fork" Badge on Tree Cards + +In `TreeGridView`, `TreeListView`, and `TreeTableView`, render a small chip when `fork_depth > 0` (or `parent_tree_id` is set on `TreeListItem`): + +```tsx +{tree.fork_depth > 0 && ( + + Fork + +)} +``` + +`fork_depth` needs to be added to `TreeListItem` (it comes from the backend list response). + +--- + +## What's NOT Being Built + +- Lineage tree view / "forked from" link — out of scope +- "Has updates available" notification — out of scope +- Fork management / ancestry tracking UI — out of scope + +--- + +## Data Flow + +``` +User clicks "Fork" on a card + → onForkTree(tree) called + → parent sets forkTarget state + opens ForkModal + → user fills Name + optional Reason + → ForkModal calls treesApi.fork(treeId, { name, fork_reason }) + → on success: toast "Flow forked!" + navigate('/my-trees') + → My Trees page loads, forked flow shows "Fork" badge +``` + +--- + +## Files Changed + +| File | Change | +|------|--------| +| `frontend/src/types/tree.ts` | Add `ForkInfo`, fork fields on `Tree`, `fork_depth` on `TreeListItem` | +| `frontend/src/components/library/ForkModal.tsx` | New component | +| `frontend/src/pages/TreeLibraryPage.tsx` | Open modal instead of silent fork | +| `frontend/src/pages/MyTreesPage.tsx` | Open modal instead of silent fork | +| `frontend/src/components/library/TreeGridView.tsx` | Fork badge | +| `frontend/src/components/library/TreeListView.tsx` | Fork badge | +| `frontend/src/components/library/TreeTableView.tsx` | Fork badge |