11 KiB
Project Research Summary
Project: Kimai Heatmap Plugin Domain: Time-tracking dashboard widget (Symfony bundle + d3.js visualization) Researched: 2026-04-08 Confidence: MEDIUM
Executive Summary
This is a Kimai 2.x plugin that adds a GitHub-style activity heatmap to the dashboard. The product pattern is well-established: calendar grid, color intensity for activity levels, tooltips, click-through navigation. The technology choices are straightforward -- a Symfony bundle for the backend (matching Kimai's framework), d3.js sub-modules for the SVG visualization, TypeScript for type safety, and esbuild for bundling. No additional database is needed; the plugin reads from Kimai's existing timesheet table.
The recommended approach is a strict 4-phase build: plugin scaffold first (prove the widget appears on the dashboard), then data layer (aggregation API), then visualization (d3 heatmap rendering), then interactivity (filters, toggles, click navigation). This ordering is dictated by hard dependencies -- you cannot render a heatmap without data, and you cannot build data queries without a working plugin scaffold. The Nix dev environment setup is a prerequisite phase that should be timeboxed to one day.
The primary risks are: (1) Kimai's plugin API is under-documented and shifts between releases -- the widget interface, DI tags, and asset conventions all need verification against the target Kimai version before writing code; (2) timezone-incorrect day aggregation will produce wrong data silently -- aggregation must happen server-side in the user's configured timezone; (3) the Nix + PHP + Kimai dev environment setup can consume days if not timeboxed. All three are manageable with the mitigations outlined below.
Key Findings
Recommended Stack
The plugin is a standard Symfony bundle running inside Kimai 2.x. The frontend is a self-contained d3.js visualization bundled with esbuild and shipped as a single JS file in Resources/public/. No integration with Kimai's Webpack Encore build is needed or desired.
Core technologies:
- PHP 8.2+ / Symfony 6.4 LTS: Must match Kimai's runtime exactly. Verify against Kimai's
composer.json. - d3.js sub-modules (d3-scale, d3-selection, d3-time, d3-time-format, d3-scale-chromatic, d3-shape): Selective imports keep bundle size to ~50-80KB vs ~500KB for full d3.
- TypeScript: d3 v7 ships types. Catches data shape bugs at build time. esbuild handles
.tsnatively. - esbuild: Single-command bundling of d3 modules into one JS file. Simpler than Webpack Encore for a single widget.
- Vitest + jsdom: ESM-native testing for d3 code. Jest struggles with d3 v7's ESM-only modules.
- PHPUnit: Matches Kimai's own test framework.
- Nix flake: Reproducible dev env with PHP 8.2, Composer, Node 22, SQLite.
Expected Features
Must have (table stakes):
- Calendar grid layout (weeks x days) with color intensity mapping
- Tooltip on hover showing date, hours, entry count
- Day-of-week and month labels
- Empty state rendering (no-data days visible, not invisible)
- Click-through to Kimai timesheet filtered by date
- Kimai theme integration via CSS variables
- Default trailing 12-month range
Should have (differentiators):
- Toggle between hours/day and entry count (different questions answered)
- Project/activity filter dropdown
- Configurable time range (3/6/12 months)
- Streak indicator and summary stats row
Defer indefinitely:
- Hour-of-day matrix, export/share, goal setting, animations, multi-user, mobile layout, custom color picker -- all anti-features for a personal tracking widget.
Architecture Approach
The architecture follows a clean separation: thin Twig template (HTML shell + controls), XHR data fetching from a dedicated API controller, and client-side d3 rendering. The widget registers via Kimai's WidgetInterface + kimai.widget DI tag. Data flows from Kimai's timesheet table through a HeatmapService (Doctrine QueryBuilder, GROUP BY date, user-scoped), to a HeatmapController (JSON API), to the d3 module (SVG rendering).
Major components:
- KimaiHeatmapBundle -- Symfony bundle class implementing
PluginInterface, auto-discovered by Kimai - HeatmapWidget --
WidgetInterfaceimplementation, registers on dashboard, renders Twig template - HeatmapController -- API endpoint (
/api/plugins/heatmap/data) returning aggregated JSON - HeatmapService -- Server-side aggregation (hours/count per day, timezone-aware, user-scoped)
- d3 heatmap module -- TypeScript, calendar grid rendering, event handling, theme integration
Critical Pitfalls
- Kimai plugin API instability -- Pin to a specific Kimai version. Write an integration test that boots the Symfony kernel with the plugin loaded. Subscribe to Kimai releases for breaking change awareness.
- Timezone mismatch (PHP vs JS day boundaries) -- Aggregate by day on the PHP side using
$user->getTimezone(). Send date strings to the frontend, never raw timestamps. Compare output against Kimai's own reports. - Widget system misunderstanding -- Use
WidgetInterface+kimai.widgetDI tag from the start. Do not build a standalone controller page. Study existing Kimai widgets before coding. - Asset loading failures -- Ship prebuilt JS in
Resources/public/, loaded via<script>in Twig. Do not hook into Kimai's Webpack Encore. Start with inline script as fallback. - Nix + PHP dev environment -- Timebox to 1 day. Use SQLite. Set
COMPOSER_HOMEto a writable temp dir. Fall back to Docker if stuck.
Implications for Roadmap
Phase 0: Development Environment
Rationale: Everything blocks on a working Kimai instance with the plugin loaded. Nix+PHP setup is the highest-risk non-code task.
Delivers: nix develop shell with PHP 8.2, Composer, Node 22, local Kimai on SQLite, plugin symlinked into var/plugins/.
Avoids: Pitfall 5 (Nix+PHP complexity) -- timeboxed to 1 day with Docker fallback.
Phase 1: Plugin Scaffold + Data Layer
Rationale: Proves the plugin loads, widget appears on dashboard, and data flows correctly. These are the hardest unknowns (Kimai plugin API, widget system, timezone aggregation). Delivers: Empty widget visible on dashboard. API endpoint returning correct per-day aggregated JSON. PHPUnit tests for service and controller. Addresses: Table stakes groundwork (no visible features yet, but the pipeline works end-to-end). Avoids: Pitfalls 1, 2, 3, 7 (plugin API, timezone, widget system, asset loading).
Phase 2: Core Heatmap Visualization
Rationale: With data flowing, build the actual product. The calendar grid with all table-stakes features is the MVP. Delivers: d3 calendar heatmap rendering from API data. Tooltips, labels, empty state, click-through, theme integration. Addresses: All table-stakes features from FEATURES.md. Avoids: Pitfalls 4, 8, 9 (d3 bundle size, color theming, empty state).
Phase 3: Interactivity and Polish
Rationale: Differentiators that make the widget genuinely useful beyond a static picture. Low complexity, high value. Delivers: Hours/count toggle, project filter dropdown, configurable time range, streak indicator, summary stats. Addresses: All differentiator features from FEATURES.md. Avoids: Pitfall 10 (hardcoded URLs -- generate timesheet URL template server-side).
Phase Ordering Rationale
- Phase 0 before everything: no plugin code without a working dev environment.
- Phase 1 combines scaffold + data because the scaffold alone is not testable in a meaningful way -- you need data flowing to confirm the widget system integration works.
- Phase 2 is pure frontend work that depends on Phase 1's API but is otherwise independent.
- Phase 3 layers interactivity onto an already-working heatmap. Each feature is independently shippable.
- This ordering front-loads risk: the hardest unknowns (Kimai plugin API, widget system, timezone handling) are resolved in Phase 1. Phases 2 and 3 use well-established d3 patterns with high confidence.
Research Flags
Phases likely needing deeper research during planning:
- Phase 0: Kimai Nix setup is uncommon. May need to inspect current Kimai
composer.jsonand PHP extension requirements live. - Phase 1: Kimai's
WidgetInterface, DI tags, and route registration need verification against the target Kimai version. The plugin API documentation is sparse -- reading existing plugin source code is essential.
Phases with standard patterns (skip research):
- Phase 2: d3 calendar heatmap is a well-documented pattern with official Observable examples. TypeScript + esbuild bundling is straightforward.
- Phase 3: Filter dropdowns, toggle buttons, and URL template interpolation are standard frontend work.
Confidence Assessment
| Area | Confidence | Notes |
|---|---|---|
| Stack | MEDIUM | PHP/Symfony/d3 choices are solid. Exact Kimai version requirements need live verification. |
| Features | MEDIUM-HIGH | GitHub-style heatmap is a proven pattern. Feature priorities are well-reasoned from reference products. |
| Architecture | MEDIUM | Symfony bundle + API + d3 rendering is sound. Kimai-specific widget API details (interface methods, DI tags, Twig blocks) need verification. |
| Pitfalls | MEDIUM-HIGH | Timezone and d3 pitfalls are universal and well-understood. Kimai-specific pitfalls (plugin API instability, asset pipeline) based on training data, not live docs. |
Overall confidence: MEDIUM
Gaps to Address
- Kimai widget API verification: The exact
WidgetInterfacemethods,AbstractWidgetTypehierarchy, andkimai.widgetDI tag must be verified against the target Kimai release. This is the single biggest unknown. - Kimai asset serving for plugins: Whether Kimai uses
assets:installto copyResources/public/topublic/bundles/or has a different mechanism needs checking. - Kimai CSS variable names: The actual theme variable names for colors are unknown. Fallback values mitigate this, but proper theme integration requires inspecting a running Kimai instance.
- Kimai timesheet URL structure: The route name and filter parameter format for click-through navigation must be verified.
- Twig widget template blocks: The exact block names (
widget_content,widget_javascript) and base template path need verification.
Sources
Primary (HIGH confidence)
- d3.js documentation and calendar heatmap patterns (d3js.org, Observable)
- Symfony bundle system documentation (symfony.com)
- Vitest and esbuild documentation
- General timezone handling and SVG performance patterns
Secondary (MEDIUM confidence)
- Kimai plugin development documentation (kimai.org/documentation/plugin-development.html) -- from training data
- Kimai GitHub repository structure and existing plugins -- from training data
- Kimai widget system internals -- from training data
Tertiary (LOW confidence)
- Kimai Twig template block names and asset path conventions -- inferred, needs validation
- Kimai CSS custom property names -- inferred, needs validation
Research completed: 2026-04-08 Ready for roadmap: yes