Files
tickte-system/docs/superpowers/plans/2026-10-05-weisses-design-p9-serverpuls.md
T

29 KiB
Raw Blame History

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:

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
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:

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

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

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.