design v5: Homelab-Cockpit, Updates und Snapshots im neuen Look; Rückweg testen und Aufräumen per Klick
- Knöpfe in normaler Schreibung, Hauptknopf mit Verlauf und Schein, Rest Glas. - Lagebild des Homelab-Cockpits: Statusblock mit Schein, Geräte als Kacheln mit Logo, Zustandspunkt und Version; Klick führt zu den Updates bzw. zur Zeile in der Geräte-Tabelle. Lage-Texte in normaler Schreibung. - Geräte-, Update- und Snapshot-Liste mit Logos; Balken in Bereichsfarbe. - Snapshots: je Gerät „Rückweg testen“ (Rückfrage vom Homelab-Teil) und oben „Alle Update-Rückwege löschen“ — nur Snapshots mit Herkunft „update“ und Sicherungen des Orchestrators, nacheinander, eine Meldung am Ende (nur per Klick des Users; per Knopf/von Hand angelegte bleiben). - Test gegen Variablen namens „Symbol“ (React Compiler braucht Symbol.for). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d524e03c68
commit
db452aa38a
@@ -3,7 +3,9 @@ import { Search } from "lucide-react"
|
||||
import { cn } from "cn"
|
||||
import { Bestaetigen } from "@/components/cockpit/Bestaetigen"
|
||||
import { Chip } from "@/components/cockpit/Chip"
|
||||
import { Geraetesymbol } from "@/components/cockpit/Geraetesymbol"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { geraetebild } from "@/lib/geraetebild"
|
||||
import {
|
||||
appBaustein, artText, listenAlter, rueckwegText, systemBaustein, updateRang, versionText,
|
||||
} from "@/lib/homelab"
|
||||
@@ -25,7 +27,7 @@ function AppZelle({ ziel, b }: { ziel: ZielStand; b: BausteinStand | null }) {
|
||||
<span className="flex min-w-0 flex-col gap-0.5">
|
||||
<span className="ziffern flex flex-wrap items-baseline gap-x-2 text-sm">
|
||||
<span className={b.zustand === "aktuell" ? "text-foreground" : "text-text-2"}>{laeuft}</span>
|
||||
{b.zustand === "neu" && b.verfuegbar && <span className="text-cyan">→ {b.verfuegbar}</span>}
|
||||
{b.zustand === "neu" && b.verfuegbar && <span className="text-cyan-text">→ {b.verfuegbar}</span>}
|
||||
{b.zustand === "aktuell" && <span className="font-sans text-xs text-gruen-text">aktuell</span>}
|
||||
{b.zustand === "unbekannt" && <span className="font-sans text-xs text-text-3">unklar</span>}
|
||||
</span>
|
||||
@@ -39,7 +41,7 @@ function PaketZelle({ b, onSuchen, gesperrt }: { b: BausteinStand | null; onSuch
|
||||
if (!b) return <span className="text-sm text-text-3">–</span>
|
||||
const docker = b.id === "docker"
|
||||
let text
|
||||
if (b.zustand === "neu") text = <span className="text-cyan">{b.kurz || "Update bereit"}</span>
|
||||
if (b.zustand === "neu") text = <span className="text-cyan-text">{b.kurz || "Update bereit"}</span>
|
||||
else if (b.zustand === "aktuell") text = <span className="text-gruen-text">{docker ? "Images aktuell" : "aktuell"}</span>
|
||||
else {
|
||||
const datum = listenAlter(b.grund)
|
||||
@@ -80,14 +82,17 @@ function Zeile({ ziel, laeuft, gesperrt, onFrage }: {
|
||||
<div
|
||||
role="row"
|
||||
className={cn(
|
||||
"grid grid-cols-1 gap-x-4 gap-y-2 border-t border-linie py-3.5 md:items-center",
|
||||
"grid grid-cols-1 gap-x-4 gap-y-2 border-t border-white/[0.06] py-3.5 md:items-center",
|
||||
SPALTEN,
|
||||
ruhig && "opacity-75",
|
||||
)}
|
||||
>
|
||||
<div role="rowheader" className="flex min-w-0 flex-col">
|
||||
<span className="text-[15px] font-semibold break-words">{ziel.name}</span>
|
||||
{artText(ziel) !== ziel.name && <span className="text-sm text-text-3">{artText(ziel)}</span>}
|
||||
<div role="rowheader" className="flex min-w-0 items-center gap-3">
|
||||
<Geraetesymbol bild={geraetebild(ziel)} groesse="sm" punkt={knoepfe.length ? "info" : null} />
|
||||
<span className="flex min-w-0 flex-col">
|
||||
<span className="text-[15px] font-semibold break-words">{ziel.name}</span>
|
||||
{artText(ziel) !== ziel.name && <span className="text-[13px] text-text-3">{artText(ziel)}</span>}
|
||||
</span>
|
||||
</div>
|
||||
<span role="cell" className="min-w-0"><span className="text-xs text-text-3 md:hidden">App </span><AppZelle ziel={ziel} b={app} /></span>
|
||||
<span role="cell" className="min-w-0">
|
||||
@@ -97,7 +102,7 @@ function Zeile({ ziel, laeuft, gesperrt, onFrage }: {
|
||||
<span role="cell" className="text-sm text-text-3">{knoepfe.length ? rueckwegText(ziel) : ""}</span>
|
||||
<span role="cell" className="flex flex-wrap gap-2 md:flex-col md:items-stretch">
|
||||
{laeuft ? (
|
||||
<span role="status" className="text-sm text-cyan">Läuft: {laeuft}</span>
|
||||
<span role="status" className="text-sm text-cyan-text">Läuft: {laeuft}</span>
|
||||
) : (
|
||||
knoepfe.map((b) => (
|
||||
<Button key={b.id} variant="outline" size="sm" disabled={gesperrt} onClick={() => b.aktion && onFrage({ ziel, aktion: b.aktion })}>
|
||||
@@ -126,7 +131,7 @@ export function UpdateTabelle({ ziele, laufend, gesperrt, onAktion }: {
|
||||
return (
|
||||
<>
|
||||
<div role="table" aria-label="Updates je Gerät" className="flex flex-col">
|
||||
<div role="row" className={cn("schild hidden gap-x-4 pb-2 text-xs font-semibold text-text-3 md:grid", SPALTEN)}>
|
||||
<div role="row" className={cn("etikett hidden gap-x-4 pb-2.5 text-[10.5px] tracking-[0.14em] text-text-3 md:grid", SPALTEN)}>
|
||||
<span role="columnheader">Gerät</span>
|
||||
<span role="columnheader">App</span>
|
||||
<span role="columnheader">Pakete</span>
|
||||
|
||||
Reference in New Issue
Block a user