Steriles Weiss, kaum Rahmen, kaum Icons, Akzent Schwarz, Ticket als eigene Seite. Pakete P0-P10, jedes einzeln ueber ein Tag live. Gegen den Code geprueft (4 Pruefer + Gegenpruefung, 66 Befunde eingearbeitet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
77 KiB
Anhang zu
2026-10-04-weisses-design-design.md: Ergebnis der Bestandsaufnahme vom 2026-10-04 (6 Leser + Synthese, nur lesend). Bei Widersprüchen gilt das Konzept. Entschieden sind inzwischen: Akzent Schwarz, Ticket als eigene Seite (Abschnitt 6) und alle Standards aus Abschnitt 8.1, außer wo das Konzept etwas anderes festlegt (Tab-Zähler und Spalte "Zeit" werden mitgeliefert, Planboard zeigt Stornierte, Kundenliste ohne "Offene Tickets", Projekte-Spalte "Zuletzt geändert" =$updatedAt).
Konvertierungskarte WOMS: dunkel zu weiß (Prototyp, Akzent Schwarz #111)
Stand 2026-10-04. Repo /home/webklar/apps/ticket.webklar.com/build (Branch test = 603d08c = origin/test).
Quellen sind der Prototyp (/root/reports/ticketsystem-design-2026-10-04/quelle/styles.css, 545 Zeilen, vollständig gelesen, und app.js), das Leser-Inventar (104 Dateien) und eigene Stichproben. Was ich selbst nachgeprüft habe, steht unter 0 und ist im Text mit [geprüft] markiert. Der Eigentümer hat Schwarz bestätigt ("schwarz passt"). Es gibt keine Akzent-Varianten (html[data-accent]) und keine .proto-Leiste.
0. Befunde aus der Gegenprüfung, die die Planung ändern
- Der Deploy-Webhook deployt nur
test. Die Aufgabenstellung und die Projektnotizwoms-ticketsystem.mdgehen noch von "jeder gepushte Branch geht live" aus [geprüft]./home/webklar/apps/ticket.webklar.com/webhook-server.jshat seit 2026-09-30 12:51:29 UTCDEPLOY_BRANCH = "test"("ignoring push to X (only test deploys)").- Der Container
webklar-ticket-buildstartete 12:51:56 UTC und mountet die Datei:ro, läuft also mit der neuen Version. - In den Logs der letzten 72 h stehen nur Pushes auf
test. Ein Push auf einen Feature-Branch wurde nie getestet. - Empfehlung: Feature-Branches weiter nicht pushen, bis ein Probe-Push den Lock bestätigt hat. Der Eigentümer bringt Pakete live mit
git push origin <branch>:test.
build/ist das Live-Checkout. Es ist im Container als/appeingehängt [geprüft].deploy.shmacht dortgit reset --hard origin/test,git clean -fd,rm -rf node_modulesundnpm install.- Dort wird nie entwickelt. Entwicklung läuft in einem Worktree, zum Beispiel
dev-designauffeature/design-weiss(vontestabgezweigt), wie die vorhandenendev-*-Worktrees. - Es gibt kein Staging. Jeder Push auf
testist Produktion, ein Poll alle 15 Minuten baut zusätzlich neue Commits auftest.
feature/terminplaner-tp2ist divergiert, nicht nur "1 voraus" [geprüft].- Der Branch liegt 1 Commit voraus und 3 hinter
test. Alle anderendev-*-Branches sind vollständig intestenthalten (ahead = 0). - Der eine Commit
6f03712("Funktionen-Reiter: Abschaltgrund und Löschdatum") ändertsrc/components/customer/CustomerFunktionen.jsx(3 Zeilen),src/lib/funktionenView.js(+21) undtests/unit/funktionenView.test.mjs(+36). - Konfliktpotenzial hat nur die 3-Zeilen-Änderung in
CustomerFunktionen.jsx, und die wird in Paket 4 neu gebaut. Siehe Abschnitt 7.
- Der Branch liegt 1 Commit voraus und 3 hinter
- Ticket als eigene Seite ist laut Projektnotiz
woms-theme-weiss.mdschon entschieden. Dort steht: "User decided: a ticket opens as its OWN PAGE (route per ticket, tabs Details/Arbeitsblätter), not inline expand." Die Aufgabe nennt die Frage noch offen. Abschnitt 6 begründet die Empfehlung unabhängig davon. Falls die Entscheidung nur mündlich fiel, bitte kurz bestätigen lassen. - Latin-1 gibt es nur in
.env.developmentund.env.production(ISO-8859, getrackt, nur Kommentarzeilen mit "Öffentliche … für") [geprüft]. Alle 93 JSX- und 65 JS-Dateien, Tests, CSS undindex.htmlsind UTF-8 ohne BOM und ohne CRLF. Die.env*bleiben unangetastet. node_modulesist teilweise in Git getrackt: 8.890 von 9.117 getrackten Dateien (date-fns5.722,caniuse-lite837,react-icons147,@tabler0) [geprüft].- Für den Build ist das egal, weil
deploy.shnode_moduleslöscht und neu installiert. - Praktische Folge: In Worktrees nur mit expliziten Pfaden
git addund niegit add -A. Einnpm uninstallin einem Worktree zeigt sonst 147 gelöschte getrackte Dateien.
- Für den Build ist das egal, weil
- Baseline:
npm test= 312 Tests, 312 grün, 0,8 s [geprüft]. Bundle heute: JS 680.818 B, CSS 64.433 B, Schriftlato-regular.ttf96.184 B. LeadsPage.jsxist toter Code [geprüft]. Sie wird nirgends importiert,/leadsleitet auf/ticketsum, undhooks/useLeads.jswird nur von ihr benutzt.lib/leads.jsdagegen lebt (Kunden-Seiten) und bleibt.- Das Test-Rezept für UI steht in
woms-ticketsystem.md. Vite dev wird per OOM getötet. Stattdessen gilt: esbuild-Harness untersystemd-runmitMemoryMax=400M, dann Playwright-Headless mit gemocktem Appwrite. Die Skripte sind weg und müssen in Paket 0 neu gebaut werden.
1. Ausgangslage in Zahlen
1.1 JSX nach Bereich (eigene Zählung, style={, Farbliterale #rrggbb/#rgb/rgb(a)(, Icon-Tags <Fa…/Icon…/Si…>)
| Bereich | JSX-Dateien | Zeilen | Inline-style |
Farbliterale | Icon-Tags | Dateien mit Icon-Import |
|---|---|---|---|---|---|---|
1 Grundgerüst/UI/Login (App, Navbar, Footer, AdminRoute, LoginPage, LetterGlitch, ui/* 12) |
19 | 1.086 | 39 | 17 | 17 | 1 |
| 2 Tickets, Arbeitsblätter, Projekt-Block, Planboard | 20 | 2.766 | 160 | 82 | 42 | 16 |
| 3 Kunden, Chat, Gutschriften, Angebote, Speicher, Leads | 18 | 2.524 | 107 | 14 | 68 | 15 |
| 4 Projekte, Zuordnen, Services, AnalyseRow | 4 | 1.419 | 95 | 4 | 25 | 3 |
| 5 Admin, Finanzen, Rechtliches, Serverpuls | 31 | 3.726 | 7 | 14 | 35 | 16 |
| AuthContext | 1 | 272 | 0 | 0 | 0 | 0 |
| Summe | 93 | 11.793 | 408 | 131 | 187 | 51 |
| davon LeadsPage (wird gelöscht) | 1 | 614 | 53 | 14 | 25 | 1 |
| Summe ohne LeadsPage | 92 | 11.179 | 355 | 117 | 162 | 50 |
Die größten Inline-Style-Träger sind ProjectsPage (37), WorksheetList (33), AssignProjectPage (31), PreviewBlock (30), LoginPage (19), AssignedProjectCard (18), AnalyseRow (16), TicketsPage und CustomersPage (je 13) und PlanboardPage (12). Fast alles ist Layout (flex, gap, margin, fontSize). Echte Farben stehen nur in WorksheetList (Gradienten, Weiß-Alphas, #9d3dfe), AssignedProjectCard, PlanboardPage, PreviewBlock, LoginPage, den Link-Buttons und LeadsPage.
1.2 CSS und Sonstiges
| Datei | Zeilen | Besonderheit |
|---|---|---|
src/styles/global.css |
1.476 | 568 Regeln, 378 Klassennamen, 272 var()-Zeilen, 79 !important, 18 Gradienten, 104 Hex + 90 rgb(a); 3 Schichten: L1-794 Legacy-Dunkel, L796-1120 "Redesign auf dunklen Tokens", L1122-1476 Rechtliches (L1122-1153) + Finanzen (L1155-1476) |
src/pages/AdminPage.css |
416 | 16 Farbliterale, --admin-line als Weiß-Alpha; enthält auch geteilte .credential*, .sr-only, .spin |
src/styles/serverpuls.css |
74 | 9 Literale; L70-74 .side-dot* gehören zur Sidebar |
src/styles/admin-areas.css |
61 | 5 Literale, 7 Importeure |
src/styles/chat.css |
52 | grün verdrahtet, var(--surface) ist undefiniert (Altfehler, L13) |
src/lib/previewEnv.js |
29 | 4 Hex (6 Vorkommen) in dotColor() |
src/assets/fonts |
2 Dateien | Lato 400 (96 KB) + OFL-Lato.txt |
- Tote CSS-Klassen: 66 Namen, etwa 88 Regeln und 285 Zeilen ohne jede Verwendung, dazu 3 tote Keyframes (
slideDown,fadeIn,slideIn). - Klassen ohne Regel:
p-4,btn-sm(sitzt in 7 JSX-Dateien!),status-filter,supporters-editor,timelineund rund 35 Bootstrap-Namen inWorksheetList.jsx(d-flex,py-3,col-md-6, …). Sie tun heute nichts. - Breakpoints heute: 640, 700, 720 (x3 plus Sidebar-Auto-Collapse), 760, 768, 900, 1060, 1100 und Container Queries 560, 640, 860 und 960. Der Prototyp kennt nur 1100 und 860.
- Tests: 34 Dateien, 312 Tests, nur reine Logik (
node --test, kein jsdom). Genau ein Test greift Markup (adminRoute.test.mjs).
1.3 Aufwandsklassen der 104 betrachteten Dateien (JSX, JS, CSS, index.html, fremde Chat-Datei)
| Bereich | trivial | klein | mittel | groß |
|---|---|---|---|---|
| Grundgerüst/UI/Login | 13 | 7 | 4 | 1 (global.css) |
| Tickets/Planboard | 4 | 7 | 6 | 3 (TicketsPage, TicketCard, WorksheetList) |
| Kunden/Chat/… | 7 | 5 | 8 | 2 (CustomerDetailPage, CreditDetail) |
| Projekte | 0 | 2 | 1 | 1 (ProjectsPage) |
| Admin/Finanzen/Recht/Puls | 9 | 13 | 10 | 1 (InvoiceCreateModal) |
| Summe | 33 | 34 | 29 | 8 |
Triviale Dateien (nur Klassen oder Text, kein Layout):
- Grundgerüst:
main.jsx,index.html(+Favicon),Footer.jsx(löschen),LetterGlitch.jsx(löschen),AuthContext.jsx(keine Änderung),lib/navLinks.js,ui/index.js,DateTimeInput,DurationSelect,EmptyState,Field,PageHeader,AdminRoute(testgebunden, siehe 7.4). - Tickets:
UrgentToggle,PreviewLinkButton,GiteaLinkButton,CopyableCredential. - Kunden:
KundenansichtButton,CreditsPage,CreditsView,OffersPage,OffersView,LeadsPage(löschen),loadChatComposer.js(nicht anfassen). - Admin:
FinanceSettingsPage,BankCallbackPage,LegalPage,AddressWarning,CustomerInvoices,FormField,Pager,legalUi,Sparkline. - Logik: alle
src/hooks/*, allesrc/lib/*bis auf die vier in 5.P1 genannten.
2. Design-Tokens
2.1 Finaler :root-Block für WOMS
Quelle ist styles.css L5-44 mit festem Akzent Schwarz. Dazu kommen wenige Ergänzungen für Stellen, an denen der Prototyp nur Literale benutzt.
:root {
--bg: #ffffff;
--bg-hover: #f6f8fa;
--bg-subtle: #f8f9fb;
--text: #1a1f36;
--text-2: #5e6678;
--text-3: #8a93a3;
--line: #ebeef1;
--line-strong: #d8dde3;
--accent: #111111;
--accent-hover: #2b2b2b;
--accent-ink: #111111;
--accent-soft: rgba(17, 17, 17, 0.06);
--accent-ring: rgba(17, 17, 17, 0.14);
--grey-bg: #ebeef1; --grey-fg: #414552;
--blue-bg: #e0f2fe; --blue-fg: #075985;
--orange-bg: #fcedb9; --orange-fg: #a82c00;
--violet-bg: #efeafe; --violet-fg: #5b21b6;
--green-bg: #d7f7c2; --green-fg: #006908;
--red-bg: #ffe7f2; --red-fg: #b3093c;
--danger: #c0123c;
--radius: 6px;
--radius-lg: 12px;
--ring: 0 0 0 1px rgba(60, 66, 87, 0.16), 0 1px 1px rgba(0, 0, 0, 0.06);
--shadow-btn: 0 1px 1px rgba(0, 0, 0, 0.12), 0 2px 5px rgba(60, 66, 87, 0.08);
--shadow-pop: 0 0 0 1px rgba(60, 66, 87, 0.06), 0 12px 32px rgba(26, 31, 54, 0.14), 0 2px 6px rgba(26, 31, 54, 0.06);
--sidebar-w: 232px;
--font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
/* Ergänzungen WOMS (im Prototyp nur als Literale) */
--nav-dot: #e5a000; /* .nav-dot */
--danger-ring: rgba(192, 18, 60, 0.18); /* aria-invalid statt Rahmenfarbe */
--overlay: rgba(26, 31, 54, 0.32);
--scrim: rgba(26, 31, 54, 0.24);
--appt-akquise: #fdf4e3; --appt-akquise-hover: #fbeccd; --appt-hover: #eef1f4;
}
html { color-scheme: light; } /* ersetzt `color-scheme: dark` an .datetime-pair (global.css L1077) */
Basis: body 14px/20px, font-family: var(--font), -webkit-font-smoothing: antialiased. Dazu button,input,select,textarea{font:inherit} (fehlt heute, Native-Controls rendern in der Browserschrift), a{color:inherit;text-decoration:none}, :focus-visible{outline:2px solid var(--accent-ring);outline-offset:2px}.
Typo-Skala:
- h1 28/34 700 (mobil 24/30), Login-h1 24/32 700, Section-h2 16/24 600, Dialog-h3 18/26 600.
- Body 14/20,
.small13,.xs12/16,.stat .v22/30 600 (mobil 18). .numsetzt tabular-nums explizit, weilbodytnum 0hat. Alle.fin-amountund Betragsspalten behalten tabular-nums.- Alle heutigen Gewichte 800 (Kpi 28/800,
.fin-fact-big,.fin-low,.sp-tile-value,.page-title24/800) werden auf 600/700 gesetzt. Mit echter Systemschrift wirkt 800 viel schwerer als mit der synthetisch gefetteten Lato. - Großbuchstaben mit Letter-spacing (
.sp-tile-label,.fin-age-label,.ui-table th,.area-row.head,.admin-table th,.admin-eyebrow,.sp-alert-level) entfallen. Kopfzellen sind 12px/500 in--text-2. Einzige Ausnahme im Prototyp ist.day-head .wdim Planboard.
2.2 Mapping alter Tokens und Legacy-Aliase auf neue Werte
Die Spalte "Verwendungen" zählt var(--x) außerhalb von global.css ([geprüft], Python-Zählung). Weil diese 4 Fremd-CSS und 11 JSX-Dateien die alten Namen nutzen, bleiben sie als Aliase bestehen und werden erst in Paket 10 gelöscht.
| Alter Token | Heute (dunkel) | Neu | Verwendungen außerhalb global.css |
|---|---|---|---|
--bg |
#0e131a | #ffffff (Name bleibt) |
0 (global 3) |
--surface-1 |
#151b23 | var(--bg) |
6 |
--surface-2 |
#1b2430 | var(--bg-hover) |
16 (11 CSS + 5 JSX, u.a. AssignProjectPage Auswahlzeile) |
--surface-3 |
#222d3a | var(--bg) (Popover bekommt --shadow-pop) |
0 |
--surface-hover |
#243040 | var(--bg-hover) |
0 |
--surface (nirgends definiert, chat.css L13) |
transparent (Bug) | var(--bg) |
1 |
--border |
weiß 8 % | var(--line) |
20 |
--border-strong |
weiß 14 % | var(--line-strong) |
0 |
--border-accent |
grün 35 % | var(--accent-ring) |
1 (serverpuls.css) |
--text |
#e6edf3 | #1a1f36 | 17 |
--text-muted |
#9aa7b4 | var(--text-2) |
31 |
--text-faint |
#6b7785 | var(--text-3) |
7 |
--accent |
#10b981 | #111111 | 19 |
--accent-2 |
#059669 | var(--accent-hover) |
1 (chat.css) |
--accent-soft |
grün 14 % | rgba(17,17,17,.06) |
6 |
--info |
#3b82f6 | var(--blue-fg) |
1 |
--warn |
#f59e0b | var(--orange-fg) (Text; als Fläche entfällt es mit den Kpi-Streifen) |
3 |
--danger |
#ef4444 | #c0123c (6,19:1 auf Weiß; #ef4444 hatte 3,76:1) | 9 |
--ok |
#10b981 | var(--green-fg) |
5 |
--sp-1..6 |
4/8/12/16/24/32 | unverändert | 38 |
--r-sm / --r-md / --r-lg / --r-pill |
8 / 12 / 16 / 999 | var(--radius) / var(--radius-lg) / var(--radius-lg) / 4px (Badge) |
6 / 4 / 0 / 2 |
--shadow-1..3 |
schwarz 30-40 % | none / var(--shadow-btn) / var(--shadow-pop) |
0 |
--sidebar-w |
248px | 232px | 0 |
--sidebar-w-collapsed |
72px | entfällt (Rail gibt es nicht mehr) | 0 |
Legacy: --dark-bg, --dark-card-bg, --dark-text, --green-primary, --green-secondary, --gray-700/800/900 |
Aliase auf Dunkelwerte | --dark-bg/--dark-card-bg = var(--bg), --dark-text = var(--text), --green-primary = var(--accent), --green-secondary = var(--accent-hover), --gray-700 = var(--line-strong), --gray-800 = var(--text-2), --gray-900 = var(--text) |
0 (nur 13 + 6 + 1 Stellen innerhalb alter Legacy-Blöcke in global.css, die Paket 1 löscht) |
Zwei Hinweise zu den Aliasen:
--text-3(#8a93a3) hat auf Weiß nur 3,1:1. Der Prototyp nutzt es nur für Tertiäres (IDs, "–", leere Zustände). Hinweise in Formularen laufen über.hintmit--text-2. Die Klasse.faintdarf in Bestandscode bleiben, soll aber nur nicht-essenziellen Text tragen.--accentist heute oft Linkfarbe (.fin-link,.legal-files a,.credit-cols a,.area-rows a:hover, Ticketnummer inCustomerDetailPage). Mit Schwarz geht die Link-Erkennbarkeit verloren, und der Hover auf Schwarz wäre unsichtbar. Im Kompat-Block bekommen diese Linkstext-decoration: underline; text-decoration-color: var(--line-strong), wie.linkim Prototyp.
2.3 Wie Altcode auf Weiß richtig aussieht (Neutralisierung)
- Der
:rootwird ersetzt und der Alias-Block nach 2.2 darunter gelegt. Alles, was nurvar()benutzt (laut Inventar etwa 94 % der Redesign-Schicht), kippt automatisch. - Dunkelspezifisches wird nicht über Variablen gerettet, sondern gelöscht oder neu geschrieben (Tabelle 2.4).
- Kompat-Block (Paket 1, wird in Paket 10 gelöscht). Er bildet Altklassen auf den neuen Look ab, damit noch nicht migrierte Dateien lesbar und boxlos sind:
.ui-card,.ui-card.pad,.ui-card-head,.ui-card-body: kein Hintergrund, kein Rand, kein Schatten, kein Padding..ui-card-head h3bekommt den Stil vonh2.section..ui-btn,-primary,-ghost,-danger,-sm: wie.btn,.btn-primary,.btn,.link-danger, ohne Größenvariante. Sie sind nötig, weil 7 Stellen die Klassen von Hand schreiben (TicketProjectsx4,TicketCardL137 u.a.)..form-control,.form-group,.form-labelentsprechen.input/.select/.textarea/.field/.field > label.select.form-controlbekommt das Chevron,textarea.form-controldie Textarea-Höhe. Die!important-Regeln (global.css L308-346, L1046-1047) werden ersatzlos gestrichen..btn/.btn-dark/.btn-green/.btn-teal: die Namen kollidieren mit dem Prototyp-.btn(weiß mit Ring). Im Bestand ist.btn(43x in 13 Dateien) ein graues Gradient-Element mit!important. Es wird komplett durch die Prototyp-.btn-Regel ersetzt,.btn-darkund.btn-tealwerden.btn.btn-primary..btn-greenstirbt mit der Login-Seite..badge-muted|info|ok|warn|dangerentsprechen.badge,.blue,.green,.orange,.red..status-open|working|awaiting|assigned|closedund.priority-critical(nur bisStatusPillneu ist, in P1 sofort ersetzt)..overlay,.overlay-close,.overlay-content:.overlaywird zum Prototyp-Backdrop (--overlay, zentriert)..overlay-contentwird zur weißen Box (radius 12,--shadow-pop, Padding 24, Text--textstatt#fff)..overlay-closewird ein kleines graues "×" oben rechts. Alle 10 handgebauten Modals sehen damit sofort weiß aus. Die finalen Dialoge kommen in den Bereichspaketen (3)..card,.card-header,.card-body,.row,.col,.col-6,.dropdown*(bis P2),.spinner(neutraler grauer Ring),.text-grey(--text-2),.text-red(--red-fg),.text-white,.bg-red(rot getönte Textzeile ohne Kasten),.ticket-projects*.- Utilities
.p-1/.p-2/.mt-2/.mt-3/.mb-2/.text-center/.flex/.gap-2/.gap-3/.items-center/.justify-between/.grow/.wrap/.muted/.faint/.mono/.text-ellipsisbleiben.
- Chat-Mindestfix in P1, damit der Senden-Knopf lesbar bleibt.
chat.cssbekommt--chat-accent: var(--accent),--chat-accent-hover: var(--accent-hover),--chat-accent-soft,--chat-accent-line: rgba(17,17,17,.14),--chat-accent-text: var(--accent),--chat-on-accent: #ffffff(heute#04130d, auf Schwarz wäre die Schrift unsichtbar) und--chat-surface: var(--bg). Der Rand der eigenen Blase wirdvar(--line), und.chat-messagesbekommtbackground: var(--bg), womit der--surface-Fehler weg ist.
2.4 global.css: Blöcke und Aktion
| Zeilen | Inhalt | Aktion |
|---|---|---|
| 1-10 | @font-face Lato |
löschen, plus src/assets/fonts/* |
| 18-71 | :root |
ersetzen (2.1 + 2.2) |
| 73-106 | body-Glows, overflow:hidden, Scrollbar, ::selection |
löschen; body auf Prototyp-Basis. Scrollbar und Selection bleiben im System-Standard |
| 108-126, 128-140, 142-185 | .bg-*/.text-* (17 Klassen, 13 tot), .container, .navbar* |
nur .bg-red, .text-red/white/grey als Kompat behalten, Rest löschen |
| 187-233 | .btn-Familie (Gradienten, grüner Glow) |
ersetzen (Prototyp .btn) |
| 235-305 | .table, .card (tot bzw. 2 Verwendungen) |
löschen / Kompat |
| 307-351 | .form-control mit !important |
ersetzen (Prototyp .input, Alias) |
| 353-382 | .row/.col, Utilities |
behalten (Kompat), tote entfernen |
| 384-494 | .ticket-row, .worksheet-expansion usw. |
löschen (komplett tot) |
| 496-509 | .status-*, .priority-* |
löschen, durch .badge-Töne ersetzen (StatusPill) |
| 511-560 | .dropdown* |
bis P2 behalten, dann löschen |
| 562-605 | .overlay*, .modal-content |
umbauen (2.3), Rest in P10 löschen |
| 607-754 | .slider, .drop-zone, .modern-card, .footer, .spinner |
löschen, außer .spinner (neutral) |
| 756-787, 1035-1039 | .ticket-tab* |
durch .tabs/.tab ersetzen |
| 789-794 | Responsive Legacy | löschen |
| 796-864 | App-Shell, .side* |
ersetzen (Prototyp .shell/.sidebar/.nav-item/...) |
| 866-1033 | Page-Header, .ui-card, .ui-btn, .badge-*, .kpi*, .tcard*, .list*, .ui-table, .seg, .empty, .field |
ersetzen bzw. in Bereichspaketen löschen (.tcard* in P2, .kpi* in P7) |
| 1050-1120 | Utilities, .automatic-assignment-note, .datetime-pair, .autocomplete* |
behalten, neu einfärben; color-scheme:dark (L1077) löschen |
| 1122-1153 | Rechtliches | in P8 umbauen |
| 1155-1476 | Finanzen (26 Dunkel-Literale: #fecaca L1377, #fbbf24 L1379/1383, rgba(6,9,14,.98) L1372, #f87171 L1400, #b45309 L1144, rgba-Tönungen) |
P1 nur Literale entfernen, Umbau in P7 |
3. Bausteine
Zeile = Baustein. "Verbraucher" nennt Dateien und, wo vom Inventar gezählt, die Anzahl.
| Baustein | Heute | Neu / Änderung | Verbraucher |
|---|---|---|---|
| App-Shell | .app-shell 100vh + overflow:hidden, innerer Scroller .app-scroll, Footer darunter, Sidebar 248px sticky im Flex |
.shell > <aside class="sidebar"> (fixed 232px) + .main (margin-left: var(--sidebar-w)) > .mobile-bar + .page. Dokument-Scroll (Body scrollt). Footer entfällt. menuOpen-State in AppContent, Schließen bei Routenwechsel (useLocation), Esc und Klick auf .scrim. ScrollToTop-Effekt bei Routenwechsel (fehlt heute komplett, rg scrollTo = 0). Ladezustand: .loading-Klasse statt Inline-Style, Text "Wird geladen …" |
App.jsx, Navbar.jsx, Footer.jsx (löschen), AdminRoute.jsx |
| Sidebar | Icons (17 Tabler), Gruppen, Collapse-Rail 72px, Avatar-Verlauf, .side-count grün |
Textliste (.nav-item, .sub, .nav-count, .hot für Chat-Ungelesene, .paused mit "pausiert", .nav-dot), .sidebar-foot mit .who (Name, Ellipsis) + "Abmelden". Collapse und lib/sidebar.js entfallen, Drawer unter 860px. Serverpuls-Punkt: .nav-dot (#e5a000) für Stufe medium, zusätzlich .nav-dot.critical (--danger), da das Live-System zwei Stufen hat. data-testid="sp-nav-dot", title, aria-disabled, 5-min-Polling und useSidebarCounts bleiben |
Navbar.jsx, lib/navLinks.js (nur optional hot:true am Chat-Kind), serverpuls.css L70-74 |
| Logo | grüne Kachel mit "W" | neue components/Logo.jsx mit dem SVG aus app.js (Konstante LOGO, Z. 115); 24px in Sidebar/Mobile-Bar, 40px im Login; public/favicon.svg aus quelle/favicon.svg plus <link rel="icon">, noindex, color-scheme in index.html |
Navbar, LoginPage, index.html |
Seitenkopf (PageHeader) |
.page-header/.page-title/.page-subtitle/.page-actions, 24/800 |
Klassen .page-head, h1 28/700, .sub, .actions; neue Props eyebrow und crumb; mobil gestapelt unter 860px |
12 Verwendungen in 9 Dateien; ProjectsPage hat einen eigenen zentrierten <h2>-Kopf und muss umgestellt werden |
| Breadcrumb | gibt es nicht ("Zurück"/"Alle Kunden" mit Pfeil-Icons) | .crumb (13px, --text-2, Slash in --text-3) |
Ticket-Seite, CustomerDetailPage, FinanceSettingsPage, AssignProjectPage |
| Toolbar/Suche | .toolbar (2x), .search-input (4x), Filterkarte mit "Anwenden" |
.toolbar + .input.search (300px, mobil 100%) + .select-quiet + .check; Suche live (Debounce oder Enter), "Anwenden" entfällt |
TicketsPage, CustomersPage, ProjectsPage, AppsTable, CustomerChatPage, ProviderContractsTab |
| Tabs | Tabs.jsx, .ticket-tabs/.ticket-tab (gefüllt, grüner Verlauf), kein Zähler |
.tabs (Unterstrich 2px --accent, overflow-x:auto) + .tab + <span class="count">; neue Prop t.count. Auch als Filter-Tabs: Tickets, Kunden, Projekte, Gutschriften, Angebote |
3 Aufrufer heute (TicketCard, CustomerDetailPage, LegalPage); neu mehr. Achtung: customerTabs.js testet Object.keys(entry) = [id, label], Zähler also nicht in diese Funktion, sondern beim Rendern anfügen |
| Segmented Control | .seg (dunkle Schiene, 4 Aufrufer), .sp-pill |
.seg (graue Schiene, aktiver Chip weiß mit Ring), aria-pressed |
CustomersPage (wird Tabs), InvoiceTable, CreditList, OfferList, Serverpuls-Pills, neu: Einsatz Fernwartung/Vor Ort (CreateWorksheetModal), Art-Wahl AssignProjectPage |
| Tabelle | .ui-table (4x), div-Raster .area-row (4 Ansichten), .tcard-Liste, .list-row (6x), .sp-table, .admin-table, .legal-contracts |
.table (Haarlinien, Kopf 12px/500), tr.row (klickbar, Hover --bg-subtle), .cell-title/.cell-sub/.id, .right, .num, .table-foot, .hide-sm (<860) und .hide-md (<1100). Container-Query-Stapelung der Finanztabellen bleibt (Wrapper statt Card trägt container-type) |
Tickets, Kunden, Projekte, Gutschriften, Angebote, E-Mail-Speicher, Funktionen, Finanzen (3), Rechtliches, Admin, Serverpuls |
| Listenzeile | .list/.list-row mit Hover-Fläche |
Haarlinie, a.list-row:hover .cell-title { color: var(--accent-ink) }, erste Zeile border-top |
ProjectContextPanel, TicketProjects, AssignProjectPage, ServicesPage, CustomerViewLog, CustomerChatPage |
| Facts | Ticketkopf als Meta-Zeile |
.facts/.fact (.k 13px, .v 500), mit .select-quiet für "Zuständig" |
Ticket-Seite (neu) |
| Stats | Kpi (Box mit Farbstreifen, 28/800), WorksheetSummary-Streifen, .fin-stats, .fin-age |
.stats/.stat (.k 13px, .v 22/600, tabular), .v.warn orange, neu .v.neg rot (negatives Geld soll rot, nicht orange, sein); Kpi wird Stat (Prop tone statt accent), Icon-Prop und Streifen entfallen |
LiquidityHead (3), WorksheetSummary, AboCard, Finance-Altersstreifen, TaxCard |
| kv / two-col / prose | .area-kv, .credit-cols, .fin-cols, .fin-bank-facts |
.kv (180px/1fr, mobil 1 Spalte), .two-col (1fr 1fr, Gap 56, mobil 1 Spalte), .prose (pre-wrap, max 680px) |
Kundenübersicht, CreditDetail, AboStatusCard, BankAccessCard, TaxCard, Beschreibung des Tickets (die heutige description bekommt pre-wrap) |
| Badge | Badge.jsx Pille (999px), Tone `muted |
info | ok |
| Status-Töne | statusClass() → .status-* (Abschnitt unten) |
StatusPill rendert badge {tone}; UrgentPill = badge red "Dringend" |
TicketCard, CustomerDetailPage, ProjectContextPanel |
| Button | Button.jsx (ui-btn, 4 Varianten, size="sm"), 114 Verwendungen in 35 Dateien (43 ghost, 43 sm, 36 primary, 5 danger) |
default und ghost → .btn; primary → .btn.btn-primary; neue Variante quiet → .btn.btn-quiet; danger → .link-danger (Textlink, im Prototyp gibt es keinen roten Knopf); size entfällt (alle 32px, Button ignoriert size zunächst); Zeilenaktionen in Tabellen werden .link |
alle; TicketProjects u.a. mit handgeschriebenem ui-btn |
| Card | .ui-card Box, 57 Verwendungen in 25 Dateien, bodyStyle, pad |
<section className={className}> + <h2 class="section">{title}{actions}</h2> + Kinder; pad/bodyStyle/style werden No-ops; className bleibt (Finance-Wrapper), Container-Queries brauchen das Element |
alle |
| Dialog | 10 handgebaute Overlays (siehe 3.1) + 2 fin-overlay; schwarz, weißes 48px-"×" als <span>, kein role, kein Esc |
neue ui/Dialog.jsx: .overlay > `form |
div.dialog(max 520, Variante breit ~760 für Rechnung) mit.dialog-head(h3 +×), .dialog-body(Flex-Spalte, Gap 16,overflow-y:auto), .dialog-foot; role="dialog" aria-modal aria-labelledby, Esc, Klick aufs Backdrop, mobil Bottom-Sheet. Bindet useDialogFocusein (OverlayonKeyDown, innerer Dialog mit ref+tabIndex=-1`) |
| Form-Felder | .form-group/.form-label/.form-control (49/49/118), Field (32x in 4 Dateien), .datetime-pair, FormField |
.field > label (500) + .input/.select/.textarea (32px, Ring-Schatten, Fokus-Ring 2-stufig, kein Rand) + .hint (13px --text-2) + .field-row/.field-row-3; Fehler: box-shadow: 0 0 0 1px var(--danger), 0 0 0 4px var(--danger-ring); .check für Checkbox/Radio |
alle Formulare |
| Urgent-Schalter | .urgent-toggle gestrichelte Pille, .on |
gleiche Klassennamen, aber Markup: <span class="sw"/> vor dem Label (28x16 Schalter, .on rot); aria-pressed bleibt; Logik unverändert |
UrgentToggle.jsx (verbraucht nur von TicketCard bzw. neuer Ticket-Seite) |
| Empty-State | EmptyState mit icon-Prop (keiner übergibt es), 3 Inline-Styles |
<div class="empty"><strong>Titel</strong>Hinweis</div> + Aktion; icon-Prop und .empty-icon entfernen |
16 Verwendungen in 13 Dateien |
| Autocomplete | .autocomplete-list auf --surface-3 mit Schatten |
weißes Popover (--bg, --shadow-pop, Radius 8), aktiver/Hover-Eintrag --bg-hover, form-control→input. Achtung Clipping im scrollenden .dialog-body |
CreateTicketModal, InvoiceCreateModal, OfferForm, CustomerChatPage |
| Meldungen | .bg-red.text-white (9x in 6 Dateien), .legal-error, .fin-dialog-error, .area-error/-message/-summary, .sp-error, .admin-notice |
Kein Kasten. Neue Klassen .msg, .msg.err (13px --red-fg), .msg.ok (--green-fg), .msg.warn (--orange-fg); transiente Meldungen optional als .toast (dunkle Pille, Prototyp) |
siehe Spalte links; im Prototyp gibt es keine Banner, deshalb Text-only |
| Lader | .spinner (40x in 25 Dateien, grüner Ring) + doppeltes FaSpinner |
Text "Wird geladen …"/"Speichert …" über .loading (color: var(--text-2)); .spinner bleibt nur als neutraler Kompat-Ring bis P10 |
alle |
| Login | Glas-Karte über LetterGlitch-Canvas, 19 Inline-Styles |
.login > .login-box (360px, Logo 40px, h1, .sub, .field, .btn-primary.btn-block), Code-Schritt mit .code-input, .login-links, .login-foot (siehe 5.P1) |
LoginPage.jsx |
| Tab-Inhalt Ticket | .ticket-tab-content |
entfällt (Reiterinhalt direkt unter .tabs) |
TicketCard |
3.1 Dialog-Verbraucher (10 handgebaute Overlays + Sonderfälle)
CreateTicketModal, CreateWorksheetModal, WorksheetTimesModal, OwnSupportModal, TicketProjects (Art-Frage "Was ist das?"), ProjectsPage (2x: Neues Projekt, Projekt bearbeiten), finance/AssignDialog, finance/InvoiceCreateModal, legal/ContractForm, legal/VersionForm.
Weitere Kandidaten:
CreateCustomerForm(ausCustomersPage.jsxexportiert, inAssignProjectPage.jsxL12/L277 inline benutzt).OfferForm(Inline-Karte).AnalyseRow(Zustimmungsablauf). Es gibt keinen Teil des Altcodes, der.overlayohne die drei Klassenoverlay,overlay-close,overlay-contentbenutzt. Deshalb funktioniert die CSS-Zwischenlösung (2.3).
3.2 Status-Töne (lib/ticketStatus.js, ungetestet, neu mit Test)
| DB-Wert (gespeichert, englisch bleibt) | Gruppe | heute | neu (Prototyp) |
|---|---|---|---|
Open |
open | .status-open (grün) |
grau (kein Ton) |
Occupied, Assigned, Added Info, In Kontakt |
working | blau | blau |
Awaiting, Halted |
waiting | amber | orange |
In Test |
review | .status-assigned (teal) |
violett |
Closed, Zugesagt |
done | grau (.status-closed) |
grün |
Cancelled, Aborted, Abgesagt |
cancelled | grau (.status-closed) |
plain (nur Text, --text-3) |
doneundcancelledteilen sich heutestatus-closedund müssen getrennt werden.- Implementiert wird eine neue Funktion
statusTone(status)inlib/ticketStatus.js. Sie liefert'' | 'blue' | 'orange' | 'violet' | 'green' | 'plain',statusClass()entfällt. Neuer Testtests/unit/ticketStatus.test.mjs. - Das ist ein sichtbarer Bedeutungswechsel (Offen war grün, Erledigt grau). Er ist im Prototyp so freigegeben, wird aber in Abschnitt 8 erneut genannt.
- Weitere Töne-Vokabulare, die in Komponenten gemappt werden und deren lib-Tests dadurch unberührt bleiben:
previewStatus().color(green|yellow|red|grey), Mapping inPreviewBlock: yellow→orange.analyseStatus().color(green|red|grey).worksheetTone():git-open→badge orange"Zeit offen",git-done→badge green"Zeit eingetragen",comment→Zeile.ws.comment,work→kein Badge.deadlineTone()(legal).formatDelta().dir(Serverpuls).ageLabel().stale.
4. Icon-Regel
Regel: Icons nur dort, wo sie handeln, und auch dann als Zeichen statt Bibliothek. Konsequenz: keine einzige Icon-Komponente bleibt, und beide Bibliotheken fliegen raus.
Heute: 71 verschiedene Icons in 187 Verwendungen in 51 Dateien (17 @tabler/icons-react in Navbar.jsx und ProjectContextPanel.jsx, 53 react-icons (fa6/fa), 1 react-icons/si = SiGitea). Mit LeadsPage (25 Tags) entfallen davon nach Paket 0 bereits 25.
A. Entfernen (dekorativ, der Text daneben genügt)
| Icon(s) | Stellen |
|---|---|
14 Menü-Icons (IconTicket, IconCalendar, IconFolders, IconLayoutGrid, IconUsers, IconMessages, IconReceiptRefund, IconDiscount, IconMailCog, IconTargetArrow, IconReportMoney, IconScale, IconActivityHeartbeat, IconSettings) |
Navbar.jsx |
IconLayoutSidebarLeftCollapse/Expand |
Navbar.jsx (Funktion entfällt) |
IconFolders |
ProjectContextPanel.jsx |
FaPlus (15) |
TicketsPage x2, TicketCard (Arbeitsblatt hinzufügen), TicketProjects, FixedCostsCard, InvoiceCreateModal, InvoiceTable, OffersView, AdminPage, AssignProjectPage x2, CustomersPage x2, ProjectsPage (dort icon-only, siehe B) |
FaFloppyDisk (4), FaGear, FaSliders, FaAngleDown, FaCodeBranch, FaCircleInfo, FaFolder x2, FaLock, FaClone, FaKey, FaArrowUp |
SettingsCard, AdminPage, CustomerDetailPage, FinancePage, TicketsPage, AssignedProjectCard, ProjectsPage |
FaClock x4, FaUser x2, FaExchangeAlt, FaComment x2, FaHandsHelping, FaPen, FaCheck, FaChevronDown/Up (pro Arbeitsblatt) |
WorksheetList.jsx (Aufklapp-Mechanik entfällt) |
FaPlay, FaStop |
TicketCard.jsx (werden zu Text "Beginn"/"Fällig" in den Facts) |
FaTriangleExclamation (7) |
AboCard x2, ForecastTable, StatusLine, TransactionTable (die Wörter "gefährdet", "Hinweis" tragen die Bedeutung) |
FaArrowUpRightFromSquare x2 |
CreditDetail, KundenansichtButton |
FaBoxArchive (3), FaBoxOpen (2), FaTrash |
CustomerDetailPage, ProjectsPage (Archiv-Knöpfe als Text), AdminPage (siehe B) |
FaStar x2 ("Wichtiger Kunde") |
CustomersPage, CustomerDetailPage (wird .badge "Wichtig") |
FaArrowLeft x5, FaArrowRight x2 |
AssignProjectPage, CustomerDetailPage x2, FinanceSettingsPage, CustomerChatPage ("Zurück", "Weiter", Breadcrumb tragen den Sinn; mobile Chat-Rückkehr als Text "← Alle Unterhaltungen") |
FaRotateRight x4, FaRotate x2 |
"Erneut versuchen", "Jetzt abrufen", "Aktualisieren" bleiben als reiner Text |
Badge.dot (5 Aufrufer), EmptyState.icon, Kpi.icon |
CustomersPage x2, CustomerDetailPage x3; Slots ohne Aufrufer |
B. Durch Text ersetzen (heute icon-only oder reiner Lader)
| Icon | Wo | Ersatz |
|---|---|---|
FaSpinner (37) |
25 Dateien (Lader und Busy-Zustand in Knöpfen) | "Wird geladen …", "Speichert …", "Sendet …" |
FaEdit, FaTrash |
AdminPage Zeilenaktionen |
.link "Bearbeiten", .link-danger "Löschen" |
FaTrashCan |
InvoiceCreateModal Positionszeile |
.link "Entfernen" |
FaEye |
PreviewLinkButton (holt Einmal-Link, onClick bleibt) |
.link "Vorschau öffnen" |
SiGitea |
GiteaLinkButton |
.link "In Gitea öffnen" |
FaLinkSlash |
AssignedProjectCard |
.link-danger "Lösen" |
FaUserPlus |
EditorDropdown (wird <select>) |
Option "Nicht zugewiesen" |
FaChevronLeft/Right |
PlanboardPage |
Zeichen ← und → neben "Heute" |
FaGlobe, FaArrowsRotate x2, FaPen, FaPlus (icon-only) |
ProjectsPage (hosten, abgleichen, neu deployen, bearbeiten, neu) |
Textknöpfe/.link |
FaEye/FaEyeSlash/FaCopy/FaCheck |
CopyableCredential (handelnd, daher Entscheidung offen; Standard Text) |
"Anzeigen/Verbergen", "Kopieren/Kopiert" |
FaCopy/FaCheck |
CustomerFunktionen (Site-Key) |
"Kopieren/Kopiert" |
Text-Glyphen ✓ Abo, + Abo, ↳ Unterstützung, → (Status), ✓ (Berechnung), ✓ ! i in Badges, ▲/▼ (Deltas), ▴/▾ (Preview-Toggle), Emoji 🔴🟠🟢 |
WorksheetList, CreateWorksheetModal, WorksheetTimesModal, CreditDetail, AppsTable/ServerpulsPage, AssignedProjectCard |
"Abo"/"+ Abo", "Unterstützung", "Status danach: X", Text-Badge, Vorzeichen der Zahl, "Vorschau anzeigen/ausblenden", Badge "Alles ok/Warnungen/Critical" |
C. Behalten, aber als Zeichen (keine Komponente)
| Zweck | Umsetzung |
|---|---|
Dialog schließen (7 FaTimes + 3 FaXmark in Dialogen/Bannern) |
<button class="dialog-close" aria-label="Schließen">×</button> (muss ein echter Button sein für Tastatur und useDialogFocus) |
| Sortierpfeile, Auf-/Zuklappen | Zeichen ↑ ↓ (AppsTable), ▾ ▴ (LiquidityHead) |
| Chat-Widget (Anhang, Kamera, Mikrofon, Senden, Play, Download usw.) | Inline-SVGs aus dem fremden Portal-Code (chat-composer.js, 15 Stück), nicht änderbar; Anhang-Menü-Icons optional per CSS ausblenden |
D. package.json danach
Beide Pakete können entfernt werden: @tabler/icons-react (^3.36.0) und react-icons (^4.12.0). Alle 50 Importdateien (nach LeadsPage-Löschung) verlieren ihre Importe. Zum Schluss rg "react-icons|@tabler" src = 0 Treffer, dann npm uninstall und die Lock-Datei prüfen.
uplotbleibt (Serverpuls).- Beim lokalen
npm uninstallentfernt npmnode_modules/react-icons, die 147 getrackten Dateien erscheinen als Löschung (siehe 7.6). - Der Tabler-Import sitzt nur in 2 Dateien. Das Löschen aller
Fa*-Imports ist der Aufwand (162 Icon-Tags in 50 Dateien).
5. Arbeitspakete
Zusammenfassung
| Paket | Inhalt | Aufwand (PT) | Der Eigentümer sieht danach |
|---|---|---|---|
| P0 | Vorbereitung, Totcode weg, Test-Harness | 0,5 | nichts |
| P1 | Tokens, Shell, Primitives, Login, Kompat, Mini-Fixes | 3,5-4 | die ganze App ist weiß, Sidebar nur Text, Karten ohne Box, Login neu |
| P2 | Tickets: Liste als Tabelle, eigene Ticket-Seite, Arbeitsblätter, Dialoge | 5-5,5 | Ticket-Hauptarbeitsplatz im neuen Design |
| P3 | Planboard | 0,5 | Wochenraster ohne Karten |
| P4 | Kundenliste und Kundenseite (9 Reiter), Funktionen/Abo/Protokoll | 2,5-3 | Kundenbereich im neuen Design |
| P5 | Projekte, Zuordnen-Assistent, Services, Projekt-Bausteine | 3,5-4 | Projektbereich im neuen Design |
| P6 | Chat, Gutschriften, Abo-Angebote, E-Mail-Speicher | 4 | Rest von Kunden-Unterseiten |
| P7 | Finanzen | 4-4,5 | Finanzseiten ohne Karten |
| P8 | Rechtliches und Admin | 2 | letzte Admin-Flächen |
| P9 | Serverpuls | 1-1,5 | Dashboard hell, Charts hell |
| P10 | Aufräumen (Icon-Pakete, Kompat, tote CSS) | 1 | Bundle kleiner, nichts Neues |
| Summe | 27,5-30,5 |
PT = Entwicklertage inklusive Selbsttest und Screenshot-Abgleich.
Ablauf für jedes Paket:
- Arbeit im Worktree
dev-design, Branchfeature/design-weiss. Nach jedem Paket gibt es einen eigenen Commit-Stapel mit Tag (zum Beispieldesign-p2). - Definition of Done:
npm testgrün (312 oder mehr),npm run buildgrün (Bundle-Größe notiert),- Vorher-/Nachher-Screens (1440px und 390px),
- keine neuen Inline-Styles außer Laufzeitwerten,
- keine Icon-Importe im Bereich,
- deutsche Texte unverändert (Diff-Stichprobe),
git addnur explizite Pfade.
- Live gebracht wird vom Eigentümer mit
git -C <worktree> push origin feature/design-weiss:test(Fast-Forward). Rückgängig mit Revert-Commit, ein Force-Push ist nicht nötig. - P7, P8 und P9 sind voneinander unabhängig und können parallel in Worktrees entstehen. Deployt wird nacheinander.
P0: Vorbereitung und Totcode (0,5 PT, unsichtbar)
- Dateien:
- Worktree
dev-designvontest. tp2(1 Commit) vorher intestbringen, siehe 7.5.- Löschen:
src/pages/LeadsPage.jsx(614 Zeilen, 53 Inline-Styles, 25 Icon-Tags),src/hooks/useLeads.js(282 Zeilen). - Bleiben: Route
/leadsmit Redirect, pausierter Menüeintrag,lib/leads.js.
- Worktree
- Test-Harness nach dem Rezept in
woms-ticketsystem.mdneu bauen (esbuild-Bundle, Playwright mit gemocktem Appwrite, untersystemd-runmit Speichergrenze) und "Vorher"-Screens für etwa 15 Schlüsselseiten erzeugen. - Test-Auswirkung: keine (kein Test importiert
LeadsPageoderuseLeads). - Sichtbar: nichts.
P1: Fundament, Tokens, Shell, Primitives, Login (3,5-4 PT)
Danach ist die ganze App weiß und boxlos, auch wenn die Seiten innen noch alte Layouts haben.
- Dateien und Änderungen:
src/styles/global.css:- Tokens (2.1), Alias- und Kompat-Block (2.2 und 2.3).
- Löschen: Lato, Glows, Scrollbar und Selection, tote Regeln (66 Namen, rund 88 Regeln, 285 Zeilen), Navbar, Ticket-Row, Slider, Dropzone, Modern-Card, Footer.
- Neu anlegen: Basis,
.shell,.sidebar,.page,.mobile-bar,.scrim,.page-head,.crumb,h2.section,.btn*,.badge*,.tabs,.input/.select/.textarea/.check,.toolbar,.table*,.empty,.facts/.stats/.kv/.two-col/.prose,.urgent-toggle,.list-row,.seg,.overlay/.dialog*,.field,.toast,.msg*,.loading,.login*, Autocomplete-Popover,.datetime-pair. - Nicht übernehmen:
.proto*,html[data-accent],.nav-gap..ws*kommt mit P2,.week/.day/.apptmit P3.
index.html, neupublic/favicon.svg.App.jsx: Shell-Markup,menuOpen,ScrollToTop,.loading-Block. Routen und Auth unverändert.Navbar.jsxplus neueLogo.jsx, Löschen vonFooter.jsx,LetterGlitch.jsx,lib/sidebar.jsundtests/unit/sidebar.test.mjs.serverpuls.cssL70-74 (.side-dot*) wandert in die Sidebar-Regeln.AdminRoute.jsx: nur den Inline-Style gegen.loadingtauschen, das Markup<p>Wird geladen …</p>bleibt wörtlich (Test, siehe 7.4).LoginPage.jsx:- Prototyp-Markup (
.login,.login-box, Logo 40px,h1"Anmelden",.sub,.field,.input,.btn.btn-primary.btn-block"Weiter"). - Code-Schritt (
h1"Code eingeben",.code-input,.login-links: "Code erneut senden" · "Abbrechen") plus Unterschritt "Code senden" (fehlt im Prototyp, bleibt) und.login-foot. - Labels deutsch ("E-Mail", "Passwort"),
autocomplete-Attribute ergänzen. - Logik (
login,verifyCode,sendCode,cancelMfa,NO_STAFF_ACCESS,safeReturnPath) bleibt exakt.
- Prototyp-Markup (
components/ui/*:Badge,Button,Card(zusection),Tabs,PageHeader,EmptyState,Field,Kpi(zuStat),StatusPill,AutocompleteInput,DateTimeInput,DurationSelect(Klassenwechsel), neuDialog.jsx,index.jsergänzen.lib/ticketStatus.js:statusTone()plus neuer Test.- Pflicht-Mini-Fixes, damit nichts unlesbar wird (je Minuten bis eine Stunde):
chat.cssVariablen (2.3 Punkt 4).AssignedProjectCard.jsxL27 (Label#cbd5e0) und L82 (Hintergrundrgba(30,41,59,.5)mit blauem Rand) auf--text-2und--bg-subtle.Chart.jsxL28-36 (Achsen#9aa7b4→#5e6678, Gridrgba(255,255,255,.06)→#ebeef1, Ticks →#d8dde3, Serien-Default#10b981→#111111).- Hell-auf-Dunkel-Textfarben in
admin-areas.css(#f87171,#fbbf24),serverpuls.css(#fca5a5,#fbbf24),AdminPage.css(#6ee7b7,#fca5a5,#34d399) undglobal.cssFinance/Legal (#fecaca,#fbbf24,#f87171) auf--red-fg/--orange-fg/--green-fg. PreviewBlock-Log (rgba(0,0,0,.35)) bleibt unschön, aber lesbar bis P5.
- Test-Auswirkung:
sidebar.test.mjsentfällt (312 → 309, dann +neuticketStatus.test.mjs).adminRoute.test.mjsbleibt grün, solange das<p>unverändert ist. Alle anderen Tests sind lib-Tests. - Der Eigentümer sieht: weiße App, Sidebar nur als Textliste mit Zählern (ohne Einklappen, auf dem Handy ein Menü-Knopf), neue Statusfarben, schwarze Primärknöpfe, weiße Dialoge (zunächst als Zwischenlösung), neues Login. Listen und Karten innen sehen noch nach altem Layout aus (Ticketkarten, Hover-Dropdowns, Pillen-Raster), sind aber weiß und lesbar.
- QA-Pflichtliste (390px und 1440px): Login, Ticket-Liste mit aufgeklappter Karte, Arbeitsblätter, Kundenseite (alle 9 Reiter), Chat (Blase, Composer, Senden-Knopf), Projekte (Karten, 2 Dialoge inklusive
PreviewBlock), Zuordnen, Services, Planboard, Finanzen (alle Blöcke, 2 Dialoge), Rechtliches, Serverpuls (Charts), Admin, Gutschriften/Angebote/Speicher.
P2: Tickets: Liste, Ticket-Seite, Arbeitsblätter, Dialoge (5-5,5 PT, davon Route 2-2,5)
Empfehlung "eigene Seite" (siehe Abschnitt 6). Bei Variante "aufklappen" entfallen etwa 1,2 PT.
- Dateien:
pages/TicketsPage.jsx: Tabs (Aktiv/Offen/In Arbeit/Wartet auf Kunde/Prüfung/Erledigt/Alle) plus.toolbar(Suche live, Art-Select, "Nur dringende") plus.table(Ticket | Status | Zuständig | Termin | Zeit | Erstellt) plus.table-foot. Die Liste bleibt beim Nachladen sichtbar (heute wird sie bei jedem Refetch durch einen Spinner ersetzt und entlädt alle Karten). Filterzustand in die URL.- Neu
pages/TicketPage.jsx(Route/tickets/:woid/:tab?) plus HookuseWorkorder(woid)(nutztQuery.equal('woid',[String(woid)]), wie die heutige Direktsuche). Loading, Fehler und "Ticket nicht gefunden". Header mit.crumb,.eyebrow("woid · Art"), h1 = Betreff,.sub= Kunde als Link,.actions=UrgentToggle+ Status-<select>+ primärer "Arbeitsblatt hinzufügen". Dann.facts(Zuständig-Select, Termin mit Uhrzeit, Fällig, Einsatz, Angefragt von, Erstellt),.tabsDetails | Arbeitsblätter(n). TicketCard.jsxentfällt, ihre Zustände und Hooks wandern inTicketPage(useWorksheets(ticket.woid)immer,useCustomerAbonur wenn Reiter oder Dialog es braucht).handleCreateWorksheet(Status-Rückschreiben und Akquise-Automatik fürcustomerStatusbei Zugesagt/Abgesagt) wird vorher inlib/Hook extrahiert und mit einem Test abgesichert, weil es heute ungetestete Geschäftslogik ist.WorksheetList.jsx: komplett neu als.ws-Zeilen (Raster 148px | 1fr | auto, Haarlinie) ohne Gradienten, ohne Aufklappen, ohne Staffel-Animation, ohne JS-Hover und ohne die 35 toten Bootstrap-Klassen.WorksheetSummarywird.stats(Arbeitszeit gesamt, davon Abo-Stunden (neu berechnet auscountsToPlan), Zeiten offen mit.warn). Reihenfolge bleibt "neueste zuerst".formatTimeist doppelt vorhanden und wird vereinheitlicht ("2:15 h").- Dialoge auf
<Dialog>:CreateTicketModal,CreateWorksheetModal,WorksheetTimesModal,OwnSupportModal,SupportersEditor(Zeile aus Select + Input + "= 30 min" +.link"Entfernen"; im 520-px-Dialog zweizeilig). StatusDropdownundEditorDropdownwerden native<select class="select">. Wichtig: der aktuelle Wert muss immer als Option vorhanden sein, auch wenn er nicht inTICKET_STATUSESsteht (AkquiseIn Kontakt/Zugesagt/Abgesagt, alteAssigned/Added Info/Halted/Aborted), sonst zeigt das Select stillschweigend "Offen".UrgentToggle.jsx: Schalter-Markup.ProjectContextPanel.jsxwird der Abschnitt "Weitere Tickets von " (immer sichtbar, "Keine weiteren Tickets.").App.jsx: neue Route; neuelib/ticketRoutes.jsmitticketPath(woid, tab).- Alle 7 Deep-Link-Stellen auf
ticketPath():PlanboardPageL80,AssignProjectPageL156 und L405,ServicesPageL72,ProjectContextPanelL70,CustomerFunktionenL104 (LeadsPageentfällt in P0).TicketsPagebehält?woid=Nals<Navigate replace to="/tickets/N">. - Löschen:
.tcard*,.urgent-toggle-Altregeln,.dropdown*,.worksheet-summary,.chevron,.ticket-tab-content. - Der Projekt-Block (
TicketProjects,AssignedProjectCard,PreviewBlock, Link-Buttons,AnalyseRow) bleibt hier unverändert (nur P1-Mini-Fixes) und kommt in P5.
- Zusatzaufwand (optional, +0,5 PT, nur Leseabfragen): Tab-Zähler (7
Query.limit(1)-Abfragen, Werttotal), Spalte "Zeit" (eine gebündelte Arbeitsblatt-Abfrage pro Seite mitworksheetMinutes), Spalte "Zuständig" aus der Mitarbeiterliste stattassignedName(das nach Neuzuweisung veraltet). - Test-Auswirkung: keine bestehenden Tests betroffen. Neu: Tests für
statusTone(aus P1), die extrahierte Worksheet-Logik und die Filter-zu-URL-Helfer. - Sichtbar: Ticketliste als schlichte Tabelle mit Tabs, Klick öffnet eine eigene Seite mit Facts, Reitern und flachen Arbeitsblatt-Zeilen. Alle Modals sind echte weiße Dialoge mit Esc und Klick außerhalb.
P3: Planboard (0,5 PT)
- Dateien:
pages/PlanboardPage.jsx. - Änderungen:
.weekals CSS-Klasse (7 Spalten, Haarlinien, Wochenende--bg-subtle, heute = schwarze Datumspille,.appt-Kacheln als<Link>, Akquise--appt-akquise, unter 860px einspaltig).- Kopf mit Untertitel "Kalenderwoche N · 5. Oktober bis 11. Oktober · n Termine" (
getISOWeek,de-Locale), Aktionen ← / Heute / →. - Die 12 Inline-Styles verschwinden.
- Zu entscheiden: stornierte Tickets ausblenden (Prototyp) oder wie heute zeigen.
- Test-Auswirkung: keine. Planboard-Logik (
groupAppointmentsByDayusw.) bleibt. - Sichtbar: Wochenansicht ohne Karten.
P4: Kundenliste und Kundenseite (2,5-3 PT)
- Dateien:
pages/CustomersPage.jsx: Tabs (Feste Kunden/Leads/Archiviert/Alle mit Zählern) + Suche +.table(Unternehmen mit.cell-subAnsprechpartner | Kundennummer | Standort | E-Mail | Telefon | Offene Tickets), "Neuer Kunde" primär, "Neuer Lead" und die Admin-Beschränkung bleiben. Avatar-Kachel und Stern entfallen ("Wichtig" als Badge).CreateCustomerFormwird<Dialog>. Kopplung:AssignProjectPage.jsxL12/L277 nutzt die Komponente inline und muss an dieser einen Stelle in P4 mitgezogen werden.pages/CustomerDetailPage.jsx: Crumb ("Kunden / Name"), Eyebrow (Kundennummer · Stufe als Klartext), h1 nur der Name, 9 Reiter als.tabsmit horizontalem Scroll. Übersicht als.two-colmith2.section+dl.kv(Stammdaten, Rechnungsdaten, Portal-Zugang). Tickets-Reiter als.tablemittr.row-Links, Projekte-Reiter bis P5 mit P1-Mini-Fixes.CustomerFunktionen.jsx(Raster-Pseudo-Tabelle →.table, Status als Badge-Ton, Aktionen als.link; hier landet dertp2-Commit, siehe 7.5),AboStatusCard.jsx,CustomerViewLog.jsx,KundenansichtButton.jsx(Standard-Variante.btnstatt primary, damit pro Kopf nur 1 schwarzer Knopf bleibt).admin-areas.css: Grundstil neu (Haarlinien,.area-kv≙.kv,.area-muted/-danger/-warn→--text-2/--red-fg/--orange-fg, Tint-Kästen raus), die Raster der Gutschriften/Angebote/Speicher folgen in P6.lib/customerTabs.jsbleibt unverändert.
- Test-Auswirkung: keine (
customerTabs.test.mjspinntid/label). - Sichtbar: Kundenbereich ohne Karten, mit Tabelle und
kv-Listen. - Entscheidung: Stammdaten bleiben wie heute immer editierbar (im
kv-Layout), oder der Prototyp-Umschalter "Bearbeiten" wird übernommen (Standard: bleiben editierbar).
P5: Projekte, Zuordnen-Assistent, Services, Projekt-Bausteine (3,5-4 PT)
- Dateien:
pages/ProjectsPage.jsx:PageHeader+ "Gitea abgleichen" (.btn) + "Neues Projekt" (primär), Tabs (Aktiv/Vorlagen/Archiviert),.toolbarmit Suche + Kunde-Select (+ Mitarbeiter-Select, siehe Abschnitt 8), eine.table(Projekt mit.cell-subRepo | Kunde | Art | Adresse | Ticket als.link| Status-Badge | Letzter Push), Fuß "n Projekte". Klick öffnet den Dialog.- Alle heutigen Steuerelemente müssen erhalten bleiben (Liste in der Projekt-Inventur): Suche, 6 Ansichten, Kunde- und Mitarbeiter-Filter, Kunde-Select je Projekt (inklusive
window.confirm), Hosten/Aktualisieren/Preview/Gitea, "Weitere Treffer"-Links,notice-Zeile, Fehlerbanner. - Neu-Dialog: Anzeigename, Subdomain, Repo-Name (im Prototyp nicht vorhanden), Kunde. Bearbeiten-Dialog: Name, Typ, Subdomain mit Ergebnis-Hinweis, Archivieren/Reaktivieren (
.link-danger,window.confirmbleibt), "Speichern & synchronisieren". PreviewBlocksteht außerhalb des<form>(sonst löst Enter im Hauptdialog Speichern aus). Der Dialog wird breiter (.dialog-wide) oder der Block ein eigener Abschnitt.fetchAllProjectszeigt "wird geladen" nur beim Erstladen (heute springt die Liste nach jeder Aktion).
- Alle heutigen Steuerelemente müssen erhalten bleiben (Liste in der Projekt-Inventur): Suche, 6 Ansichten, Kunde- und Mitarbeiter-Filter, Kunde-Select je Projekt (inklusive
AssignProjectPage.jsx:.crumb+ h1 "Projekt zuordnen" +.sub; die Header-Card entfällt, jeder Schritt =h2.section+ Inhalt; Art-Wahl als.seg/.btn-Reihe, Ja/Nein als zwei.btn, Kunden- und Ticketliste als.list-rowmit--bg-hover-Auswahl (Auswahl nicht nur über das altevar(--surface-2), sondern auch textlich); Zusammenfassung als.kv; Preview-Zeile mit.checkund.field/.hint. Reine Logik (nextStep/prevStep/canFinish/openTicketsFor, R9-Regel, 409) bleibt.ServicesPage.jsx:h2.sectionje Art mit Zähler, Projektzeilen als.list-row, Feature-Badges (Verfügbar grün/In Entwicklung orange), Preview-Punkt → Badge (Online grün, Wird gebaut orange, Fehler rot, Aus plain).- Projekt-Bausteine:
TicketProjects,AssignedProjectCard(zugleich inCustomerDetailPage),AnalyseRow(Zustimmung wird<Dialog>, Wortlaut auslib/analyseView.jsunverändert),PreviewBlock(Statuspunkt → Badge, Fehlertext--red-fg, Log auf--bg-subtle, 27 Inline-Styles in 4-5 Klassen),PreviewLinkButton,GiteaLinkButton,CopyableCredential(auch vonCustomerDetailPage,AdminPage,CustomerFunktionengenutzt, Signatur voncopyText()unverändert). lib/previewEnv.js,lib/previewView.js: Vokabular bleibt, die Zuordnung auf Badge-Ton passiert in der Komponente (so bleibenpreviewEnv.test.mjsundpreviewView.test.mjsgrün).dotColor()bleibt als Funktion bestehen, wird aber nicht mehr gerendert (kann in P10 samt Test entfallen).btn-sm-Altlast (7 Dateien) verschwindet.
- Test-Auswirkung: keine (Tests prüfen nur Logik).
servicesView.test.mjsdeepEqual-tfeatures– nur ändern, wennservicesView.jsein Feld bekommt (Standard: nicht). - Sichtbar: Projektbereich als Tabelle, Zuordnen als ruhige Schrittseite, flache Projekt-Abschnitte im Ticket und beim Kunden.
P6: Chat, Gutschriften, Abo-Angebote, E-Mail-Speicher (4 PT)
Im Prototyp sind diese vier Seiten nur Platzhalter, die Gestaltung wird aus den Bausteinen abgeleitet und am ersten fertigen Screenshot freigegeben.
- Dateien:
- Chat:
CustomerChatPage.jsx(Liste als Haarlinien-Zeilen ohne Card, senkrechte Trennlinie--line, Ungelesen-Zähler als kleine Zahl oder schwarze Pille,.inputfür die Suche),ChatThread.jsx(nur Fehlerzeile ohne Icon,woms-chat,chat-messagesund die Container-Kinder bleiben),chat.csskomplett neu (siehe 7.2). - Gutschriften:
CreditsPage,CreditsView,CreditList(Filter als.tabs,.tablemit klickbarer Zeile, 7 Spalten brauchenhide-sm/hide-md-Priorisierung),CreditDetail(zweispaltig:.two-col+h2.section+.kv, Berechnung als.field-row, Pruef-Badges ohne Glyphen; Knöpfe: Genehmigen.btn-primary, Rückfrage.btn, Ablehnen.link-danger;busyRef,window.confirm, Debounce und die Eingabe-IDscalc-*/credit-*bleiben). - Abo-Angebote:
OffersPage,OffersView,OfferList(Tabs/Select statt.seg,.table, Aktionen als.link),OfferForm(Dialog, Autocomplete-Clipping prüfen). - E-Mail-Speicher:
StorageTasksPage(.table,data-storage-doneam Knopf erhalten). admin-areas.cssfertig (Rest-Raster) bzw. löschen, sobald alle 7 Importeure umgestellt sind.
- Chat:
- Test-Auswirkung: keine (reine Lib-Tests).
- Sichtbar: Messenger hell, Gutschriften/Angebote/Speicher als Tabellen.
P7: Finanzen (4-4,5 PT)
- Dateien:
global.cssL1155-1476 neu (rund 320 Zeilen, 26 Dunkel-Literale).FinancePage(h2.sectionje Block,INVOICES_ANCHORundfinanzen-buchungenalsidsamtscroll-margin-topbleiben,.fin-stack gapauf 0 setzen, sonst 36px + 24px Abstand),FinanceSettingsPage,BankCallbackPage.LiquidityHead(.statsmit.v.neg),StatusLine(unboxed),TodoList(Zeilen mit Badge, kein farbiger Streifen),ForecastTable,TaxCard,AboCard,InvoiceTable(Altersstreifen als.stats, Filter.seg, Status-Badges, Aktionen als Text),TransactionTable,Pager(.table-foot),BankAccessCard,FixedCostsCard,SettingsCard,FormField(Fehlerring statt Rahmenfarbe),AddressWarning,CustomerInvoices.- Dialoge:
AssignDialog(Zweischritt-Bestätigung unverändert, Vorschläge als.check-Zeilen statt Karten,useDialogFocus, Esc-Listener,useScrollIntoView),InvoiceCreateModal(breiter Dialog, Positionsraster behält die Spalten, Autocomplete-Popover, Enter sendet nie). - Responsive: die Container-Query-Stapelung (@container 560/640/860) bleibt,
container-typewandert vom.fin-*-cardauf den Wrapper (.fin-block/.fin-table-wrap). Die Klassen-Hooks (fin-forecast-date,fin-inv-*,fin-tx-*unddata-label) und dierole-Attribute bleiben. - Beträge:
euro()/euroWhole()/signedEuro()(U+2212) und tabular-nums bleiben. Negative Salden--red-fg, die Vorzeichen bleiben der eigentliche Träger der Bedeutung.
- Test-Auswirkung: keine (finance-Tests sind Lib-Tests, deepEqual auf Töne bleibt, weil das Mapping in
Badge.jsxsitzt). - Sichtbar: Finanzen ohne Karten, Dialoge weiß.
P8: Rechtliches und Admin (2 PT)
- Dateien:
- Rechtliches:
LegalPage(Tabs),LegalTextsTab(Kartenraster → eine Spalte oder Tabelle, Versionen als Haarlinienzeilen ohne Kästen und gestrichelte Ränder, Aufklapp-Zeile wird echter<button>),ProviderContractsTab(.table, Dringlichkeit nur im Termin-Feld als Badge/Text, keine getönten Zeilen),ContractFormundVersionFormauf<Dialog>mituseDialogFocus(heute ohne Fokusfalle und Esc),legalUi,global.cssL1122-1153. - Admin:
AdminPage.jsx(Header, TabelleMitarbeiterkonten, Formular als Dialog, Zeilenaktionen als Text),AdminPage.csskomplett neu schreiben (rund 350 von 416 Zeilen entfallen). Behalten und neu einfärben:.credential*,.sr-only,.spin(werden auch vonCopyableCredentialinCustomerDetailPage,CustomerFunktionenbenutzt; sie wandern nachglobal.css).
- Rechtliches:
- Test-Auswirkung: keine (
legal.test.mjspinnt nur die Tokenred|orange|normal|grey|missing, die als Daten bleiben). - Sichtbar: Rechtliches und Admin ohne Karten.
P9: Serverpuls (1-1,5 PT)
- Dateien:
ServerpulsPage.jsx,serverpuls.css,AppsTable.jsx,Chart.jsx,Sparkline.jsx. - Änderungen:
- Kopf: Emoji → Text-Badge. Alerts als
.list-rowmit Stufen-Badge (kein Linksstreifen). Kacheln als.statsmit Sparkline darunter. Deltas als Textfarbe (rot = gestiegen, grün = gesunken). Panels alsh2.section. Pills als.seg. - Chart-Farben als Konstanten (Canvas kann kein
var()), eine gemeinsame Konstante: Linie#111111, Füllungrgba(17,17,17,.06), Spitze#8a93a3gestrichelt, Achsen#5e6678, Grid#ebeef1(nur horizontal), Ticks#d8dde3. Die Projekt-Detail-Charts werden alle#111111. Legende 13px--text-2. data-testid(19 Stück in Serverpuls plussp-nav-dot) bleiben.
- Kopf: Emoji → Text-Badge. Alerts als
- Test-Auswirkung: keine (
serverpulsFormat.test.mjsprüftdirundstale, nicht Klassen). - Sichtbar: helles Dashboard.
P10: Aufräumen (1 PT)
- Dateien:
package.jsonplus Lock:@tabler/icons-reactundreact-iconsentfernen (nachrg= 0 Treffer).- Kompat-Block, tote Klassen und Aliase löschen (
.ui-btn*,.ui-card*,.badge-*,.overlay-content,.overlay-close,.form-control/.form-group/.form-label,.spinner,.btn-dark,.btn-teal,.text-grey,.dropdown*, Alt-Token,--surface-*usw.). src/assets/fonts(falls nicht schon in P1),previewEnv.dotColorsamt Test,Kpi-Reste.- Kommentare in
navLinks.jsundnavLinks.test.mjs("Navbar ordnet Icons zu") anpassen.
- Optional, als eigenes kleines Paket:
git rm -r --cached node_modules(8.890 Dateien)..gitignoreführt es ohnehin, der Build installiert neu,tp2ist unkritisch (siehe 7.6). - Abschluss: Bundle-Größe vorher/nachher, Screens aller Bereiche,
rg "style=\{"dokumentieren (Ziel: nur Laufzeitwerte). - Sichtbar: nichts Neues, kleineres Bundle.
6. Offene Entscheidung: Ticket als eigene Seite oder aufklappen
Zusammenfassung der Vergleichsanalyse des Ticket-Lesers (Aufwand und Risiko jeweils auf den visuellen Umbau draufgerechnet):
| Kriterium | Eigene Seite (/tickets/:woid) |
Aufklappen (Karte bleibt) |
|---|---|---|
| Abweichung vom freigegebenen Prototyp | keine | groß (Prototyp hat Crumb, Eyebrow, h1, Facts, Tabs mit URL) |
| Zusatzaufwand Struktur | +2 bis 2,5 PT (Route+Seite+Hook 0,5; Karte → Seite, handleCreateWorksheet extrahieren 0,75; Filter in URL, Tabellenzeilen als Links 0,75; Deep-Links, Planboard, Navigation nach Neuanlage 0,25; QA 0,25-0,5) |
+0,75 bis 1 PT (Zeile als Tabellenzeile mit Detail darunter, <button aria-expanded>, Auto-Aufklappen bei ?woid=) |
| Deep-Link auf ein Ticket oder einen Reiter | erstklassig (/tickets/123/arbeitsblaetter) |
?woid= zeigt eine einzelne eingeklappte Karte, kein Reiter-Link |
| Browser-Zurück und Lesezeichen | ja | nein |
| Lange Listen | Liste bleibt kurz | Seite wird lang |
| Performance | Liste mountet nicht mehr pro Zeile 3 Modals + useEmployees + useWorksheets + useCustomerAbo |
bleibt pro Zeile |
| Filterzustand | muss in die URL (löst nebenbei das heutige Zurücksetzen beim Zurückkehren) | entfällt |
| Bekannter Fehler | Liste entlädt sich bei jedem Refetch → muss in beiden Varianten behoben werden | dort besonders schlimm: Refetch klappt alle offenen Karten zu und verliert deren Zustand |
| Risiko | mittel: sechs bis sieben Links, Filter-in-URL, Tickets ohne woid (Fallback $id.slice(-5)), Zurück-Verhalten |
niedrig |
Empfehlung: eigene Seite. Sie entspricht dem freigegebenen Prototyp und, laut Projektnotiz, der Entscheidung des Eigentümers (siehe 0.4). Außerdem verschwindet der teure Pro-Zeile-Zustand, und Deep-Links werden einfach. Die Mehrkosten von rund 1,5 PT gegenüber "aufklappen" bestehen größtenteils aus der Filter-in-URL-Arbeit, die ohnehin einen heutigen Schmerzpunkt behebt.
Umsetzungsskizze (alles in P2, kein Datenmodell nötig):
- Route
/tickets/:woid/:tab?vor dem Fallback*. Die Sidebar markiert "Tickets" für Unterpfade bereits (isActivenutztstartsWith(href + '/')). - Datenabruf per
woid(nicht$id):useWorkorder(woid)mitQuery.equal('woid',[String(woid)]), Rückgabe wieuseWorkordersinklusiveupdateWorkorder. Zustände: Laden, Fehler, "Ticket nicht gefunden". Fallback für Tickets ohnewoid: Parameter als$idzulassen (Direktabruf). - Reiter
details/arbeitsblaetterim Pfad (nicht im State), Modals einmal pro Seite. - Legacy
?woid=bleibt per<Navigate replace>. Die sieben Link-Stellen wechseln aufticketPath(). - Filter-, Such- und Limit-Zustand der Liste in
useSearchParams; Breadcrumb "Tickets" führt mit demselben Suchstring zurück. Nach "Neues Ticket" Navigation auf/tickets/<neue woid>(createWorkorderliefertdata.woid). TicketProjectslöst bei jedem SeitenaufrufimportNewReposThrottled()aus (auf 60 s gedrosselt). Das ist heute beim Aufklappen der Fall und ändert sich nicht.- Fallback bei Rückzug der Entscheidung: P2 ohne Route (0,75-1 PT statt 2-2,5 PT), Zeile bleibt
<button aria-expanded>, das Refetch-Entladen muss trotzdem behoben werden.
7. Risiken und Stolpersteine
7.1 Charts (Serverpuls, uPlot)
- Canvas-Farben kommen als JS-Optionen (
Chart.jsxL28-36 und L45,ServerpulsPage.jsxL223-237). CSS-Variablen wirken dort nicht, es müssen Literale sein. Die Instanz wird nur bei geänderten Serien-Labels oder Höhe neu gebaut, die Farben müssen also statische Konstanten sein. - Weiße Alpha-Grid- und Tick-Linien sind auf Weiß unsichtbar,
#9aa7b4hat 2,45:1. Beides steht als Pflicht-Mini-Fix in P1. - Alt-Inkonsistenz: Amber ist im Hauptchart "Spitze", im Detailchart "CPU"; Smaragd ist im Hauptchart "CPU", im Detailchart "RAM". Mit durchgängigem Schwarz verschwindet das Problem.
uplot/dist/uPlot.min.css(Cursor#607D8B, Selektionrgba(0,0,0,.07)) passt schon auf Weiß.
7.2 Fremd gerenderter Chat-Composer
- Das Portal-CSS
chat-composer.csswird erst beim ersten Öffnen eines Chats ans Ende von<head>gehängt (lib/loadChatComposer.js), also nach dem gebauten WOMS-CSS. Gleich spezifische WOMS-Regeln verlieren. Deshalb müssen alle WOMS-Chat-Regeln das Präfix.woms-chatbehalten oder!importanttragen. - Der Fremdcode ist nur über
--chat-*-Variablen auf.woms-chatund überstimmende Selektoren steuerbar. Hart verdrahtete Weiß- und Schwarz-Alphas (Icon-Knöpfe, Datei-Chipsrgba(0,0,0,.22), Tray, Audio-Leiste, Menü-Hover) sind auf Weiß unsichtbar oder nur halb lesbar. Sie müssen einzeln überschrieben werden: transparente Knöpfe mit Ring-Schatten, Chip--bg-hoverohne Rand, Menü weiß mit--shadow-pop, Senden-Knopf schwarz mit weißer Schrift, Fehlerfarben--red-fg. - Lightbox und Kamera hängen am
<body>und sehen nur die Portal-Defaults (Lila, dunkel). Standard: dunkel lassen (Medienbetrachter), da sie die--chat-*-Werte von.woms-chatnicht erreichen. - Nicht änderbar aus WOMS: Texte ("Senden", "Aufnahme", "Bild", "Kamera", "Datei", "Dateien hier ablegen" als CSS-
content), 15 Inline-SVGs, das Portal-Repo (teilt die Datei mit dem dunklen, lila Kundenportal). Eine Tokenisierung dort wäre ein Eingriff ins Kundenportal und nur nach Absprache sinnvoll. - Vertrag zwischen WOMS und Portal: Klassen
woms-chat,chat-messages,chat-bubble,chat-meta,chat-dropzone/chat-drop-active,dataset.stick. React besitzt nur die leeren Container.ChatThread.jsxdarf dort keine Kinder oder Struktur ergänzen. - Desktop-/Handy-Breakpoint des Fremdcodes ist 640 px (Platzhalter) und 880 px / coarse pointer (Senden-Symbol), unabhängig von den WOMS-Breakpoints. Der Chat-Wechsel Liste/Verlauf (
chat.css, 720 px) bleibt davon unberührt. - Tipp:
tests/unit/loadChatComposer.test.mjssichert die Lade-Reihenfolge. Nicht anfassen.
7.3 Latin-1
Nur .env.development und .env.production. Nicht editieren. Alle Quellen sind UTF-8, das Bearbeiten mit Edit/Write ist unproblematisch. Die einzige Binärdatei im Quellbaum ist lato-regular.ttf (wird gelöscht).
7.4 Tests
- 312 Tests, reine Logik, keine Komponententests. Ein stilles Brechen von Markup oder Layout bemerkt kein Test, die Absicherung ist ausschließlich der Screenshot-Abgleich.
- Design-gekoppelte Tests:
adminRoute.test.mjsliestAdminRoute.jsxals Quelltext und verlangt<p>Wird geladen …</p>ohneclassName/style(und kein "Loading"). Das Markup bleibt wörtlich.sidebar.test.mjs(Breakpoint 720,startsCollapsed) wird mitlib/sidebar.jsgelöscht.previewEnv.test.mjsprüft die Hex-Werte vondotColor(),previewView.test.mjsdie Tokengreen|yellow|red|grey. Gegenmittel: Vokabular und Hex unverändert lassen, nur in der Komponente mappen.- Ton-Namen
muted|info|ok|warn|dangersind deepEqual-gepinnt infinanceFormat,financeView,financeSettings,adminFormat,funktionenView. Gegenmittel: Mapping nur inBadge.jsx. legal.test.mjspinntdeadlineTone(red|orange|normal|grey|missing).customerTabs.test.mjspinntObject.keys(entry)aufidundlabel.navLinks.test.mjs: deepEqual aufkey/label/href/count, ein zusätzliches Feldhotist erlaubt.servicesView.test.mjsdeepEqual-tfeatures [{label, statusLabel}].
- Lücken (neu mit Tests):
ticketStatus.js(Status-Töne),ticketForm.worksheetTone,planboard, alle Hooks, alle Komponenten, die extrahierte Worksheet-Logik, die URL-/Filter-Helfer. tests/unit/load.mjsbündelt mitesbuild, das nur transitiv überviteda ist (0.21.5) und nicht in dendevDependenciessteht. Ein Vite-Major-Upgrade kann die Tests still brechen.
7.5 Paralleler Branch feature/terminplaner-tp2
- Divergiert (1 ahead, 3 behind). Der eine Commit
6f03712berührtCustomerFunktionen.jsx(3 Zeilen),funktionenView.js,funktionenView.test.mjs. - Empfehlung: vor P4 in
testmergen (Push durch den Eigentümer) oder den Commit per Cherry-Pick indev-designholen. Dann gibt es keinen Konflikt in der in P4 komplett neu gebauten Datei. Alternativ nach P4 per Rebase mit manuellem Auflösen der 3 Zeilen. - Sollte TP2 später weitere WOMS-Oberfläche bauen: ab P1 mit den neuen Klassen und
Dialog, nicht mit den Altklassen. Der WOMS-Anteil des Plans ist mir nicht im Detail bekannt (der Plan-Commita72380cliegt im Portal-Repo).
7.6 Getracktes node_modules
- Siehe 0.6: für den Build wirkungslos, aber Stolperfalle für Commits (nur explizite Pfade) und für
npm uninstall(147 gelöschte getracktereact-icons-Dateien erscheinen imgit status). - Neue Worktrees checken die 8.890 Dateien mit aus und sind danach sauber (geprüft in
dev-termine). Einnpm installim Worktree ändert getrackte Dateien. - Optionaler Cleanup
git rm -r --cached node_modulesist unkritisch, auch wenntp2das Verzeichnis unverändert trägt.
7.7 Deploy-Webhook und Auslieferung
- Siehe 0.1 und 0.2. Nur
testdeployt,build/ist Live-Checkout, kein Staging. - Jedes Paket muss einzeln lauffähig und rückrollbar sein. Rollback per Revert-Commit.
- Container-Build hat 1 GB Node-Heap und macht pro Deploy
npm install(1-3 min). Neuepublic/-Dateien werden von Vite nachdist/kopiert,deploy.shkopiertdist/*nach/out,public/favicon.svgist damit unkritisch. - Server-Speicher knapp (Vite dev wird per OOM getötet). Nur der esbuild-Harness unter
systemd-runist belastbar.
7.8 Weitere Stolpersteine
- Eine CSS-Bundle:
App.jsximportiert alle Seiten statisch, es gibt genau ein CSS. Ein entfernter oder umbenannter Klassenname bricht Seiten, die niemand öffnet, und fällt erst zur Laufzeit auf. Das ist die Begründung für den Kompat-Block in P1 und die QA-Pflichtliste. - Namenskollisionen zwischen Prototyp und Bestand mit anderer Bedeutung:
.btn,.badge,.page,.field,.seg,.toolbar,.empty,.list-row,.urgent-toggle(Schalter statt gestrichelter Pille),.overlay(Backdrop plus zentrierter.dialogstatt schwarzer Vollbild),.id,.muted/.faint..tableist unkritisch, die Bestandsregel.tableist tot. - 79
!important(.btn-green,.btn-primary,.form-control,.card,.status-*,.priority-*,.dropdown .btn) kämpfen gegen neue Regeln. Löschen, nicht überschichten. - Dokument-Scroll statt Scroll-Container:
position: stickyinAdminPage.cssL186 bekommt einen neuen Bezug,scrollIntoViewist unkritisch (useScrollIntoView,ChatThreadhat eine eigene Scrollbox). 100vh kommt nur inLoginPageundglobal.cssL801 vor. - Breakpoints: Sidebar 720 → 860, dazwischen halbfertige Layouts, wenn Seiten noch die alten Breakpoints nutzen (
@media 720,700,640,768). Pro Paket die Seitenbreakpoints auf 860/1100 ziehen. - Dialoge:
useDialogFocusbrauchtonKeyDownam Overlay, einen inneren Dialog mitrefundtabIndex={-1}und fokussierbare Elemente innerhalb des Overlays (der Schließen-Button muss drin sein). Escape-Listener der Finanzdialoge:InvoiceCreateModalrespektierte.defaultPreventedfür das Autocomplete. Nativerequiredundnovalidate-Verhalten bleiben. Autocomplete-Popover (280px) im scrollenden.dialog-bodykann abgeschnitten werden. - Link-Erkennbarkeit: viele Links hängen an
--accent(siehe 2.2) und werden schwarz.a { color: inherit }plus.linkmit Unterstreichung ist der Ausweg.a.list-rowund Tabellenlinks brauchen eine Hover-Reaktion. - Statusfarben-Semantik (Offen grün → grau, Erledigt grau → grün, Prüfung teal → violett) ist für Mitarbeiter ein sichtbarer Bedeutungswechsel.
- Zeilenklick vs. Controls:
tr.rowdarf Klicks aufa,button,select,inputnicht verschlucken (der Prototyp ignoriert sie). Für Tastatur und Mittelklick ein echter Link in der ersten Zelle. - Abo-Stunden-Sichtbarkeitsregel in
WorksheetList(onTogglePlan && !isComment && totalTime>0 && (aboHoursAllowed || countsToPlan)) undcanEditTimes,canAddSupport,isGit,needsTimeEntrybleiben beim Rewrite exakt. - Datenabdeckung der Prototyp-Spalten: "Zeit", "Tab-Zähler", "Offene Tickets (Kunden)", "Letzter Push" haben keine fertigen Felder (siehe Abschnitt 8).
tr-Hover-Trick des Prototyps (box-shadowlinks/rechts 12px) setzt eineborder-collapse: collapse-Tabelle ohneoverflow:hiddenvoraus. In Dialogen und Container-Wrappern prüfen.- StrictMode: doppelte Effekt-Läufe in Dev sind nach Entfernen von
LetterGlitch(rAF-Dauerlast, keine Reduced-Motion-Abfrage) kein Thema mehr.
8. Was bewusst NICHT geändert wird
Das Redesign ist rein visuell. Ausnahme ist allein die Ticket-Seite samt Route (Abschnitt 6).
- Verhalten:
- Alle Hooks (
src/hooks/*) und alle Lib-Logik:finance*,assign*,support,mfa,staffAccess,serverpuls*,projectList,analyse*,previewApiusw. - Auth- und 2FA-Ablauf, Polling (Sidebar 5 min, Serverpuls 60 s plus bei Sichtbarkeit, Chat 15 s und 30 s, Preview 5 s), Pflichtfelder und Validierungen, 15-Minuten-Schritte, Doppelklick-Sperren (
busyRef,lockRef). - Alle
window.confirm/alert-Rückfragen bleiben (rund 12 Stellen). - URL-Parameter
?tab=,?antrag=,?kunde=,?woid=(letzteres als Redirect), alledata-testid. - Navigation:
navLinks.js(Reihenfolge, Labels, Hrefs, pausierte Leads) unverändert, Routen außer/tickets/:woid. - Lead-Automatik bleibt pausiert.
- Alle Hooks (
- Daten:
- Appwrite-Schema, Collections, Attribute, Permissions, Indizes. Keine neuen Felder, keine Datenmigration.
- Gespeicherte Statuswerte (englisch, das Kundenportal leitet daraus die Fortschrittsleiste ab).
- Portal-Repo und
chat-composer.css/js. .env*(Latin-1).- Server-API.
- Sortierung der Ticketliste bleibt
$createdAtabsteigend. Der Prototyp-Fußtext "Sortiert nach Dringlichkeit, dann neueste" würde einen Index aufprioritybrauchen und wird deshalb im Fußtext angepasst, nicht in der Datenbank.
- Texte:
- Alle rechtlich relevanten Texte (AVV-/Zustimmungstext der Website-Analyse samt Textvorlage, Abo-/Gutschrift-/§19-Hinweise,
TaxCard-Disclaimer), Mail- und Chat-Vorlagen und die Lib-Fehlermeldungen. - Statuslabels und Tab-Überschriften, soweit sie schon mit dem Prototyp übereinstimmen (Offen, In Arbeit, Wartet auf Kunde, Prüfung, Erledigt, Storniert).
- Die ae/ue-Schreibung in Bestandstexten (zum Beispiel "oeffnen", "Menue") wird nicht flächig korrigiert.
- Ausnahmen, die dem Prototyp-Wortlaut folgen (freigegeben): Login ("Anmelden", "E-Mail", "Passwort", "Weiter", "Code eingeben", Untertitel "Ticketsystem für das WEBklar-Team"), "Abmelden", "Wird geladen …" statt "Loading...", "Menü".
- Alle rechtlich relevanten Texte (AVV-/Zustimmungstext der Website-Analyse samt Textvorlage, Abo-/Gutschrift-/§19-Hinweise,
- Nicht im Umfang: Dark-Mode-Umschalter, neue Funktionen, Terminplaner TP2, Server-/Compose-Änderungen.
8.1 Abweichungen Prototyp zu Bestand, die vor dem jeweiligen Paket entschieden werden müssen
Standardvorschlag in der letzten Spalte (wird übernommen, wenn der Eigentümer nichts anderes sagt).
| # | Paket | Punkt | Standard |
|---|---|---|---|
| 1 | P1/P2 | Statusfarben-Wechsel (Offen/Erledigt/Prüfung) | wie Prototyp |
| 2 | P2 | Einfachwahl-Tabs (Aktiv, Offen, …) statt Mehrfachauswahl plus "Anwenden"; "Aktiv" = bisheriger Standard (open+working+waiting+review) | wie Prototyp, Filter live |
| 3 | P2 | Tab-Zähler und Spalte "Zeit" (zusätzliche Lesezugriffe) | mitliefern (+0,5 PT), "Zuständig" aus Mitarbeiterliste |
| 4 | P2 | Status-Auswahl: Prototyp bietet bei Akquise nur [In Kontakt, Erledigt, Storniert], Bestand zeigt für alle Tickets die sechs generischen Werte | Bestand beibehalten, aktueller Wert immer als Option |
| 5 | P2 | "Neues Ticket": Prototyp mit Zuständig-Select und Einsatz-Segment, Bestand mit automatischer Zuweisung und ausgeblendetem Einsatz | Bestand beibehalten |
| 6 | P2 | Arbeitsblatt-Dialog: Hinweis "Der Kunde sieht diesen Text im Kundenportal" ist im Prototyp, im Bestand unklar | nur übernehmen, wenn nachweislich wahr |
| 7 | P2 | Reihenfolge der Arbeitsblätter (Prototyp älteste zuerst, Bestand neueste zuerst) | Bestand |
| 8 | P2/P3 | Stornierte Tickets im Planboard | wie Bestand (anzeigen); Prototyp blendet sie aus |
| 9 | P4 | Spalten "Telefon" und "Offene Tickets" in der Kundenliste | "Telefon" ja (Feld vorhanden), "Offene Tickets" nein (zusätzliche Abfrage) |
| 10 | P4 | Stammdaten: Lese/Bearbeiten-Umschalter (Prototyp) oder immer editierbar (Bestand) | immer editierbar |
| 11 | P5 | Projektansichten "Unzugeordnet", "Zugeordnet", "Alle inkl. Archiv" und Mitarbeiter-Filter haben im Prototyp keinen Platz | Tabs Aktiv/Vorlagen/Archiviert plus Selects (Kunde mit Option "Ohne Kunde", Mitarbeiter); "Alle" als vierter Tab |
| 12 | P5 | Spalte "Letzter Push" (kein Feld in websiteProjects), "Art" meint typeOf() (Hosting-Modus), nicht project.kind (Services-Art) |
$updatedAt zeigen oder weglassen; Begriffe sauber trennen |
| 13 | P6/P7/P8/P9 | Chat, Gutschriften, Angebote, Speicher, Services, Finanzen, Rechtliches, Admin, Serverpuls sind im Prototyp nur Platzhalter | Gestaltung aus den Bausteinen, Abnahme am ersten fertigen Screenshot je Paket |
| 14 | P6 | Chat-Blasen: eigene Blase grau oder schwarz | fremd --bg-hover, eigene --grey-bg (Schwarz wäre zu viel Akzent); Lightbox/Kamera dunkel |
| 15 | P5 | Kopier-/Zeigen-Knöpfe in CopyableCredential: Icons oder Text |
Text |
| 16 | P1 | Footer ("Webklar Ticket-Plattform · © Jahr") | entfällt; falls nötig, 12px in .sidebar-foot |
| 17 | P1 | <title> ("Webklar WOMS 2.0" oder "Ticketsystem · WEBklar") |
unverändert lassen |
| 18 | P1 | Serverpuls-Punkt: ein Zustand (Prototyp) oder zwei (Bestand: medium und critical) | zwei (amber, rot) |
| 19 | P6 | Pseudo-Tabellen mit 5-7 Spalten (Gutschriften, Angebote): Prototyp blendet Spalten aus (hide-sm/hide-md) statt zu stapeln |
Spaltenpriorität je Tabelle festlegen, Aktionen in letzte Spalte |
| 20 | P2 | .btn-Variante "danger" und size="sm" haben im Ziel keine Entsprechung |
.link-danger bzw. Einheitsgröße (32px) |