import { useState } from "react"
import { cn } from "cn"
import { Bestaetigen } from "@/components/cockpit/Bestaetigen"
import { Chip, type ChipArt, Zustandsfeld } from "@/components/cockpit/Chip"
import { Panel } from "@/components/cockpit/Panel"
import { Button } from "@/components/ui/button"
import {
type UpdateLauf, useFreigeben, useJetztSichern, useJobs, useSicherungen, useStart, useUpdateDetails,
useUpdateStarten, useUpdateVerlauf, useZurueckspielen,
} from "@/lib/abfragen"
import { versionen } from "@/lib/anzeige"
import type { Festgehalten, Job, Sicherung } from "@/lib/typen"
import { flugplanZeit } from "@/lib/zeit"
const BAUSTEINE = [
{ id: "os", name: "Betriebssystem" },
{ id: "engine", name: "Motor (llama.cpp)" },
{ id: "swap", name: "llama-swap" },
{ id: "hermes", name: "Hermes" },
] as const
function BausteinZeile({ id, name, hatUpdate, fest, onStart, onFreigeben, laeuftGerade }: {
id: string
name: string
hatUpdate: boolean
fest: Festgehalten | undefined
onStart: () => void
onFreigeben: () => void
laeuftGerade: boolean
}) {
const { data, isPending } = useUpdateDetails(id)
const v = versionen(id, data)
const neu = hatUpdate ? v.neu ?? "neu" : null
const was = !hatUpdate
? "Aktuell."
: data?.summary?.trim() || (id === "os" && data?.count ? "Paketliste unten in den Details." : isPending ? "Wird gelesen …" : "")
return (
| {name} |
{v.laeuft} |
{neu ? {neu} : –} |
{fest && (
Festgehalten seit {fest.seit} auf {fest.version}: {fest.grund}. Der Sonntags-Lauf überspringt {name}.
)}
{was}
|
{fest && (
)}
{hatUpdate && (
)}
|
)
}
const LAUF_ART: Record = {
rot: "rot", gelb: "bernstein", gruen: "gruen", info: "cyan", grau: "grau",
}
const PUNKT: Record = {
rot: "bg-rot", gelb: "bg-bernstein", gruen: "bg-gruen", info: "bg-cyan", grau: "bg-text-3",
}
/** Ein Update-Lauf mit dem Ergebnis je Baustein — so, wie autoupdate.sh es gemeldet hat. */
function LaufEintrag({ lauf }: { lauf: UpdateLauf }) {
return (
{flugplanZeit(lauf.start)}
{lauf.anlass}
{lauf.titel}
{lauf.stufe !== "grau" && lauf.zeilen.length > 0 && (
{lauf.zeilen.map((z) => (
-
{z.baustein} · {z.text}
))}
)}
)
}
function JobKasten({ job }: { job: Job & { group?: string; log?: string[] } }) {
const art = job.state === "done" ? "gruen" : job.state === "failed" ? "rot" : "cyan"
const text = job.state === "done" ? "Fertig" : job.state === "failed" ? "Fehler" : job.state === "queued" ? "Wartet" : "Läuft"
return (
{job.label}
{text}
{job.log && job.log.length > 0 && (
{job.log.slice(-8).join("\n")}
)}
)
}
function sicherungsZeit(s: Sicherung) {
return s.mtime ? flugplanZeit(new Date(s.mtime * 1000).toISOString()) : s.snapshot
}
export function UpdatesSeite() {
const start = useStart()
const jobs = useJobs()
const verlauf = useUpdateVerlauf()
const sicherungen = useSicherungen()
const updateStarten = useUpdateStarten()
const freigeben = useFreigeben()
const sichern = useJetztSichern()
const zurueck = useZurueckspielen()
const [frage, setFrage] = useState(null)
const mitUpdate = new Set(start.data?.updates.bausteine.map((b) => b.id) ?? [])
const festgehalten = new Map((start.data?.updates.festgehalten ?? []).map((f) => [f.baustein, f]))
const wartungsJobs = (jobs.data?.jobs ?? []).filter((j) => (j as { group?: string }).group === "maintenance")
const laeuft = wartungsJobs.some((j) => j.state === "running" || j.state === "queued")
return (
<>
}
>
Jeden Sonntag um 04:30 automatisch. Vorher wird gesichert, danach geprüft.
{start.isPending ? (
) : (
| Baustein |
Läuft |
Neu |
Was sich ändert |
Aktion |
{BAUSTEINE.map((b) => (
updateStarten.mutate(b.id)}
onFreigeben={() => freigeben.mutate(b.id)}
/>
))}
)}
{wartungsJobs.length > 0 && (
Laufende und letzte Aufträge
{wartungsJobs.slice(-3).reverse().map((j) => )}
)}
{verlauf.data?.laeufe.length ? (
{verlauf.data.laeufe.map((l) => )}
) : (
{verlauf.isPending ? "Wird gelesen …" : "Noch keine Update-Läufe aufgezeichnet."}
)}
sichern.mutate(undefined)} disabled={sichern.isPending}>
Jetzt sichern
}
>
Täglich um 03:35, mit Kopie auf einem zweiten Gerät.
{(sicherungen.data?.backups ?? []).slice(0, 5).map((s) => (
-
{sicherungsZeit(s)}
{s.size_mb.toLocaleString("de-DE")} MB
))}
setFrage(null)}
onJa={() => {
updateStarten.mutate("alle")
setFrage(null)
}}
/>
setFrage(null)}
onJa={() => {
if (frage?.art === "zurueck") zurueck.mutate(frage.datei)
setFrage(null)
}}
/>
>
)
}