feat: overall overtime balance on history page

Backend:
- ClosedWeekStore.SumDelta: single SQL aggregate returning total delta_ms and
  row count across all closed_weeks
- WeekService.Balance: thin passthrough returning BalanceResult{TotalDeltaMs, ClosedWeekCount}
- GET /api/weeks/balance handler; route registered alongside /weeks list/close/reopen
- Tests: store-level SumDelta (empty + populated), service-level Balance (empty + 2 weeks)

Frontend:
- weeks.balance() added to API client
- History page: balance card at top, fetched in parallel with existing data
- Loading state shows '—'; once loaded shows formatDelta value in green/red/gray
- Shows 'across N closed weeks' count alongside the value
This commit is contained in:
2026-04-30 20:01:24 +02:00
parent 15bf3c3a18
commit 8ca838fa6e
7 changed files with 184 additions and 5 deletions

View File

@@ -42,6 +42,21 @@ func NewWeekService(
}
}
// BalanceResult holds the overall overtime/undertime balance across all closed weeks.
type BalanceResult struct {
TotalDeltaMs int64 `json:"total_delta_ms"`
ClosedWeekCount int `json:"closed_week_count"`
}
// Balance returns the sum of delta_ms across all closed weeks.
func (s *WeekService) Balance(ctx context.Context) (BalanceResult, error) {
total, count, err := s.closedWeeks.SumDelta(ctx)
if err != nil {
return BalanceResult{}, err
}
return BalanceResult{TotalDeltaMs: total, ClosedWeekCount: count}, nil
}
// WeekDayKeysExported is exported for testing.
var WeekDayKeysExported = weekDayKeys