feat(ui): improve delivery time handling#214
Conversation
markboenigk
left a comment
There was a problem hiding this comment.
Overall: Clean, well-scoped PR — the formatter extraction and the From/By label logic are solid, and the unit tests cover the intent clearly. Three things worth a look before merge:
VehicleDetailsOverlay.tsx ~line 474 — side effect in state updater (correctness)
minutesAutoFilledRef.current is mutated inside the setMinutes functional updater. React 18 Strict Mode calls state updaters twice with the same previous state, and because the first invocation mutates the ref, the second invocation reads a stale value and can produce a different result.
Concrete case: auto-filled 00 is in minutes (ref = true), user then changes the hour to an invalid value (e.g. "0").
- First run → returns
"", setsref = false - Second run → reads
ref = false, returnscurrentMinutes("00") instead of""
React keeps the second result, so the minutes field fails to clear. Easiest fix is to move the ref update out of the updater and into the onChange handler directly after setMinutes:
const nextMinutes = getMinutesAfterHourChange(val, minutes, minutesAutoFilledRef.current);
minutesAutoFilledRef.current = nextMinutes === "00" && (minutes === "" || minutesAutoFilledRef.current);
setMinutes(nextMinutes);(Reading minutes from the closure is fine here since this is a synchronous event handler, not an async update.)
VehicleDetailsOverlay.tsx line 96 — utility placement
getMinutesAfterHourChange is a pure function with no React dependencies. Exporting it from a component file is a bit surprising — the natural home is deliveryHelpers.ts, where the other delivery/time helpers live and where vehicleDepartureTime.test.ts could import it without touching a component. Minor, but worth moving if you're already touching the file.
VehicleDetailsOverlay.tsx lines 89 & 102 — duplicated hour bound
isValidTime and getMinutesAfterHourChange each independently encode >= 1 && <= 12. Right now they agree, but they're easy to drift apart. A shared constant or a small isValidHour(h: number) predicate would keep them locked together.
|
Hey Mark, |
|
All three points from the last round are fixed: the ref mutation is out of the One new issue came up in the rework, plus a couple of minor things worth a look before merge:
Concrete case: hour is Probably easiest to gate the ref set on the actual "did this change" condition rather than just
export function isValidDepartureHour(hour: string | number): boolean {
const hourNumber = typeof hour === "number" ? hour : parseInt(hour.trim(), 10);
return hourNumber >= 1 && hourNumber <= 12;
}
Calling
minutesAutoFilledRef.current =
nextMinutes === "00" && (minutes === "" || minutesAutoFilledRef.current);If the helper's branching ever changes, this copy can quietly drift out of sync. Might be cleaner for the helper to return Nothing else stood out — the |
Gate the minutes-focus flag on an actual hours/minutes state change so a
same-value retype followed by a real edit can't steal focus with a stale
ref. Harden isValidDepartureHour against partially-numeric input, drop the
now-redundant select() call in the focus effect, and make
getMinutesAfterHourChange the single owner of the auto-fill decision by
returning { minutes, autoFilled } instead of re-deriving it at the call site.
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Hey @markboenigk, |
Summary
00after a valid hour is entered, allowing common on-the-hour departures to save without an extra manual minute entry.From <time>, end-only windows showBy <time>, full ranges and empty windows keep their prior display.Motivation
Changes
00only for valid hours from1through12.00was generated by the UI so user-entered minutes, including a deliberate00, are not overwritten.Validation
Frontend
npm --prefix app/ui test -- deliveryHelpers.test.ts— passed: 1 test file, 10 tests.npm --prefix app/ui run typecheck— passed.npm --prefix app/ui test— passed: 24 test files, 122 tests.npm --prefix app/ui run lint— skipped locally; CI will run the full lint gate for the branch.npm --prefix app/ui run format:check— skipped locally; CI will run the formatting gate for the branch.npm --prefix app/ui run build— skipped locally; CI will run the production build gate for the branch.Other Checks
npm --prefix app/mobile run lint— skipped locally because the mobile app is outside this UI change.npm --prefix app/mobile run typecheck— skipped locally because the mobile app is outside this UI change.cmake --preset dev— skipped locally because backend code is outside this UI change..github/scripts/check-backend-static.sh build/dev— skipped locally because backend code is outside this UI change.cmake --build --preset dev --parallel— skipped locally because backend code is outside this UI change.ctest --preset dev --output-on-failure --no-tests=error -LE 'e2e|docker'— skipped locally because backend code is outside this UI change.Risk
Rollout and Recovery
High-Signal PR Checklist