feat: US-012 — GanttChart SVG component and global Timeline view

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Roberto Musso
2026-02-20 12:47:34 +01:00
parent e92d58a46e
commit ab517549a9
5 changed files with 487 additions and 4 deletions

View File

@@ -17,6 +17,8 @@
- `z.string().nullable().optional()` in tRPC inputs enables three-state semantics: undefined = don't change, null = clear, string = set value
- NewTaskDialog component at `src/renderer/components/tasks/NewTaskDialog.tsx` accepts `defaultProjectId` and `defaultStatus` props for reuse in Kanban column "+ Add" buttons
- `date-fns` is available as a transitive dependency of `react-day-picker` (shadcn/ui calendar)
- GanttChart component at `src/renderer/components/timeline/GanttChart.tsx` is reusable: accepts `defaultProjectId` to scope to a project (for US-015 inline timeline)
- AddCheckpointDialog at `src/renderer/components/timeline/AddCheckpointDialog.tsx` accepts `defaultProjectId` — hides project select when provided
- TanStack Router `validateSearch` with Zod schema for passing selected-item IDs via URL search params (e.g., `?projectId=...`)
---
@@ -203,3 +205,24 @@
- The Popover+Calendar date picker pattern is standard shadcn/ui: Popover wraps a Button trigger showing the formatted date, PopoverContent contains the Calendar
- Electron app runs at `http://localhost:5173` in dev mode but only within the Electron BrowserWindow — Playwright browser testing requires the Electron-specific test harness, not direct URL navigation
---
## 2026-02-20 - US-012
- What was implemented:
- Reusable `GanttChart` SVG component at `src/renderer/components/timeline/GanttChart.tsx`
- Accepts `{ checkpoints: GanttCheckpoint[], startDate: Date, endDate: Date, onDelete? }` props
- Custom SVG rendering: month labels on X axis, horizontal baseline `<line>`, `<circle>` dots for checkpoints positioned by date
- Dot fill logic: dark (#171717) for future approved checkpoints, green (#16a34a) for past approved, dashed outline (#737373) for pending AI suggestions (isApproved=0)
- Vertical red "Today" marker line at current date
- ResizeObserver for responsive SVG width
- foreignObject + shadcn/ui Popover on each dot click: shows title, formatted date, project name, and destructive Delete button
- `AddCheckpointDialog` component at `src/renderer/components/timeline/AddCheckpointDialog.tsx`: title Input (required), Popover+Calendar date (required), Select project dropdown (required in global view, hidden when `defaultProjectId` provided)
- Global Timeline route (`/timeline`) renders GanttChart with all checkpoints, project name lookup via Map from `projects.listAll`
- Legend showing dot types, empty state message when no checkpoints
- Files changed: `src/renderer/components/timeline/GanttChart.tsx` (new), `src/renderer/components/timeline/AddCheckpointDialog.tsx` (new), `src/renderer/routes/timeline.tsx`
- **Learnings for future iterations:**
- `foreignObject` inside SVG is the cleanest way to embed React components (like Popover) on SVG elements — set `overflow-visible` class to prevent clipping
- Checkpoints don't have a `status` field; use `isApproved=1` + `date < now` heuristic for "completed" vs "todo" dot color
- Date range for the Gantt is computed dynamically: 1 month before earliest date, 2 months after latest date — ensures comfortable visual padding
- GanttChart is designed for reuse: the `defaultProjectId` prop on AddCheckpointDialog pre-selects the project and hides the dropdown (for per-project timeline in US-015)
- `trpc.projects.listAll.useQuery(undefined, { enabled: showProjectSelect })` prevents unnecessary queries when project is already known
---