fix(forms): send one Idempotency-Key per opened create form

A mood, symptom, lab value, custom metric value, unscheduled dose,
allergy, vaccination, visit or measurement saved on a weak signal could
be stored twice: the POST committed, the answer missed the 15 s
timeout, the form said it could not save, and the second tap was a new
call with a new key, so the server ran it again.

useIdempotentPost keeps one key until a save lands and renews it for the
next entry, so the retry replays the first answer. The create forms and
the three create hooks post through it; the comment on the per-call key
in api-fetch now says what that key does and does not cover.

LabForm and MoodForm each gain the one line that mounts the key, so
their function-size baselines go up by one. The intake toast guard now
recognises a dose posted through the hook.
This commit is contained in:
Marc-André Bombeck committed 2026-11-11 20:02:40 +01:00
1 parent 121b23f4a2
commit f761500247
15 files changed
+172 -35

No files matched your search

+5 -6
View File
@@ -262,12 +262,11 @@ export function apiGet<T = unknown>(
* Attach a per-call `Idempotency-Key` unless the caller already set one.
*
* Every `apiHandler` route accepts the header, and the write routes
* wrapped in `withIdempotency` dedupe on it — but almost no client call
* site sent one, so a transport-level replay of a create (a retried
* request, a re-sent form submission) could land a duplicate dose or
* vaccination and shift every derived series. Minting the key here
* protects every `apiPost` / `apiPatch` surface at once; a caller with a
* real natural key still wins by setting the header itself.
* wrapped in `withIdempotency` dedupe on it. A key minted per call covers a
* transport-level replay of that one request. A second tap on Save is a
* new call and gets a new key, so a create form sends one key for as long
* as it is open (`useIdempotentPost`); a caller that sets the header
* itself always wins.
*/
function withDefaultIdempotencyKey(init: RequestInit): RequestInit {
const headers = new Headers(init.headers);