round19: Startseite aufgeraeumt, Favicon-Fallback ueber Ports, Favicon-Picker, Live-Status per Ping

This commit is contained in:
2026-07-23 18:10:03 +02:00
parent bae9e48fc1
commit ec49534b60
16 changed files with 393 additions and 51 deletions

View File

@@ -17,6 +17,44 @@ function InfoRow({ label, value }: { label: string; value: string }) {
);
}
function LiveStatusToggle({ enabled }: { enabled: boolean }) {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: async (next: boolean) => {
const res = await fetch("/api/settings", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ liveStatusEnabled: next }),
});
if (!res.ok) throw new Error(`Speichern fehlgeschlagen (HTTP ${res.status})`);
return res.json();
},
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["settings"] }),
});
return (
<div className="flex items-center justify-between py-1">
<span className="text-sm text-black/50 dark:text-white/50">Aktiviert</span>
<button
onClick={() => mutation.mutate(!enabled)}
disabled={mutation.isPending}
role="switch"
aria-checked={enabled}
className={`relative h-6 w-11 rounded-full transition-colors ${
enabled ? "bg-indigo-500" : "bg-black/15 dark:bg-white/15"
}`}
>
<span
className={`absolute top-0.5 h-5 w-5 rounded-full bg-white shadow transition-transform ${
enabled ? "translate-x-[22px]" : "translate-x-0.5"
}`}
/>
</button>
</div>
);
}
function LimitSetting({
label,
hint,
@@ -26,7 +64,7 @@ function LimitSetting({
}: {
label: string;
hint: string;
settingKey: "recentVisitsLimit" | "readLaterLimit" | "staleDeviceThresholdDays";
settingKey: "recentVisitsLimit" | "readLaterLimit" | "staleDeviceThresholdDays" | "liveStatusIntervalMinutes";
value: number | undefined;
max?: number;
}) {
@@ -338,6 +376,26 @@ export function SettingsPage() {
/>
</div>
<div className="rounded-2xl border border-black/10 p-5 dark:border-white/10">
<h2 className="mb-1 font-medium text-black dark:text-white">Live-Status</h2>
<p className="mb-2 text-xs text-black/40 dark:text-white/40">
Prüft in regelmäßigem Abstand per Ping, ob ein Gerät erreichbar ist, und
aktualisiert nur dessen Online-Punkt. Legt nie neue Dienste an und schlägt nie
Namen/Kategorien vor das bleibt weiterhin ausschließlich manuellen Scans
vorbehalten. Standardmäßig aus.
</p>
<LiveStatusToggle enabled={settings?.liveStatusEnabled ?? false} />
{settings?.liveStatusEnabled ? (
<LimitSetting
label="Prüfintervall (Minuten)"
hint="Wie oft alle Geräte angepingt werden."
settingKey="liveStatusIntervalMinutes"
value={settings?.liveStatusIntervalMinutes}
max={1440}
/>
) : null}
</div>
<div className="rounded-2xl border border-black/10 p-5 dark:border-white/10">
<h2 className="mb-2 font-medium text-black dark:text-white">HTTPS</h2>
<p className="mb-3 text-sm text-black/50 dark:text-white/50">