275 lines
19 KiB
Markdown
275 lines
19 KiB
Markdown
# WOMS „Weißes Design“ – Übersicht der Umsetzungspläne
|
||
|
||
> **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:** Das ganze Ticketsystem WOMS bekommt das freigegebene weiße Design (Akzent Schwarz, kaum Rahmen, kaum Icons). Ein Ticket öffnet künftig als eigene Seite. Die Umsetzung kommt in elf einzeln auslieferbaren Paketen.
|
||
|
||
**Architecture:**
|
||
- P1 legt Tokens, Gerüst, Bausteine und eine Übergangsschicht (KOMPAT) in `global.css` an. Danach ist die App sofort weiß.
|
||
- P2 bis P9 bauen je einen Bereich auf die neuen Bausteine um.
|
||
- P10 räumt Altlasten und Icon-Bibliotheken ab.
|
||
- Jedes Paket geht nur über ein Git-Tag live.
|
||
|
||
**Tech Stack:**
|
||
- React 18
|
||
- react-router-dom 6
|
||
- Vite 5
|
||
- Appwrite Web SDK 13 (Legacy-Query-Syntax)
|
||
- reines CSS ohne Framework
|
||
- Tests: `node --test` mit esbuild-Lader (`tests/unit/load.mjs`)
|
||
- Screenshots: playwright-core 1.62.1 und chromium-headless-shell 1234
|
||
|
||
**Spec:**
|
||
- `docs/superpowers/specs/2026-10-04-weisses-design-design.md`, verbindlich
|
||
- Anhang `docs/superpowers/specs/2026-10-04-weisses-design-umbaukarte.md`
|
||
- Prototyp: `/root/reports/ticketsystem-design-2026-10-04/quelle/` (`styles.css`, `app.js`)
|
||
|
||
## Pläne
|
||
|
||
| Paket | Datei | Kurz |
|
||
|---|---|---|
|
||
| P0 | `2026-10-05-weisses-design-p0-vorbereitung.md` | Worktree lauffähig, Totcode, Screenshot-Werkzeug |
|
||
| P1 | `2026-10-05-weisses-design-p1-fundament.md` | Tokens, KOMPAT, Gerüst, Menü, Bausteine, Dialog, Login, Pflicht-Fixes |
|
||
| P2 | `2026-10-05-weisses-design-p2-tickets.md` | Ticketliste, Ticket-Seite, Arbeitsblätter, Ticket-Dialoge |
|
||
| P3 | `2026-10-05-weisses-design-p3-planboard.md` | Planboard |
|
||
| P4 | `2026-10-05-weisses-design-p4-kunden.md` | Kundenliste, Kundenseite |
|
||
| P5 | `2026-10-05-weisses-design-p5-projekte.md` | Projekte, Zuordnen, Services, Projekt-Bausteine |
|
||
| P6 | `2026-10-05-weisses-design-p6-kunden-unterseiten.md` | Chat, Gutschriften, Abo-Angebote, E-Mail-Speicher |
|
||
| P7 | `2026-10-05-weisses-design-p7-finanzen.md` | Finanzen |
|
||
| P8 | `2026-10-05-weisses-design-p8-rechtliches-admin.md` | Rechtliches, Admin |
|
||
| P9 | `2026-10-05-weisses-design-p9-serverpuls.md` | Serverpuls |
|
||
| P10 | `2026-10-05-weisses-design-p10-aufraeumen.md` | KOMPAT, Aliase, Icon-Pakete entfernen |
|
||
|
||
## Reihenfolge
|
||
|
||
- Die Pakete werden strikt in der Reihenfolge P0 → P10 umgesetzt.
|
||
- P0 geht nicht allein live, sondern zusammen mit P1. Das Tag `design-p1` enthält die P0-Commits.
|
||
- An Paket N+1 wird erst gearbeitet, wenn `design-p<N>` gesetzt ist.
|
||
|
||
## Global Constraints
|
||
|
||
Diese Regeln gelten für jede Aufgabe aller Pakete.
|
||
|
||
**Befehle und Prüfungen**
|
||
- Jede Bash-Ausführung beginnt mit `source /root/reports/ticketsystem-design-2026-10-04/werkzeug/env.sh`. Das Skript (aus P0) setzt:
|
||
- `WT=/home/webklar/apps/ticket.webklar.com/dev-design`
|
||
- `W=/root/reports/ticketsystem-design-2026-10-04/werkzeug`
|
||
- `TMPDIR=$W/tmp`
|
||
- `set -o pipefail`
|
||
- `cd "$WT"`
|
||
|
||
Temporäre Dateien liegen nie direkt in `/tmp`. Auch die ```bash-Commit-Blöcke der Pläne laufen in einem Shell-Aufruf, der zuerst `env.sh` lädt.
|
||
- Tests und Build werden nur über `"$W/check.sh"` als Bedingung genutzt. Varianten:
|
||
- `"$W/check.sh"` für Tests und Build
|
||
- `"$W/check.sh" --nur-tests`
|
||
- `"$W/check.sh" --nur-build`
|
||
|
||
Das Skript gibt die Zeilen `# pass`/`# fail` und die Bundle-Größen aus und endet mit dem Exit-Code von `npm test` bzw. `npm run build`. Ein Tag wird nur mit `"$W/check.sh" && git tag design-p<N>` gesetzt. Ketten wie `npm test | grep … && …` sind verboten, weil sie den Exit-Code von `grep` werten.
|
||
- Browser-Skripte (Playwright, Pixelvergleich) laufen nie direkt mit `node`, sondern über die Hüllen `$W/shots.sh` oder `$W/run-browser.sh <skript> [args]`. Beide starten unter `systemd-run --scope -q -p MemoryMax=1500M -p MemorySwapMax=0`.
|
||
- `rg` auf diesem Host ist Version 13.0.0 ohne PCRE2. Deshalb kein `rg -P`, keine Lookarounds.
|
||
|
||
**CSS-Reihenfolge**
|
||
- `src/main.jsx` importiert `App` vor `./styles/global.css`. Deshalb stehen alle von Komponenten importierten Stylesheets im Bundle **vor** `global.css`: `chat.css`, `serverpuls.css`, `admin-areas.css`, `pages/AdminPage.css` und `uplot.min.css`.
|
||
- Bei gleicher Spezifität gewinnt `global.css`. Bereichs-CSS, das eine globale Regel überstimmen muss, braucht eine höhere Spezifität (z. B. `.page.admin-page`) und nie `!important`.
|
||
|
||
**Parallele Arbeit an WOMS**
|
||
- Auf `feature/terminplaner-tp2` arbeitet eine andere Sitzung weiter. Stand 2026-10-05: 5 Commits vor `test`, sie berühren u. a. `CustomerFunktionen.jsx`, `lib/funktionenView.js` und Rechtliches.
|
||
- Vor dem Start jedes Pakets wird `git log --oneline origin/test..feature/terminplaner-tp2 -- <Dateien des Pakets>` geprüft. Gibt es Treffer, stoppt der Agent und fragt den Eigentümer. Er empfiehlt dabei, TP2 zuerst nach `test` zu bringen.
|
||
|
||
**Paketstart und Abnahme-Reihenfolge**
|
||
- Paketstart:
|
||
- `git fetch origin`
|
||
- `git merge-base --is-ancestor design-p<N-1> HEAD`, sonst stoppen und melden
|
||
- `git rebase origin/test`
|
||
- `"$W/check.sh"`
|
||
- Abnahme: erst `git fetch origin && git rebase origin/test`, dann Gate-Lauf, Screenshots und Review. So zeigen die Bilder den Stand, der getaggt wird.
|
||
- Jede Übergabe nennt neben der Push-Zeile auch die Rücknahme: `git revert --no-commit design-p<N-1>..design-p<N>` als Commit auf `test`. Das gilt nur für das jüngste Paket.
|
||
|
||
**Bausteine benutzen**
|
||
- Jeder Knopf in einem `<Dialog as="form">` außer dem Absenden-Knopf trägt `type="button"`. `Button` setzt keinen eigenen Standard-Typ, weil Bestandsformulare darauf bauen.
|
||
- Abstände zwischen aufeinanderfolgenden Abschnitten regelt P1 global mit `.page section + section { margin-top: 36px }`. Pakete bauen dafür keine eigenen Regeln. Eigene Abstände sind nur innerhalb von Bereichs-Containern erlaubt, die keine `section` sind.
|
||
- Klickbare Tabellenzeilen prüfen Klicks immer mit `isRowActivation(event)` aus `src/lib/rowClick.js` (entsteht in P2, Task 2). Kein Paket baut diese Regel neu.
|
||
|
||
**Commit-Trailer**
|
||
- In den Commit-Beispielen steht `Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>`. Maßgeblich ist die Attribution, die die ausführende Sitzung vorgibt.
|
||
|
||
**Arbeitsort und Git**
|
||
- Gearbeitet wird nur in `/home/webklar/apps/ticket.webklar.com/dev-design` auf dem Branch `feature/design-weiss`. In `/home/webklar/apps/ticket.webklar.com/build/` (Live-Checkout) wird nie etwas geändert.
|
||
- `git add` nur mit ausdrücklichen Pfaden, nie `-A` oder `.`.
|
||
- Commit-Nachrichten auf Deutsch, letzte Zeile `Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>`.
|
||
- Niemals `git push`. Live bringt nur der Eigentümer mit `git -C /home/webklar/apps/ticket.webklar.com/dev-design push origin 'design-p<N>^{commit}:refs/heads/test'`.
|
||
- Vor dem Paketstart und vor dem Setzen des Tags: `git fetch origin && git rebase origin/test`. Das Tag `design-p<N>` wird danach gesetzt und nie bewegt. Korrekturen nach dem Push: `design-p<N>-fix<k>`.
|
||
- Nicht anfassen:
|
||
- `.env*` (Latin-1)
|
||
- das Portal-Repo
|
||
- `src/lib/loadChatComposer.js` samt Test
|
||
- die Container-Klassen in `src/components/chat/ChatThread.jsx` (`woms-chat`, `chat-messages`)
|
||
- Appwrite-Schema und gespeicherte Werte
|
||
|
||
**Speicher und Prozesse**
|
||
- Speicherintensives (`npm ci`, `npm run build`, Playwright) läuft unter `systemd-run --scope -q -p MemoryMax=1500M -p MemorySwapMax=0`, ein Build zusätzlich mit `NODE_OPTIONS=--max-old-space-size=1200`.
|
||
- Prozesse nur per PID beenden, nie mit `pkill -f` (beendet sonst die eigene Shell).
|
||
|
||
**Tests und Screenshots**
|
||
- Tests laufen ohne Produktionszugriff. Das Screenshot-Werkzeug blockiert jede Anfrage an einen echten Host.
|
||
- Lauf: `npm test`, alle Tests grün.
|
||
- Neue Lib-Funktionen bekommen Tests unter `tests/unit/<name>.test.mjs` im Stil der bestehenden Tests (`node:test`, `assert/strict`, `loadModule`).
|
||
|
||
**Gestaltung**
|
||
- Akzent `#111111`. Pro Seitenkopf höchstens ein `.btn-primary`.
|
||
- Breakpoints 1100 px (`.hide-md`) und 860 px (`.hide-sm`, Schublade, Bottom-Sheet).
|
||
- Die Token-Werte stehen exakt in der Spec, Abschnitt „Design-Tokens“, und werden nicht neu erfunden.
|
||
- Keine Icon-Komponenten in umgebauten Dateien. Handelnde Zeichen werden Text: „ד, „←“, „→“, „↑“, „↓“.
|
||
- Keine neuen `style={{…}}` außer für Werte, die erst zur Laufzeit feststehen.
|
||
- Kein Farbliteral in JSX. Farben nur über Tokens. Einzige Ausnahme: Canvas-Farben in `src/components/serverpuls/chartColors.js`.
|
||
|
||
**Texte und Verhalten**
|
||
- Texte bleiben wörtlich. Geändert wird nur, was die Spec unter „Texte“ und in den Bausteinen nennt.
|
||
- Rechtstexte, Mail- und Chat-Vorlagen, Fehlermeldungen und der Wortlaut in `lib/analyseView.js` bleiben unverändert.
|
||
- Verhalten bleibt, außer „Bewusste Änderungen am Verhalten“ der Spec. Alle `window.confirm`-Rückfragen, `data-testid`, Doppelklick-Sperren (`busyRef`, `lockRef`) und URL-Parameter bleiben.
|
||
|
||
**Abschluss jedes Pakets**
|
||
- Abnahme nach Spec „Abnahme“, Punkte 1 bis 7.
|
||
- Code-Review mit dem Modell `sonnet`. Wichtige Befunde werden behoben, Kleinigkeiten notiert.
|
||
|
||
## Gemeinsame Schnittstellen aus P1
|
||
|
||
Diese Bausteine und Klassen sind ab P1 vorhanden. Spätere Pakete benutzen sie und erfinden keine eigenen.
|
||
|
||
**Komponenten**
|
||
|
||
- **`src/components/ui/Button.jsx`**
|
||
- Signatur: `Button({ variant = 'default', size, as = 'button', className = '', ...props })`.
|
||
- Varianten: `'default' | 'ghost'` → `btn`, `'primary'` → `btn btn-primary`, `'quiet'` → `btn btn-quiet`, `'danger'` → `link-danger`.
|
||
- `size` wird ignoriert.
|
||
- **`src/components/ui/Badge.jsx`**
|
||
- Signatur: `Badge({ tone = 'muted', className = '', style, children })`.
|
||
- Tonzuordnung: `muted|grey` → `''`, `info|blue` → `blue`, `ok|green` → `green`, `warn|orange` → `orange`, `danger|red` → `red`, dazu `violet` und `plain`.
|
||
- Die Prop `dot` wird ignoriert.
|
||
- **`src/components/ui/Card.jsx`**
|
||
- Signatur: `Card({ title, actions, className, children })`.
|
||
- Rendert `<section className={className}>`, mit Titel `<h2 className="section"><span>{title}</span>{actions && <span className="actions">{actions}</span>}</h2>`.
|
||
- Ohne `title` und ohne `actions` entfällt das `h2`, die Kinder stehen direkt in `<section>`.
|
||
- `pad`, `bodyStyle` und `style` werden ignoriert.
|
||
- **`src/components/ui/PageHeader.jsx`**
|
||
- Signatur: `PageHeader({ title, subtitle, actions, eyebrow, crumb, children })`.
|
||
- `crumb` ist ein Array `[{ label: string, to?: string }]`, gerendert als `.crumb` mit „/“ in `--text-3` vor `.page-head`.
|
||
- **`src/components/ui/Tabs.jsx`**
|
||
- Signatur: `Tabs({ tabs: [{ id, label, count? }], active, onChange })`.
|
||
- Rendert `.tabs`, `role="tablist"`, Buttons `.tab` mit `role="tab"`, `aria-selected`, Zähler `<span class="count">` bei `count != null`.
|
||
- **`src/components/ui/EmptyState.jsx`**: `EmptyState({ title, hint, action })` → `.empty`.
|
||
- **`src/components/ui/Field.jsx`**: `Field({ label, htmlFor, hint, children })` → `.field`, `.hint`.
|
||
- **`src/components/ui/Stat.jsx`**
|
||
- Signatur: `Stat({ label, value, sub, tone = '' })`, wobei `tone` `'' | 'warn' | 'neg'` ist.
|
||
- Rendert `.stat` mit `.k` (Label), `.v {tone}` (Wert) und `.sub`.
|
||
- Der Container `.stats` kommt vom Aufrufer.
|
||
- **`src/components/ui/Kpi.jsx`**
|
||
- Signatur: `Kpi({ label, value, sub, icon, accent })`.
|
||
- Gibt `<Stat … tone={accent === 'var(--danger)' ? 'neg' : ''}>` zurück und ignoriert `icon`.
|
||
- Wird in P7 gelöscht.
|
||
- **`src/components/ui/StatusPill.jsx`**
|
||
- `StatusPill({ status })` → `<span className={'badge ' + statusTone(status)}>{statusLabel(status)}</span>`.
|
||
- `UrgentPill({ priority })` → `badge red` „Dringend“, nur wenn `isUrgent(priority)`.
|
||
- **`src/components/ui/Dialog.jsx`**
|
||
- Signatur: `Dialog({ title, onClose, busy = false, wide = false, as = 'div', onSubmit, footer, children })`.
|
||
- Aufbau: Overlay `.overlay`, Box `.dialog` (`.dialog-wide`), Kopf `.dialog-head` mit `<h3 id>` und `<button type="button" className="dialog-close" aria-label="Schließen" disabled={busy}>×</button>`, darunter `.dialog-body` und `.dialog-foot`.
|
||
- Mit `as="form"` ist die Box ein `<form onSubmit>`, die native Pflichtfeld-Prüfung bleibt aktiv.
|
||
- Schließen: Esc nur bei `!e.defaultPrevented && !busy`. Ein Klick auf den Hintergrund schließt nie.
|
||
- Fokus über `useDialogFocus`.
|
||
- **`src/components/Logo.jsx`**: `Logo({ size = 24 })` mit dem SVG aus dem Prototyp (`app.js`, Konstante `LOGO`).
|
||
- **`src/lib/ticketStatus.js`**: `statusTone(status) → '' | 'blue' | 'orange' | 'violet' | 'green' | 'plain'`.
|
||
- **`src/components/serverpuls/chartColors.js`**: `export const CHART = { line: '#111111', fill: 'rgba(17,17,17,.06)', peak: '#8a93a3', axis: '#5e6678', grid: '#ebeef1', tick: '#d8dde3' }`.
|
||
|
||
**CSS-Klassen in `global.css`**
|
||
|
||
Prototyp-Namen, Werte aus `quelle/styles.css`:
|
||
|
||
| Bereich | Klassen |
|
||
|---|---|
|
||
| Gerüst | `.shell(.menu-open)`, `.sidebar`, `.brand`, `.nav`, `.nav-item(.sub .active .paused)`, `.nav-count(.hot)`, `.nav-dot(.critical)`, `.sidebar-foot`, `.main`, `.mobile-bar`, `.scrim`, `.page` |
|
||
| Seitenkopf | `.crumb`, `.page-head`, `.eyebrow`, `.sub`, `.actions`, `h2.section` |
|
||
| Knöpfe | `.btn`, `.btn-primary`, `.btn-quiet`, `.btn-block` |
|
||
| Links | `.link`, `.link-danger` |
|
||
| Badges | `.badge(.blue .orange .violet .green .red .plain)` |
|
||
| Reiter | `.tabs`, `.tab(.active)`, `.count` |
|
||
| Formular | `.toolbar`, `.grow`, `.input`, `.select`, `.textarea`, `.search`, `.select-quiet`, `.check`, `.field`, `.hint`, `.field-row`, `.field-row-3`, `.code-input` |
|
||
| Tabelle | `.table`, `tr.row`, `.right`, `.cell-title`, `.cell-sub`, `.id`, `.table-foot`, `.hide-md`, `.hide-sm` |
|
||
| Zustände | `.empty`, `.loading`, `.msg(.err .ok .warn)`, `.toast` |
|
||
| Werte | `.facts`, `.fact`, `.k`, `.v`, `.stats`, `.stat`, `.v.warn`, `.v.neg`, `.kv`, `.two-col`, `.prose` |
|
||
| Bedienelemente | `.urgent-toggle(.on)`, `.sw`, `.list-row`, `.seg(button.on)` |
|
||
| Dialog | `.overlay`, `.dialog`, `.dialog-wide`, `.dialog-head`, `.dialog-close`, `.dialog-body`, `.dialog-foot` |
|
||
| Login | `.login`, `.login-box`, `.login-links`, `.login-foot` |
|
||
| Hilfsklassen | `.num`, `.muted`, `.faint`, `.small`, `.xs`, `.strong`, `.nowrap` |
|
||
| Zustände | `:disabled` (Deckkraft .55) und `[readonly]` (`--bg-subtle`) |
|
||
|
||
`.ws*` kommt mit P2, `.week`/`.day`/`.appt` mit P3.
|
||
|
||
**Screenshot-Werkzeug aus P0**
|
||
|
||
Es liegt unter `$W`.
|
||
|
||
| Befehl | Wirkung |
|
||
|---|---|
|
||
| `env.sh` | Umgebung, siehe oben |
|
||
| `check.sh [--nur-tests\|--nur-build]` | Tests und/oder Build mit echtem Exit-Code |
|
||
| `build.sh $WT <name>` | Vite-Build mit Test-Endpunkten nach `$W/dist/<name>` |
|
||
| `shots.sh <name> <ausgabe> [--only id,id] [--gate p<N>]` | Screenshots plus Prüfbericht `<ausgabe>/report.json` |
|
||
| `run-browser.sh <skript.mjs> [args]` | jedes andere Browser-Skript unter Speichergrenze |
|
||
|
||
Zu `shots.sh`:
|
||
- Eine unbekannte ID in `--only` bricht mit Exit 2 ab.
|
||
- `--gate p<N>` endet bei Verstößen mit Exit 1.
|
||
|
||
**`$W/views.mjs`**
|
||
- Format: `VIEWS: Array<{ id, path, fixtures = 'standard', scenario = 'angemeldet', viewports?: Array<[w, h]>, steps?: async (page, ctx) => void }>`.
|
||
- `ctx.backend` gibt Zugriff auf `calls` (alle beantworteten Requests) und `violations`.
|
||
- Ein Fehler in `steps` erzeugt den Verstoß `step-failed`.
|
||
- Standard-Viewports sind `[[1440, 900], [390, 844]]`.
|
||
|
||
**`$W/gates.mjs`**
|
||
- Format: `GATES: { p<N>: { checks: string[], views: '*' | string[], scoped?: { [check]: { views: string[], ignore?: string[] } } } }`.
|
||
- `checks` gilt für `views`. Einträge in `views` dürfen mit `*` enden und gelten dann als Präfix (z. B. `'tickets-*'`). Einträge mit führendem `!` schließen aus (nach den Einschlüssen). Ein Muster, das keine Ansicht trifft, eine leere Liste oder ein unbekannter Prüfname beenden den Lauf mit Exit 2.
|
||
- Immer aktiv sind zusätzlich `unhandled-request`, `backend-error` und `missing-asset` (fehlende statische Datei im Build).
|
||
- `bad-text` meldet sichtbare Texte wie „Invalid Date“, „NaN“, „undefined“, „[object Object]“ und das Wort „null“. Ab P2 ist die Prüfung im Gate scharf.
|
||
- Alle Läufe nutzen die feste Uhr `FIXTURE_TODAY=2026-10-05` (überschreibbar per Umgebungsvariable). So sind Bilder verschiedener Tage vergleichbar.
|
||
- Gate `p1` prüft `overflow-x` mit `['*', '!admin', '!admin-neu']`, weil die Admin-Seite heute schon waagerecht scrollt. P8 behebt das und nimmt die Ausnahme heraus.
|
||
- Das Werkzeug ist ein eigenes Git-Repo unter `$W`. Wer es ändert, committet dort. Selbsttests: `node selftest.mjs` und `"$W/run-browser.sh" "$W/checks-selftest.mjs"`.
|
||
- `scoped` schaltet eine weitere Prüfung nur für die genannten Ansichten ein. Beispiel: `icon-svg` für die Ansichten des Pakets, `ignore` mit Selektoren, die bis zu einem späteren Paket ausgenommen bleiben.
|
||
- `step-failed`, `external-request` und `console-error` sind in jedem Gate aktiv.
|
||
- Paket N legt `p<N>` als `{ ...GATES['p<N-1>'], scoped: { ...vorherige, 'icon-svg': { views: [… Ansichten von N und allen früheren Paketen] } } }` an.
|
||
|
||
**Ansichten-IDs der Kundenseite**
|
||
- Die Reiter-IDs kommen aus `src/lib/customerTabs.js`: `kunde-overview`, `kunde-tickets`, `kunde-invoices`, `kunde-projects`, `kunde-chat`, `kunde-abo`, `kunde-gutschriften`, `kunde-funktionen`, `kunde-protokoll`.
|
||
- Pakete verwenden diese IDs und legen keine zweite Ansicht für dieselbe URL an.
|
||
|
||
## Review Focus
|
||
|
||
Diese Eingaben und Zustände werden von keinem Lib-Test abgedeckt, treffen aber echte Nutzer. Jede Zeile hat eine Prüfung im Paket, das den Code besitzt.
|
||
|
||
1. **Altdaten in Tickets:**
|
||
- Freitext-Datum `2.5.2026`, `31.02.2026` oder ISO-Datum
|
||
- Ticket ohne `woid`
|
||
- unbekannter oder leerer Status
|
||
- fehlender `customerName`
|
||
|
||
Erwartet wird, dass die Seite rendert und „–“ oder den Rohtext zeigt. Die Prüfung liegt in P0 (Testdaten) und P2 (Ansicht `ticket-altdaten`).
|
||
2. **Lange Inhalte am Handy:**
|
||
- Betreff mit 120 Zeichen
|
||
- Firmenname mit 60 Zeichen
|
||
- viele Badges
|
||
|
||
Erwartet wird kein waagerechtes Scrollen bei 390 px. Die Prüfung `overflow-x` liegt in P0 und ist ab P1 für alle Ansichten scharf.
|
||
3. **Teilausfall der Abfragen** (Zähler, Spalte „Zeit“, Mitarbeiterliste antworten mit 500): Die Liste bleibt bedienbar, statt Zahlen steht „–“. Die Prüfung ist das Szenario `fehler` in P0, angewendet in P2.
|
||
4. **Nur Tastatur:**
|
||
- Dialog per Esc schließen
|
||
- Tab bleibt im Dialog
|
||
- „ד ist bei `busy` gesperrt
|
||
- Tabellenzeilen per Tab erreichbar
|
||
|
||
Die Prüfung liegt in P1 (Dialog) und P2 (Ticketzeilen), jeweils als Tastaturschritte in `views.mjs`.
|
||
5. **Viele Einträge:**
|
||
- Ticket mit 300 Arbeitsblättern
|
||
- Suche mit 250 Treffern
|
||
|
||
Erwartet werden: Spalte „Zeit“ nur für die ersten 200 Zeilen mit Hinweis im Fuß, keine falschen Summen, Seite bleibt bedienbar. Die Prüfung ist das Testdaten-Set `viel` in P0, angewendet in P2.
|