--- quick_id: 260924-h7x phase: quick plan: 260924-h7x subsystem: web / proxmox-modul, dashboard, kopfzeile status: complete tags: [proxmox, design, statusfarben, dashboard-reiter, kopfzeile, a11y] requires: [quick-260923-dhh (Proxmox-Modul), quick-260923-le6 (Nur-Admin-Regeln), quick-260923-ad9 (Dashboard-Reiter)] provides: - Statuslogik proxmox-status.ts (serverHealth, THRESHOLDS, meterLevel, formatAge, Sortierung, Zusammenfassung) - Statusfarben-Tokens --status-* / --status-*-fg / --well in globals.css - Einhaengepunkt header-center-slot in der Kopfzeile affects: - apps/web/src/app/(portal)/modules/proxmox/* - apps/web/src/components/dashboard/dashboard-tabs.tsx - apps/web/src/components/layout/header.tsx tech-stack: added: [] patterns: - Statusklassen als statische Tailwind-Zeichenketten (status-styles.ts), damit Tailwind sie findet - Tests mit echtem NextIntlClientProvider + de.json statt Uebersetzungs-Attrappe (ICU-Plural, Zahlformate mitgeprueft) - createPortal in einen Kopfzeilen-Einhaengepunkt, Rueckfall = nichts rendern key-files: created: - apps/web/src/app/(portal)/modules/proxmox/components/proxmox-status.ts - apps/web/src/app/(portal)/modules/proxmox/components/proxmox-status.test.ts - apps/web/src/app/(portal)/modules/proxmox/components/HealthBar.tsx - apps/web/src/app/(portal)/modules/proxmox/components/status-styles.ts - apps/web/src/components/layout/header-slot.ts modified: - 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/ServerCard.test.tsx - apps/web/src/app/(portal)/modules/proxmox/proxmox-page-roles.test.tsx - apps/web/src/components/dashboard/dashboard-tabs.tsx - apps/web/src/components/dashboard/dashboard-tabs.test.tsx - apps/web/src/components/layout/header.tsx - apps/web/src/components/layout/header.test.tsx - apps/web/src/app/(portal)/page.tsx - apps/web/src/messages/de.json - apps/web/src/messages/en.json - apps/web/src/messages/umlaut-dictionary.ts - CHANGELOG.md decisions: - Statusfarben bekommen eine Textvariante --status-*-fg; die Flaechentoene aus dem Plan bleiben fuer Balken, Punkte und Leisten, erreichen als Schrift auf Weiss aber nur 2,6 bis 4,9:1 - Bei „offline & verwaist“ werden nur Symbol und Name gedaempft; gedaempfte muted-Schrift fiele unter 3:1 - Der „+“-Knopf der Reiter bleibt wie bisher nur im Bearbeitungsmodus sichtbar, sitzt aber ausserhalb der scrollenden Spur („immer sichtbar“ = nie weggescrollt) - Pfeiltasten verschieben nur den Fokus, gewaehlt wird mit Eingabe/Leertaste (ein Reiterwechsel laedt das ganze Dashboard) - Ziehhinweis als sr-only-Beschreibung der Spur plus Tooltip statt eigener Zeile metrics: duration: 16min completed: 2026-09-24 tasks: 3 files: 19 plan_head_before: 3d266418fc83f4b0e5f5240f0fcd5017aebe0d10 actuals: tokens: 35900 tasks: 3 commits: 4 --- # Quick 260924-h7x: Proxmox-Seite mit Status-Design, Dashboard-Reiter in der Kopfzeile Die Proxmox-Seite zeigt den Zustand jetzt über Farbe und Tiefe: oben ein Gesundheitsbalken mit Legende, darunter Karten, sortiert nach Zustand, mit Statusleiste, im Statuston getöntem Schatten und Statuspille. Die Messwerte liegen in eingelassenen Feldern mit Balken, relativen Zeitangaben und deutschem Zahlformat. Deaktivierte Server erscheinen als „Offline & verwaist“, ohne veraltete Messwerte. Die Dashboard-Reiter sitzen als kompakter Umschalter per Portal in der Mitte der Kopfzeile. ## Was gebaut wurde **Task 1 — Statuslogik (TDD, 0fa7ce0).** `proxmox-status.ts` enthält nur reine Funktionen: - `serverHealth`: `orphan` hat Vorrang vor allem anderen, dann `idle`, `down`, `warn`, `ok`. - `THRESHOLDS` ist die einzige Quelle für die Schwellen: 0,80 Warnung, 0,92 kritisch, 26 h Sicherungsalter. - `meterLevel`, `ratio`, `isBackupStale`, `sortServersByHealth`, `summarizeHealth`. - `formatAge` nutzt `Intl.RelativeTimeFormat` und liefert „vor 3 Min.“, „vor 5 Std.“, „vor 2 Tagen“ sowie „jetzt“ unter einer Minute. Ein `null` löst nirgends eine Warnung aus. 24 Tests. Die RED-Phase war belegt, weil das Modul fehlte. **Task 2 — Seite und Karte (57c338f, Nachbesserung 0b659d6).** - **Tokens:** `--status-ok/warn/down/idle/orphan` und `--well` nach der Tabelle im Plan, jeweils hell und dunkel. Dazu kommt eine Textvariante `--status-*-fg` (Begründung unter Kontrast). Alle Tokens sind in `@theme inline` abgebildet. Mit einem Tailwind-Probelauf ist nachgewiesen, dass `bg-status-ok/12`, `bg-status-down/8`, `bg-well`, die getönten Schatten und `motion-reduce:animate-none` wirklich erzeugt werden. - **Kopf:** Titel und Beschreibung stehen links. Rechts steht „Einstellungen“ als ruhiger Textlink, danach „Jetzt aktualisieren“ als Hauptknopf mit Kreispfeil. Der Kreispfeil dreht sich während des Laufs, bei reduzierter Bewegung nicht. Beides ist nur für Admins sichtbar. Die Seitenbreite ist `max-w-5xl`. - **Gesundheitsbalken:** 8 px hoch, anteilig geteilt in der Reihenfolge down, warn, ok, idle, orphan. Leere Zustände entfallen. Der Balken ist `role="img"` und liest die Zusammenfassung vor, zum Beispiel „Zustand der Server: 1 nicht erreichbar, 2 in Ordnung“. Die Legende zeigt Punkt, fette Zahl und Wort über ICU-Plural. - **Karte:** Statusleiste links, 4 px breit. Zweischichtiger, im Statuston getönter Schatten; bei `orphan` gibt es keinen farbigen Schatten, sondern einen gestrichelten Rand. Das Produktsymbol steht in einem 32-px-Well (Server-Einschübe, Archivkiste, Briefumschlag). Darunter stehen Name, ausgeschriebener Produktname und Adresse. Die Statuspille hat keine Animation. Im Fuß steht „Letzte Abfrage vor 4 Min.“, der exakte Zeitpunkt liegt im `title`. - **PVE:** Laufende Gäste groß, gestoppte gedämpft, dazu die Knotenzahl. Je Knoten ein Einschub mit Balken für Prozessor und Arbeitsspeicher und „12,0 / 64,0 GB“. Unbekannte Werte erscheinen als schraffierte Spur mit „unbekannt“, nie als 0 %. - **PBS:** Je Datenspeicher der Füllstand mit „1,2 / 4,0 TB“ und „Letzte Sicherung vor 5 Std.“; bei mehr als 26 h in Warnfarbe. Dazu eine Prüfpille: grün bei ok, Warnfarbe bei anderem Ergebnis, grau bei „unbekannt“. - **PMG:** Ein 2×2-Raster aus Zahlfeldern. Viren über 0 stehen in der Fehlerfarbe. - **Zustände:** - `down`: Hinweisblock mit linkem Rand. Die Fehlermeldung steht im Klartext, `errorDetail` in einer eigenen Zeile, darunter „Zuletzt erreichbar vor 2 Tagen“. - `idle`: Admin- und Nicht-Admin-Text wie bisher. - `orphan`: Hinweissatz; Admins bekommen zusätzlich einen Verweis auf die Einstellungen. - Laden: zwei Skelettkarten. - Keine Server: Well mit Symbol. - **Aktuelle Zeitangaben:** Ein Minutentakt hält die relativen Zeitangaben aktuell. **Task 3 — Reiter in der Kopfzeile (7416a92).** - **Einhängepunkt:** Die Kopfzeile rendert einen leeren `#header-center-slot`. Die Kennung steht in `components/layout/header-slot.ts`. Auf `/` entfällt „Startseite“, auf allen anderen Seiten bleibt alles wie bisher. Die Kopfzeilenmitte ist `min-w-0`, Logo und Aktionen rechts sind `shrink-0`. - **Portal:** `DashboardTabs` rendert per `createPortal` in den Einhängepunkt. Ohne Einhängepunkt rendert die Leiste nichts. Die eigene Zeile über dem Raster ist entfallen. - **Gestaltung:** Eine eingelassene Spur (`bg-muted`, inset-Schatten, 32 px), darauf liegt der aktive Reiter erhaben (`bg-card`, `shadow-sm`, `font-medium`). - **Überlauf:** Die Spur ist `max-w-[min(56vw,720px)]` breit, auf Mobilgeräten volle Breite. Sie scrollt ohne sichtbare Leiste. Die Ränder werden über `mask-image` weich ausgeblendet, aber nur bei Überlauf. Der aktive Reiter wird ins Bild gescrollt. - **Tastatur:** `role="tablist"`/`tab` mit `aria-selected` und wanderndem `tabIndex`. Pfeil links/rechts, Pos1 und Ende bewegen den Fokus, mit Umlauf. Der Fokusring ist sichtbar. - **Bearbeitungsmodus:** Der „+“-Knopf ist ein 28-px-Rundknopf außerhalb der Spur. Umbenennen und Löschen sitzen klein im Reiter. Der Löschdialog hängt am Dokumentkörper statt im Stapelkontext der Kopfzeile. Die Beschriftung `navigation "Dashboard-Reiter"` bleibt. **CHANGELOG** unter „Unveröffentlicht“: ein Eintrag unter „Neu“ (Proxmox-Seite) und ein neuer Abschnitt „Geändert“ (Reiter in der Kopfzeile). ## Kontrast (gemessen, WCAG-Formel, OKLCH → sRGB) Die Flächentöne aus dem Plan erreichen als Schrift auf `--card` (hell) nur diese Werte: | Token | hell | dunkel | |---|---|---| | ok | 3,41 | 6,46 | | warn | 2,55 | 7,92 | | down | 4,76 | 4,80 | | idle | 3,64 | 3,82 | | orphan | 4,85 | 2,74 | Nach der Planregel wurde deshalb die Helligkeit angepasst, und zwar als eigene Textvariante `--status-*-fg`. Die Flächentöne bleiben wie geplant. | Textvariante | hell auf card | hell auf Pille (12 %) | dunkel auf card | dunkel auf Pille | |---|---|---|---|---| | ok `0.50 0.13 152` / `0.76 0.15 152` | 5,64 | 4,94 | 7,44 | 6,02 | | warn `0.52 0.12 60` / `0.82 0.14 75` | 5,72 | 5,15 | 8,47 | 6,63 | | down `0.52 0.20 27` / `0.74 0.16 27` | 6,11 | 5,10 | 6,09 | 5,24 | | idle `0.50 0.02 260` / `0.72 0.02 260` | 6,00 | 5,28 | 6,08 | 5,24 | | orphan `0.50 0.01 260` / `0.70 0.01 260` | 6,00 | 5,17 | 5,64 | 5,06 | Alle Werte liegen bei mindestens 4,5:1. Gedämpfte Schrift auf verwaisten Karten (`opacity-70`) hätte bei `muted-foreground` nur 2,75:1 (hell) bzw. 3,02:1 (dunkel). Deshalb wird dort nur der Name in Vordergrundfarbe gedämpft (7,54 bzw. 7,13). Die Fläche `warn` hat im hellen Modus als Grafik 2,55:1 zu Weiß. Balken und Punkte in Warnfarbe stehen aber immer neben einer Zahl oder einem Wort, die Farbe trägt die Aussage also nicht allein. ## Abweichungen vom Plan **1. [Rule 2 – Kontrast] Textvariante der Statusfarben.** Der Plan erlaubt ausdrücklich, die Helligkeit anzupassen. Umgesetzt ist das als zusätzliches `-fg`-Token und nicht als Änderung der Flächentöne, damit Balken und Leisten die geplante Leuchtkraft behalten. Betrifft `globals.css` und `status-styles.ts`. **2. [Rule 2 – Kontrast] Dämpfung bei „verwaist“ eingegrenzt.** Der Plan sieht `Inhalt opacity-70 saturate-50` vor. Umgesetzt ist das nur an Symbol und Name. Pille, Produktname, Adresse, Hinweis und Fuß bleiben voll lesbar, weil gedämpfte Schrift unter 3:1 fiele. Commit 0b659d6. **3. Auslegung „+“ „immer sichtbar“.** Der Knopf bleibt wie bisher nur im Bearbeitungsmodus sichtbar, so verlangt es „Verhalten beibehalten“ und bestehender Test 4. Er sitzt jedoch außerhalb der scrollenden Spur, damit er nie weggescrollt wird. **4. Zusätzliche Dateien:** - `HealthBar.tsx` und `status-styles.ts` (Proxmox): die Klassen werden an zwei Stellen gebraucht. - `components/layout/header-slot.ts`: die Kopfzeile soll nicht die Reiter-Komponente importieren. - `umlaut-dictionary.ts`: „Prozessor“ und „Arbeitsspeicher“ sind korrektes Deutsch mit „ss“ und stehen jetzt auf der Freigabeliste der Umlaut-Wache. **5. Übersetzungsschlüssel.** Nicht mehr genutzte Schlüssel wurden ersetzt: - aus `card.pve.*`: `nodeCount`, `guests` - aus `card.pbs.*`: `lastBackup`, `verifyState` - aus `card.*`: `lastPolledLabel`, `lastOkLabel` Neu sind `health.*`, `legend.*`, `card.product.*` und weitere. Die Änderungen stehen in de.json und en.json gleichermaßen. **6. Formatierung.** `biome format` wurde auf die geänderten Proxmox-Dateien angewendet. Dadurch ist auch `proxmox-status.ts` aus Task 1 in Commit 57c338f rein umformatiert. ## Tore - `pnpm --filter web exec vitest run`: 87 Dateien, 789 Tests grün. Proxmox allein: 65. - `pnpm turbo run type-check lint`: 9/9 erfolgreich. - Biome-Warnungen web: 53, nicht mehr als vorher (53). - Verbotene Muster: kein `uppercase`, kein `tracking-widest`, keine Mittelpunkt-Ketten und kein „→“ in den neuen Dateien (per grep geprüft). - Den Browser-Nachweis (hell/dunkel, 1400 px/390 px) macht wie vereinbart der Orchestrator. Docker wurde nicht neu gebaut. ## Hinweise für die Browser-Prüfung - **Mobile Kopfzeile bei 390 px:** Rechnerisch bleiben für die Mitte etwa 80 px, also etwa ein Reiter sichtbar, der Rest ist scrollbar. Im Bearbeitungsmodus kommen 28 px für „+“ dazu. Bitte prüfen, dass nichts überläuft. - **Ränder der Spur:** Die weiche Ausblendung erscheint nur bei Überlauf. Mit drei oder mehr langen Reiternamen kann man das prüfen. - **Verwaiste Karte:** gestrichelter Rand, graue Leiste, kein farbiger Schatten, keine alten Messwerte. ## Known Stubs Keine. ## Self-Check: PASSED - FOUND: proxmox-status.ts, proxmox-status.test.ts, HealthBar.tsx, status-styles.ts, header-slot.ts - FOUND: 0fa7ce0, 57c338f, 7416a92, 0b659d6