diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 4cf7da8..b1e33aa 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -39,8 +39,8 @@ 4. Adding a new visualization mode requires only implementing a ModeRenderer interface and registering it **Plans:** 2 plans Plans: -- [ ] 06-01-PLAN.md — Type contracts, state, registry, and shared utility extraction -- [ ] 06-02-PLAN.md — YearModeRenderer, orchestrator rewrite, test migration, visual check +- [x] 06-01-PLAN.md — Type contracts, state, registry, and shared utility extraction +- [x] 06-02-PLAN.md — YearModeRenderer, orchestrator rewrite, test migration, visual check ### Phase 7: Mode Switcher + Week Mode **Goal**: Users can switch between year and week visualization modes and toggle between hours and entry-count display @@ -52,7 +52,10 @@ Plans: 3. Hours/count toggle switches the color scale metric across both year and week modes without re-fetching data 4. Switching modes preserves the current filter selection 5. Vitest tests cover mode switcher interaction, week renderer output, and display toggle behavior -**Plans**: TBD +**Plans:** 2 plans +Plans: +- [ ] 07-01-PLAN.md — UI controls (mode switcher + metric toggle), Twig template, orchestrator wiring +- [ ] 07-02-PLAN.md — WeekModeRenderer implementation, registration, visual verification **UI hint**: yes ### Phase 8: Backend Aggregation + Filtering @@ -106,7 +109,7 @@ Note: Phases 7 and 8 can execute in parallel (both depend only on Phase 6). | 4. Heatmap Interaction | v1.0 | 2/2 | Complete | 2026-04-08 | | 5. Polish | v1.0 | 2/2 | Complete | 2026-04-08 | | 6. Renderer Architecture | v1.1 | 0/2 | Planned | - | -| 7. Mode Switcher + Week Mode | v1.1 | 0/? | Not started | - | +| 7. Mode Switcher + Week Mode | v1.1 | 0/2 | Planned | - | | 8. Backend Aggregation + Filtering | v1.1 | 0/? | Not started | - | | 9. Day + Combined Modes | v1.1 | 0/? | Not started | - | | 10. Entity Pickers | v1.1 | 0/? | Not started | - | diff --git a/.planning/phases/07-mode-switcher-week-mode/07-01-PLAN.md b/.planning/phases/07-mode-switcher-week-mode/07-01-PLAN.md new file mode 100644 index 0000000..cb8593a --- /dev/null +++ b/.planning/phases/07-mode-switcher-week-mode/07-01-PLAN.md @@ -0,0 +1,264 @@ +--- +phase: 07-mode-switcher-week-mode +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - Resources/views/widget/heatmap.html.twig + - assets/src/ui/controls.ts + - assets/src/heatmap.ts + - Resources/public/heatmap.css + - assets/test/controls.test.ts +autonomous: true +requirements: [VIZ-01, VIZ-05, TEST-01] +must_haves: + truths: + - "A segmented control in the widget header shows Year and Week buttons" + - "Clicking Week sets state.mode to week and triggers doRender" + - "A separate compact segmented control shows Hours and Count buttons" + - "Clicking Count sets state.metric to count and triggers doRender" + - "Controls persist across re-renders (live in card header, not SVG area)" + - "Stats row is hidden when mode is week, shown when mode is year" + artifacts: + - path: "assets/src/ui/controls.ts" + provides: "createModeControl and createMetricControl functions" + exports: ["createModeControl", "createMetricControl"] + - path: "Resources/views/widget/heatmap.html.twig" + provides: "Controls placeholder div in card header" + contains: "heatmap-controls" + - path: "assets/test/controls.test.ts" + provides: "Tests for mode switcher and metric toggle" + min_lines: 40 + key_links: + - from: "assets/src/heatmap.ts" + to: "assets/src/ui/controls.ts" + via: "import createModeControl, createMetricControl" + pattern: "import.*controls" + - from: "assets/src/ui/controls.ts" + to: "state.mode / state.metric" + via: "onChange callbacks" + pattern: "onChange" +--- + + +Build the mode switcher and hours/count toggle UI controls, wire them into the heatmap orchestrator, and add the Twig template placeholder. + +Purpose: Enables switching between year/week modes and hours/count metrics -- the UI backbone for all v1.1 visualization features. +Output: Two Tabler nav-segmented controls in the widget header, wired to HeatmapState, with tests. + + + +@/home/toph/code/toph/kimai-heatmap/.claude/get-shit-done/workflows/execute-plan.md +@/home/toph/code/toph/kimai-heatmap/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/07-mode-switcher-week-mode/07-CONTEXT.md +@.planning/phases/07-mode-switcher-week-mode/07-RESEARCH.md +@.planning/phases/07-mode-switcher-week-mode/07-UI-SPEC.md +@.planning/phases/06-renderer-architecture/06-01-SUMMARY.md +@.planning/phases/06-renderer-architecture/06-02-SUMMARY.md + + + + +From assets/src/types.ts: +```typescript +export type DisplayMetric = 'hours' | 'count'; +export type HeatmapMode = 'year' | 'week' | 'day' | 'combined'; +``` + +From assets/src/state.ts: +```typescript +export interface HeatmapState { + mode: HeatmapMode; + metric: DisplayMetric; + filters: FilterState; + weekStart: string; + data: HeatmapData | null; +} +export function createInitialState(weekStart: string): HeatmapState; +``` + +From assets/src/renderers/registry.ts: +```typescript +export function getRenderer(mode: string): ModeRenderer; +``` + +From assets/src/shared/stats.ts: +```typescript +export function renderStats(container: HTMLElement, days: DayEntry[]): void; +``` + +From assets/src/heatmap.ts (current doRender pattern): +```typescript +const doRender = () => { + if (!state.data) return; + const renderer = getRenderer(state.mode); + renderer.destroy?.(); + renderer.render({ container: svgArea, data: state.data, state, config: {...}, onCellClick, emptyMessage: ... }); + renderStats(container, state.data.days); + svgArea.scrollLeft = svgArea.scrollWidth; +}; +``` + + + + + + + Task 1: Create UI controls module and wire into heatmap orchestrator + + assets/src/ui/controls.ts, + assets/src/heatmap.ts, + Resources/views/widget/heatmap.html.twig, + Resources/public/heatmap.css, + assets/test/controls.test.ts + + + assets/src/heatmap.ts, + assets/src/types.ts, + assets/src/state.ts, + assets/src/shared/stats.ts, + Resources/views/widget/heatmap.html.twig, + Resources/public/heatmap.css, + .planning/phases/07-mode-switcher-week-mode/07-UI-SPEC.md + + + - createModeControl('year', [{key:'year',label:'Year'},{key:'week',label:'Week'}], cb) returns nav element with class 'nav nav-segmented' and role='tablist' + - createModeControl: first button has class 'nav-link active', second has 'nav-link' (no active) + - createModeControl: clicking inactive button adds 'active' to it, removes 'active' from previous, calls onChange with key + - createMetricControl('hours', cb) returns nav element with class 'nav nav-segmented nav-sm' + - createMetricControl: clicking 'Count' calls onChange with 'count' + - Both controls have role='tab' on buttons and aria-selected attribute + + + **1. Create `assets/src/ui/controls.ts`** with two exported functions: + + `createModeControl(activeMode: string, modes: Array<{key: string; label: string}>, onChange: (mode: string) => void): HTMLElement` + - Create `