fix(workouts): report manual saves through the outcome module and register the delete

A repeated submit that wrote nothing reads as a neutral notice rather
than a success. The delete control is entered in the destructive-control
registry, and its 409 code in the published error catalogue.

Refs #1085
This commit is contained in:
Marc-André Bombeck committed 2026-10-02 21:31:25 +02:00
1 parent cfbb57ab3e
commit 6eb79a1d47
4 files changed
+22 -9

No files matched your search

@@ -380,6 +380,13 @@ const REGISTRY: DestructiveEntry[] = [
recovery: "tombstoned-no-restore",
confirm: ["AlertDialog"],
},
{
file: "components/workouts/delete-workout-button.tsx",
destroys:
"one workout entered by hand, with the personal records it set; synced workouts are refused by the route",
recovery: "permanent",
confirm: ["AlertDialog"],
},
// ── Visits and the address book ─────────────────────────────────────────
{
@@ -4,7 +4,6 @@ import { useState } from "react";
import { useRouter } from "next/navigation";
import { useQueryClient, type QueryClient } from "@tanstack/react-query";
import { Loader2, Trash2 } from "lucide-react";
import { toast } from "sonner";
import {
AlertDialog,
@@ -17,6 +16,7 @@ import {
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { toastWrittenOutcome } from "@/components/outcome/outcome-toast";
import { useRecordCapabilities } from "@/hooks/use-record-capabilities";
import { apiDelete } from "@/lib/api/api-fetch";
import { useTranslations } from "@/lib/i18n/context";
@@ -78,11 +78,11 @@ export function DeleteWorkoutButton({
setDeleting(true);
try {
await deleteManualWorkout(workout.id, queryClient);
toast.success(t("insights.workouts.manual.deleted"));
toastWrittenOutcome("success", t("insights.workouts.manual.deleted"));
setOpen(false);
router.replace("/insights/workouts");
} catch {
toast.error(t("insights.workouts.manual.deleteError"));
toastWrittenOutcome("failed", t("insights.workouts.manual.deleteError"));
} finally {
setDeleting(false);
}
@@ -4,9 +4,9 @@ import { useId, useMemo, useState } from "react";
import { createPortal } from "react-dom";
import { useQueryClient, type QueryClient } from "@tanstack/react-query";
import { Loader2, Plus } from "lucide-react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { toastWrittenOutcome } from "@/components/outcome/outcome-toast";
import { DateTimeField } from "@/components/ui/date-time-field";
import { FieldError } from "@/components/forms/field-error";
import { Input } from "@/components/ui/input";
@@ -166,11 +166,16 @@ export function ManualWorkoutForm({
setSaving(true);
try {
const outcome = await saveManualWorkout(built.entry, queryClient);
toast.success(
outcome === "duplicate"
? t("insights.workouts.manual.alreadySaved")
: t("common.saved"),
);
// A duplicate wrote nothing new: the same form already landed this
// session. That is reported as such, not as a fresh save.
if (outcome === "duplicate") {
toastWrittenOutcome(
"empty",
t("insights.workouts.manual.alreadySaved"),
);
} else {
toastWrittenOutcome("success", t("common.saved"));
}
onSuccess?.();
} catch (err) {
setSaveError(
+1
View File
@@ -357,6 +357,7 @@ export const ERROR_CODE_CATALOGUE: Readonly<Record<string, readonly string[]>> =
"workout.batch.payload_too_large",
"workout.batch.source_not_permitted",
"workout.batch.too_large",
"workout.delete.synced_source",
],
"(unprefixed)": [
"about_me_conflict",