kimai-plugin-heatmap/.planning/research/STACK.md

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.

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

// 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:

  1. PHP 8.2 with extensions: mbstring, intl, pdo_mysql (or pdo_sqlite for dev), xml, zip
  2. Composer 2.x
  3. Node.js 22 LTS with npm
  4. MariaDB or SQLite for the local Kimai DB
  5. 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 WidgetInterface API (check src/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

All sources are from training data, not live-fetched. Confidence levels reflect this limitation.