391 lines
29 KiB
Markdown
391 lines
29 KiB
Markdown
# WOMS „Weißes Design“ P9: Serverpuls – Umsetzungsplan
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
||
**Goal:** Nach P9 ist Serverpuls hell und kastenlos. Der Kopf trägt ein Text-Badge statt eines Emojis, Warnungen sind Listenzeilen mit Stufen-Badge, die Kacheln sind `.stats` mit Sparkline. Deltas zeigen sich als Textfarbe, Umschalter sind `.seg`, Panels sind `h2.section`, die Projekttabelle ist eine `.table`. Die Diagramme nutzen die Farben aus `chartColors.js`. Alle `data-testid` bleiben.
|
||
|
||
**Architecture:**
|
||
- Drei reine Helfer in `src/lib/serverpulsFormat.js` mit Tests: `deltaDir`, `deltaSign`, `levelTone`.
|
||
- Bausteine aus P1: `Stat`, `Badge` (`badgeToneClass` aus `ui/tones.js`), `PageHeader`, `.stats`, `.list-row`, `.seg`, `.table`, `.msg`, `.toolbar`, `.input.search`.
|
||
- `serverpuls.css` wird neu geschrieben (74 → rund 70 Zeilen).
|
||
- `Chart.jsx` und `Sparkline.jsx` bleiben unverändert, weil P1 die Diagrammfarben schon umgestellt hat. Dasselbe prüft Task 6.
|
||
- Abfragen, 60-Sekunden-Takt, Neuladen bei Sichtbarkeit, `reqId`-Schutz, Sortierlogik und Zeitachsen bleiben unverändert.
|
||
|
||
**Tech Stack:** React 18, uPlot (Canvas), reines CSS. Tests mit `node --test`, Screenshot-Werkzeug aus P0.
|
||
|
||
**Spec:** `docs/superpowers/specs/2026-10-04-weisses-design-design.md`, Abschnitte „Gestaltungsregeln“, „Design-Tokens“ (Diagrammfarben), „Bausteine“ (Kennzahlen, Umschalter, Tabelle, Liste, Badge, Meldungen, Laden) und Paket P9. Anhang `2026-10-04-weisses-design-umbaukarte.md`: Abschnitt 5 (P9) und 7.1. Globale Regeln und Schnittstellen: `2026-10-05-weisses-design-00-uebersicht.md`.
|
||
|
||
## Global Constraints
|
||
|
||
Es gelten die Regeln der Übersicht. Für P9 kommt hinzu:
|
||
|
||
- Jeder Run-Block beginnt mit `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh`. Danach gelten `$WT` (Worktree, Arbeitsverzeichnis), `$W` (Werkzeug) und `$TMPDIR`. Tests und Build werden nur über `"$W/check.sh"` ausgewertet, Browser-Skripte laufen nur über `"$W/shots.sh"`.
|
||
- Ausgangspunkt ist das Tag `design-p8`, alle Diff-Prüfungen vergleichen gegen `design-p8`.
|
||
- CSS-Reihenfolge: `main.jsx` importiert `App` vor `global.css`, also lädt `serverpuls.css` vor `global.css`, und bei gleicher Spezifität gewinnt `global.css`. Jede Regel in `serverpuls.css`, die eine Global-Klasse überstimmt (`.stats`, `.list-row`, `.msg`, `.table`, `.seg`), hat höhere Spezifität (0,2,0, z. B. `.stats.sp-tiles`, `.sp-page .msg`) und nie `!important`.
|
||
- Abstände zwischen aufeinanderfolgenden Abschnitten kommen von `.page section + section { margin-top: 36px }` aus P1. P9 baut dafür keine eigenen Regeln: `.sp-panel` und `.stats.sp-tiles` setzen keinen Außenabstand nach oben. Eigene Abstände gibt es nur innerhalb der Abschnitte (`div`-Container).
|
||
- Die CSS-Tabellen dieses Plans nennen nur die Abweichungen vom Prototyp und von den Tokens. Was Basis (`*`-Reset, `button`-Reset), `.table`, `.stat`, `.list-row` und `.seg` schon liefern, wird nicht wiederholt.
|
||
- Canvas-Farben stehen nur in `chartColors.js`. In `ServerpulsPage.jsx`, `Chart.jsx`, `AppsTable.jsx` und `Sparkline.jsx` gibt es kein Farbliteral.
|
||
- Keine Icon-Importe, keine Emojis, keine Glyphen ▲ und ▼. Die Sortierzeichen ↑ und ↓ bleiben.
|
||
- Sichtbare Textänderungen: keine außer den Badge-Texten des Kopfes aus `LEVEL_TEXT`. Alle Ladetexte („Lädt …“) und Meldungen bleiben wörtlich.
|
||
- Keine neuen `style={{…}}`.
|
||
|
||
## Review Focus
|
||
|
||
Aus der Übersicht gehören drei Punkte zu P9:
|
||
- **Punkt 2, lange Inhalte am Handy:** Sechs Kacheln, Umschalter mit 6 und 5 Knöpfen und die Tabelle bei 390 px ohne waagerechtes Scrollen. Testdaten `serverpuls-lang`: Projektname mit 60 Zeichen ohne Leerzeichen, Warnungstitel mit 90 Zeichen. `overflow-x` ist seit dem Gate `p1` für alle Ansichten scharf, also auch im Gate `p9`.
|
||
- **Punkt 3, Teilausfall:** Ansicht `serverpuls-ausfall`. Die Übersicht antwortet 500, `sp-error` erscheint, Diagramm und Tabelle bleiben nutzbar, die Kacheln zeigen „–“.
|
||
- **Punkt 4, nur Tastatur:**
|
||
- Projektzeilen lassen sich über den Button in der ersten Zelle öffnen (`aria-pressed`).
|
||
- Sortierköpfe sind Buttons mit `aria-sort` auf dem `th`.
|
||
- Die `.seg`-Knöpfe tragen `aria-pressed`.
|
||
|
||
Zusätzlich gilt für P9:
|
||
- Checkliste der Bedienelemente, nichts darf fehlen: Metrik-Umschalter (6 Knöpfe), Zeitraum-Umschalter (5), Projektsuche, 6 Sortierköpfe (Projekt, Größe, Δ 7 T, Δ 30 T, CPU, RAM), Projektzeile (öffnet und schließt das Detail), Zeitraum-Umschalter im Detail (5).
|
||
- Die 20 `data-testid` im Serverpuls-Code bleiben identisch (Task 1 und 6). Ohne den Muster-Eintrag `sp-tile-${id}` sind es die 19 der Spec.
|
||
- Rot heißt gestiegen und Grün gesunken, das Vorzeichen steht weiter in der Zahl.
|
||
|
||
---
|
||
|
||
### Task 1: Paketstart und Ausgangswerte
|
||
|
||
**Files:** keine Änderung.
|
||
|
||
**Interfaces:** keine.
|
||
|
||
- [ ] **Step 1: Paketstart**
|
||
|
||
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git fetch origin && git merge-base --is-ancestor design-p8 HEAD && git rebase origin/test && "$W/check.sh" && git status --short`
|
||
Expected: Exit 0, Rebase ohne Konflikt, `# fail 0` und eine leere Statusliste. Endet `merge-base` mit Exit 1, liegt `design-p8` nicht im Branch: stoppen und melden. Die Testzahl heißt im Folgenden N0, die Bundle-Größen aus der Ausgabe von `check.sh` werden notiert.
|
||
|
||
- [ ] **Step 2: Abgleich mit TP2**
|
||
|
||
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git log --oneline origin/test..feature/terminplaner-tp2 -- src/pages/ServerpulsPage.jsx src/components/serverpuls src/styles/serverpuls.css src/lib/serverpulsFormat.js src/styles/global.css`
|
||
Expected: keine Ausgabe. Gibt es Treffer, stoppt der Agent vor Task 2, nennt dem Eigentümer die Commits und fragt nach. Er empfiehlt dabei, TP2 zuerst nach `test` zu bringen.
|
||
|
||
- [ ] **Step 3: Referenzliste der Test-IDs anlegen**
|
||
|
||
Run: ``source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -o --no-filename 'testid=("sp-[a-z-]+"|\{`sp-tile-\$\{id\}`\})' src/pages/ServerpulsPage.jsx src/components/serverpuls | sort > "$W/p9-testids.before.txt" && wc -l < "$W/p9-testids.before.txt"``
|
||
Expected: `20`.
|
||
|
||
- [ ] **Step 4: Voraussetzungen aus P1 und P2 prüfen**
|
||
|
||
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && ls src/components/serverpuls/chartColors.js src/components/ui/Stat.jsx src/components/ui/tones.js src/lib/rowClick.js && rg -n "isRowActivation" src/lib/rowClick.js && rg -n -m 1 "CHART\." src/components/serverpuls/Chart.jsx src/pages/ServerpulsPage.jsx && rg -n "#[0-9a-fA-F]{3,8}\b|rgba?\(" src/components/serverpuls src/pages/ServerpulsPage.jsx --glob '!chartColors.js'`
|
||
Expected: Die Dateien sind da, `src/lib/rowClick.js` (P2 Task 2) enthält `isRowActivation(event)`, `CHART.` kommt in beiden Dateien vor, und die letzte Suche liefert keine Treffer (Exit 1). Fehlt `rowClick.js`, stoppt der Agent und meldet es, P9 baut keinen eigenen Zeilenklick-Helfer.
|
||
|
||
### Task 2: Format-Helfer `deltaDir`, `deltaSign`, `levelTone` (TDD)
|
||
|
||
**Files:**
|
||
- Modify: `src/lib/serverpulsFormat.js` (neue Exporte nach `formatNumber`, vor dem Abschnitt `// --- Zeitachsen`)
|
||
- Test: `tests/unit/serverpulsFormat.test.mjs` (Z. 5 Import, zwei neue Tests)
|
||
|
||
**Interfaces:**
|
||
- Produces:
|
||
- `deltaDir(d) → 'up' | 'down' | 'flat'` (`flat` bei Betrag unter `1e-9`, genau die Regel aus `Tile`).
|
||
- `deltaSign(dir) → '+' | '−' | ''` (U+2212).
|
||
- `levelTone(level) → 'ok' | 'warn' | 'danger' | 'muted'`. Es sind die Ton-Namen von `Badge`.
|
||
- Bestehende Exporte bleiben unverändert.
|
||
|
||
- [ ] **Step 1: Failing Test schreiben**
|
||
|
||
Die Destrukturierung in Z. 5 bekommt `deltaDir, deltaSign, levelTone`. Dazu:
|
||
|
||
```js
|
||
test('deltaDir und deltaSign: Richtung und Vorzeichen der Kachel-Deltas', () => {
|
||
assert.deepEqual([deltaDir(0), deltaDir(5e-10), deltaDir(-5e-10)], ['flat', 'flat', 'flat'])
|
||
assert.equal(deltaDir(0.4), 'up')
|
||
assert.equal(deltaDir(-0.4), 'down')
|
||
assert.deepEqual([deltaSign('up'), deltaSign('down'), deltaSign('flat')], ['+', '−', ''])
|
||
})
|
||
|
||
test('levelTone: Badge-Ton je Stufe', () => {
|
||
assert.deepEqual(['ok', 'medium', 'critical'].map(levelTone), ['ok', 'warn', 'danger'])
|
||
assert.deepEqual([levelTone('low'), levelTone(undefined), levelTone('x')], ['muted', 'muted', 'muted'])
|
||
})
|
||
```
|
||
|
||
- [ ] **Step 2: Test laufen lassen**
|
||
|
||
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/serverpulsFormat.test.mjs`
|
||
Expected: FAIL, `deltaDir is not a function`.
|
||
|
||
- [ ] **Step 3: Implementieren**
|
||
|
||
Die drei Funktionen in `src/lib/serverpulsFormat.js`. Die Schwelle `1e-9` und das Zeichen U+2212 sind wie in `Tile` und `formatDelta`.
|
||
|
||
- [ ] **Step 4: Test grün**
|
||
|
||
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node --test tests/unit/serverpulsFormat.test.mjs && "$W/check.sh" --nur-tests`
|
||
Expected: PASS, Exit 0, Testzahl N0 + 2, `# fail 0`.
|
||
|
||
- [ ] **Step 5: Commit**
|
||
|
||
```bash
|
||
git add src/lib/serverpulsFormat.js tests/unit/serverpulsFormat.test.mjs
|
||
git commit -m "Serverpuls: Richtung, Vorzeichen und Stufen-Ton als Format-Helfer mit Test" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
|
||
```
|
||
|
||
### Task 3: Werkzeug – Testdaten, Ansichten, Gate `p9`, Ausnahme für Sparklines
|
||
|
||
**Files** (alle unter `$W/`, kein Commit im Repo):
|
||
- Create: `fixtures/serverpuls-ok.mjs`, `fixtures/serverpuls-veraltet.mjs`, `fixtures/serverpuls-lang.mjs`
|
||
- Modify: `views.mjs`, `gates.mjs`, `checks.mjs`
|
||
|
||
**Interfaces:**
|
||
- Consumes: `createBackend`, `VIEWS`, `GATES` aus P0 und die Konsolen-Ausnahme des Szenarios `fehler` (P0, in P8 Task 4 Step 3 erweitert).
|
||
- Produces: die Ansichten aus Step 2, `GATES.p9`, die Ausnahme `svg.sp-spark` im Check `icon-svg` und die Ausnahme für `**/api/serverpuls/**` im Szenario `fehler`.
|
||
|
||
- [ ] **Step 1: Testdaten-Varianten anlegen**
|
||
|
||
Alle erweitern `standard`.
|
||
- `serverpuls-ok`: Übersicht mit `level: 'ok'` und `alerts: []`.
|
||
- `serverpuls-veraltet`: `level: 'medium'`, eine Warnung, `lastSample` vor 40 Minuten.
|
||
- `serverpuls-lang`: `level: 'critical'` mit drei Warnungen (`critical`, `medium`, `low`), davon eine mit 90-Zeichen-Titel. Zwölf Projektzeilen, eine mit 60-Zeichen-Namen ohne Leerzeichen.
|
||
|
||
- [ ] **Step 2: Ansichten in `views.mjs` anlegen oder anpassen**
|
||
|
||
Der `path` ist `/serverpuls`. Die Schritte haben die Signatur `steps(page, ctx)`, `ctx.backend.calls` listet die vom Backend beantworteten Requests. Geprüft wird mit `throw new Error(…)`, das den Verstoß `step-failed` erzeugt.
|
||
|
||
| ID | Daten | Schritte |
|
||
|---|---|---|
|
||
| `serverpuls` | `serverpuls-lang` | Warten, bis `[data-testid="sp-chart"]` ein `canvas` mit Breite über 0 trägt und sechs `[data-testid^="sp-tile-"]` da sind. |
|
||
| `serverpuls-ok` | `serverpuls-ok` | Wie `serverpuls`. |
|
||
| `serverpuls-veraltet` | `serverpuls-veraltet` | Wie `serverpuls`. |
|
||
| `serverpuls-projekt` | `serverpuls-lang` | Fokus auf `button.link` der ersten `[data-testid="sp-app-row"]`, Enter: `sp-detail` muss sichtbar sein und drei `canvas` enthalten. Fokus auf den Sortierkopf „Größe“ (`th[data-sort="sizeBytes"] button`), Enter: `aria-sort` muss `ascending` heißen. Klick auf „RAM“ in `[data-testid="sp-metric-select"]` (nicht auf den gleichnamigen Tabellenkopf), dann muss dieser Knopf `aria-pressed="true"` tragen. |
|
||
| `serverpuls-ausfall` | `standard`, Szenario `fehler` | `GET **/api/serverpuls/overview` mit 500 `{ error: 'Test: Serverpuls ist nicht erreichbar.' }` beantworten. `sp-error` muss sichtbar sein, das Diagramm gezeichnet und die Tabelle da. |
|
||
|
||
- [ ] **Step 3: Gate festlegen**
|
||
|
||
In `gates.mjs` kommt nach der Deklaration von `GATES` dazu. Das Gate `p9` erbt Prüfungen und Ansichten von `p8` (die `p1`-Prüfungen für alle Ansichten, `step-failed`, `external-request` und `console-error` immer) und schaltet `icon-svg` zusätzlich für die Ansichten von P9 und aller früheren Pakete ein:
|
||
|
||
```js
|
||
const iconSvg = GATES.p8.scoped['icon-svg']
|
||
GATES.p9 = {
|
||
...GATES.p8,
|
||
scoped: {
|
||
...GATES.p8.scoped,
|
||
'icon-svg': {
|
||
...iconSvg,
|
||
views: [...iconSvg.views, 'serverpuls', 'serverpuls-ok', 'serverpuls-veraltet', 'serverpuls-projekt', 'serverpuls-ausfall'],
|
||
},
|
||
},
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 4: Sparklines vom Icon-Check ausnehmen**
|
||
|
||
`checks.mjs`: Der Check `icon-svg` nimmt `svg.sp-spark` dauerhaft aus (wie `.brand`), nicht über `ignore` im Gate. Es sind Datengrafiken (Inline-SVG-Minikurven), keine Icons. Die Konsolen-Ausnahme des Szenarios `fehler` deckt auch die Meldungen zu `**/api/serverpuls/**` ab, falls sie das noch nicht tut.
|
||
|
||
- [ ] **Step 5: Ausgangslauf**
|
||
|
||
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/build.sh" "$WT" p9-vorher && "$W/shots.sh" p9-vorher "$W/shots/p9-vorher" --only serverpuls --gate p9`
|
||
Expected: Exit 0. Der Stand nach P1 ist hell, und die Sparklines sind ausgenommen. Die Bilder dienen als Vergleich.
|
||
|
||
### Task 4: Kopf, Warnungen, Kacheln, Umschalter, Panels
|
||
|
||
**Files:**
|
||
- Modify: `src/pages/ServerpulsPage.jsx` (`LEVEL_*` Z. 31–32, `Tile` Z. 79–100, `Pills` Z. 102–117, `Cleanup` Z. 119–139, Markup Z. 254–331; Abfragen und Hooks Z. 141–239 bleiben)
|
||
- Rewrite: `src/styles/serverpuls.css` (ohne die Tabellenregeln, die bleiben bis Task 5)
|
||
|
||
**Interfaces:**
|
||
- Consumes: `Stat({ label, value, sub, tone })`, `PageHeader`, `badgeToneClass(tone)` aus `src/components/ui/tones.js`, `Badge`, `deltaDir`, `deltaSign`, `levelTone` (Task 2), `formatDelta`.
|
||
- Produces: nichts für spätere Pakete.
|
||
|
||
- [ ] **Step 1: Kopf und Fehlerzeile**
|
||
|
||
- Fehler: `<div className="msg err" data-testid="sp-error" role="alert">`.
|
||
- Kopf: `<section data-testid="sp-head">` ohne `sp-level-*`. Die Kopfzeile `div.sp-head-top` enthält:
|
||
- `<span className={badgeToneClass(levelTone(level))} data-testid="sp-ampel" data-level={level}>{LEVEL_TEXT[level] || level}</span>`. `LEVEL_TEXT` ist die schon vorhandene, bisher ungenutzte Konstante („Alles ok“, „Warnungen“, „Critical“), das Emoji und `dot` entfallen.
|
||
- die `<strong className="sp-headline">` mit dem unveränderten Text und `sp-age`. Bei `age.stale` kommt zusätzlich die Klasse `sp-age-stale`.
|
||
- Ohne `overview` zeigt das Badge `badge plain` mit „–“, `data-level` bleibt `ok`.
|
||
|
||
Entscheidung: Das Badge nennt die Stufe aus `LEVEL_TEXT`, die Überschrift bleibt wörtlich („Alles ok“, „Critical – 2 Warnungen“, „2 Warnungen“). Die Doppelung ist gewollt. Ohne Daten steht „–“ statt „Alles ok“, damit der Fehlerzustand nicht grün aussieht.
|
||
|
||
- [ ] **Step 2: Warnungen**
|
||
|
||
`ul.sp-alerts[data-testid=sp-alerts]` mit `li.list-row.sp-alert[data-testid=sp-alert]`:
|
||
- Links `div.sp-alert-main` mit `<Badge tone={levelTone(a.level)}>{LEVEL_LABEL[a.level] || a.level}</Badge>`, `span.sp-alert-title` und `span.sp-alert-value`.
|
||
- Rechts `<span className="muted small nowrap">seit {formatSince(a.since)}</span>`.
|
||
|
||
- [ ] **Step 3: Kacheln, Umschalter, Panels, Aufräumbar**
|
||
|
||
- Kacheln: `<section className="stats sp-tiles" data-testid="sp-tiles">`. Jede Kachel ist `div.sp-tile[data-testid="sp-tile-<id>"]` mit `Stat label value sub`, `Sparkline width={120} height={32}` und der Delta-Zeile.
|
||
- `sub` ist `model?.sub || '\u00a0'`, damit die Sparklines einer Reihe fluchten.
|
||
- Die Delta-Zeile ist `div.sp-tile-delta.sp-delta-{dir}` mit `{deltaSign(dir)}{model.fmtDelta(d)} ggü. gestern`. `dir` kommt aus `deltaDir(d)`, die Glyphen ▲ und ▼ entfallen.
|
||
- `Pills` rendert `<div className="seg" data-testid={testid}>` mit `<button type="button" className={active ? 'on' : undefined} aria-pressed={active}>`.
|
||
- Panels: `<section className="sp-panel" data-testid="sp-chart-panel">` mit `<h2 className="section">Verlauf</h2>`, darunter `div.sp-controls` mit beiden `Pills`, dann das Diagramm.
|
||
- `sp-projects` und `sp-cleanup` folgen demselben Muster.
|
||
- „Größen von {Tag}“ steht als `<span className="muted small">` in `h2.section` rechts.
|
||
- Die Detail-Überschrift ist `<h3 className="sp-detail-title">`, ihre `Pills` stehen in `div.sp-controls`.
|
||
- „Aufräumbar“: `div.stats.sp-cleanup-stats` mit drei `Stat` („Ungenutzte Images“, „Build-Cache“, „Gestoppte Container“), die Liste der gestoppten Container als `li.list-row` mit Name und `<span className="num">`-Größe, der Stand-Satz als `<p className="muted small">`. „Noch keine Messung.“ bekommt `.muted`.
|
||
- Alle `sp-muted`, `sp-small` werden `muted`, `small`. `.sp-nodata` bleibt als Klasse für „Lädt …“ und „Für diesen Zeitraum …“.
|
||
|
||
Entscheidung: Die Panels sind `<section data-testid>` mit `h2.section` statt `Card`, weil `Card` kein `data-testid` weitergibt. Die Umschalter stehen unter der Überschrift, nicht darin, damit der Kopf auf dem Handy nicht überläuft.
|
||
|
||
- [ ] **Step 4: `serverpuls.css` neu schreiben**
|
||
|
||
Gelöscht werden `.sp-page`-Flex, alle Kästen, Rahmen und Literale, `.sp-level-*`, `.sp-ampel`, `.sp-tile-label/-value/-sub`, `.sp-pill*`, `.sp-panel-head`, `.sp-cleanup-grid`. Neu:
|
||
|
||
| Regel | Wert |
|
||
|---|---|
|
||
| `.sp-page .msg` | `margin: 0 0 16px` |
|
||
| `.sp-head-top` | `display: flex; align-items: center; gap: 12px; flex-wrap: wrap` |
|
||
| `.sp-headline` | `font-size: 16px; line-height: 24px; font-weight: 600` |
|
||
| `.sp-age` | `margin-left: auto; font-size: 13px; color: var(--text-2)` |
|
||
| `.sp-age-stale` | `color: var(--orange-fg); font-weight: 500` |
|
||
| `.sp-alerts` | `list-style: none; margin: 12px 0 0; padding: 0` |
|
||
| `.sp-alert-main` | `display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; min-width: 0` |
|
||
| `.sp-alert-title` | `font-weight: 500; overflow-wrap: anywhere` |
|
||
| `.sp-alert-value` | `color: var(--text-2)` |
|
||
| `.stats.sp-tiles` | `display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 24px 32px` (ohne `margin`) |
|
||
| `.sp-tile` | `min-width: 0` |
|
||
| `.sp-tile .sub` | `min-height: 16px` (Größe und Farbe kommen von `.stat .sub`) |
|
||
| `.sp-spark` | `display: block; max-width: 100%; margin-top: 6px` |
|
||
| `.sp-tile-delta` | `font-size: 12px; line-height: 16px; margin-top: 4px` |
|
||
| `.sp-delta-up` / `-down` / `-flat` | `color: var(--red-fg)` / `var(--green-fg)` / `var(--text-2)` |
|
||
| `.sp-panel` | `min-width: 0` (ohne `margin`) |
|
||
| `.sp-controls` | `display: flex; flex-wrap: wrap; gap: 8px 16px; margin-bottom: 12px` |
|
||
| `.sp-controls .seg` | `flex-wrap: wrap` |
|
||
| `.sp-chart` | `width: 100%; min-height: 200px` |
|
||
| `.sp-chart .u-legend` | `font-size: 13px; color: var(--text-2)` |
|
||
| `.sp-nodata` | `padding: 40px 0; text-align: center; color: var(--text-2)` |
|
||
| `.sp-detail` | `margin-top: 28px` (ein `div` im Panel, keine `section`) |
|
||
| `.sp-detail-title` | `margin: 0 0 8px; font-size: 15px; line-height: 22px; font-weight: 600; overflow-wrap: anywhere` |
|
||
| `.sp-detail-charts` | `display: grid; grid-template-columns: repeat(auto-fit, minmax(min(360px, 100%), 1fr)); gap: 24px` |
|
||
| `.sp-chart-title` | `font-size: 13px; color: var(--text-2); margin-bottom: 4px` |
|
||
| `.sp-cleanup-list` | `list-style: none; margin: 12px 0; padding: 0` |
|
||
| ≤ 1100 px `.stats.sp-tiles` | `grid-template-columns: repeat(3, minmax(0, 1fr))` |
|
||
| ≤ 860 px `.stats.sp-tiles` | `grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px 24px` |
|
||
| ≤ 860 px `.sp-age` | `margin-left: 0` |
|
||
| ≤ 860 px `.sp-page .sp-cleanup-stats` | `grid-template-columns: repeat(2, minmax(0, 1fr))` |
|
||
|
||
Den Abstand zwischen Kopf, Kacheln und Panels liefert `.page section + section` aus P1. `.stats.sp-tiles` und `.sp-panel` setzen deshalb keinen oberen Außenabstand. Ein `margin` auf `.stats.sp-tiles` würde den globalen Abstand wegen der höheren Spezifität überstimmen.
|
||
|
||
- [ ] **Step 5: Bauen und prüfen**
|
||
|
||
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/build.sh" "$WT" p9 && "$W/shots.sh" p9 "$W/shots/p9-t4" --only serverpuls,serverpuls-ok,serverpuls-veraltet,serverpuls-ausfall --gate p9`
|
||
Expected: PASS. In den Bildern gilt:
|
||
- Keine Kästen um Kopf, Kacheln und Panels.
|
||
- Das Badge trägt „Critical“ (rot), „Warnungen“ (orange) bzw. „Alles ok“ (grün). Veraltete Daten stehen als oranger Text.
|
||
- Warnungen sind Zeilen mit Haarlinien und Stufen-Badge (Critical rot, Medium orange, Low grau).
|
||
- Die Kacheln stehen bei 1440 px in sechs Spalten, bei 390 px in zwei. Deltas sind rot oder grün ohne Pfeil.
|
||
- Der Umschalter ist grau mit weißem aktivem Teil und bricht bei 390 px um.
|
||
- Das Diagramm hat eine schwarze Linie, die Spitze ist grau gestrichelt, es gibt nur horizontale Rasterlinien, und die Legende ist 13 px grau.
|
||
- Kopf, Kacheln und Panels haben überall denselben Abstand (36 px aus `.page section + section`).
|
||
|
||
- [ ] **Step 6: Commit**
|
||
|
||
```bash
|
||
git add src/pages/ServerpulsPage.jsx src/styles/serverpuls.css
|
||
git commit -m "Design: Serverpuls-Kopf mit Text-Badge, Warnungen als Zeilen, Kacheln als Kennzahlen" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
|
||
```
|
||
|
||
### Task 5: Projekttabelle
|
||
|
||
**Files:**
|
||
- Modify: `src/components/serverpuls/AppsTable.jsx` (Spaltenliste Z. 5–12, `Delta` Z. 14–18, Markup Z. 42–89; Sortier- und Filterlogik Z. 20–40 bleibt)
|
||
- Modify: `src/styles/serverpuls.css` (Tabellenregeln `.sp-search` bis `.sp-delta` ersetzen)
|
||
|
||
**Interfaces:**
|
||
- Consumes: `.table`, `tr.row`, `.toolbar`, `.input.search`, `.hide-sm`, `.num`, `.right` (P1), `isRowActivation(event)` aus `src/lib/rowClick.js` (P2 Task 2, Prüfung in Task 1), `formatBytes`, `formatDelta`, `formatPct`, `Sparkline`.
|
||
- Produces: `AppsTable({ rows, selectedId, onSelect })` behält seine Props.
|
||
|
||
- [ ] **Step 1: Markup umbauen**
|
||
|
||
- Die Wurzel `div.sp-apps[data-testid=sp-apps]` bleibt. Die Suche steht in `<div className="toolbar">` als `<input className="input search" type="search" data-testid="sp-apps-search" placeholder="Projekt suchen …">`.
|
||
- `<table className="table">` ohne `.sp-table-wrap`. Die Spaltenliste bekommt je Eintrag `num` (Größe, Δ 7 T, Δ 30 T, CPU, RAM) und `hide` (Δ 30 T, CPU und die Spalte „30 Tage“). Diese Spalten tragen `right num` bzw. `hide-sm` auf `th` und `td`.
|
||
- Sortierkopf: `<th data-sort={c.key} aria-sort={…}>` mit `<button type="button" className="sp-sort" onClick={() => toggle(c.key)}>{c.label}<span aria-hidden="true">{' ↑' | ' ↓' | ''}</span></button>`.
|
||
- `aria-sort` ist `ascending` bzw. `descending` am aktiven Kopf, sonst fehlt es.
|
||
- Die Zeichen ↑ und ↓ bleiben wie bisher.
|
||
- Zeile: ``<tr className={`row${selected ? ' sp-row-active' : ''}`} data-testid="sp-app-row" data-app-id={r.id}>``.
|
||
- Der Zeilenklick ruft `onSelect(selectedId === r.id ? null : r.id)`, aber nur wenn `isRowActivation(e)` wahr ist.
|
||
- Die erste Zelle ist `<td className="sp-name"><button type="button" className="link" aria-pressed={selected} onClick={() => onSelect(selectedId === r.id ? null : r.id)}>{r.name}</button></td>` mit demselben Aufruf. Der Button braucht ihn, weil `isRowActivation` Klicks auf `button` ausfiltert.
|
||
- `Delta` rendert ``<span className={`sp-delta sp-delta-${d.dir}`}>{d.text}</span>`` ohne Pfeil. Das Vorzeichen steckt in `d.text`.
|
||
- Die Leerzeile bleibt (`colSpan={7}`, „Keine Projekte gefunden.“, Klasse `sp-empty`).
|
||
|
||
Entscheidung: Die Zeilen folgen der Tabellenregel der Spec. Der Button in der ersten Zelle trägt die Tastaturbedienung, der Zeilenklick bleibt für die Maus. Unter 860 px bleiben Projekt, Größe, Δ 7 T und RAM.
|
||
|
||
- [ ] **Step 2: Tabellen-CSS ersetzen**
|
||
|
||
Gelöscht werden `.sp-search`, `.sp-table-wrap`, `.sp-table*`, `.sp-sortable`, `.sp-row`, `.sp-row:hover`, `.sp-delta`. Neu:
|
||
|
||
| Regel | Wert |
|
||
|---|---|
|
||
| `.sp-sort` | `display: block; width: 100%; text-align: inherit; white-space: nowrap` (Knopf-Reset und Innenabstand 0 kommen aus der Basis) |
|
||
| `.sp-name` | `overflow-wrap: anywhere` |
|
||
| `.sp-name .link` | `text-align: left; overflow-wrap: anywhere` |
|
||
| `.sp-page .table tr.sp-row-active td` | `background: var(--bg-subtle)` |
|
||
| `.sp-page .table tr.sp-row-active td:first-child` / `:last-child` | `box-shadow: -12px 0 0 var(--bg-subtle)` / `12px 0 0 var(--bg-subtle)` |
|
||
| `.sp-empty` | `text-align: center; color: var(--text-2)` |
|
||
| `.sp-delta` | `font-variant-numeric: tabular-nums` |
|
||
|
||
- [ ] **Step 3: Bauen und prüfen**
|
||
|
||
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/build.sh" "$WT" p9 && "$W/shots.sh" p9 "$W/shots/p9-t5" --only serverpuls,serverpuls-projekt --gate p9`
|
||
Expected: PASS, das heißt:
|
||
- Die Tastaturschritte laufen durch.
|
||
- Die Tabelle hat nur Haarlinien, Zahlen stehen rechts, die ausgewählte Zeile ist `--bg-subtle`.
|
||
- Deltas sind rot, grün oder grau ohne Pfeil. Das Detail zeigt drei Diagramme in Schwarz.
|
||
- Bei 390 px sind vier Spalten da und der lange Projektname bricht um.
|
||
|
||
- [ ] **Step 4: Commit**
|
||
|
||
```bash
|
||
git add src/components/serverpuls/AppsTable.jsx src/styles/serverpuls.css
|
||
git commit -m "Design: Serverpuls-Projekttabelle (Sortierkoepfe und Zeilen als Buttons, Deltas als Farbe)" -m "Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"
|
||
```
|
||
|
||
### Task 6: Gate P9, Review, Tag
|
||
|
||
- [ ] **Step 1: Rebase**
|
||
|
||
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git fetch origin && git rebase origin/test && "$W/check.sh"`
|
||
Expected: Exit 0, Rebase ohne Konflikt, `# fail 0`. Die Testzahl ist N0 + 2. Liegen neue Commits auf `test`, kann sie höher sein, die Zahl wird notiert. So zeigen die Bilder der folgenden Schritte den Stand, der getaggt wird.
|
||
|
||
- [ ] **Step 2: Vollständiger Gate-Lauf**
|
||
|
||
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/build.sh" "$WT" p9 && "$W/shots.sh" p9 "$W/shots/p9" --gate p9`
|
||
Expected: Exit 0. Das Gate `p9` prüft alle Ansichten mit den `p1`-Prüfungen (`overflow-x`, `dark-surface`, `low-contrast`) und `icon-svg` für die Ansichten von P2 bis P9. Die fünf P9-Ansichten haben Bilder bei 1440 und 390 px ohne Verstöße.
|
||
|
||
- [ ] **Step 3: Test-IDs, Farben, unveränderte Logik**
|
||
|
||
Run: ``source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -o --no-filename 'testid=("sp-[a-z-]+"|\{`sp-tile-\$\{id\}`\})' src/pages/ServerpulsPage.jsx src/components/serverpuls | sort | diff - "$W/p9-testids.before.txt"``
|
||
Expected: keine Ausgabe, also dieselben 20 Einträge.
|
||
|
||
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && rg -n "react-icons|@tabler|style=\{|#[0-9a-fA-F]{3,8}\b|rgba?\(|!important|▲|▼|🔴|🟠|🟢" src/pages/ServerpulsPage.jsx src/styles/serverpuls.css src/components/serverpuls --glob '!chartColors.js'`
|
||
Expected: keine Treffer.
|
||
|
||
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && git diff design-p8 -- src/pages/ServerpulsPage.jsx | rg '^[-+].*(setInterval|60000|visibilit|reqId|serverpulsApi)'; git diff --stat design-p8 -- src/components/serverpuls/Chart.jsx src/components/serverpuls/Sparkline.jsx`
|
||
Expected: beide Ausgaben leer, weil Takt, Abfragen, `Chart.jsx` und `Sparkline.jsx` unverändert sind.
|
||
|
||
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && node "$W/css-klassen.mjs" "$WT" | node -e "const j=JSON.parse(require('fs').readFileSync(0));console.log(j.dead.filter(n=>/^sp-/.test(n)).concat(j.undefinedUsed.filter(n=>/^sp-/.test(n))).join('\n'))"`
|
||
Expected: leer, also keine tote und keine undefinierte `sp-*`-Klasse.
|
||
|
||
- [ ] **Step 4: Bundle-Größe**
|
||
|
||
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/check.sh" --nur-build`
|
||
Expected: Exit 0, CSS kleiner als bei Task 1. Die Werte werden notiert.
|
||
|
||
- [ ] **Step 5: Code-Review (sonnet)**
|
||
|
||
Geprüft wird der Diff `design-p8..HEAD` gegen die Spec-Abschnitte „Diagrammfarben“, „Bausteine“ (Kennzahlen, Umschalter, Tabelle, Liste, Badge, Meldungen) und Paket P9. Wichtige Befunde werden behoben. Danach kommen Step 1 (Rebase) noch einmal und Step 2 als Kurzlauf mit `--only serverpuls,serverpuls-projekt`. Schwerpunkte:
|
||
- Die Test-IDs und Abfragen sind unverändert.
|
||
- Der Fehlerzustand zeigt kein grünes „Alles ok“.
|
||
- Das Vorzeichen steht weiter in jeder Delta-Zahl.
|
||
- Die Tastaturbedienung funktioniert (Zeilen, Sortierköpfe, Umschalter).
|
||
|
||
- [ ] **Step 6: Tag und Übergabe**
|
||
|
||
Run: `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh && "$W/check.sh" && git tag design-p9 && git log --oneline origin/test..design-p9 && git rev-parse design-p9`
|
||
|
||
Der Eigentümer bekommt:
|
||
- die Screenshots aus `$W/shots/p9` (Unterordner `1440x900` und `390x844`; Auswahl: `serverpuls`, `serverpuls-ok`, `serverpuls-projekt`, `serverpuls-ausfall`)
|
||
- den Hinweis, dass das Kopf-Badge „Alles ok“ und die Überschrift „Alles ok“ doppelt stehen (Entscheidung aus Task 4), falls er die Doppelung streichen will
|
||
- die Push-Zeile `git -C /home/webklar/apps/ticket.webklar.com/dev-design push origin 'design-p9^{commit}:refs/heads/test'`
|
||
- die Rücknahme `git revert --no-commit design-p8..design-p9`, Commit und Push durch den Eigentümer (gilt nur, solange P9 das jüngste Paket auf `test` ist)
|
||
|
||
- [ ] **Step 7: Nach dem Push (erst, wenn der Eigentümer gepusht hat)**
|
||
|
||
- Im Log von `webklar-ticket-build` steht „Deployment completed successfully“.
|
||
- `curl -s https://ticket.webklar.com/ | grep -o 'assets/index-[^"]*\.js'` entspricht `dist/index.html` des Tags.
|
||
- Ein lesender Sichttest im Chrome des Eigentümers: Serverpuls mit Diagramm und Projekttabelle. Der Menüpunkt zeigt weiter den Punkt `sp-nav-dot`, wenn es Warnungen gibt.
|