Skip to content

fix(web): reliable start times and live duration in time-entry forms - #788

Merged
evilguy4000 merged 7 commits into
DRYTRIX:developfrom
Giorgio-Germani:fix/start-time-duration
Oct 16, 2026
Merged

evilguy4000 merged 7 commits into
DRYTRIX:developfrom
Giorgio-Germani:fix/start-time-duration

Conversation

@Giorgio-Germani

Copy link
Copy Markdown

Problem

Two related usability bugs in the time-entry forms, both rooted in the same pattern: date/time inputs are Flatpickr-wrapped (Flatpickr hides the native input and shows its own altInput), so code that reads or writes the native input's .value silently misses what the user sees.

  1. New timers could start in the past. The start-timer form pre-filled a native datetime-local override with the time the form was opened. Submitting minutes later started the timer at that stale time. Worse, the "Set to now" button updated the hidden native input only — the visible Flatpickr input never changed, so the button appeared dead.
  2. Edit form's duration label never updated while editing. It listened for change on the hidden native inputs, which don't fire while the user types into the Flatpickr altInput.

Changes

Start Timer modal (dashboard)

  • The start time moved from a bottom form field to a compact live time chip next to the modal title: Start Timer 15:03 [Start]. It shows the current time on open, ticks every 15s while the modal is open, and is submitted as-is — a timer always starts at the displayed moment.
  • Overwriting the chip backdates the entry within today (composed with today's date; future values clamped to now, matching server validation). Accepted input: 14:30, 1430, 930, or 2:45 PM when the profile uses 12h.
  • Display follows the in-app time format preference (userPrefs.timeFormat), not the browser locale — a native type=time chip rendered 12h AM/PM regardless of the system setting.
  • Compact entries like 1345 normalize to 13:45 on blur.
  • The hidden native input keeps the start_time_override form contract; no server changes needed for this part.

Timer page

  • Same Flatpickr visibility fix: "Set to now" and the submit-time refresh route through _flatpickr.setDate(), and the submit-time refresh compares against the last auto-set value so user-entered backdates survive.

Edit form duration

  • The duration label now updates live while editing: hooks the Flatpickr instances (config.onChange + altInput events), includes the break field in the calculation like the server does, with a light 1s poll as a safety net for late Flatpickr initialization.

Break duration parsing (server + client)

  • The break/worked-time fields only accepted strict HH:MM; 0100 was silently stored as zero. A shared _parse_duration_to_seconds() parser now accepts HH:MM, compact HMM/HHMM (0100 = 1h), decimal hours (1h, 1.5, 1,5) and plain minutes (90), used by the edit route, the manual-entry route, and mirrored in the live label.

Testing

  • Jinja parse + node --check on all touched files; parser unit-checked against 0100, 130, 1:30, 1.5h, 1,5, 45, 0160 (invalid), empty, garbage
  • tests/test_routes.py timer tests pass (4 passed)
  • Verified on a production instance in a real browser: chip shows the preference-formatted current time and ticks; 1345 → 13:45 on blur; backdate composed correctly; edit-form duration updates live (14:35→16:30 → 01:55:00); break 0100 subtracts 1h live and persists
  • tests/test_reconnect_on_resume.py has one failure on this branch (assert '5000'), but it fails identically on develop — it asserts the resume-probe backoff shipped separately in fix(web): suppress false offline errors and refetch data on tab resume #787

Giorgio added 7 commits October 16, 2026 15:58
The start-timer forms pre-filled the 'Start Time (Optional)' override with
the time the form was opened. Users who take a few minutes to fill in the
form then unknowingly start the timer in the past.

Both the dashboard modal and the timer page now track whether the user
edited the override field themselves; on submit an untouched field is
refreshed to the current time, so a timer started without an explicit
backdate always begins at the actual start moment. Manually entered times
are preserved, and the 'Set to now' button continues to load the current
time on click.
The modal's start-time control moved from a datetime field at the bottom
of the form to a compact time chip next to the modal title:

- shows the current time, ticks every 15s while the modal is open, and is
  submitted as-is — so a timer always starts at the time shown when Start
  is pressed
- overwriting the chip backdates the entry within today (composed with
  today's date; future values are clamped to now)
- the old datetime-local field was hidden by Flatpickr's altInput swap,
  so the previous 'Set to now' button updated an invisible input — the
  reason it appeared dead; the button is gone along with the field
- hidden native input keeps the start_time_override form contract

The timer page keeps its field but the same Flatpickr visibility fix is
applied there: updates route through _flatpickr.setDate(), and its
submit-time refresh compares against the last auto-set value so user
backdates survive.
The native type=time input rendered in the browser locale (e.g. 12h
AM/PM for en-US), ignoring the system-wide time format setting and the
per-user profile override. Replace it with a text chip formatted and
parsed via window.userPrefs.timeFormat, reusing the shared
__parseUserTimeInput helper (accepts HH:MM, HHMM, and AM/PM input).
The edit form's duration label only listened for change events on the
native date/time inputs, but those are Flatpickr-wrapped (altInput
visible, native hidden), so typing never updated the label. Hook the
Flatpickr instances and altInputs directly, include the break field in
the calculation like the server does, and poll lightly as a safety net
for late Flatpickr initialization.
The break field only accepted strict HH:MM; '0100' was silently treated
as zero. Share one duration parser (HH:MM, compact HMM/HHMM, decimal
hours with h suffix or separator, plain minutes) between the edit route,
the manual-entry route and the live duration label, and document the
accepted formats in the field hint.
@evilguy4000
evilguy4000 merged commit dfce1c0 into DRYTRIX:develop Oct 16, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants