mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 22:02:16 +00:00
feat(validation): bound entry timestamps with a shared plausibility guard
Only medication intake bounded its capture instant — a hand-rolled POST or a faulty import could backdate a measurement to epoch zero or forward-date a mood entry into next year, and both sailed through validation. Extract the intake bound into a shared `validateEntryInstant` helper: reject any instant more than a five-minute clock-skew tolerance in the future, and reject anything before a 1900 far-past floor. Callers can clamp the past window tighter — medication intake keeps its five-year floor — and override the past-bound message to preserve an established contract. Apply it to the measurement create/edit schema (`measuredAt`), the mood schema (`moodLoggedAt`), and the cycle period + day-log (`loggedAt`); medication intake now delegates to the same helper. The capture forms gain a matching `max` on the datetime picker so a future instant can't be selected in the UI either. The wire shape is unchanged, so the OpenAPI contract holds.
This commit is contained in:
1 parent
e9138bb4f6
commit
3e7aa49eb0
12 files changed
+323
-23
No files matched your search
@@ -472,6 +472,10 @@ export function MeasurementForm({
|
||||
id="measuredAt"
|
||||
value={measuredAt}
|
||||
onChange={(e) => setMeasuredAt(e.target.value)}
|
||||
// v1.17 W1b — mirror the server-side plausibility bound: the
|
||||
// picker cannot select a future instant. Matches the
|
||||
// `validateEntryInstant` refine on `measuredAt`.
|
||||
max={getDefaultMeasuredAtValue()}
|
||||
required
|
||||
aria-required="true"
|
||||
aria-invalid={!!error || undefined}
|
||||
|
||||
@@ -1273,6 +1273,8 @@ export function MeasurementList({
|
||||
id="edit-measuredAt"
|
||||
value={editMeasuredAt}
|
||||
onChange={(e) => setEditMeasuredAt(e.target.value)}
|
||||
// v1.17 W1b — match the server bound: no future instant.
|
||||
max={toDateTimeLocalValue(new Date().toISOString())}
|
||||
required
|
||||
aria-required="true"
|
||||
aria-invalid={!!editError || undefined}
|
||||
|
||||
@@ -328,6 +328,8 @@ export function MoodForm({ onSuccess, onCancel, footerSlot }: MoodFormProps) {
|
||||
id="mood-logged-at"
|
||||
value={moodLoggedAt}
|
||||
onChange={(e) => setMoodLoggedAt(e.target.value)}
|
||||
// v1.17 W1b — match the server bound: no future instant.
|
||||
max={getDefaultMoodLoggedAtValue()}
|
||||
required
|
||||
aria-required="true"
|
||||
aria-invalid={!!error || undefined}
|
||||
|
||||
@@ -842,6 +842,8 @@ export function MoodList({ onAddFirst }: MoodListProps = {}) {
|
||||
id="edit-mood-logged-at"
|
||||
value={editMoodLoggedAt}
|
||||
onChange={(e) => setEditMoodLoggedAt(e.target.value)}
|
||||
// v1.17 W1b — match the server bound: no future instant.
|
||||
max={toDateTimeLocalValue(new Date().toISOString())}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user