Merge branch 'phase/009-host-key-pruefung'
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useRef, useState, useTransition, type ReactNode } from "react";
|
||||
import { Button, type ButtonVariante } from "@/components/ui/Button";
|
||||
|
||||
/**
|
||||
* Startet einen Vorgang im Worker und zeigt seinen Fortschritt (#009). Fragt den Status
|
||||
* jede Sekunde ab und laedt nach dem Ende die Seite neu, damit Server Components die neuen
|
||||
* Daten zeigen.
|
||||
*/
|
||||
type Antwort = { readonly vorgangId?: string; readonly fehler?: string };
|
||||
type Status = { status: string; fehler: string | null };
|
||||
|
||||
const LAUFEND = new Set(["WARTEND", "LAEUFT"]);
|
||||
|
||||
export function VorgangsKnopf({
|
||||
aktion,
|
||||
beschriftung,
|
||||
variante = "sekundaer",
|
||||
icon,
|
||||
klein,
|
||||
}: {
|
||||
aktion: () => Promise<Antwort>;
|
||||
beschriftung: string;
|
||||
variante?: ButtonVariante;
|
||||
icon?: ReactNode;
|
||||
klein?: boolean;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const [vorgangId, setVorgangId] = useState<string>();
|
||||
const [status, setStatus] = useState<Status>();
|
||||
const [fehler, setFehler] = useState<string>();
|
||||
const [startet, starten] = useTransition();
|
||||
const abbrechen = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!vorgangId) return;
|
||||
abbrechen.current = false;
|
||||
const abfragen = async () => {
|
||||
while (!abbrechen.current) {
|
||||
const antwort = await fetch(`/api/vorgaenge/${vorgangId}`, { cache: "no-store" });
|
||||
if (!antwort.ok) {
|
||||
setFehler("Status des Vorgangs nicht abrufbar.");
|
||||
return;
|
||||
}
|
||||
const neu = (await antwort.json()) as Status;
|
||||
setStatus(neu);
|
||||
if (!LAUFEND.has(neu.status)) {
|
||||
router.refresh();
|
||||
return;
|
||||
}
|
||||
await new Promise((weiter) => setTimeout(weiter, 1000));
|
||||
}
|
||||
};
|
||||
void abfragen();
|
||||
return () => {
|
||||
abbrechen.current = true;
|
||||
};
|
||||
}, [vorgangId, router]);
|
||||
|
||||
const laeuft =
|
||||
startet || (status !== undefined && LAUFEND.has(status.status)) || (vorgangId !== undefined && !status);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<Button
|
||||
variante={variante}
|
||||
icon={icon}
|
||||
klein={klein}
|
||||
disabled={laeuft}
|
||||
onClick={() =>
|
||||
starten(async () => {
|
||||
setFehler(undefined);
|
||||
setStatus(undefined);
|
||||
const antwort = await aktion();
|
||||
if (antwort.fehler) setFehler(antwort.fehler);
|
||||
else setVorgangId(antwort.vorgangId);
|
||||
})
|
||||
}
|
||||
>
|
||||
{laeuft ? "Läuft …" : beschriftung}
|
||||
</Button>
|
||||
<p role="status" className="text-[13px]">
|
||||
{fehler && <span className="text-danger">{fehler}</span>}
|
||||
{!fehler && status?.status === "FEHLGESCHLAGEN" && (
|
||||
<span className="text-danger">{status.fehler}</span>
|
||||
)}
|
||||
{!fehler && status?.status === "ERLEDIGT" && <span className="text-ok">Erledigt.</span>}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user