Reset-Button, Service-Reorder, Port/HTTPS-Spalten, Healthcheck in Sidebar, Kategorien-Sync

This commit is contained in:
2026-07-19 15:14:44 +02:00
parent 20ea4d382d
commit fc1ea4fa70
16 changed files with 447 additions and 20 deletions

View File

@@ -1,3 +1,6 @@
import { useState } from "react";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { Button } from "@launchpad/ui";
import { useBackendHealth } from "../../hooks/useBackendHealth.js";
import { useTheme } from "../../hooks/useTheme.js";
import { AdminPageHeader } from "./AdminPageHeader.js";
@@ -11,6 +14,66 @@ function InfoRow({ label, value }: { label: string; value: string }) {
);
}
async function resetEverything() {
const res = await fetch("/api/reset", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ confirm: true }),
});
const body = await res.json();
if (!res.ok) {
throw new Error(body.error ?? `Reset fehlgeschlagen (HTTP ${res.status})`);
}
return body as { deletedDevices: number };
}
function DangerZone() {
const queryClient = useQueryClient();
const [message, setMessage] = useState<string | null>(null);
const mutation = useMutation({
mutationFn: resetEverything,
onSuccess: (result) => {
setMessage(`${result.deletedDevices} Gerät(e) und alle zugehörigen Dienste gelöscht.`);
queryClient.invalidateQueries({ queryKey: ["devices"] });
queryClient.invalidateQueries({ queryKey: ["services"] });
queryClient.invalidateQueries({ queryKey: ["logs"] });
},
onError: (err: Error) => setMessage(err.message),
});
function handleClick() {
const firstConfirm = window.confirm(
"Wirklich ALLE Geräte und Dienste unwiderruflich löschen? Kategorien bleiben erhalten."
);
if (!firstConfirm) return;
const secondConfirm = window.confirm(
"Ganz sicher? Das kann nicht rückgängig gemacht werden."
);
if (!secondConfirm) return;
setMessage(null);
mutation.mutate();
}
return (
<div className="rounded-2xl border border-red-500/20 p-5">
<h2 className="mb-1 font-medium text-red-600 dark:text-red-400">Gefahrenzone</h2>
<p className="mb-4 text-sm text-black/50 dark:text-white/50">
Löscht alle Geräte und alle zugehörigen Dienste unwiderruflich. Kategorien und Logs
bleiben erhalten.
</p>
<Button variant="danger" onClick={handleClick} disabled={mutation.isPending}>
{mutation.isPending ? "Lösche …" : "Alle Geräte & Dienste löschen"}
</Button>
{message ? (
<p className="mt-3 text-sm text-black/60 dark:text-white/60">{message}</p>
) : null}
</div>
);
}
export function SettingsPage() {
const { health, error } = useBackendHealth();
const [theme, toggleTheme] = useTheme();
@@ -49,6 +112,8 @@ export function SettingsPage() {
<p className="text-sm text-black/40 dark:text-white/40">Lade </p>
)}
</div>
<DangerZone />
</div>
</div>
);