255 lines
11 KiB
Markdown
255 lines
11 KiB
Markdown
# 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 %}
|
|
<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.
|
|
|
|
```json
|
|
// 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
- 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.**
|