115 lines
8.4 KiB
Markdown
115 lines
8.4 KiB
Markdown
---
|
|
phase: 07-mode-switcher-week-mode
|
|
verified: 2026-04-09T17:02:00Z
|
|
status: human_needed
|
|
score: 5/5
|
|
human_verification:
|
|
- test: "Verify mode switcher and week mode visually in running Kimai"
|
|
expected: "Year/Week segmented control in header toggles between calendar and 7-cell weekday view. Hours/Count toggle re-colors cells. Stats row hidden in week mode. No console errors."
|
|
why_human: "Visual rendering, SVG layout, Tabler CSS integration, and tooltip positioning cannot be verified programmatically without a running Kimai instance"
|
|
- test: "Verify filter preservation across mode switches"
|
|
expected: "Select a project filter, switch from year to week and back -- filter remains active and data stays filtered"
|
|
why_human: "Requires live data fetch and DOM interaction sequence"
|
|
---
|
|
|
|
# Phase 7: Mode Switcher + Week Mode Verification Report
|
|
|
|
**Phase Goal:** Users can switch between year and week visualization modes and toggle between hours and entry-count display
|
|
**Verified:** 2026-04-09T17:02:00Z
|
|
**Status:** human_needed
|
|
**Re-verification:** No -- initial verification
|
|
|
|
## Goal Achievement
|
|
|
|
### Observable Truths
|
|
|
|
| # | Truth | Status | Evidence |
|
|
|---|-------|--------|----------|
|
|
| 1 | A segmented control in the widget header lets the user switch between year and week views | VERIFIED | `heatmap.html.twig` has `#heatmap-controls` div in card header; `heatmap.ts:49-55` wires `createModeControl` with year/week options; onChange sets `state.mode` and calls `doRender()` |
|
|
| 2 | Week-mode shows a day-of-week aggregation heatmap revealing which weekdays are busiest | VERIFIED | `WeekModeRenderer` in `renderers/week.ts` (155 lines) aggregates DayEntry by weekday, renders 7 SVG rects with `buildColorScale`; registered in `heatmap.ts:12` |
|
|
| 3 | Hours/count toggle switches the color scale metric across both modes without re-fetching data | VERIFIED | `createMetricControl` wired at `heatmap.ts:57-60`; sets `state.metric` and calls `doRender()` with no fetch; week renderer reads `ctx.state.metric` at lines 78 and 118 |
|
|
| 4 | Switching modes preserves the current filter selection | VERIFIED | Mode onChange only mutates `state.mode` (`heatmap.ts:53`); `state.filters` is untouched; no fetch on mode switch |
|
|
| 5 | Vitest tests cover mode switcher interaction, week renderer output, and display toggle behavior | VERIFIED | `controls.test.ts` (11 tests): rendering, click handling, ARIA. `week.test.ts` (14 tests): aggregation, rendering, tooltips, labels, destroy. 174 total tests pass. |
|
|
|
|
**Score:** 5/5 truths verified
|
|
|
|
### Required Artifacts
|
|
|
|
| Artifact | Expected | Status | Details |
|
|
|----------|----------|--------|---------|
|
|
| `assets/src/ui/controls.ts` | createModeControl and createMetricControl functions | VERIFIED | 42 lines, exports both functions, Tabler nav-segmented pattern, ARIA attributes |
|
|
| `assets/src/renderers/week.ts` | WeekModeRenderer implementing ModeRenderer | VERIFIED | 155 lines, aggregateByWeekday, 7-cell SVG, color scale, tooltips, destroy |
|
|
| `assets/test/controls.test.ts` | Tests for mode switcher and metric toggle | VERIFIED | 111 lines, 11 tests covering rendering, click, ARIA |
|
|
| `assets/test/week.test.ts` | Tests for week renderer | VERIFIED | 178 lines, 14 tests covering aggregation, rendering, tooltips, labels |
|
|
| `Resources/views/widget/heatmap.html.twig` | Controls placeholder in card header | VERIFIED | `#heatmap-controls` div in `box_title` block with flex layout |
|
|
| `Resources/public/heatmap.css` | heatmap-week-cell style | VERIFIED | `.heatmap-week-cell { cursor: default; }` at line 97 |
|
|
|
|
### Key Link Verification
|
|
|
|
| From | To | Via | Status | Details |
|
|
|------|----|-----|--------|---------|
|
|
| `heatmap.ts` | `ui/controls.ts` | `import { createModeControl, createMetricControl }` | WIRED | Line 8: import; lines 49-63: both controls created and appended to DOM |
|
|
| `heatmap.ts` | `renderers/week.ts` | `import + registerRenderer` | WIRED | Line 6: import WeekModeRenderer; line 12: `registerRenderer(new WeekModeRenderer())` |
|
|
| `ui/controls.ts` | `state.mode / state.metric` | onChange callbacks | WIRED | `heatmap.ts:53`: `state.mode = mode`; line 58: `state.metric = metric`; both call `doRender()` |
|
|
| `renderers/week.ts` | `shared/color-scale.ts` | `buildColorScale` | WIRED | Line 4: import; line 78: `buildColorScale(syntheticDays, ctx.state.metric)` |
|
|
| `renderers/week.ts` | `shared/tooltip.ts` | `createTooltip/showTooltip/hideTooltip` | WIRED | Line 3: imports; line 70: create; line 142: show; line 145: hide |
|
|
|
|
### Data-Flow Trace (Level 4)
|
|
|
|
| Artifact | Data Variable | Source | Produces Real Data | Status |
|
|
|----------|---------------|--------|--------------------|--------|
|
|
| `renderers/week.ts` | `ctx.data.days` | Passed from `heatmap.ts` `state.data` | Yes -- fetched from `/heatmap_data` API endpoint | FLOWING |
|
|
| `ui/controls.ts` | `activeMode`, `activeMetric` | Passed from `heatmap.ts` `state.mode`/`state.metric` | Yes -- initialized from `createInitialState` | FLOWING |
|
|
| `heatmap.ts` doRender | `state.data` | `fetch(baseUrl)` at line 145 | Yes -- API fetch with JSON parse | FLOWING |
|
|
|
|
### Behavioral Spot-Checks
|
|
|
|
| Behavior | Command | Result | Status |
|
|
|----------|---------|--------|--------|
|
|
| All tests pass | `npm test` | 174 passed, 0 failed | PASS |
|
|
| Build succeeds | `npm run build:dev` | 91.0kb bundle, exit 0 | PASS |
|
|
| controls.ts exports functions | `grep "export function" assets/src/ui/controls.ts` | createModeControl, createMetricControl | PASS |
|
|
| week.ts exports class | `grep "export class" assets/src/renderers/week.ts` | WeekModeRenderer | PASS |
|
|
| WeekModeRenderer registered | `grep "registerRenderer.*WeekModeRenderer" assets/src/heatmap.ts` | Found at line 12 | PASS |
|
|
|
|
### Requirements Coverage
|
|
|
|
| Requirement | Source Plan | Description | Status | Evidence |
|
|
|-------------|------------|-------------|--------|----------|
|
|
| VIZ-01 | 07-01 | Mode switcher UI allows toggling between year, week, day, and combined views | SATISFIED (partial -- year/week only) | Controls built for year/week; day/combined modes deferred to Phase 9 per REQUIREMENTS.md traceability |
|
|
| VIZ-02 | 07-02 | Week-mode renders day-of-week aggregation showing which weekdays are busiest | SATISFIED | WeekModeRenderer with aggregateByWeekday, 7-cell SVG, color scale |
|
|
| VIZ-05 | 07-01 | Hours vs entry-count toggle switches color scale metric across all modes | SATISFIED (partial -- current modes) | createMetricControl wired; week renderer reads state.metric for color scale |
|
|
| TEST-01 | 07-01, 07-02 | Vitest tests for mode switcher, each renderer, and display toggle | SATISFIED (partial -- current renderers) | 25 new tests for controls and week renderer; 174 total passing |
|
|
|
|
### Anti-Patterns Found
|
|
|
|
| File | Line | Pattern | Severity | Impact |
|
|
|------|------|---------|----------|--------|
|
|
| -- | -- | No anti-patterns found | -- | -- |
|
|
|
|
Note: `createModeControl` applies `nav-sm` class to both mode and metric controls (plan specified `nav-sm` only for metric). Commit `12e734a` ("uniform control sizing") indicates this was a deliberate design refinement, not a bug.
|
|
|
|
### Human Verification Required
|
|
|
|
### 1. Visual Rendering in Kimai Dashboard
|
|
|
|
**Test:** Start local Kimai, navigate to dashboard. Verify Year/Week segmented control and Hours/Count toggle appear in widget header. Click Week -- verify 7 horizontal cells with weekday labels. Hover cells for tooltips. Click Count -- verify re-coloring. Click Year -- verify calendar returns with stats row.
|
|
**Expected:** Controls render with Tabler styling, mode switching is instant, tooltips show correct aggregated data, stats row toggles visibility.
|
|
**Why human:** SVG layout, Tabler CSS integration, tooltip positioning, and visual polish require a running browser with Kimai's CSS loaded.
|
|
|
|
### 2. Filter Preservation Across Mode Switches
|
|
|
|
**Test:** Select a project filter, then switch between Year and Week modes.
|
|
**Expected:** Filter dropdown stays on selected project, heatmap data remains filtered in both modes.
|
|
**Why human:** Requires live data fetch and interactive DOM state observation.
|
|
|
|
### Gaps Summary
|
|
|
|
No code-level gaps found. All 5 roadmap success criteria verified in the codebase. All artifacts exist, are substantive, wired, and data flows through. 174 tests pass. Build succeeds.
|
|
|
|
Two items require human visual verification: (1) the rendered output in a running Kimai instance, and (2) filter preservation during mode switching. These are the plan's own human-verify checkpoint (07-02 Task 2).
|
|
|
|
---
|
|
|
|
_Verified: 2026-04-09T17:02:00Z_
|
|
_Verifier: Claude (gsd-verifier)_
|