mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
fix(medications): give backdated/manual intakes the same Undo the card has
Logging a dose via Medications → Add → "Log intake" (incl. backdated) or the dashboard's medication-intake quick-add toasted success with no Undo, unlike the card's own take/skip path — a dose logged against the wrong medication (easy on the quick-add, which auto-picks) was only correctable via detail page → history → find row → delete. Both paths now capture the created event id and attach the same Undo action.
This commit is contained in:
1 parent
8c385b9ac2
commit
7ac9e7833e
3 files changed
+81
-16
No files matched your search
@@ -30,6 +30,7 @@ import {
|
||||
} from "@/lib/medications/window-status";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { ApiError, apiGet, apiPost } from "@/lib/api/api-fetch";
|
||||
import { runUndoIntake } from "@/components/medications/use-medication-intake";
|
||||
|
||||
/**
|
||||
* v1.4.37 W7b — dashboard "Hinzufügen" → "Medikamenteneinnahme" quick-add.
|
||||
@@ -271,11 +272,18 @@ export function MedicationIntakeQuickAdd({
|
||||
const doseDeviates =
|
||||
trimmedDose.length > 0 &&
|
||||
trimmedDose !== (selectedMedication?.dose ?? "").trim();
|
||||
await apiPost(`/api/medications/${medicationId}/intake`, {
|
||||
takenAt: timestamp,
|
||||
skipped: false,
|
||||
...(doseDeviates && { doseTaken: trimmedDose }),
|
||||
});
|
||||
// v1.30.1 M7 — the route returns the created event
|
||||
// (`apiSuccess(event, 201)`), same shape the card's take/skip path
|
||||
// already reads; the id drives the new Undo action below.
|
||||
const created = await apiPost<{ id?: string } | undefined>(
|
||||
`/api/medications/${medicationId}/intake`,
|
||||
{
|
||||
takenAt: timestamp,
|
||||
skipped: false,
|
||||
...(doseDeviates && { doseTaken: trimmedDose }),
|
||||
},
|
||||
);
|
||||
const eventId = created?.id;
|
||||
|
||||
await invalidateKeys(queryClient, medicationDependentKeys);
|
||||
// Fan-out to every inline compliance chart key (one per medication)
|
||||
@@ -284,7 +292,26 @@ export function MedicationIntakeQuickAdd({
|
||||
queryKey: queryKeys.complianceChartInline(),
|
||||
});
|
||||
|
||||
toast.success(t("common.saved"));
|
||||
toast.success(
|
||||
t("common.saved"),
|
||||
eventId && selectedMedication
|
||||
? {
|
||||
action: {
|
||||
label: t("medications.intakeUndo"),
|
||||
onClick: () =>
|
||||
void runUndoIntake({
|
||||
medication: {
|
||||
id: medicationId,
|
||||
name: selectedMedication.name,
|
||||
},
|
||||
eventId,
|
||||
t,
|
||||
queryClient,
|
||||
}),
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
onSuccess?.();
|
||||
} catch (err) {
|
||||
setError(
|
||||
|
||||
@@ -33,7 +33,10 @@ import { NativeSelect } from "@/components/ui/native-select";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { formatDose } from "@/lib/medications/format-dose";
|
||||
import { runLogIntake } from "@/components/medications/use-medication-intake";
|
||||
import {
|
||||
runLogIntake,
|
||||
runUndoIntake,
|
||||
} from "@/components/medications/use-medication-intake";
|
||||
|
||||
interface LogIntakeSchedule {
|
||||
windowStart: string;
|
||||
@@ -194,6 +197,15 @@ export function LogIntakeDialog({
|
||||
...(doseDeviates && { doseTaken: trimmedDose }),
|
||||
t,
|
||||
queryClient,
|
||||
// v1.30.1 M7 — a backdated/manual intake now gets the same
|
||||
// Undo affordance the card's take/skip path already has.
|
||||
undoIntake: (eventId) =>
|
||||
runUndoIntake({
|
||||
medication: { id: med.id, name: med.name },
|
||||
eventId,
|
||||
t,
|
||||
queryClient,
|
||||
}),
|
||||
});
|
||||
if (ok) onOpenChange(false);
|
||||
} finally {
|
||||
|
||||
@@ -215,6 +215,12 @@ export async function runLogIntake(deps: {
|
||||
doseTaken?: string;
|
||||
t: Translator;
|
||||
queryClient: QueryClient;
|
||||
/**
|
||||
* v1.30.1 M7 — reverse the just-logged intake. Optional so existing
|
||||
* unit tests that don't care about Undo can omit it; the toast simply
|
||||
* carries no action when it's absent.
|
||||
*/
|
||||
undoIntake?: (eventId: string) => void | Promise<void>;
|
||||
}): Promise<boolean> {
|
||||
const {
|
||||
medication,
|
||||
@@ -224,22 +230,42 @@ export async function runLogIntake(deps: {
|
||||
doseTaken,
|
||||
t,
|
||||
queryClient,
|
||||
undoIntake,
|
||||
} = deps;
|
||||
try {
|
||||
const body: Record<string, unknown> = { skipped };
|
||||
if (!skipped) body.takenAt = takenAt;
|
||||
if (scheduledFor) body.scheduledFor = scheduledFor;
|
||||
if (!skipped && doseTaken) body.doseTaken = doseTaken;
|
||||
await apiPost(`/api/medications/${medication.id}/intake`, body);
|
||||
await invalidateMedicationReads(queryClient);
|
||||
toast.success(
|
||||
t(
|
||||
skipped
|
||||
? "medications.intakeToastSkipped"
|
||||
: "medications.intakeToastTaken",
|
||||
{ name: medication.name },
|
||||
),
|
||||
// v1.30.1 M7 — the route returns the created event
|
||||
// (`apiSuccess(event, 201)`, same as the card's `runRecordIntake`);
|
||||
// its id drives the Undo action below.
|
||||
const created = await apiPost<{ id?: string } | undefined>(
|
||||
`/api/medications/${medication.id}/intake`,
|
||||
body,
|
||||
);
|
||||
const eventId = created?.id;
|
||||
await invalidateMedicationReads(queryClient);
|
||||
const message = t(
|
||||
skipped
|
||||
? "medications.intakeToastSkipped"
|
||||
: "medications.intakeToastTaken",
|
||||
{ name: medication.name },
|
||||
);
|
||||
// v1.30.1 M7 — only pass a second `toast.success` argument when there's
|
||||
// a real Undo action to attach; keeps the no-undo call signature
|
||||
// identical to the pre-fix behaviour (existing unit tests assert the
|
||||
// single-argument call).
|
||||
if (eventId && undoIntake) {
|
||||
toast.success(message, {
|
||||
action: {
|
||||
label: t("medications.intakeUndo"),
|
||||
onClick: () => void undoIntake(eventId),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
toast.success(message);
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
toast.error(t("medications.intakeToastFailed", { name: medication.name }));
|
||||
|
||||
Reference in new issue
Block a user