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,15 @@
import { FormEvent, useState } from "react";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { deactivateApiary, updateApiary } from "@/lib/apiaries/api";
import { toast } from "sonner";
import { deactivateApiary, reactivateApiary, updateApiary } from "@/lib/apiaries/api";
import { Apiary } from "@/lib/apiaries/types";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { ConfirmDialog } from "@/components/confirm-dialog";
import {
Card,
CardContent,
@@ -40,7 +42,20 @@ export function ApiaryEditCard({ apiary }: { apiary: Apiary }) {
mutationFn: () => deactivateApiary(apiary.id),
onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: ["apiaries"] });
toast.success("Bienenstand deaktiviert", {
description: "Er kann jederzeit über „Reaktivieren“ wiederhergestellt werden.",
});
},
onError: () => toast.error("Bienenstand konnte nicht deaktiviert werden"),
});
const reactivateMutation = useMutation({
mutationFn: () => reactivateApiary(apiary.id),
onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: ["apiaries"] });
toast.success("Bienenstand reaktiviert");
},
onError: () => toast.error("Bienenstand konnte nicht reaktiviert werden"),
});
function handleSubmit(event: FormEvent<HTMLFormElement>) {
@@ -95,14 +110,25 @@ export function ApiaryEditCard({ apiary }: { apiary: Apiary }) {
<Button type="submit" disabled={updateMutation.isPending}>
{updateMutation.isPending ? "Speichert…" : "Speichern"}
</Button>
<Button
type="button"
variant="outline"
disabled={apiary.status !== "ACTIVE" || deactivateMutation.isPending}
onClick={() => deactivateMutation.mutate()}
>
Deaktivieren
</Button>
{apiary.status === "ACTIVE" ? (
<ConfirmDialog
triggerLabel="Deaktivieren"
triggerDisabled={deactivateMutation.isPending}
title="Bienenstand deaktivieren?"
description={`„${apiary.name}“ wird deaktiviert. Die Historie bleibt erhalten und der Bienenstand kann jederzeit wieder reaktiviert werden.`}
confirmLabel="Deaktivieren"
isPending={deactivateMutation.isPending}
onConfirm={() => deactivateMutation.mutate()}
/>
) : (
<Button
type="button"
disabled={reactivateMutation.isPending}
onClick={() => reactivateMutation.mutate()}
>
{reactivateMutation.isPending ? "Wird reaktiviert…" : "Reaktivieren"}
</Button>
)}
</div>
</form>
</CardContent>