142 lines
5.6 KiB
Markdown
142 lines
5.6 KiB
Markdown
# 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 navigation** — `window.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`:
|
|
```typescript
|
|
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*
|