151 lines
11 KiB
Markdown
151 lines
11 KiB
Markdown
# 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 `.ts` natively.
|
|
- **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:**
|
|
1. **KimaiHeatmapBundle** -- Symfony bundle class implementing `PluginInterface`, auto-discovered by Kimai
|
|
2. **HeatmapWidget** -- `WidgetInterface` implementation, registers on dashboard, renders Twig template
|
|
3. **HeatmapController** -- API endpoint (`/api/plugins/heatmap/data`) returning aggregated JSON
|
|
4. **HeatmapService** -- Server-side aggregation (hours/count per day, timezone-aware, user-scoped)
|
|
5. **d3 heatmap module** -- TypeScript, calendar grid rendering, event handling, theme integration
|
|
|
|
### Critical Pitfalls
|
|
|
|
1. **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.
|
|
2. **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.
|
|
3. **Widget system misunderstanding** -- Use `WidgetInterface` + `kimai.widget` DI tag from the start. Do not build a standalone controller page. Study existing Kimai widgets before coding.
|
|
4. **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.
|
|
5. **Nix + PHP dev environment** -- Timebox to 1 day. Use SQLite. Set `COMPOSER_HOME` to 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.json` and 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 `WidgetInterface` methods, `AbstractWidgetType` hierarchy, and `kimai.widget` DI tag must be verified against the target Kimai release. This is the single biggest unknown.
|
|
- **Kimai asset serving for plugins**: Whether Kimai uses `assets:install` to copy `Resources/public/` to `public/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*
|