Files
tessera-ctl/.planning/quick/260924-h7x-proxmox-seite-status-design-und-dashboar/260924-h7x-PLAN.md
T
schalli 8bfa4fc467
Tessera CI/CD / Lint & Type Check (push) Successful in 52s
Tessera CI/CD / Tests (push) Successful in 1m14s
Tessera CI/CD / Desktop-Pakete bauen (push) Successful in 19s
Tessera CI/CD / Build & Publish Images (push) Successful in 3m7s
docs(quick-260924-h7x): Proxmox-Status-Design und Reiter in der Kopfzeile
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 12:48:56 +02:00

10 KiB
Raw Blame History

quick_id, type, wave, autonomous, files_modified
quick_id type wave autonomous files_modified
260924-h7x quick 1 true
apps/web/src/app/globals.css
apps/web/src/app/(portal)/modules/proxmox/page.tsx
apps/web/src/app/(portal)/modules/proxmox/components/ServerCard.tsx
apps/web/src/app/(portal)/modules/proxmox/components/proxmox-status.ts (new)
apps/web/src/app/(portal)/modules/proxmox/components/*.test.tsx
apps/web/src/components/layout/header.tsx
apps/web/src/components/dashboard/dashboard-tabs.tsx
apps/web/src/app/(portal)/page.tsx
apps/web/src/messages/de.json, en.json
CHANGELOG.md

Quick 260924-h7x — Proxmox-Seite: Status-Design mit Tiefe; Dashboard-Reiter in die Kopfzeile

Nutzerauftrag (24.09.): „Kennzeichnen als offline & verwaist. Das Proxmox-Modul scheint zu funktionieren, aber es gefällt mir optisch gar nicht. Benutze den Design-Skill und überarbeite etwas — mehr Tiefe, Farben nach Status etc. Die Reiter sind da, aber die müssen woanders hin, die nehmen zu viel Platz ein. Auch mit Design.“

Die frühere Sperre „KEIN Umbau der Proxmox-Modulseite“ (23.09.) ist damit durch den Nutzer selbst aufgehoben.

Design-Plan vom Orchestrator (frontend-design-Skill) — verbindlich, nicht neu erfinden. Keine neuen Pakete. App-Texte deutsch in Sie-Form, alle Schlüssel in de.json UND en.json. Keine Großbuchstaben-Etiketten (kein uppercase, kein tracking-widest), keine Mittelpunkt-Ketten „A · B · C“ in neuen Texten, kein „→“ an Knöpfen.

Design-Tokens (in globals.css, :root und .dark)

Statusfarben als OKLCH-Variablen plus Tailwind-Abbildung in @theme inline (--color-status-ok usw., damit bg-status-ok, text-status-ok, border-status-ok/40 gehen):

Token hell dunkel Bedeutung
--status-ok oklch(0.62 0.15 152) oklch(0.72 0.15 152) in Ordnung
--status-warn oklch(0.72 0.15 70) oklch(0.80 0.14 75) Warnung
--status-down oklch(0.58 0.21 27) oklch(0.68 0.19 27) nicht erreichbar / Fehler
--status-idle oklch(0.62 0.02 260) oklch(0.60 0.02 260) noch nicht abgefragt
--status-orphan oklch(0.55 0.01 260) oklch(0.52 0.01 260) offline & verwaist
--well oklch(0.975 0.003 260) oklch(0.235 0.01 260) eingelassene Messfelder

Kontrast prüfen: Text in Statusfarbe auf --card muss ≥ 4.5:1 (Fließtext) bzw. ≥ 3:1 (große/fette Zahlen) erreichen — sonst Helligkeit anpassen und die gemessene Zahl in der SUMMARY nennen.

Task 1 — Statuslogik als reine Funktionen (TDD)

Neue Datei components/proxmox-status.ts:

  • serverHealth(server): 'ok' | 'warn' | 'down' | 'idle' | 'orphan'
    • !server.isActive → orphan (VORRANG vor allem anderen — „offline & verwaist“, auch wenn alte Messwerte im Zwischenlager liegen)
    • kein status oder status.lastPolledAt === null → idle
    • !status.reachable → down
    • erreichbar + irgendein Messwert über Warnschwelle → warn
    • sonst ok
  • Schwellen als EINE benannte Konstante THRESHOLDS: Last/Arbeitsspeicher/Datenspeicher warn ≥ 0.80, kritisch ≥ 0.92; PBS letzte Sicherung älter als 26 h → warn; lastVerifyState ungleich ok (und nicht null) → warn; PMG virusCount > 0 → warn.
  • meterLevel(fraction | null): 'ok' | 'warn' | 'crit' | 'unknown' für die Balken.
  • formatAge(epochSeconds|iso, now) → „vor 3 Min.“, „vor 5 Std.“, „vor 2 Tagen“ über Intl.RelativeTimeFormat('de') bzw. next-intl — keine neue Abhängigkeit.
  • Tests für jede Verzweigung inkl. null-Werte (unbekannt ≠ 0, nie Warnung aus null).

Task 2 — Proxmox-Seite und Karte neu gestalten

Seitenkopf: Titel „Proxmox“ links, darunter die Beschreibung. Rechts „Jetzt aktualisieren“ (nur Admins, wie heute) als richtiger Knopf mit Kreispfeil-Symbol (dreht sich während des Laufs; motion-reduce:animate-none) und „Einstellungen“ als ruhiger Textlink. Breite max-w-5xl.

Gesundheitsbalken (das eine prägnante Element der Seite): direkt unter dem Kopf ein waagrechter Balken, 8 px hoch, voll gerundet, anteilig in Segmente je Zustand geteilt (Reihenfolge down, warn, ok, idle, orphan; leere Zustände entfallen). Darunter eine Zeile mit Legende: farbiger Punkt + Zahl + Wort („2 in Ordnung“, „1 nicht erreichbar“, „1 offline & verwaist“ …, ICU-Plural). Segmente role="img" mit aria-label, das die Zusammenfassung vorliest. Keine Zahl-in-riesig-plus-Verlauf-Heldenkachel.

Kartenraster: grid gap-4 lg:grid-cols-2. Sortierung: down, warn, ok, idle, orphan, darin position. (Die Einstellungsseite behält ihre Reihenfolge.)

Karte (Tiefe über Status, nicht Einheits-Schatten):

  • Karte rounded-xl bg-card mit Rand border-border, links eine 4 px breite Statusleiste (absolut positioniert, volle Höhe, Farbe = Status).
  • Schatten zweischichtig und im Statuston getönt, z. B. shadow-[0_1px_2px_oklch(0_0_0/0.06),0_12px_28px_-14px_var(--status-x)] — bei orphan KEIN farbiger Schatten, stattdessen gestrichelter Rand (border-dashed), Inhalt opacity-70 saturate-50.
  • Kopfzeile: Produktsymbol (kleines inline-SVG je Typ: PVE = Server-Einschübe, PBS = Archivkiste/Datenträger, PMG = Briefumschlag) in einem 32 px Feld mit bg-well, daneben Name (fett) und darunter Produktname ausgeschrieben („Virtualisierung“, „Datensicherung“, „Mail-Gateway“) plus Adresse klein und gedämpft. Rechts eine Statuspille: Punkt + Wort („In Ordnung“, „Warnung“, „Nicht erreichbar“, „Noch nicht abgefragt“, „Offline & verwaist“), Hintergrund Statusfarbe /12, Text in Statusfarbe. Bei ok pulsiert der Punkt EINMAL beim Laden nicht — keine Dauer-Animation.
  • Fuß der Karte: „Letzte Abfrage vor 4 Min.“ (relativ, title mit exaktem Zeitpunkt).

Messbereich je Produkt, als eingelassene Felder (bg-well, rounded-lg, shadow-[inset_0_1px_2px_oklch(0_0_0/0.06)]):

  • PVE: oben zwei Kennzahlen nebeneinander: laufende Gäste (groß, tabular-nums) und gestoppte Gäste (gedämpft), Knotenzahl klein. Darunter je Knoten ein „Einschub“: Knotenname links, rechts zwei schmale Balken „Prozessor“ und „Arbeitsspeicher“ mit Prozentzahl (tabular-nums) und bei Speicher „12,0 / 64,0 GB“ als title/kleine Zeile. Balkenfarbe nach meterLevel. Unbekannter Wert: Balken schraffiert/leer + Text „unbekannt“, NIE 0 %. Deutsche Zahlformate (Komma) über Intl.NumberFormat('de-DE').
  • PBS: je Datenspeicher ein Einschub: Name, Füllstandsbalken mit „1,2 / 4,0 TB“, darunter „Letzte Sicherung vor 5 Std.“ (warn-Farbe, wenn > 26 h; „noch keine Sicherung“ gedämpft) und Prüfstatus als kleine Pille (ok grün, sonst warn, null „unbekannt“ grau).
  • PMG: vier Zahlfelder im 2×2-Raster: Eingehend, Ausgehend, Spam, Viren; Viren > 0 in down-Farbe, sonst normal. null → „unbekannt“.

Zustände ohne Messwerte:

  • down: roter Hinweisblock im Well (bg-status-down/8, Rand links in Statusfarbe) mit der bekannten Fehlermeldung (errors.*) und darunter gedämpft „Zuletzt erreichbar: vor 2 Tagen“ bzw. nichts, wenn nie. errorDetail klein und gedämpft in eigener Zeile, nicht in Klammern an den Satz gehängt.
  • idle: ruhiger Text wie heute (Admin-/Nicht-Admin-Variante bleibt, 260923-le6).
  • orphan: Text „Dieser Server ist deaktiviert und wird nicht mehr abgefragt.“ plus für Admins der Hinweis, dass er in den Einstellungen wieder aktiviert werden kann (Link). Alte Messwerte werden bei orphan NICHT angezeigt.

Leer-, Lade-, Fehlerzustand der Seite: Leerzustand als Well mit Serversymbol und Satz + Link (Admins); Laden als zwei Skelett-Karten (animate-pulse, motion-reduce:animate-none).

Tests: bestehende ServerCard-/Seitentests anpassen (Texte/Strukturen), neue Tests für orphan (Vorrang, keine alten Werte), Sortierung, Gesundheitsbalken-Zusammenfassung, unbekannte Werte als „unbekannt“. Die Nur-Admin-Regeln aus 260923-le6 müssen weiter getestet sein.

Task 3 — Dashboard-Reiter in die Kopfzeile

Heute belegt DashboardTabs eine eigene Zeile über dem Raster (app/(portal)/page.tsx), die Kopfzeilenmitte (components/layout/header.tsx) zeigt nur den Text „Startseite“.

  • Kopfzeile bekommt in der Mitte einen Einhängepunkt <div id="header-center-slot">. Auf der Startseite (pathname === '/') entfällt der Text „Startseite“; auf allen anderen Seiten bleibt alles wie heute.
  • DashboardTabs rendert per createPortal in diesen Einhängepunkt (sobald er im DOM ist — useEffect + State; Rückfall: solange kein Einhängepunkt da ist, nichts rendern). Die eigene Zeile über dem Raster entfällt ersatzlos.
  • Gestaltung als kompakter Umschalter mit Tiefe: eine eingelassene Spur (bg-muted, rounded-lg, p-0.5, shadow-[inset_0_1px_2px_oklch(0_0_0/0.08)], Höhe 32 px), darin die Reiter als Textknöpfe (text-sm, px-3, h-7); der aktive Reiter liegt erhaben darauf (bg-card, shadow-sm, font-medium, Text foreground), inaktive gedämpft mit Hover. Keine Unterstreichung, kein Gelb-Flächen-Reiter.
  • Viele Reiter: Spur höchstens max-w-[min(56vw,720px)], waagrecht scrollbar ohne sichtbare Leiste, an den Rändern weich ausgeblendet (mask-image nur wenn überläuft), aktiver Reiter wird ins Bild gescrollt.
  • Bearbeitungsmodus: Umbenennen/Löschen/Ziehen/Anlegen bleiben funktional gleich (alle bestehenden Tests grün halten bzw. nur Selektoren anpassen). „+“ als 28-px-Rundknopf am Ende der Spur (immer sichtbar, wie heute der Anlegen-Knopf sichtbar ist — Verhalten beibehalten). Löschen-Kreuz erscheint im Bearbeitungsmodus klein im Reiter.
  • Genau EIN Reiter: Umschalter trotzdem zeigen (sonst weiß niemand, dass es Reiter gibt), aber nur mit „+“ daneben.
  • Mobil (< md): die Spur darf die Kopfzeilenmitte füllen; Logo-Schriftzug bleibt, Aktionen rechts bleiben erreichbar — nichts darf die Kopfzeile sprengen (overflow prüfen).
  • Tastatur: Pfeiltasten links/rechts zwischen Reitern (role="tablist"/tab, aria-selected), sichtbarer Fokusring.
  • navigation "Dashboard-Reiter"-Beschriftung bleibt als aria-label.

Tests: dashboard-tabs.test.tsx an Portal anpassen (Einhängepunkt im Test anlegen), neuer Test: Kopfzeile zeigt „Startseite“ nicht auf /, aber auf anderen Pfaden; Pfeiltasten.

Tore

  • pnpm --filter web exec vitest run komplett grün
  • pnpm turbo run type-check lint grün, Biome-Warnungen web nicht mehr als 53
  • CHANGELOG „Unveröffentlicht“: je ein Eintrag unter „Neu“/„Geändert“ in Nutzersprache
  • Browser-Nachweis macht der Orchestrator (hell UND dunkel, 1400 px und 390 px Breite)