# Phase 3: Core Heatmap Rendering — Plan **Goal:** The dashboard widget renders a fully styled d3.js calendar heatmap from live Kimai data **Requirements:** HEAT-01, HEAT-02, HEAT-03, HEAT-04, HEAT-05, HEAT-06, HEAT-08, TEST-03 **Research:** phase-3/03-RESEARCH.md ## Success Criteria 1. The widget displays a weeks-by-days calendar grid with cells colored by hours tracked (darker = more hours) 2. Hovering any cell shows a tooltip with date, hours, and entry count 3. Day-of-week labels appear on the Y-axis and month boundary labels along the top 4. Days with no tracked time render with a distinct "no data" color 5. Colors use Kimai's theme CSS variables (works with both light and dark themes) ## Key Decisions from Research - **IIFE bundle** — esbuild compiles d3 modules into a single IIFE file (not ESM), matching Kimai's plain ` {% endblock %} {% endembed %} ``` **Task 2: Install assets** ```bash cd dev/kimai && bin/console assets:install public --symlink ``` This creates `dev/kimai/public/bundles/kimaiheatmap/` → plugin's `Resources/public/`. **Task 3: Add build step to dev workflow** Add a note in the README or dev setup that `npm run build` must be run after JS changes. Optionally add an esbuild watch command to process-compose. **Task 4: Update .gitignore** Add to `.gitignore`: ``` node_modules/ ``` Ensure `Resources/public/heatmap.js` and `Resources/public/heatmap.css` are NOT gitignored — they're the built artifacts that ship with the plugin. **Commit:** `feat: wire heatmap JS/CSS into dashboard widget template` **Task 5: Verify (CHECKPOINT — requires manual verification)** 1. Build JS: `npm run build` 2. Install assets: `cd dev/kimai && bin/console assets:install public --symlink` 3. Clear cache: `cd dev/kimai && bin/console cache:clear` 4. Start dev stack: `process-compose -f dev/process-compose.yaml -p 0 up` 5. Open browser: `http://127.0.0.1:8010` 6. Login: `susan_super` / `password` **Verification checklist:** - [ ] Heatmap widget renders a calendar grid with colored cells - [ ] Hovering a cell shows tooltip with date, hours, count - [ ] Day-of-week labels (Mon, Wed, Fri) visible on left - [ ] Month labels visible along top - [ ] Empty days show distinct "no data" color - [ ] Switch to dark theme — colors adapt correctly - [ ] PHPUnit tests still pass: `php dev/kimai/vendor/bin/phpunit --configuration Tests/phpunit.xml` - [ ] Vitest tests pass: `npm test` ## Requirement Coverage | Requirement | Plan | Verified By | |-------------|------|-------------| | HEAT-01 | 03-02 | Calendar grid renders with d3; Vitest grid structure test | | HEAT-02 | 03-02 | Color scale maps hours to intensity; Vitest color mapping test | | HEAT-03 | 03-02 | Tooltip on hover; Vitest tooltip test | | HEAT-04 | 03-02 | Day labels in SVG; Vitest day labels test | | HEAT-05 | 03-02 | Month labels in SVG; Vitest month labels test | | HEAT-06 | 03-02 | Empty cells have distinct class; Vitest empty cell test | | HEAT-08 | 03-02, 03-03 | CSS uses `--tblr-*` variables; manual dark/light theme check | | TEST-03 | 03-02 | Vitest test suite for d3 rendering | ## Risks | Risk | Mitigation | |------|------------| | `assets:install --symlink` doesn't follow plugin symlink chain | Fall back to `assets:install` (copy mode) or manual symlink | | d3 modules don't work in IIFE bundle | esbuild handles ESM→IIFE natively; verified in Task 4 of 03-01 | | jsdom lacks SVG support for Vitest | d3-selection works with jsdom; test DOM attributes not visual rendering | | `kimai_context` not available in widget template | Guard with `is defined` check; fall back to event listener only | | Widget too wide/narrow in different dashboard layouts | Use SVG viewBox for responsive scaling; `overflow-x: auto` as safety net | --- *Plan created: 2026-04-08*