fix(checkups): show a marked-done check-up as done before the refetch

The "Mark done" button re-enables as soon as the satisfy call settles,
while the row kept reading as due until the list refetch landed, so a
second tap stored a second satisfied entry; skip and snooze behaved the
same. The row each of the three calls answers with is now written into
the cached list on success, so the row moves to its next due date at
once, and the refetch follows as before.
This commit is contained in:
Marc-André Bombeck committed 2026-11-11 22:56:32 +01:00
1 parent 46906916d8
commit d79060e26b
2 files changed
+47 -4

No files matched your search

@@ -1,7 +1,11 @@
import { describe, it, expect, vi } from "vitest";
import { QueryClient, QueryObserver } from "@tanstack/react-query";
import { invalidateReminderReads } from "@/hooks/use-measurement-reminders";
import {
invalidateReminderReads,
putReminderRow,
type MeasurementReminder,
} from "@/hooks/use-measurement-reminders";
import { queryKeys } from "@/lib/query-keys";
/**
@@ -71,3 +75,23 @@ describe("invalidateReminderReads — the Vorsorge daily-rail freshness contract
client.clear();
});
});
describe("putReminderRow", () => {
it("shows the answered row at once, so it no longer reads as due", () => {
const client = new QueryClient();
const due = {
id: "r1",
nextDueAt: "2026-11-11T08:00:00.000Z",
} as MeasurementReminder;
const other = { id: "r2" } as MeasurementReminder;
client.setQueryData(queryKeys.measurementReminders(), [due, other]);
const done = { ...due, nextDueAt: "2027-11-11T08:00:00.000Z" };
putReminderRow(client, done);
expect(client.getQueryData(queryKeys.measurementReminders())).toEqual([
done,
other,
]);
});
});
+22 -3
View File
@@ -115,10 +115,29 @@ export function useMeasurementReminders(enabled = true) {
});
}
/**
* Write the row a satisfy, skip or snooze answered with into the cached list.
* The button re-enables as the call settles, and with only the refetch behind
* it the row still read as due, so a second tap marked it done twice.
*/
export function putReminderRow(
qc: QueryClient,
reminder: MeasurementReminder,
): void {
qc.setQueryData<MeasurementReminder[]>(
queryKeys.measurementReminders(),
(rows) => rows?.map((row) => (row.id === reminder.id ? reminder : row)),
);
}
export function useMeasurementReminderMutations() {
const qc = useQueryClient();
// Not returned: a mutation would wait for every refetch before it settles.
const invalidate = () => void invalidateReminderReads(qc);
const answered = (reminder: MeasurementReminder) => {
putReminderRow(qc, reminder);
invalidate();
};
const create = useMutation({
mutationKey: queryKeys.measurementReminderCreate(),
@@ -150,7 +169,7 @@ export function useMeasurementReminderMutations() {
mutationKey: queryKeys.measurementReminderSatisfy(),
mutationFn: (id: string) =>
apiPost<MeasurementReminder>(`${BASE}/${id}/satisfy`),
onSuccess: invalidate,
onSuccess: answered,
});
// v1.37.20 (#223) — honest skip: the interval restarts from the skip
@@ -161,7 +180,7 @@ export function useMeasurementReminderMutations() {
apiPost<{ skipped: boolean; reminder: MeasurementReminder }>(
`${BASE}/${id}/skip`,
),
onSuccess: invalidate,
onSuccess: ({ reminder }) => answered(reminder),
});
// v1.37.20 (#223) — snooze to a calendar day; the server resolves it to
@@ -172,7 +191,7 @@ export function useMeasurementReminderMutations() {
apiPost<{ reminder: MeasurementReminder }>(`${BASE}/${id}/snooze`, {
until,
}),
onSuccess: invalidate,
onSuccess: ({ reminder }) => answered(reminder),
});
return { create, update, remove, satisfy, skip, snooze };