# Technology Stack **Project:** Kimai Heatmap Plugin **Researched:** 2026-04-08 **Note:** WebSearch/WebFetch unavailable. Recommendations based on training data (cutoff May 2025). Versions should be verified against current Kimai releases before starting development. ## Recommended Stack ### Core Framework (Kimai Plugin) | Technology | Version | Purpose | Why | Confidence | |------------|---------|---------|-----|------------| | PHP | 8.2+ | Plugin runtime | Kimai 2.x requires PHP 8.1 minimum; 8.2 for current features and performance. Verify against Kimai's composer.json. | MEDIUM | | Symfony | 6.4 LTS | Bundle framework | Kimai 2.x is built on Symfony 6.4 LTS. Plugins must match the host Symfony version exactly. | MEDIUM | | Kimai | 2.x (latest) | Host application | Target the current stable release. Check github.com/kimai/kimai/releases before starting. | MEDIUM | ### Visualization | Technology | Version | Purpose | Why | Confidence | |------------|---------|---------|-----|------------| | d3 | ^7.9 | Core visualization library | d3 v7 is the current stable. ESM-native, tree-shakeable. Use specific sub-modules, not the full bundle. | HIGH | | d3-scale | ^4.0 | Color and position scales | Needed for mapping hours/counts to color intensities and day positions. | HIGH | | d3-selection | ^3.0 | DOM manipulation | Core d3 pattern for binidng data to SVG elements. | HIGH | | d3-time | ^3.1 | Date calculations | Week/day grid layout calculations for the calendar. | HIGH | | d3-time-format | ^4.1 | Date formatting | Tooltip and axis labels. | HIGH | | d3-scale-chromatic | ^3.1 | Color schemes | Provides sequential color scales (Greens, Blues) as starting points before mapping to Kimai theme vars. | HIGH | | d3-shape | ^3.2 | Rect generation | For the heatmap cell rectangles. | HIGH | **Do NOT use:** - `cal-heatmap` or other d3-wrapper heatmap libraries: They add abstraction over d3 that limits customization (Kimai theme integration, click-to-navigate, toggle modes). Rolling your own with raw d3 modules is straightforward for a calendar heatmap and gives full control. - `d3` full bundle import: Import only the sub-modules you need. Keeps the asset small and avoids polluting the Kimai frontend. ### Testing | Technology | Version | Purpose | Why | Confidence | |------------|---------|---------|-----|------------| | PHPUnit | ^10.5 or ^11.0 | Backend tests | Kimai uses PHPUnit for its own tests. Match the version Kimai ships with in its dev dependencies. | MEDIUM | | Vitest | ^3.0 | JS heatmap tests | Fast, ESM-native, works with d3's ESM modules out of the box. Jest struggles with ESM d3 imports without transformation config. | HIGH | | jsdom | (via vitest) | DOM environment | Vitest's jsdom environment provides enough DOM for d3 selection/rendering tests without a browser. | HIGH | **Do NOT use:** - `Jest` for JS tests: d3 v7 is ESM-only. Jest's ESM support requires `--experimental-vm-modules` and transform config. Vitest handles ESM natively. - `Cypress`/`Playwright` for the heatmap: Overkill for a single widget. SVG output assertions via jsdom + snapshot testing covers the rendering. Save E2E for integration testing against a running Kimai instance if needed later. ### Development Environment | Technology | Version | Purpose | Why | Confidence | |------------|---------|---------|-----|------------| | Nix flake | - | Reproducible dev env | Matches Toph's NixOS infra. Provides PHP, Node, Composer, and a local Kimai instance. | HIGH | | Composer | ^2.7 | PHP dependency management | Standard for Symfony/Kimai. | HIGH | | npm | ^10.x | JS dependency management | For d3 modules and Vitest. Simpler than yarn/pnpm for a single-widget plugin. | HIGH | | esbuild | ^0.24 | JS bundling | Bundle d3 modules into a single file for Kimai's asset pipeline. Faster than webpack, simpler config, handles ESM natively. | HIGH | **Do NOT use:** - `Webpack Encore`: Kimai's own frontend uses it, but for a plugin shipping a single JS file, esbuild is simpler. One build command, no Symfony Encore config to maintain. - `Docker` for dev: The project spec calls for Nix flake. Docker would work but adds friction on NixOS and doesn't match the user's infrastructure. ### Database No additional database needed. The plugin reads from Kimai's existing `kimai2_timesheet` table via Kimai's `TimesheetRepository` or a custom DQL query. No migrations required. ## Kimai Plugin Structure This is the critical structural knowledge. A Kimai plugin is a Symfony bundle installed in `var/plugins/` (development) or via Composer (distribution). ### Directory Layout ``` KimaiHeatmapBundle/ KimaiHeatmapBundle.php # Bundle class, extends PluginInterface DependencyInjection/ KimaiHeatmapExtension.php # Loads services config Resources/ config/ services.yaml # Service definitions views/ widget/ heatmap.html.twig # Widget template public/ heatmap.js # Bundled d3 heatmap (esbuild output) heatmap.css # Widget styles EventSubscriber/ DashboardSubscriber.php # Registers widget on dashboard Widget/ HeatmapWidget.php # Widget class implementing WidgetInterface Repository/ HeatmapRepository.php # Data aggregation queries composer.json # Package metadata package.json # JS dependencies (d3, vitest) esbuild.config.mjs # JS build config assets/ src/ heatmap.ts # Source d3 heatmap code test/ heatmap.test.ts # Vitest tests tests/ Widget/ HeatmapWidgetTest.php # PHPUnit tests Repository/ HeatmapRepositoryTest.php # PHPUnit tests ``` **Confidence: MEDIUM** -- This structure follows Kimai's documented plugin patterns as of my training data. Verify against the current plugin developer guide and existing plugins like `ExpensesBundle` or `CustomContentBundle` on GitHub. ### Bundle Registration ```php // KimaiHeatmapBundle.php namespace KimaiPlugin\KimaiHeatmapBundle; use App\Plugin\PluginInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; class KimaiHeatmapBundle extends Bundle implements PluginInterface { } ``` Kimai auto-discovers bundles in `var/plugins/` by scanning for classes implementing `PluginInterface`. No manual kernel registration needed. ### Dashboard Widget Registration Kimai uses an event-based widget system. You implement `WidgetInterface` and subscribe to the dashboard event. ```php // Widget/HeatmapWidget.php namespace KimaiPlugin\KimaiHeatmapBundle\Widget; use App\Widget\Type\AbstractWidget; use App\Widget\WidgetInterface; class HeatmapWidget extends AbstractWidget { public function getTitle(): string { return 'Activity Heatmap'; } public function getTemplateName(): string { return '@KimaiHeatmap/widget/heatmap.html.twig'; } public function getData(array $options = []): mixed { // Query timesheet data, aggregate by day // Return array of [date => hours/count] } } ``` **Confidence: MEDIUM** -- Widget API may have changed. Check `App\Widget\Type\AbstractWidget` and `App\Widget\WidgetInterface` in current Kimai source. ### Twig Template Pattern ```twig {# Resources/views/widget/heatmap.html.twig #} {% extends '@theme/widget.html.twig' %} {% block widget_content %}