feat(frontend): confirmation dialogs and undo for destructive-feeling actions

Volk aufloesen, Bienenstand deaktivieren, Benutzer deaktivieren und
Webhook loeschen liefen bisher ohne jede Rueckfrage direkt auf Klick.
Neue wiederverwendbare ConfirmDialog-Komponente erzwingt jetzt eine
bewusste Bestaetigung. Zusaetzlich zeigen Volk/Bienenstand/Benutzer
nach dem Aufloesen/Deaktivieren einen Reaktivieren-Button (nutzt die
neuen Backend-Endpunkte), da diese Aktionen nie ein echtes Loeschen
sind und ein versehentlicher Klick bisher nicht rueckgaengig zu
machen war.
This commit is contained in:
Nick Szaunig committed 2026-08-23 11:50:45 +02:00
1 parent 57979f0371
commit 6ab028d1be
9 files changed
+231 -36

No files matched your search

@@ -2,13 +2,21 @@
import { FormEvent, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { dissolveColony, mergeColony, moveColony, splitColony } from "@/lib/colonies/api";
import { toast } from "sonner";
import {
dissolveColony,
mergeColony,
moveColony,
reactivateColony,
splitColony,
} from "@/lib/colonies/api";
import { Colony, ColonyType } from "@/lib/colonies/types";
import { fetchApiaries } from "@/lib/apiaries/api";
import { fetchColonies } from "@/lib/colonies/api";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { ConfirmDialog } from "@/components/confirm-dialog";
import {
Dialog,
DialogContent,
@@ -248,16 +256,46 @@ function DissolveButton({ colony }: { colony: Colony }) {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: () => dissolveColony(colony.id),
onSuccess: () => invalidateColony(queryClient, colony.id),
onSuccess: () => {
invalidateColony(queryClient, colony.id);
toast.success("Volk aufgelöst", {
description: "Das Volk kann jederzeit über „Reaktivieren“ wiederhergestellt werden.",
});
},
onError: () => toast.error("Volk konnte nicht aufgelöst werden"),
});
return (
<ConfirmDialog
triggerLabel="Auflösen"
triggerDisabled={colony.status !== "ACTIVE" || mutation.isPending}
title="Volk auflösen?"
description={`„${colony.code}“ wird als aufgelöst markiert und aus den aktiven Völkern entfernt. Die Historie bleibt erhalten und das Volk kann jederzeit wieder reaktiviert werden.`}
confirmLabel="Volk auflösen"
isPending={mutation.isPending}
onConfirm={() => mutation.mutate()}
/>
);
}
function ReactivateButton({ colony }: { colony: Colony }) {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: () => reactivateColony(colony.id),
onSuccess: () => {
invalidateColony(queryClient, colony.id);
toast.success("Volk reaktiviert");
},
onError: () => toast.error("Volk konnte nicht reaktiviert werden"),
});
return (
<Button
variant="outline"
disabled={colony.status !== "ACTIVE" || mutation.isPending}
type="button"
disabled={mutation.isPending}
onClick={() => mutation.mutate()}
>
Auflösen
{mutation.isPending ? "Wird reaktiviert…" : "Reaktivieren"}
</Button>
);
}
@@ -265,10 +303,16 @@ function DissolveButton({ colony }: { colony: Colony }) {
export function ColonyActions({ colony }: { colony: Colony }) {
return (
<div className="flex flex-wrap gap-2">
<MoveDialog colony={colony} />
<SplitDialog colony={colony} />
<MergeDialog colony={colony} />
<DissolveButton colony={colony} />
{colony.status === "ACTIVE" ? (
<>
<MoveDialog colony={colony} />
<SplitDialog colony={colony} />
<MergeDialog colony={colony} />
<DissolveButton colony={colony} />
</>
) : (
<ReactivateButton colony={colony} />
)}
</div>
);
}