# 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`