- Add ErrFutureDay sentinel error
- CreateInterval: rejects if startDayKey > todayKey (400)
- Update: rejects if new start_time moves entry to a future day (400)
- Handler maps ErrFutureDay → 400 Bad Request for both endpoints
- Add TestCreateIntervalRejectsFutureDay
- Add TestUpdateRejectsMoveToFutureDay
- UI already gates this via dayCapabilities (canAddInterval=false,
canEditEntries=false for future days), but server now enforces it too
- weekKey and selectedDay driven by ?week=&?day= query params
- Bare /week canonicalizes via replaceState (adds week+day params)
- Chip clicks: replaceState (no history push, no scroll jump)
- Week prev/next: goto with history push (back/forward works)
- Default day when week changes: today if in week, else Monday
- Keyboard navigation: ArrowLeft/Right cycles through chips
- Selected chip scrolls into view on selection change
- DayDetail always rendered for selectedDay (not just today)
- detailCaps reactive on closedDaysMap — updates immediately after
close/reopen/mark without extra load()
- Chips now wired: selected prop, onclick handler
- DayChip tabindex: 0 for selected chip, -1 for others (roving tabindex)
- New DayDetail.svelte: self-contained day panel with full capability
gating (canStartStop, canAddInterval, canEditEntries, canMarkKind,
canCloseDay, canReopenDay)
- Closed-day state: banner + read-only entry list with 'Reopen day to
edit' hint; mark-kind buttons still available
- Close Day button disabled (with tooltip) when running entry exists
- Timer only ticks when dayKey === todayKey() and entry is running;
properly cleaned up on dayKey change and component destroy
- oninvalidate() callback: called after every mutation so parent
(week view) can refetch and update the chip strip
- /today route: refactored to thin wrapper using DayDetail
- Week page: renders DayDetail below summary when today is in the
displayed week; oninvalidate triggers full week reload
- New DayChip.svelte component: weekday label, date number, progress
bar (worked/expected), kind badge (H/V/S), closed checkmark, today
highlight, selected state, accessible role=tab/aria-selected
- Week page: replace days-grid with horizontal scroll-snap chip strip
- Per-day workedMs: uses closed_days.worked_ms when closed, else sums
open entries for that day (so in-progress work shows immediately)
- dailyExpectedMs: evenly split hours_per_week across workdays; 0 for
weekends (no progress bar rendered for non-workdays)
- Progress bar turns amber when worked > expected (overtime)
- weekEntries stored in state (was discarded after computing set);
daysWithEntries now derived from weekEntries
- Install vitest + jsdom
- Add test/test:watch scripts to package.json
- Add test:web and test:all tasks to mise.toml
- Add dayCapabilities() to utils.ts — single source of truth for
what actions are permitted per day (future/today/past, open/closed)
- Add DayCapabilities interface to utils.ts
- 11 unit tests: dayCapabilities (5 cases), weekDayKeys (3 cases),
isWorkday (3 cases)
Settings configured mid-week (e.g. Thursday) have an effective_from
of that date. CloseWeek was looking up settings as-of Monday, which
predates the new settings row and fell back to the old default.
Now uses today's date for the settings lookup, so any settings change
made before closing the week is correctly reflected in expected_ms.
The naive formula used (jan4.Weekday() - time.Monday) produces -1
when Jan 4 is a Sunday (Weekday()==0), shifting the computed Monday
one week forward. 2026 is affected: Jan 4 is a Sunday, so every
week key in 2026 was mapped to the wrong 7-day range, causing
CloseWeek to look for closed_days on the wrong dates and finding
nothing — resulting in worked_ms=0 and a full -Nh delta.
Fix: use (weekday+6)%7 to get days-since-Monday (Mon=0…Sun=6),
which is always non-negative.
Adds table-driven TestWeekDayKeys covering 2024 (Thu), 2026 (Sun),
and 2023 (Wed) to prevent regression.
When a day is closed, re-closed, or reopened, DayService now
recomputes worked_ms and delta_ms on the closed week containing
that day (if the week is already closed). This prevents stale
delta values after editing entries and re-closing a day.
- DayService.recomputeWeek: sums worked_ms from all closed_days
in the week, updates closed_weeks row preserving expected_ms
- NewDayService now takes ClosedWeekStore
- WeekKeyForDayKey exported helper (used by DayService)
- TestWeekSnapshotUpdatesWhenDayReopened regression test
closedWeek.delta_ms is a snapshot taken at close time and goes stale
if entries are edited or days are re-closed afterward. The summary
rows above already use the live totalWorkedMs - expectedMs; the
closed-week banner now uses the same expression.
Fetch entries for the week alongside days/weeks in the week view.
canCloseWeek now mirrors the server rule: every past workday that
has at least one entry must have a closed_days record. Days with
no entries are still skipped (they count as 0h implicitly).
The frontend was blocking week close until every workday had a
closed_days record, which no longer matches the backend's rules
(untracked days are implicitly 0h). Replace the all-workdays-closed
guard with a simple check: week has started (Monday ≤ today) and
is not already closed. The server returns a clear error if a day
with entries still needs closing.
Also fixes a pre-existing TS type narrowing error on currentSettings
and removes the now-unused .hint CSS rule.
Previously any past workday without a closed_days record blocked week
close. Now only days that actually have entries require an explicit
close. Empty workdays count as 0h worked, which is reflected in the
weekly delta automatically.
- WeekService.CloseWeek: after finding no closed_days record, check
whether the day has any entries; only error if it does
- NewWeekService: takes EntryStore to support the above check
- Updated TestCloseWeekMissingDayFails to reflect the new semantic
(test now creates entries on Friday but leaves it unclosed)
CloseWeek was requiring every workday in the ISO week to have a
closed_days record, including days in the future. Now only workdays
up to and including today are checked; future workdays are skipped.
Adds TestCloseWeekMidWeek regression test.