diff --git a/.planning/phases/08-backend-aggregation-filtering/08-CONTEXT.md b/.planning/phases/08-backend-aggregation-filtering/08-CONTEXT.md new file mode 100644 index 0000000..b17dc2a --- /dev/null +++ b/.planning/phases/08-backend-aggregation-filtering/08-CONTEXT.md @@ -0,0 +1,94 @@ +# Phase 8: Backend Aggregation + Filtering - Context + +**Gathered:** 2026-04-09 +**Status:** Ready for planning + + +## Phase Boundary + +Backend serves hour-level and day/hour aggregation data for day-mode and combined-mode renderers (Phase 9), plus activity and customer filter params on the existing data endpoint, and cascade entity endpoints for the TomSelect pickers (Phase 10). No frontend rendering changes — this phase builds the data layer. + + + + +## Implementation Decisions + +### Claude's Discretion +User deferred all decisions to Claude. The following defaults are based on existing codebase patterns: + +- **D-01:** Extend `HeatmapService` with `getHourlyAggregation()` and `getDayHourAggregation()` methods alongside the existing `getDailyAggregation()` — separate methods, not one flexible method, matching the existing service pattern +- **D-02:** Extend the existing `/heatmap/data` endpoint with optional `mode` query param (`daily` default, `hourly`, `dayhour`) rather than separate endpoints per mode — keeps frontend fetch logic simple (one URL, add `?mode=X`) +- **D-03:** Add `activity` and `customer` query params to the existing `/heatmap/data` endpoint — additive AND logic with existing `project` filter. Customer filter queries all projects under that customer. +- **D-04:** Cascade endpoints: `/heatmap/customers`, `/heatmap/projects?customer={id}`, `/heatmap/activities?project={id}` — session auth (same as existing), scoped to user's own entities (timesheets they've created) +- **D-05:** Cascade endpoint response format: `[{id: number, name: string}]` — matches existing `getUserProjects()` return shape +- **D-06:** Hourly aggregation returns `{hour: number (0-23), hours: float, count: int}` — groups by hour-of-day across entire date range +- **D-07:** Day/hour aggregation returns `{day: number (0-6), hour: number (0-23), hours: float, count: int}` — 7x24 matrix data, day index relative to weekStart +- **D-08:** Timezone handling: use Kimai's user timezone setting (available via `$user->getTimezone()`) for hour grouping — entries are stored UTC, grouping must respect user's local time +- **D-09:** Frontend extends the existing fetch call with mode/filter params — no new fetch functions, just URL parameter construction + + + + +## Canonical References + +**Downstream agents MUST read these before planning or implementing.** + +### Existing Backend +- `Controller/HeatmapController.php` — Current `/heatmap/data` endpoint with project filter +- `Service/HeatmapService.php` — `getDailyAggregation()` and `getUserProjects()` query patterns +- `Tests/Controller/HeatmapControllerTest.php` — Existing controller test pattern with mock service + +### Renderer Architecture (for understanding data consumers) +- `assets/src/types.ts` — HeatmapData, DayEntry types that backend produces +- `assets/src/heatmap.ts` — Frontend fetch and state management + +### Requirements +- `.planning/REQUIREMENTS.md` — API-01 (mode param), API-02 (filter params), FILT-02 (activity filter), FILT-03 (customer filter), TEST-03 (PHPUnit tests) + +### Kimai Internals +- Kimai's `TimesheetRepository` — base for all queries (injected into HeatmapService) +- Kimai's entity relationships: Timesheet → Project → Customer, Timesheet → Activity +- `App\Entity\User::getTimezone()` — user timezone for hour grouping + + + + +## Existing Code Insights + +### Reusable Assets +- `HeatmapService`: extend with new aggregation methods — same QueryBuilder pattern +- `HeatmapController`: extend with new action methods and filter params +- `getUserProjects()`: reference for cascade endpoint response shape +- Existing test pattern: mock service, mock user via container/tokenStorage + +### Established Patterns +- Symfony route attributes (`#[Route]`, `#[IsGranted]`) +- QueryBuilder with parameter binding for SQL injection safety +- JSON response with simple array structure +- Session auth via `IS_AUTHENTICATED_REMEMBERED` (not API auth) + +### Integration Points +- New controller actions register automatically via Symfony routing +- Frontend fetch adds query params to existing `data-url` base URL +- Cascade endpoints consumed by TomSelect pickers in Phase 10 + + + + +## Specific Ideas + +No specific requirements — open to standard approaches + + + + +## Deferred Ideas + +None — discussion stayed within phase scope + + + +--- + +*Phase: 08-backend-aggregation-filtering* +*Context gathered: 2026-04-09* diff --git a/.planning/phases/08-backend-aggregation-filtering/08-DISCUSSION-LOG.md b/.planning/phases/08-backend-aggregation-filtering/08-DISCUSSION-LOG.md new file mode 100644 index 0000000..7953758 --- /dev/null +++ b/.planning/phases/08-backend-aggregation-filtering/08-DISCUSSION-LOG.md @@ -0,0 +1,34 @@ +# Phase 8: Backend Aggregation + Filtering - Discussion Log + +> **Audit trail only.** Do not use as input to planning, research, or execution agents. +> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered. + +**Date:** 2026-04-09 +**Phase:** 08-backend-aggregation-filtering +**Areas discussed:** None (user deferred all to Claude's discretion) + +--- + +## Gray Areas Presented + +| Area | Description | Selected | +|------|-------------|----------| +| Aggregation query design | Query structure, timezone handling | | +| Filter param handling | Activity + customer filter integration | | +| Cascade endpoints | Entity list endpoints shape and scoping | | +| Frontend data flow | Fetch URL extension vs separate endpoints | | + +**User's choice:** "Nothing" — deferred all areas to Claude's discretion +**Notes:** All decisions made by Claude based on existing codebase patterns (HeatmapService, HeatmapController) + +## Claude's Discretion + +All 4 areas deferred. Decisions D-01 through D-09 in CONTEXT.md are Claude's defaults based on: +- Existing `getDailyAggregation()` query pattern +- Existing controller route/auth pattern +- Existing `getUserProjects()` response shape +- Requirements constraints (session auth, own endpoints) + +## Deferred Ideas + +None