Fix Weekly Timesheet Total Rounding Mismatch - #507
Conversation
🚀 Preview Deployment Ready
Preview auto-deletes when this PR is closed. |
There was a problem hiding this comment.
Pull request overview
Updates timesheet display aggregation so totals sum per-session rounded durations.
Changes:
- Adds a shared duration aggregation helper and unit tests.
- Applies it to personal and admin summaries.
- Adds an end-to-end regression test.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/features/clock/timesheetUtils.ts |
Adds rounded-duration summation. |
src/features/clock/timesheetUtils.test.ts |
Tests rounding behavior. |
src/features/clock/PersonalTimesheetPanel.tsx |
Updates personal summary calculations. |
src/features/teams/AdminTimesheetPanel.tsx |
Updates admin total calculation. |
tests/e2e/timesheet/timesheet-calculation-fixes.spec.ts |
Verifies displayed row and total agreement. |
Suppressed comments (1)
src/features/clock/PersonalTimesheetPanel.tsx:453
- Break Hours now rounds one aggregate per session rather than each visible break row. Two 30-second breaks in one session render as two
1mBreak Period rows, butgetSessionBreakSecondsreturns 60 seconds and this helper reports1m. Round the individual break durations used byTimesheetRow, or retain the prior Break Hours behavior if this PR is intended to change only Total Hours.
const totalBreakSeconds = sumRoundedDurationsForDisplay(
filteredSessions.map((s) => getSessionBreakSeconds(s, currentTime)),
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Dharp02
left a comment
There was a problem hiding this comment.
This is a lot of code for simple rounding error.
@Dharp02 fair point — this got bigger than the bug. What #434 asked for: rows already show nearest-minute durations (8h × 5 = 40h), but Total Hours summed raw seconds first and rounded once, so leftover seconds across the week could surface as 40h 1m. Expected: the card matches the visible minutes (40h 0m). What landed: two different fixes.
Proposal: keep this PR scoped to only #434. Revert the seconds / ~ / shared-timeline work. Copilot’s break-split edge case (30s + break + 30s rounding to 1m+1m vs session 1m) is real but not what #434 reported. I’d leave it out of this PR unless we hit it in the wild. |
|
Restoring that path should take this from +557 / −300 (13 files) down to about +220 / −20 (5 files) — roughly 60% fewer additions and ~70% less churn. Of that +220, about 200 are tests (≈90 unit, ≈110 e2e) and ~20 are product code (the helper plus the two Total Hours cards). The Playwright case alone is half the leftover diff. |
Round each session to the nearest minute before summing so leftover seconds cannot add an extra minute to the weekly total (#434). Co-authored-by: Cursor <cursoragent@cursor.com>
1ebcfbf to
6fd1c6d
Compare
|
I shrunk the PR back to the original #434 fix only:
Break Hours / Avg Session are unchanged. Admin day-row vs expanded-row mismatches (breaks/midnight), clock-vs-duration ±1m, and wall-clock vs work-seconds fallbacks are out of scope here and can be separate PRs if we want them next. Happy to re-review when you have a minute. |
Co-authored-by: Cursor <cursoragent@cursor.com>
Dharp02
left a comment
There was a problem hiding this comment.
Review: no breaking changes found.
Checked out the PR branch and verified:
sumRoundedDurationsForDisplay()unit tests pass (including the new #434 regression cases:40h 0mvs old40h 1m).- ESLint clean on all 4 changed files.
- Both display sites (
PersonalTimesheetPanel.tsx,AdminTimesheetPanel.tsx) rendertotalSecondsdirectly without re-wrapping inroundDurationSecondsForDisplay, so there's no double-rounding. averageSessionSecondscorrectly still uses the raw (unrounded) sum — unchanged, matches issue scope.
Diff is small and tightly scoped to #434. Minor non-blocking nit: both panels iterate filteredSessions twice (once for rawWorkSeconds, once inside sumRoundedDurationsForDisplay's .map) — trivial for typical session counts, not worth blocking on.
LGTM.
Fixes #434
Summary
40h 1m) while the visible sessions still added to40h 0m.sumRoundedDurationsForDisplayfor Total Hours only. Break Hours / Avg Session are unchanged.Rounding rule
Leftover 0–29s → round down, 30–59s → round up (
Math.round(seconds / 60) * 60).Before vs after (#434)
Five sessions at 8h 0m 12s each:
Out of scope (separate PRs if needed)
accumulatedTimeis missing~nearest-minute cardsTest plan
npx vitest run src/features/clock/timesheetUtils.test.ts