docs: phase 5 planning artifacts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d0dc64f333
commit
843ac84805
4 changed files with 250 additions and 6 deletions
|
|
@ -16,7 +16,7 @@ Decimal phases appear between their surrounding integers in numeric order.
|
|||
- [x] **Phase 2: Plugin Scaffold + Data Layer** - Symfony bundle, dashboard widget, aggregation API with PHPUnit tests
|
||||
- [x] **Phase 3: Core Heatmap Rendering** - d3.js calendar grid with color mapping, labels, tooltips, theme integration, and JS tests
|
||||
- [x] **Phase 4: Heatmap Interaction** - Click-through navigation, project/activity filtering, interaction tests (completed 2026-04-08)
|
||||
- [ ] **Phase 5: Polish** - Streak indicator, summary stats, weekend styling
|
||||
- [x] **Phase 5: Polish** - Streak indicator, summary stats, weekend styling (completed 2026-04-08)
|
||||
|
||||
## Phase Details
|
||||
|
||||
|
|
@ -92,12 +92,12 @@ Plans:
|
|||
1. A streak indicator shows the current number of consecutive days with tracked time
|
||||
2. A summary stats row displays total hours, average hours/day, and the busiest day
|
||||
3. Weekend days are visually distinct from weekdays (subtle border or opacity difference)
|
||||
**Plans**: TBD
|
||||
**Plans**: phase-5/PLAN.md
|
||||
**UI hint**: yes
|
||||
|
||||
Plans:
|
||||
- [ ] 05-01: TBD
|
||||
- [ ] 05-02: TBD
|
||||
- [x] 05-01: Streak, stats, weekend styling, week-start preference
|
||||
- [x] 05-02: Tests for polish features
|
||||
|
||||
## Progress
|
||||
|
||||
|
|
@ -110,4 +110,4 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5
|
|||
| 2. Plugin Scaffold + Data Layer | 2/2 | Done | 2026-04-08 |
|
||||
| 3. Core Heatmap Rendering | 3/3 | Done | 2026-04-08 |
|
||||
| 4. Heatmap Interaction | 2/2 | Complete | 2026-04-08 |
|
||||
| 5. Polish | 0/2 | Not started | - |
|
||||
| 5. Polish | 2/2 | Done | 2026-04-08 |
|
||||
|
|
|
|||
105
.planning/phases/05-polish/05-01-PLAN.md
Normal file
105
.planning/phases/05-polish/05-01-PLAN.md
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
# Plan 05-01: Streak, Stats, Weekend Styling, Week Start
|
||||
|
||||
**Phase:** 5 — Polish
|
||||
**Requirements:** POLI-01, POLI-02, POLI-03 + start-of-week preference
|
||||
**Estimated tasks:** 6
|
||||
|
||||
## Goal
|
||||
|
||||
Add streak indicator, summary stats row, weekend cell styling, and configurable week start to the heatmap widget.
|
||||
|
||||
## Tasks
|
||||
|
||||
### Task 1: Add week start to PHP widget data
|
||||
|
||||
**File:** `Widget/HeatmapWidget.php`
|
||||
**Action:** In `getData()`, read `$user->getFirstDayOfWeek()` and include it in the returned array as `'weekStart'`.
|
||||
|
||||
**File:** `Resources/views/widget/heatmap.html.twig`
|
||||
**Action:** Add `data-week-start="{{ data.weekStart }}"` to the `#heatmap-container` div.
|
||||
|
||||
### Task 2: Make week start configurable in TypeScript
|
||||
|
||||
**File:** `assets/src/heatmap.ts`
|
||||
**Changes:**
|
||||
- In `init()`, read `data-week-start` attribute from container (default: `'monday'`)
|
||||
- Pass `weekStart` string to `renderHeatmap()` via a new optional parameter
|
||||
- In `generateCells()`, replace hardcoded `timeMonday` with the appropriate d3 time interval based on `weekStart`:
|
||||
- `'sunday'` → `timeSunday` (import from d3-time)
|
||||
- `'monday'` → `timeMonday` (existing)
|
||||
- Update `DAY_LABELS` to rotate based on week start
|
||||
- Update `dayOfWeek` calculation to match (row 0 = first day of week)
|
||||
|
||||
### Task 3: Add weekend cell styling
|
||||
|
||||
**File:** `assets/src/heatmap.ts`
|
||||
**Changes:**
|
||||
- In `generateCells()`, add `isWeekend: boolean` to `DayCell` interface (Saturday=6, Sunday=0 in JS `getDay()`)
|
||||
- In the cell rendering `.attr('class', ...)`, append `heatmap-weekend` class for weekend cells
|
||||
|
||||
**File:** `Resources/public/heatmap.css`
|
||||
**Add:**
|
||||
```css
|
||||
.heatmap-weekend {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.heatmap-weekend:hover {
|
||||
opacity: 0.65;
|
||||
}
|
||||
```
|
||||
|
||||
### Task 4: Add streak calculation
|
||||
|
||||
**File:** `assets/src/heatmap.ts`
|
||||
**Add function** `calculateStreak(days: DayEntry[]): number`:
|
||||
- Sort days by date descending
|
||||
- Starting from today (or yesterday if today has no entry), count consecutive calendar days with entries
|
||||
- Return the count
|
||||
- A day with `hours > 0` counts as tracked
|
||||
|
||||
### Task 5: Add summary stats calculation
|
||||
|
||||
**File:** `assets/src/heatmap.ts`
|
||||
**Add function** `calculateStats(days: DayEntry[]): { totalHours: number, avgHours: number, busiestDay: { date: string, hours: number } | null }`:
|
||||
- `totalHours`: sum of all hours, rounded to 1 decimal
|
||||
- `avgHours`: total hours / number of days with entries, rounded to 1 decimal
|
||||
- `busiestDay`: day entry with maximum hours
|
||||
|
||||
### Task 6: Render streak and stats below heatmap
|
||||
|
||||
**File:** `assets/src/heatmap.ts`
|
||||
**In `init()`**, after `renderHeatmap()` resolves:
|
||||
- Call `calculateStreak()` and `calculateStats()` with the fetched data
|
||||
- Create a stats row div below the SVG area with class `heatmap-stats`
|
||||
- Content: `🔥 N days | Total: Xh | Avg: Xh/day | Busiest: Mon, Mar 15 — Xh`
|
||||
- When filter changes and data reloads, recalculate and update stats
|
||||
|
||||
**File:** `Resources/public/heatmap.css`
|
||||
**Add:**
|
||||
```css
|
||||
.heatmap-stats {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
padding: 8px 0 0;
|
||||
font-size: 0.8125rem;
|
||||
color: var(--tblr-secondary, #6c757d);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.heatmap-stats .stat-value {
|
||||
color: var(--tblr-body-color);
|
||||
font-weight: 600;
|
||||
}
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
- `npm run build` succeeds
|
||||
- Widget shows streak count, three stat values, weekend cells with reduced opacity
|
||||
- Changing week start preference changes grid layout and labels
|
||||
- Filter change recalculates stats
|
||||
|
||||
## Commit
|
||||
|
||||
`feat: add streak, stats, weekend styling, week-start preference`
|
||||
46
.planning/phases/05-polish/05-02-PLAN.md
Normal file
46
.planning/phases/05-polish/05-02-PLAN.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# Plan 05-02: Tests for Polish Features
|
||||
|
||||
**Phase:** 5 — Polish
|
||||
**Requirements:** POLI-01, POLI-02, POLI-03 (test coverage)
|
||||
**Estimated tasks:** 3
|
||||
|
||||
## Goal
|
||||
|
||||
Add Vitest tests for streak calculation, summary stats, weekend styling, and week start configurability.
|
||||
|
||||
## Tasks
|
||||
|
||||
### Task 1: Streak calculation tests
|
||||
|
||||
**File:** `assets/test/stats.test.ts` (new)
|
||||
**Tests:**
|
||||
- Streak of 3 consecutive days ending today → returns 3
|
||||
- Gap in the middle breaks streak → returns days after gap
|
||||
- No entries today but entries yesterday → streak counts from yesterday
|
||||
- No entries at all → returns 0
|
||||
- Single day entry (today) → returns 1
|
||||
|
||||
### Task 2: Summary stats tests
|
||||
|
||||
**File:** `assets/test/stats.test.ts` (same file)
|
||||
**Tests:**
|
||||
- Total hours sums correctly
|
||||
- Average hours divides by days-with-entries (not total calendar days)
|
||||
- Busiest day picks the max-hours entry
|
||||
- Empty data returns zeroes and null busiest day
|
||||
|
||||
### Task 3: Weekend and week start rendering tests
|
||||
|
||||
**File:** `assets/test/heatmap.test.ts` (extend existing)
|
||||
**Tests:**
|
||||
- Weekend cells (Sat/Sun) have `heatmap-weekend` class
|
||||
- Weekday cells do NOT have `heatmap-weekend` class
|
||||
- Stats row renders below heatmap with streak and stat values
|
||||
|
||||
## Verification
|
||||
|
||||
- `npm test` passes all new and existing tests
|
||||
|
||||
## Commit
|
||||
|
||||
`test: add tests for streak, stats, weekend styling`
|
||||
93
.planning/phases/05-polish/05-CONTEXT.md
Normal file
93
.planning/phases/05-polish/05-CONTEXT.md
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# Phase 5: Polish - Context
|
||||
|
||||
**Gathered:** 2026-04-08
|
||||
**Status:** Ready for planning
|
||||
|
||||
<domain>
|
||||
## Phase Boundary
|
||||
|
||||
Add streak indicator, summary statistics row, and weekend visual distinction to the heatmap widget. Also integrate user's start-of-week preference from Kimai.
|
||||
|
||||
</domain>
|
||||
|
||||
<decisions>
|
||||
## Implementation Decisions
|
||||
|
||||
### Streak Indicator
|
||||
- Count consecutive calendar days (including weekends) with tracked time, counting backward from today
|
||||
- Display as a simple text line above or below the heatmap: "Current streak: N days"
|
||||
- If today has no entries yet, check from yesterday backward (user may still be tracking today)
|
||||
- Computed client-side from the fetched heatmap data — no new API endpoint
|
||||
|
||||
### Summary Stats Row
|
||||
- Rendered below the heatmap SVG, inside the same Tabler card
|
||||
- Three stats: Total Hours, Avg Hours/Day (days with tracked time only), Busiest Day
|
||||
- Computed client-side from the fetched heatmap data array
|
||||
- Styled as inline stat items using Tabler's existing typography classes
|
||||
- "Busiest Day" shows date + hours (e.g., "Mar 15 — 8.5h")
|
||||
|
||||
### Weekend Styling
|
||||
- Weekend cells (Saturday/Sunday) get reduced opacity (0.85) compared to weekday cells
|
||||
- Applied via CSS class `.heatmap-weekend` with opacity rule
|
||||
- Empty weekend cells also get the distinction so the grid pattern is visible
|
||||
- No border changes — keep existing rounded rect style
|
||||
|
||||
### Start-of-Week Preference
|
||||
- Read user's `first_weekday` from Kimai (exposed via User entity or system config)
|
||||
- Pass as `data-week-start` attribute on the container div (0=Sunday, 1=Monday, etc.)
|
||||
- Use in d3 grid layout to set which day is row 0
|
||||
- Adjust day-of-week labels to match
|
||||
- Default to Monday if not available (current behavior)
|
||||
|
||||
### Testing Strategy
|
||||
- Vitest tests for streak calculation (consecutive days, gaps, weekend inclusion, today edge case)
|
||||
- Vitest tests for summary stats computation (totals, averages, busiest day)
|
||||
- Vitest tests for weekend cell class assignment
|
||||
- CSS changes don't need unit tests — visual verification
|
||||
|
||||
### Claude's Discretion
|
||||
- Exact Tabler CSS classes for the stats row layout
|
||||
- How to extract `first_weekday` from Kimai's User entity (check source)
|
||||
- Whether streak resets on weekends or not (decision: it does — every calendar day counts)
|
||||
- d3 time interval function for configurable week start (d3.timeSunday vs d3.timeMonday etc.)
|
||||
|
||||
</decisions>
|
||||
|
||||
<code_context>
|
||||
## Existing Code Insights
|
||||
|
||||
### Reusable Assets
|
||||
- `renderHeatmap(container, data, config)` — main render, needs extension for weekend classes and stats
|
||||
- `init()` — fetches data and calls render, will also compute streak/stats after data arrives
|
||||
- `HeatmapData.days` array — source for all client-side computations
|
||||
- `generateCells()` helper — can add weekend detection here
|
||||
- `HeatmapWidget::getData()` — can add `weekStart` to template data
|
||||
|
||||
### Established Patterns
|
||||
- d3 selections for SVG element class assignment
|
||||
- Data attributes on container div for passing server config to JS
|
||||
- Tabler card structure with widget template embedding
|
||||
- CSS classes added to cells based on data state (`.heatmap-empty`)
|
||||
|
||||
### Integration Points
|
||||
- `heatmap.html.twig` — add `data-week-start` attribute, stats container div
|
||||
- `heatmap.css` — add `.heatmap-weekend`, `.heatmap-stats` styles
|
||||
- `heatmap.ts` — extend `renderHeatmap` for weekends, add streak/stats functions
|
||||
- `HeatmapWidget.php` — pass user's week start preference to template
|
||||
|
||||
</code_context>
|
||||
|
||||
<specifics>
|
||||
## Specific Ideas
|
||||
|
||||
- Memory note: heatmap must respect Kimai user's start-of-week preference (not hardcoded Monday)
|
||||
- Stats should feel lightweight — not a full stats panel, just quick context below the heatmap
|
||||
|
||||
</specifics>
|
||||
|
||||
<deferred>
|
||||
## Deferred Ideas
|
||||
|
||||
None — all three requirements (POLI-01, POLI-02, POLI-03) plus start-of-week fit within polish scope.
|
||||
|
||||
</deferred>
|
||||
Loading…
Add table
Reference in a new issue