import { type ReactNode, useState } from "react" import { ChevronRight } from "lucide-react" import { cn } from "cn" import { Bestaetigen } from "@/components/cockpit/Bestaetigen" import { Chip, type ChipArt } from "@/components/cockpit/Chip" import { Button } from "@/components/ui/button" import { auftraegeText, groesse, STUFE_TEXT } from "@/lib/speicher" import type { HomelabSpeicher, NasStand, SicherungsLage, SpeicherEmpfehlung, SpeicherPool, SpeicherStufe, Systemplatte, ZielAktion, } from "@/lib/typen" import { flugplanZeit } from "@/lib/zeit" const CHIP: Record = { ok: "gruen", gelb: "bernstein", rot: "rot", keine: "grau" } const FARBE: Record = { ok: "var(--reihe-1)", gelb: "var(--bernstein)", rot: "var(--rot)" } const zeitAus = (sekunden: number) => flugplanZeit(new Date(sekunden * 1000).toISOString()) /** Füllstand mit Marken bei 80 % (gelb) und 90 % (rot, 10 % frei). */ function Fuellstand({ anteil, stufe, name }: { anteil: number; stufe: SpeicherStufe; name: string }) { const prozent = Math.round(anteil * 100) return (
) } function Kachel({ titel, unter, chip, chipText, children }: { titel: string unter?: string chip: ChipArt chipText: string children: ReactNode }) { return (
{titel} {unter && {unter}}
{chipText}
{children}
) } function PoolKachel({ p }: { p: SpeicherPool }) { const prozent = Math.round(p.anteil * 100) return (

{prozent} % · {groesse(p.belegt)} von {groesse(p.gesamt)} · {groesse(p.frei)} frei

{p.meta_anteil !== null && (

Metadaten {Math.round(p.meta_anteil * 100)} % belegt

)}
) } function SystemplatteKachel({ s }: { s: Systemplatte }) { return (

{Math.round(s.anteil * 100)} % · {groesse(s.belegt)} von {groesse(s.gesamt)} · {groesse(s.frei)} frei

) } function NasKachel({ n }: { n: NasStand }) { const chipText = n.problem ? (n.stufe === "rot" ? "gestört" : "unklar") : STUFE_TEXT[n.stufe] return ( {n.problem ? (

{n.problem}

) : ( n.anteil !== null && ( <>

{Math.round(n.anteil * 100)} % · {groesse(n.belegt)} von {groesse(n.gesamt)} · {groesse((n.gesamt ?? 0) - (n.belegt ?? 0))} frei

) )}

{n.erreichbar === false ? "antwortet nicht" : "antwortet"} · {n.eingebunden ? "auf dem Proxmox-Host eingebunden" : "nicht eingebunden"}

) } function SicherungsKachel({ s }: { s: SicherungsLage }) { const chipText = { ok: "aktuell", gelb: "Lücke", rot: "gestört", keine: "kein Auftrag" }[s.stufe] return ( {s.probleme.map((p) => (

{p}

))} {s.stufe === "ok" && (

Alle {s.erfasst} Geräte gesichert{s.zuletzt ? <>, zuletzt {zeitAus(s.zuletzt)} : null}.

)} {s.fehlend.length > 0 && (
    {s.fehlend.map((f) => (
  • {f.name}: {f.zuletzt ? <>zuletzt {zeitAus(f.zuletzt)} : "noch nie gesichert"}
  • ))}
)}
) } /** * Speicher und Sicherungen des Homelabs: Speicherpool, NAS und die nächtlichen Sicherungen, darunter was im Pool * Platz bringt. Die Vorschläge sind aufgeklappt, sobald der Pool knapp wird (ab 80 %). */ export function Speicher({ daten, laeuft = null, gesperrt = false, onAktion, snapshotsLink }: { daten: HomelabSpeicher /** Läuft auf dem Proxmox-Host gerade etwas (Update, Kernel-Aufräumen)? Dann kein Knopf. */ laeuft?: string | null gesperrt?: boolean onAktion?: (a: ZielAktion) => void /** Verweis auf die Seite Snapshots, beim Vorschlag zu den Snapshots (seit 25.09.2026; die Seite reicht ihn herein). */ snapshotsLink?: ReactNode }) { const [frage, setFrage] = useState(null) const knapp = daten.pools.some((p) => p.stufe !== "ok") || (daten.systemplatte?.stufe ?? "ok") !== "ok" const summe = daten.empfehlungen.reduce((n, e) => n + (e.platz ?? 0), 0) return (
{daten.pools.map((p) => )} {daten.systemplatte && } {daten.nas.length > 0 ? ( daten.nas.map((n) => ) ) : (

Auf dem Proxmox-Host ist kein Netzlaufwerk eingebunden.

)}
{daten.empfehlungen.length > 0 && (
Was Platz bringt {daten.empfehlungen.length} {daten.empfehlungen.length === 1 ? "Vorschlag" : "Vorschläge"} {summe > 0 && ` · bis zu ${groesse(summe)}`}
    {daten.empfehlungen.map((e) => ( ))}
)} { if (frage && onAktion) onAktion(frage) setFrage(null) }} onNein={() => setFrage(null)} />
) } const ORT: Record = { pool: "Speicherpool", systemplatte: "Systemplatte des Hosts" } /** Befehle stehen im Text zwischen Backticks: als Code, ohne Ligaturen — sonst zog die Schrift „--“ zu „—“ zusammen. */ function MitBefehlen({ text }: { text: string }) { return text.split(/`([^`]+)`/).map((teil, i) => i % 2 === 1 ? ( {teil} ) : ( teil ), ) } function Vorschlag({ e, laeuft, gesperrt, onFrage, verweis }: { e: SpeicherEmpfehlung laeuft: string | null gesperrt: boolean onFrage: (a: ZielAktion) => void verweis?: ReactNode }) { return (
  • {e.titel} {e.platz ? `≈ ${groesse(e.platz)}` : "Größe unbekannt"}
    {e.ort && {ORT[e.ort] ?? e.ort}}

    {verweis} {e.aktion && (laeuft ? (

    Läuft: {laeuft}

    ) : ( ))}
  • ) }