kimai-plugin-heatmap/.planning/milestones/v1.0-phases/05-polish/05-01-PLAN.md
Christopher Mühl 244c7c66fc
chore: archive v1.0 milestone
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 23:25:26 +02:00

3.4 KiB

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:

.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:

.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