mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 06:52:04 +00:00
Give custom-metric values the same second chance as every other entry
Refs A3-11. Deleting a logged custom-metric value was the one hard delete left on an entry surface — everywhere else a deletion tombstones and the toast offers Undo. The DELETE verb now stamps deletedAt, every read (list, latest-value, counts, revive check) filters the tombstone, and the new entries/restore endpoint clears it scoped to owner and parent metric — a foreign, live or mismatched id is a silent no-op, never an existence leak. The history table's delete toast carries the Undo action.
This commit is contained in:
1 parent
689cacc039
commit
7bf8dbd8af
14 files changed
+467
-23
No files matched your search
@@ -21,7 +21,7 @@ import {
|
||||
} from "@/components/ui/table";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { useTableSort } from "@/hooks/use-table-sort";
|
||||
import { ApiError, apiDelete, apiPatch } from "@/lib/api/api-fetch";
|
||||
import { ApiError, apiDelete, apiPatch, apiPost } from "@/lib/api/api-fetch";
|
||||
import { formatDateShort } from "@/lib/format";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
@@ -48,8 +48,8 @@ function parseDecimal(raw: string): number | null {
|
||||
|
||||
/**
|
||||
* v1.25.5 — reverse-chronological value history for one custom metric, with
|
||||
* full edit + delete. An edit overwrites in place; delete hard-deletes (the
|
||||
* value store has no soft-delete tier).
|
||||
* full edit + delete. An edit overwrites in place; delete tombstones with an
|
||||
* Undo toast (v1.37.20, A3-11 — parity with every other entry surface).
|
||||
*/
|
||||
export function CustomMetricHistoryList({
|
||||
customMetricId,
|
||||
@@ -87,12 +87,31 @@ export function CustomMetricHistoryList({
|
||||
});
|
||||
}
|
||||
|
||||
// v1.37.20 (A3-11) — undo parity with every peer entry surface: the
|
||||
// delete tombstones server-side and the toast offers the restore.
|
||||
async function restoreEntry(id: string) {
|
||||
try {
|
||||
await apiPost(`/api/custom-metrics/${customMetricId}/entries/restore`, {
|
||||
ids: [id],
|
||||
});
|
||||
invalidate();
|
||||
toast.success(t("customMetrics.entry.restoredToast"));
|
||||
} catch {
|
||||
toast.error(t("customMetrics.entry.restoreError"));
|
||||
}
|
||||
}
|
||||
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: (id: string) =>
|
||||
apiDelete(`/api/custom-metrics/${customMetricId}/entries/${id}`),
|
||||
onSuccess: () => {
|
||||
onSuccess: (_data, id) => {
|
||||
invalidate();
|
||||
toast.success(t("customMetrics.entry.deletedToast"));
|
||||
toast.success(t("customMetrics.entry.deletedToast"), {
|
||||
action: {
|
||||
label: t("common.undo"),
|
||||
onClick: () => void restoreEntry(id),
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: () => toast.error(t("customMetrics.entry.deleteError")),
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user