5.6 KiB
Phase 4: Heatmap Interaction — Plan
Goal: Users can click through to daily details and filter the heatmap by project or activity Requirements: HEAT-07, INTR-01, TEST-04 Research: phase-4/04-RESEARCH.md
Success Criteria
- Clicking a day cell navigates to Kimai's timesheet view filtered to that specific date
- A dropdown allows filtering the heatmap to show data for a single project or activity
- Filtering updates the heatmap in place without a full page reload
- JavaScript tests verify click navigation and tooltip interaction behavior
Key Decisions from Research
- Click navigation —
window.location.hrefto/timesheetwith date param. Timesheet URL passed viadata-timesheet-urltemplate attribute. - Project dropdown — Fetch
/api/projectsclient-side, render<select>above heatmap. On change, re-fetch/heatmap/data?project=<id>and re-render. - No backend changes — HeatmapController already supports
?project=filtering. Project API is built into Kimai. - Activity filtering deferred — INTR-01 says "project or activity". Start with project filter; activity can be added as a follow-up if needed since the API pattern is identical.
Waves
Wave 1: Click-through + Project Filter (autonomous)
Plan 04-01: Day cell click navigation + project filter dropdown
Objective: Add click-to-navigate on day cells and a project filter dropdown that re-renders the heatmap.
Task 1: Update types
Add to assets/src/types.ts:
export interface Project {
id: number;
name: string;
}
export interface HeatmapOptions {
dataUrl: string;
timesheetUrl: string;
}
Task 2: Add click handler to heatmap cells
In assets/src/heatmap.ts:
- Add a
timesheetUrlparameter torenderHeatmap(passed via options or config) - On each
<rect>cell, add aclickevent handler:- Navigate to
${timesheetUrl}?dateRange=${dateStr}+-+${dateStr} - Only navigate if the cell has data (skip empty days)
- Navigate to
- Add
cursor: pointerstyle to cells with data - Add
data-dateattribute to each rect for testability
Task 3: Add project filter dropdown
In assets/src/heatmap.ts, update the init function:
- Before fetching heatmap data, fetch
/api/projects?visible=1&orderBy=name - Create a
<select>element with:- Default option: "All projects"
- One
<option>per project withvalue=project.id
- Insert the select above the heatmap container (or in a header area)
- On
change, re-fetch heatmap data with?project=<selectedId>and callrenderHeatmapagain - Style the select to match Kimai's form controls (use
form-selectBootstrap class)
Task 4: Update widget template
Update Resources/views/widget/heatmap.html.twig:
- Add
data-timesheet-url="{{ path('timesheet') }}"to the container div - The
data-urlattribute already exists for the heatmap API
Task 5: Build and verify
npm run build— produces updated bundlenpm test— existing tests still passphp dev/kimai/vendor/bin/phpunit --configuration Tests/phpunit.xml— PHP tests pass
Commit: feat: add day click navigation and project filter dropdown
Wave 2: Interaction Tests (autonomous)
Plan 04-02: Vitest tests for click navigation and filter behavior
Objective: Add JavaScript tests covering the new interaction features.
Task 1: Write click navigation tests
Add to assets/test/heatmap.test.ts:
- Click on cell with data — simulate click on a cell, verify
window.location.hrefwas set to expected timesheet URL with date - Click on empty cell — simulate click, verify no navigation occurred
- Cell cursor style — cells with data have
cursor: pointer, empty cells don't
Test approach:
- Mock
window.location(useObject.definePropertyor vitest'svi.stubGlobal) - Pass
timesheetUrloption torenderHeatmap - Use
container.querySelector('[data-date="2026-04-01"]')to find specific cells - Dispatch
clickevent
Task 2: Write project filter tests
Add tests for the filter dropdown behavior:
- Dropdown renders with projects — after init with mocked fetch, a
<select>element exists with project options - Filter triggers re-fetch — changing dropdown value triggers a new fetch with
?project=<id>param - Heatmap re-renders — after filter change, new data is rendered (different rect count or content)
- "All projects" resets filter — selecting default option fetches without project param
Test approach:
- Mock
fetchto return project list and heatmap data - Call
init(container)and await fetch completion - Programmatically change select value and dispatch
changeevent - Assert fetch was called with correct URL params
Verification:
npm test— all tests pass (existing + new)npm run build— bundle builds successfully
Commit: test: add click navigation and project filter tests
Requirement Coverage
| Requirement | Plan | Verified By |
|---|---|---|
| HEAT-07 | 04-01 | Click navigates to timesheet; Vitest click test |
| INTR-01 | 04-01 | Project dropdown filters heatmap; Vitest filter test |
| TEST-04 | 04-02 | Vitest tests for click + filter interaction |
Risks
| Risk | Mitigation |
|---|---|
| Timesheet date filter URL format wrong | Test with actual Kimai instance; adjust format if needed |
/api/projects requires different auth |
API uses same session auth as the dashboard; should work |
| Dropdown styling doesn't match Kimai | Use Bootstrap form-select class from Tabler theme |
| Re-render flickers | Clear container and re-render; fast enough for ~365 rects |
Plan created: 2026-04-08