kimai-plugin-heatmap/.planning/milestones/v1.0-phases/phase-4/PLAN.md
Christopher Mühl 244c7c66fc
chore: archive v1.0 milestone
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 23:25:26 +02:00

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

  1. Clicking a day cell navigates to Kimai's timesheet view filtered to that specific date
  2. A dropdown allows filtering the heatmap to show data for a single project or activity
  3. Filtering updates the heatmap in place without a full page reload
  4. JavaScript tests verify click navigation and tooltip interaction behavior

Key Decisions from Research

  • Click navigationwindow.location.href to /timesheet with date param. Timesheet URL passed via data-timesheet-url template attribute.
  • Project dropdown — Fetch /api/projects client-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 timesheetUrl parameter to renderHeatmap (passed via options or config)
  • On each <rect> cell, add a click event handler:
    • Navigate to ${timesheetUrl}?dateRange=${dateStr}+-+${dateStr}
    • Only navigate if the cell has data (skip empty days)
  • Add cursor: pointer style to cells with data
  • Add data-date attribute to each rect for testability

Task 3: Add project filter dropdown

In assets/src/heatmap.ts, update the init function:

  1. Before fetching heatmap data, fetch /api/projects?visible=1&orderBy=name
  2. Create a <select> element with:
    • Default option: "All projects"
    • One <option> per project with value=project.id
  3. Insert the select above the heatmap container (or in a header area)
  4. On change, re-fetch heatmap data with ?project=<selectedId> and call renderHeatmap again
  5. Style the select to match Kimai's form controls (use form-select Bootstrap 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-url attribute already exists for the heatmap API

Task 5: Build and verify

  • npm run build — produces updated bundle
  • npm test — existing tests still pass
  • php 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:

  1. Click on cell with data — simulate click on a cell, verify window.location.href was set to expected timesheet URL with date
  2. Click on empty cell — simulate click, verify no navigation occurred
  3. Cell cursor style — cells with data have cursor: pointer, empty cells don't

Test approach:

  • Mock window.location (use Object.defineProperty or vitest's vi.stubGlobal)
  • Pass timesheetUrl option to renderHeatmap
  • Use container.querySelector('[data-date="2026-04-01"]') to find specific cells
  • Dispatch click event

Task 2: Write project filter tests

Add tests for the filter dropdown behavior:

  1. Dropdown renders with projects — after init with mocked fetch, a <select> element exists with project options
  2. Filter triggers re-fetch — changing dropdown value triggers a new fetch with ?project=<id> param
  3. Heatmap re-renders — after filter change, new data is rendered (different rect count or content)
  4. "All projects" resets filter — selecting default option fetches without project param

Test approach:

  • Mock fetch to return project list and heatmap data
  • Call init(container) and await fetch completion
  • Programmatically change select value and dispatch change event
  • 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