From e16d12023684e05d546f8f3508678c1b136e7d97 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 10 Jun 2026 11:00:20 +0200 Subject: [PATCH] fix(time-tracking): cap smart-fill start at now so the range is never inverted Smart-fill set the From time to the configured default start (09:00) when there was no recent entry to continue from. Before that time of day the default lands in the future, after the To time of now, producing an inverted range the backend rejects (end_time before start_time). The save then failed silently and the entry never appeared. This surfaced as a flaky time-tracking e2e suite: the smart-fill specs failed only when CI happened to run before 09:00 UTC. --- frontend/src/helpers/time/smartFillStart.test.ts | 10 ++++++++++ frontend/src/helpers/time/smartFillStart.ts | 8 ++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/frontend/src/helpers/time/smartFillStart.test.ts b/frontend/src/helpers/time/smartFillStart.test.ts index e623125cf..e48caf130 100644 --- a/frontend/src/helpers/time/smartFillStart.test.ts +++ b/frontend/src/helpers/time/smartFillStart.test.ts @@ -44,4 +44,14 @@ describe('smartFillStart', () => { it('falls back to 09:00 when no default is configured', () => { expect(smartFillStart([], '', now)).toEqual(new Date('2026-06-07T09:00:00')) }) + + it('caps the default start at now when it would be in the future (before 09:00)', () => { + const beforeNine = new Date('2026-06-07T07:30:00') + expect(smartFillStart([], '09:00', beforeNine)).toEqual(beforeNine) + }) + + it('caps a future last-entry end at now', () => { + const entries = [entry(new Date('2026-06-07T16:00:00'), new Date('2026-06-07T17:00:00'))] + expect(smartFillStart(entries, '09:00', now)).toEqual(now) + }) }) diff --git a/frontend/src/helpers/time/smartFillStart.ts b/frontend/src/helpers/time/smartFillStart.ts index c131c1d0e..0a84a7eea 100644 --- a/frontend/src/helpers/time/smartFillStart.ts +++ b/frontend/src/helpers/time/smartFillStart.ts @@ -5,16 +5,20 @@ import type {ITimeEntry} from '@/modelTypes/ITimeEntry' // continue from, fall back to the user's configured default start (HH:MM) on // the given day. export function smartFillStart(recentEntries: ITimeEntry[], defaultStart: string, now: Date): Date { + // The filled range ends at now, so a start after now would be inverted (and + // rejected on save). Cap at now — e.g. the 09:00 fallback before 9am. + const cap = (start: Date) => (start.getTime() > now.getTime() ? new Date(now) : start) + const lastEnd = recentEntries .map(entry => entry.endTime) .filter((end): end is Date => end !== null) .sort((a, b) => b.getTime() - a.getTime())[0] if (lastEnd !== undefined) { - return new Date(lastEnd) + return cap(new Date(lastEnd)) } const [hours, minutes] = (defaultStart || '09:00').split(':').map(Number) const start = new Date(now) start.setHours(hours || 0, minutes || 0, 0, 0) - return start + return cap(start) }