# Phase 4: Heatmap Interaction - Context **Gathered:** 2026-04-08 **Status:** Ready for planning ## Phase Boundary Add click-through navigation from heatmap day cells to Kimai's timesheet view, and a project filter dropdown that re-fetches and re-renders the heatmap without page reload. ## Implementation Decisions ### Click Navigation - Clicking any day cell navigates to Kimai's timesheet list filtered by that date (`/en/timesheet/?daterange=YYYY-MM-DD`) - Empty cells (no data) are clickable too — user may want to add time for that day - Pointer cursor + subtle hover highlight (opacity change) on all cells as click affordance - If a project filter is active, preserve it in the timesheet URL (`&projects[]=N`) ### Project Filter - Filter dropdown positioned next to the heatmap SVG (right side), using the spare horizontal space since the heatmap isn't full card width - Projects only — no activity filter (API already supports `?project=N`) - Default state: "All Projects" selected, no filter applied - Selecting a project fetches from API with `?project=N`, re-renders heatmap in place; color scale recalculates for filtered data ### Testing Strategy - Vitest tests for click navigation: verify click handler sets `window.location.href` to correct Kimai URL pattern - Vitest tests for filter dropdown: verify dropdown renders, selecting fires fetch with correct query param, heatmap re-renders with new data - Fixture data matching existing HeatmapData shape from prior tests ### Claude's Discretion - Exact Kimai timesheet URL format (verify against Kimai source) - Filter dropdown styling details (should use Tabler/Kimai form classes) - How to populate the project list (new API endpoint or extend existing one) ## Existing Code Insights ### Reusable Assets - `renderHeatmap(container, data, config)` in `assets/src/heatmap.ts` — main rendering function, can be called again with new data for filter updates - `HeatmapData` / `DayEntry` types in `assets/src/types.ts` — shared data shape - `HeatmapController::data()` already accepts `?project` query parameter - `HeatmapService::getDailyAggregation()` already supports `?int $projectId` parameter ### Established Patterns - d3 selections for SVG rendering with data binding - Tooltip positioning via fixed-position div (mouseenter/mouseleave) - IIFE format with `KimaiHeatmap` global, `kimai.initialized` event for deferred init - Tabler CSS variables for theming (`--tblr-bg-surface`, `--tblr-body-color`, etc.) ### Integration Points - `data-url` attribute on container div passes API endpoint to JS - Widget template at `Resources/views/widget/heatmap.html.twig` with Tabler card embed - CSS at `Resources/public/heatmap.css` - Route `heatmap_data` resolves to `/heatmap/data` ## Specific Ideas - User wants filter next to the heatmap (not in header or below) since the SVG doesn't fill the full card width — use the horizontal space to the right ## Deferred Ideas None — discussion stayed within phase scope.