11 KiB
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-heatmapor 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.d3full 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:
Jestfor JS tests: d3 v7 is ESM-only. Jest's ESM support requires--experimental-vm-modulesand transform config. Vitest handles ESM natively.Cypress/Playwrightfor 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.Dockerfor 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
// 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.
// 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
{# Resources/views/widget/heatmap.html.twig #}
{% extends '@theme/widget.html.twig' %}
{% block widget_content %}
<div id="kimai-heatmap"
data-entries="{{ widget.data|json_encode }}"
data-base-url="{{ path('timesheet') }}">
</div>
{% endblock %}
{% block widget_javascript %}
<script src="{{ asset('bundles/kimaiheatmap/heatmap.js') }}"></script>
{% endblock %}
Pass data as a JSON data attribute. The JS reads it and renders the SVG. This avoids API calls and works with Kimai's server-side rendering model.
Confidence: LOW -- The exact Twig block names and asset path conventions should be verified against a working Kimai plugin. The @theme/widget.html.twig base template may differ.
Alternatives Considered
| Category | Recommended | Alternative | Why Not |
|---|---|---|---|
| JS Visualization | d3 sub-modules | cal-heatmap | Limits customization for Kimai theme integration, click navigation, and mode toggling |
| JS Visualization | d3 sub-modules | Chart.js matrix | Less control over calendar layout, weaker SVG customization |
| JS Bundler | esbuild | Webpack Encore | Overkill for bundling a single widget's JS. Encore adds Symfony config overhead. |
| JS Testing | Vitest | Jest | d3 v7 is ESM-only; Jest's ESM support requires workarounds |
| JS Language | TypeScript | Plain JS | Type safety for the d3 code, catches data shape mismatches at build time |
| PHP Testing | PHPUnit | Pest | Kimai's own tests use PHPUnit. Consistency with the host app matters. |
| Dev Env | Nix flake | Docker Compose | User runs NixOS; Nix is native. Docker adds a layer. |
TypeScript for d3
Use TypeScript for the heatmap source code. d3 v7 ships with type definitions. TypeScript catches common d3 mistakes (wrong scale types, missing data fields) at build time. esbuild handles .ts natively.
// tsconfig.json (minimal)
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noEmit": true,
"types": ["vitest/globals"]
},
"include": ["assets/src/**/*.ts", "assets/test/**/*.ts"]
}
Installation Commands
# PHP dependencies (in the plugin directory)
composer init --name="kimai/heatmap-bundle" --type="kimai-plugin"
# No extra PHP packages needed beyond Kimai's own dependencies
# JS dependencies
npm init -y
npm install d3-scale d3-selection d3-time d3-time-format d3-scale-chromatic d3-shape
npm install -D typescript esbuild vitest jsdom @types/d3-scale @types/d3-selection @types/d3-time @types/d3-time-format @types/d3-scale-chromatic @types/d3-shape
Nix Flake Approach
The flake should provide:
- PHP 8.2 with extensions:
mbstring,intl,pdo_mysql(orpdo_sqlitefor dev),xml,zip - Composer 2.x
- Node.js 22 LTS with npm
- MariaDB or SQLite for the local Kimai DB
- A devShell script that clones Kimai, runs migrations, seeds test data, and symlinks the plugin into
var/plugins/
SQLite is recommended for local dev to avoid running a separate DB server. Kimai supports SQLite out of the box.
Confidence: HIGH for the Nix approach concept. The specific Kimai setup commands need verification.
Key Version Verification Checklist
Before starting development, verify these against current sources:
- Kimai latest stable version (check GitHub releases)
- Kimai's required PHP version (check
composer.json) - Kimai's Symfony version (check
composer.lock) - Kimai's
WidgetInterfaceAPI (checksrc/Widget/in Kimai source) - Kimai's Twig widget template blocks (check existing dashboard widgets in Kimai source)
- Kimai's asset serving mechanism for plugins (may use Symfony AssetMapper now instead of Encore)
- d3 v7 latest patch version on npm
Sources
- Kimai plugin documentation: https://www.kimai.org/documentation/plugin-development.html
- Kimai GitHub: https://github.com/kimai/kimai
- d3.js documentation: https://d3js.org/
- d3 calendar heatmap examples: https://observablehq.com/@d3/calendar
- Vitest documentation: https://vitest.dev/
- esbuild documentation: https://esbuild.github.io/
All sources are from training data, not live-fetched. Confidence levels reflect this limitation.