From 6cfe0c1fd51e339844d644d35e56a3ab2f9b3c72 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 24 Aug 2026 15:17:22 +0200 Subject: [PATCH] Design Anpassungen Dashboard + Rep. PC Importer --- AGENTS.md | 29 ++++ docs/IMPLEMENTATION_PLAN.md | 22 ++++ package-lock.json | 10 ++ package.json | 1 + src/app/App.tsx | 4 +- .../DonutMetricCard/DonutMetricCard.tsx | 16 ++- .../ResizableResultTable.tsx | 6 +- src/features/dashboard/DashboardPanel.tsx | 43 +++--- src/features/dataset-viewer/DatasetViewer.tsx | 14 +- src/importers/html-extraction.ts | 6 +- src/main.tsx | 2 + src/styles/base.css | 17 ++- src/styles/components.css | 97 ++++++++------ src/styles/datahub-redesign.css | 13 +- src/styles/datahub.css | 124 ++++++++++-------- src/styles/import-workflow.css | 2 +- src/styles/layout.css | 28 ++-- src/styles/legacy-views.css | 51 +++---- src/styles/mitre-graph.css | 54 ++++---- src/styles/obsidian-graph.css | 20 +-- src/styles/policy-analysis-actions.css | 4 +- src/styles/policy-analysis.css | 4 +- src/styles/tokens.css | 17 +-- src/styles/ui-tokens.css | 104 +++++++++++---- tests/app/app-version.test.ts | 2 + tests/app/typography-system.test.ts | 38 ++++++ tests/components/donut-metric-card.test.tsx | 35 +++-- tests/design-language-check.mjs | 26 ++-- tests/features/dashboard-layout.test.ts | 14 +- .../features/datahub-centered-layout.test.ts | 31 +++-- .../datahub-tenant-detail-reference.test.ts | 6 +- .../features/finding-title-typography.test.ts | 28 ++++ tests/features/obsidian-graph.test.ts | 4 +- tests/features/panel-inset-standard.test.ts | 2 +- tests/features/tile-segment-grammar.test.ts | 75 +++++++++++ tests/local-only-check.mjs | 2 +- tests/server/node-import-engine.test.ts | 23 ++++ 37 files changed, 658 insertions(+), 316 deletions(-) create mode 100644 tests/app/typography-system.test.ts create mode 100644 tests/features/finding-title-typography.test.ts create mode 100644 tests/features/tile-segment-grammar.test.ts diff --git a/AGENTS.md b/AGENTS.md index e88f442..82a98fc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,6 +20,10 @@ ## UI typography +- Use the locally bundled Inter variable font as the single application typeface. Do not introduce view-specific display, condensed, serif, or monospace families; hierarchy comes from size and weight. +- Use only the visual weights Regular 400, Semibold 600, and Bold 700. Bold is reserved for branding and exceptional emphasis. +- Use the semantic typography roles: H1 56px/600, H2 32px/600, H3 18px/600, H4 16px/600, body and table content 16px/400, labels 14px/600, metadata and table headers 13px, and buttons 14px/600. +- Preserve normal title case for H1-H4, field labels, and buttons. Uppercase is limited to compact table headers, badges/categories, and the ASSESSOR wordmark. - Render continuous prose and other content-bearing text at no less than `16px`, including finding content, report text, Grid cell content, and Raw Data. - Compact interface text such as button captions, field labels, metadata, badges, and comparable control annotations may use a smaller type size. - When adding or changing styles, preserve this distinction instead of reducing content text to a compact UI-label token. @@ -31,6 +35,31 @@ - Apply the inset exactly once on the view's root content container. Split panes and edge-to-edge structures belong inside that inset and must not add a second outer inset. - Keep the 20px outer inset at responsive breakpoints unless the user explicitly changes this standard. +## UI segments and tiles + +- Treat segments and tiles as a binding two-level visual grammar independent of their content. A segment is the rounded grouped background; a tile is a rounded, individually bordered element inside it. +- Use only the shared `--segment-*` and `--tile-*` tokens for segment surfaces, borders, radii, insets, gaps, tile surfaces, shadows, widths, hover states, and selected contours. Do not create view-local substitute geometry. +- Segments use the light neutral group surface, a fine border, `--segment-radius`, one uniform inset, and one uniform gap. Segments remain flat and do not receive a floating shadow. +- Tiles use the common white or theme-equivalent surface, fine gray border, `--tile-radius`, and the single subtle `--tile-card-shadow`. Hover may change border and surface but must not lift or translate the tile. +- Status color belongs only in a small accent line, marker, data visualization, or selected contour. Never fill the complete tile with a status color. +- Selected tiles use the shared selected border/outline. Action tiles and add tiles use the same geometry as content tiles. +- Keep unused segment space intentionally empty: lay tiles out from the start at their shared compact or content width and never stretch a partial final row merely to fill the container. +- Tile content must never cross the tile boundary or cover a neighboring tile. Nothing may be clipped, clamped, truncated, or replaced by an ellipsis. Text and actions reflow inside the tile; media scales within the available inner area. If content does not fit, enlarge or reflow the tile. +- Avoid line breaks in compact tile labels where practical. If wrapping is necessary, wrap only at word boundaries: never split ordinary words or numeric values across lines. Use a wider or responsive tile whenever a complete word or value cannot fit. + +## UI donut charts + +- Every donut chart uses the same full 360-degree geometry: a continuous neutral-gray track and a colored clockwise value arc starting at 12 o'clock. +- Donut arcs use flat ends and the shared `--donut-ring-stroke`, `--donut-track`, value, and typography tokens. Do not introduce 270-degree gauges, open gaps, rounded arc caps, or local substitute geometry. +- Place the primary numeric value itself on the exact visual center of the ring. Append a percent sign or other short unit to its right on the same baseline in the smaller shared UI size without letting that unit shift the number away from the center. Supporting counts may remain as subdued secondary text inside the ring, but every text element must stay fully within the clear inner area and must never touch or overlap the ring at any supported value width. +- Multi-segment distributions retain their categorical segments, but use the same track, stroke proportion, start position, centered value treatment, and shared tokens as single-value donuts. + +## UI branding position + +- Use the Data Hub header as the binding position reference for the ASSESSOR logo, wordmark, and version. +- Keep the brand container geometry identical across every application view; do not add area- or view-specific brand padding, margin, alignment, height, or transforms. +- Switching views must never move the logo, wordmark, or version. + ## Security and verification - Preserve local-only behavior until a server transition is explicitly implemented and documented. diff --git a/docs/IMPLEMENTATION_PLAN.md b/docs/IMPLEMENTATION_PLAN.md index f55d4bb..6318019 100644 --- a/docs/IMPLEMENTATION_PLAN.md +++ b/docs/IMPLEMENTATION_PLAN.md @@ -1349,6 +1349,12 @@ Aktualisierung nach dem Schritt-11-Orchestrierungsvertikalschnitt vom 2026-08-04 | Datum | Entscheidung | Begründung | |---|---|---| +| 2026-08-24 | Segmente und Kacheln folgen anwendungsweit einer gemeinsamen visuellen Grammatik: flacher abgerundeter Segmentcontainer mit einheitlichem Inset und Gap; darin gleichartige abgerundete Kacheln mit feiner Kontur, dezentem gemeinsamem Schatten und fester Inhaltsbreite. Statusfarbe bleibt Akzent oder Auswahlkontur. Unvollstaendige Zeilen werden nicht gestreckt. Jede Kachel begrenzt ihren Inhalt; Titel, Werte, Labels, Zusatztexte, Medien und Aktionen duerfen weder abgeschnitten, geklemmt, ellipsiert oder verborgen werden noch die Kachelgrenze ueberschreiten oder Nachbarkacheln ueberdecken. Notwendige Umbrueche erfolgen ausschliesslich an Wortgrenzen, Zahlen werden nicht getrennt. | Ein gemeinsamer Vertrag verhindert springende Geometrie und ansichtsspezifische Sonderkarten. Die harte Inhaltsgrenze schuetzt Nachbarkacheln, waehrend vollstaendige Inhalte die fachliche Lesbarkeit erhalten. Wenn Inhalt nicht vollstaendig passt, wird er intern neu angeordnet oder die Kachel verbreitert beziehungsweise responsiv neu angeordnet. | +| 2026-08-24 | Die neueste Nutzerkorrektur ersetzt fuer Finding-Titel die unmittelbar folgende fruehere 18-px-Reader-Entscheidung: Trefferlistentitel bleiben auf der 16-px-Inhaltsstufe, werden aber mit Medium 500 statt Semibold gesetzt; der Titel des geoeffneten Findings verwendet eine eigene 20-px-Detailtitelrolle mit Semibold. Die Finding-Abschnittsueberschrift bleibt 18 px, regulaere Matrixkacheln bleiben 16 px. | Die Trefferliste soll ruhig und scanbar bleiben, waehrend das geoeffnete Finding als primaerer Detailkontext klar hervortreten muss. Die getrennten semantischen Rollen loesen den Kontrast ohne die 16-px-Untergrenze fuer inhaltstragenden Text zu unterschreiten. | +| 2026-08-24 | Finding-Titel verwenden innerhalb von Work eine kompakte semantische Hierarchie: Reader-Titel und Finding-Abschnitt nutzen H3 mit 18 px; Titel in Trefferlisten und regulaeren Matrixkacheln nutzen H4 mit 16 px. Die globale H1-/H2-Hierarchie und die eigenstaendige Pathfinder-Micro-Card bleiben unveraendert. | Lange Scannerbezeichnungen benoetigen in Listen und Kacheln sonst zu viele Zeilen und verdraengen Status sowie Metadaten. Die vorhandenen Typografietokens erhalten die Lesbarkeitsuntergrenze von 16 px fuer inhaltstragenden Text und vermeiden lokale Sondergroessen. | +| 2026-08-24 | Alle Donutdiagramme verwenden einen gemeinsamen visuellen Vertrag: vollstaendige neutrale 360-Grad-Ringspur, farbiger Wertbogen im Uhrzeigersinn ab 12 Uhr, flache Bogenenden, gemeinsame Strichstaerke und eine mittige Primaerkennzahl mit kleiner Einheit auf derselben Grundlinie. Mehrteilige Verteilungen behalten ihre fachlichen Farbsegmente innerhalb derselben Geometrie. | Eine gemeinsame Form verhindert wechselnde Gauge-, Ring- und Prozentdarstellungen zwischen Datahub, Dashboard und Sidebar. Die neutrale Restspur macht den verbleibenden Anteil sichtbar; die zentrale Kennzahl bleibt unabhaengig von Kartenbreite und Legende unmittelbar lesbar. | +| 2026-08-24 | ASSESSOR verwendet anwendungsweit die lokal gebuendelte variable Schriftfamilie Inter. Die verbindliche Hierarchie ist H1 56 px/600, H2 32 px/600, H3 18 px/600, H4 und Fliesstext 16 px mit 600 beziehungsweise 400, UI-Labels und Buttons 14 px, Metadaten und Tabellenkoepfe 13 px sowie kompakte Finding-Badges 12 px. Uppercase bleibt Tabellenkoepfen und kurzen technischen Kennzeichnungen vorbehalten; regulaere Ueberschriften und Feldbezeichnungen verwenden Title Case. | Eine lokal gebuendelte Einzelschrift verhindert plattformabhaengige Fallbacks und visuelles Springen. Semantische Rollen statt lokaler Einzelwerte schaffen nachvollziehbare Abstufungen, waehrend 16 px fuer inhaltstragenden Text die bestehende Lesbarkeitsgrenze bewahrt und dichte Finding-Zustandszeilen visuell hinter den Titel treten. | +| 2026-08-24 | HTML-Berichte oberhalb von 1 MiB werden in Browser- und Backendlaufzeit direkt mit dem begrenzten lexikalischen Extraktor verarbeitet; kleinere Dokumente duerfen weiterhin den DOM-Adapter verwenden. Die isolierte Worker-Zeitgrenze von 30 Sekunden und alle bestehenden Ressourcenlimits bleiben unveraendert. | Scanner-HTML kann schon deutlich unter der bisherigen 4-MiB-Schwelle in eine DOM-Struktur expandieren, die das Worker-Zeitbudget ueberschreitet. Der vorhandene lineare Extraktor bewahrt Tabellen-, Locator-, Sicherheits- und Source-Extractor-Vertraege, ohne einen vollstaendigen DOM-Baum aufzubauen oder die Schutzgrenze aufzuweichen. | | 2026-08-23 | Im Pathfinder gerenderte Knoteninformationen sind das eigenstaendige Designelement `Pathfinder Micro Card` und keine allgemeine Matrix-/Finding-TileCard. Der Vertrag besitzt die Varianten `finding` und `relation`; Category bleibt eine groessere Relation-Auspragung. Bestaetigte Geometrie, Typografie und Radien sind eigene Tokens beziehungsweise Konstanten, und die gesamte Komposition skaliert unveraendert mit Faktor 0,4 im Graphkoordinatensystem. | Graphkarten beschriften Knoten und muessen Beziehungen sichtbar lassen. Eine Uebernahme allgemeiner Kartenmasse oder nachtraegliche kamerainverse Typografie wuerde die bestaetigte Darstellung verdecken beziehungsweise ihre inneren Proportionen veraendern. Ein eigener Vertrag erlaubt gezielte Regressionen, ohne regulaere TileCards ausserhalb des Pathfinders zu beeinflussen. | | 2026-08-23 | Scanner-Kontext wird zweistufig gespeichert: quellengetreue Reportabschnitte mit Prosa und Tabellen sowie typisierte EnvironmentObservations mit stabilen Schluesseln und explizitem Erfassungszustand. Findings bleiben ein getrennter Vertrag. | Nur die Rohstruktur verhindert Informationsverlust bei neuen PingCastle-Versionen; nur typisierte Fakten erlauben zugleich belastbare Dashboard-, Result-DB-, LLM- und Toolabfragen. Ein Zustandsfeld trennt echte Abwesenheit von leerer Ausgabe, fehlender Berechtigung, Lizenzgrenze und Parserluecke. | | 2026-08-23 | Typisierte Scanner-EnvironmentObservations werden als eigener Unified-Result-Quelltyp `environment-observation` projiziert und niemals als Finding umgedeutet. Die Projektion ist ein abgeleitetes Readmodell ueber die bestehende Environment-Persistenz; sie bewahrt Original-/Normalwert und vollstaendige Provenienz, bietet eigene Environment-Facetten und nimmt diese Zeilen aus Finding-spezifischen Facetten heraus. | Result DB, Exporte, LLMs und weitere Tools benoetigen einen gemeinsamen timeslotgebundenen Zugriff, ohne aus DFL, OS, PKI, Trusts oder Coverage kuenstliche Schweregrade, IOEs, Abweichungen oder Reviews abzuleiten. Ein Readmodell vermeidet Datenkopien und eine weitere Migration, waehrend der getrennte SourceKind den Vertrag eindeutig haelt. | @@ -1603,6 +1609,13 @@ Diese Entscheidungen werden erst getroffen, wenn die jeweilige Phase ansteht ode | Risiko | Gegenmaßnahme | |---|---| +| Lokale Kartenraster koennen Kacheln wieder auf Restbreite strecken; lange Titel, Werte, Labels, Zusatztexte, Medien oder Aktionen koennen abgeschnitten, ellipsiert oder verborgen werden, aus einer Kachel herausragen, Nachbarkacheln ueberdecken oder durch aggressive Umbruchregeln mitten im Wort getrennt werden. | Gemeinsame Segment-/Kacheltokens, feste Startausrichtung und eine zuletzt geladene Kachel-Vertragsschicht verwenden. Woerter nur an Wortgrenzen umbrechen und Zahlen zusammenhalten. Bei Platzmangel Inhalt intern neu anordnen oder die Kachel verbreitern beziehungsweise responsiv neu anordnen; Clipping, Clamp, Ellipse und Truncation sind keine zulaessigen Rueckfaelle. Statische Regressionen sichern Vertrag, relevante Ansichten und Kaskadenreihenfolge. | +| Trefferlistentitel und geoeffneter Finding-Titel koennen trotz unterschiedlicher Informationshierarchie optisch gleich laut oder in der falschen Reihenfolge erscheinen. | Trefferliste auf 16 px/500 und geoeffneten Detailtitel auf 20 px/600 ueber eigene zentrale Tokens binden; Finding-Abschnitt und Matrixkacheln getrennt regressionspruefen. | +| Finding-Badges und Outcome-Texte koennen bei derselben Groesse wie regulaere Labels eine lange Titelzeile optisch ueberladen und MITRE-Zuordnungen unnoetig umbrechen. | Fuer Finding-Zustandszeilen ausschliesslich den zentralen 12-px-Badgetoken verwenden; Farbe, Glyph, Rahmen und feste Dimensionsreihenfolge bleiben als zusaetzliche Bedeutungstraeger erhalten. | +| Lange Finding-Titel koennen bei Verwendung der globalen H2- oder allgemeinen Heading-Rolle Reader, Trefferlisten und Matrixkacheln visuell dominieren und Statusinformationen aus dem sichtbaren Bereich druecken. | Finding-spezifische Titelrollen regressionspruefen: Reader 18 px, Listen- und Matrixkacheln 16 px; Fliesstext bleibt mindestens 16 px. Globale Seitentitel und Pathfinder-Micro-Cards nicht ueber lokale Work-Regeln mitveraendern. | +| Unabhaengige Donutimplementierungen koennen erneut in offene 270-Grad-Gauges, abweichende Ringspuren, runde Bogenenden oder unterschiedlich platzierte Prozentwerte auseinanderlaufen; mehrteilige Verteilungen koennten den gemeinsamen Standard umgehen. | Vollkreis, Startposition, flache Enden, neutrale Spur, Strichstaerke und zentrale Wertebehandlung ueber gemeinsame Donuttokens und Regressionen fuer Datahub-Karten, Sidebar, Dashboardstatus und mehrteilige Risiko-Verteilung absichern. Neue Donuts muessen denselben Vertrag verwenden. | +| Lokale Systemschriften, konkurrierende Font-Stacks oder frei gesetzte Schriftgroessen koennen zwischen Betriebssystemen und Ansichten unterschiedliche Laufweiten, Zeilenumbrueche und Hierarchien erzeugen. | Inter als lokale WOFF2-Abhaengigkeit ohne CDN laden, den Font-Stack und alle semantischen Schriftrollen zentral tokenisieren und Import, Hierarchie, Mindestgroesse, erlaubte Grossschreibung sowie das Fehlen abweichender Display-Schriften regressionspruefen. | +| Mittelgrosse, strukturell komplexe Scanner-HTML-Berichte koennen vor der groessenbasierten DOM-Umschaltung die isolierte 30-Sekunden-Parsergrenze erreichen; ein pauschal hoeheres Timeout wuerde problematische Eingaben nur laenger binden. | Berichte oberhalb von 1 MiB frueh in den bestehenden begrenzten lexikalischen Extraktor routen, Workerzeit-, Speicher-, Tabellen- und Zeilengrenzen beibehalten und den 1,6-MiB-PingCastle-Pfad mit synthetischer Tabelle sowie `html-bounded-extraction`-Diagnose regressionspruefen. | | Eine Anzeige der technischen Paketversion koennte faelschlich als aktueller fachlicher Verarbeitungsstand verstanden werden; eine lokal im React-Code hartcodierte Zahl koennte ausserdem vom Processingvertrag abweichen. | Die zentrale Konstante `CURRENT_ASSESSOR_PROCESSING_VERSION` aus dem Processingvertrag im Markenblock rendern, weder `v` noch `Version` voranstellen und Quelle, Wert, Position sowie kompakte Metadaten-Typografie regressionspruefen. | | Ein Scanner-Reimport koennte den alten Lauf vor erfolgreicher Validierung loeschen, den falschen Timeslot beziehungsweise ein anderes Werkzeug ersetzen oder alte und neue Ergebnisse gleichzeitig in Work und Result DB anzeigen. | Vorschau und Abschluss kryptografisch an dasselbe Projekt-, Assessment- und ScanRun-Ziel binden, das erkannte Werkzeug gegen den aktiven Vorgaenger pruefen und Supersession samt neuem kanonischem Lauf in einer Repositorymutation committen. Den alten Lauf nicht loeschen; aktive Read-Modelle filtern `supersededByScanRunId`, waehrend der Projektexport beide Laeufe und ihre Beziehung erhaelt. | | Ein Bericht desselben Werkzeugs mit gleichem Dateinamen oder Datum koennte faelschlich als Reimport akzeptiert werden; bei Mehrdatei-Reports koennte eine Teilmenge oder eine zusaetzliche Datei den falschen Bestand ersetzen. Ein Client koennte ausserdem eine reine Vorschaupruefung umgehen. | Anzahl und vollstaendiges SHA-256-Multiset der Originaldateien in Preview und Complete vergleichen und dieselbe Invariante in Local- und PostgreSQL-Repository sowie beim Uebernehmen bereits importierter Ersatzlaeufe erzwingen. Fehlende Originalhashes, Offline-Fallback und jede Abweichung fail closed ablehnen; der bestehende Scan bleibt aktiv. | @@ -1780,6 +1793,15 @@ Diese Entscheidungen werden erst getroffen, wenn die jeweilige Phase ansteht ode ## 12. Änderungsprotokoll | Datum | Änderung | +| 2026-08-24 | Phase-7-Segment-/Kachelgrammatik nach Nutzerreferenz anwendungsweit umgesetzt und als Projektvorgabe verankert: gemeinsame Segment- und Kacheltokens fuer Flaeche, Kontur, Radius, Inset, Gap, Schatten, Auswahl und Breiten; flache Gruppencontainer; gleichartige Inhalts-, Aktions- und Hinzufuegekacheln; Statusfarben nur als Akzent; unvollstaendige Zeilen bleiben unstretched. Datahub, Dashboard, Sidebar, Work-TileGrid, DB Structure, Policy Analysis und statische Pathfinder-Karten verwenden den Vertrag. Die verbindliche Nutzerregel verbietet abgeschnittene, geklemmte, ellipsierte oder verborgene Titel, Werte, Labels, Zusatztexte und Aktionen. Dashboardstatuskarten wurden auf 290 beziehungsweise 210 px abgestimmt; Anzahl und Prozentwert stehen getrennt, sodass `Compliance` und Werte wie `453` vollstaendig bleiben. Das Donut-Raster nutzt auf Desktop sechs Spalten und die Matrix verteilt ihre feste Spaltenzahl ueber die volle Segmentbreite. Bei Platzmangel wird Inhalt neu angeordnet oder die Kachel verbreitert beziehungsweise responsiv umgebrochen. Sechzehn fokussierte Layout-/Donuttests und der Gesamtlauf mit 736 bestandenen sowie 8 uebersprungenen optionalen PostgreSQL-Tests, Local-only- und Designpruefung mit 0 Rohfarben sowie Client-/Server-Produktionsbuild sind erfolgreich. Keine Fachlogik-, API-, Persistenz- oder Schemaaenderung; Phase 7 bleibt abgeschlossen. | +| 2026-08-24 | Phase-7-Finding-Titelhierarchie nach visueller Nutzerkorrektur nachgeschaerft: Trefferlistentitel bleiben 16 px, verwenden aber Medium 500 statt Semibold; der geoeffnete Finding-Titel verwendet eine neue zentrale 20-px-Detailtitelrolle mit Semibold. Finding-Abschnitt 18 px, Matrixkacheln 16 px und die 16-px-Untergrenze fuer Inhalt bleiben unveraendert. Titel brechen nur an Wortgrenzen. Drei fokussierte Typografieregressionen sowie der Gesamtlauf mit 736 bestandenen und 8 uebersprungenen Tests, Local-only-/Designpruefung und Client-/Server-Produktionsbuild erfolgreich. Keine Fachlogik-, API-, Persistenz- oder Schemaaenderung; Phase 7 bleibt abgeschlossen. | +| 2026-08-24 | Phase-7-Finding-Labels nach Nutzerkorrektur verdichtet: Outcome (`Failed`/`Passed`), Weight, Severity, Category, Score, CIS- und MITRE-Zuordnungen verwenden in Reader und regulaeren Matrixkacheln nun einen zentralen 12-px-Badgetoken statt der bisherigen 13-px-Labelstufe. Farb-, Glyph-, Rahmen-, Reihenfolge- und Statussemantik bleiben unveraendert; inhaltstragender Text wird nicht verkleinert. Fuenf fokussierte Typografieregressionen, 736 Standardtests, Local-only-Pruefung, Designpruefer und vollstaendiger Client-/Serverbuild erfolgreich; 8 optionale PostgreSQL-Tests uebersprungen. Keine Daten-, API-, Persistenz- oder Schemaaenderung; Phase 7 bleibt abgeschlossen. | +| 2026-08-24 | Phase-7-Finding-Typografie nach Nutzerkorrektur verdichtet: Der Reader-Titel und die Finding-Abschnittsueberschrift verwenden jetzt die bestehende H3-Rolle mit 18 px statt H2 mit 32 px; Titel in Trefferlisten und regulaeren Matrixkacheln verwenden die H4-Rolle mit 16 px statt der bisherigen 18-px-Heading-Rolle. Lange Scannerbezeichnungen beanspruchen damit weniger Zeilen, waehrend inhaltstragender Text die verbindliche 16-px-Untergrenze behaelt. Die Aenderung betrifft ausschliesslich bestehende Work-Selektoren; globale Seitentitel, Pathfinder-Micro-Cards, Daten-, Import- und Persistenzlogik bleiben unveraendert. Zwei neue Typografieregressionen, 735 Standardtests, Local-only-Pruefung, Designpruefer und vollstaendiger Client-/Serverbuild erfolgreich; 8 optionale PostgreSQL-Tests uebersprungen. Phase 7 bleibt abgeschlossen. | +| 2026-08-24 | Phase-7-Donut-Innenraum nach visueller Nutzerpruefung korrigiert: Zahl, Prozentzeichen und vorhandener Unterwert bleiben unveraendert im Ring, duerfen seine Kontur aber nicht beruehren. Die Zahl ist unabhaengig von der Einheit geometrisch zentriert; dreistellige Prozentwerte erhalten eine automatische Kompaktstufe. Fuer die kleineren Sidebar-Ringe sind Wert, Einheit und Einheitenabstand ueber eigene gemeinsame Tokens reduziert. 13 fokussierte Donut-, Sidebar- und Datahub-Regressionen, Local-only-Sicherheitspruefung, Designcheck mit 0 Rohfarben sowie Client-/Server-Produktionsbuild erfolgreich. Keine Daten-, Filter-, API-, Persistenz- oder Schemaaenderung; Phase 7 bleibt abgeschlossen. | +| 2026-08-24 | Phase-7-Donutstandard nach Nutzerreferenz vereinheitlicht und verbindlich dokumentiert: Datahub-Statuskarten und Sidebar-Severity-Karten verwenden statt offener 270-Grad-Gauges nun vollstaendige neutrale Ringspuren mit farbigem Uhrzeigersinnbogen ab 12 Uhr, flachen Enden und mittigem Prozentwert samt kleiner Einheit; der absolute Wert bleibt als dezente Sekundaerinformation erhalten. Dashboard-Statusringe verwenden dieselbe Spur und Strichstaerke. Die mehrteilige Risk-Severity-Verteilung behaelt ihre fachlichen Segmente, uebernimmt aber Geometrie, Spur, Strichstaerke, Startposition und zentrierte Prozentdarstellung. 730 Tests bestanden, 8 optionale PostgreSQL-Tests uebersprungen; Local-only-Sicherheitspruefung, Designcheck mit 0 Rohfarben sowie Client-/Server-Produktionsbuild erfolgreich. Keine Fachlogik-, Filter-, API-, Persistenz- oder Schemaaenderung; Phase 7 bleibt abgeschlossen. | +| 2026-08-24 | Phase-7-Typografiesystem vereinheitlicht und als verbindliche Projektvorgabe verankert: Inter Variable wird ausschliesslich lokal gebuendelt und ersetzt abweichende System- und Display-Schriften. Zentrale semantische Rollen definieren H1 bis H4, Fliesstext, UI-Labels, Metadaten, Tabellenkoepfe und Buttons; Uppercase ist auf Tabellenkoepfe und kurze technische Kennzeichnungen begrenzt. Datahub, Bereichsheader, Mandantendetail, Tabellen, Controls, Raw Data und Kennzahlen verwenden die gemeinsame Hierarchie. 730 Tests bestanden, 8 optionale PostgreSQL-Tests uebersprungen; Local-only-Sicherheitspruefung, Designcheck mit 0 Rohfarben sowie Client-/Server-Produktionsbuild erfolgreich. Keine Fachlogik-, API-, Persistenz- oder Schemaaenderung; Phase 7 bleibt abgeschlossen. | +| 2026-08-24 | Phase-7-Designkonflikte bereinigt: Mandantenkarten und die gemeinsam verwendeten Data-/Pathfinder-Tiles tragen weder regulaere noch Hover-Schatten und verwenden keinen Hover-Lift; Interaktion bleibt ueber Haarlinie und Hintergrund sichtbar. Redundante Pathfinder-Null-Ueberschreibungen wurden entfernt, Radius-/Control-Verweise auf vorhandene Tokens vereinheitlicht, die dynamische Dashboard-Segmentbreite erhielt einen CSS-Fallback und berechnete Result-Spaltenbreiten bleiben als erlaubte Geometrie gekapselt. Die Schattenobergrenze fuer `legacy-views.css` wurde von sechs auf drei abgesenkt. 728 Tests bestanden, 8 optionale PostgreSQL-Tests uebersprungen; Local-only-Sicherheitspruefung, vollstaendiger Designcheck mit 0 Rohfarben sowie Client-/Server-Produktionsbuild erfolgreich. Keine Fachlogik-, API-, Persistenz- oder Schemaaenderung; Phase 7 bleibt abgeschlossen. | +| 2026-08-24 | Phase-13A-HTML-Import gegen den reproduzierten 30-Sekunden-Abbruch mittelgrosser Scannerberichte nachgehaertet: Die gemeinsame Browser-/Node-Umschaltung verwendet den bereits begrenzten lexikalischen HTML-Extraktor nun ab mehr als 1 MiB statt erst oberhalb von 4 MiB. Worker-Timeout und Ressourcenlimits bleiben unveraendert. Ein synthetischer 1,6-MiB-PingCastle-Bericht bestaetigt Format, Profil, Tabelleninhalt und `html-bounded-extraction`; 30 fokussierte Parser-/Worker-/PingCastle-Tests sowie vollstaendiger Client-/Server-Produktionsbuild erfolgreich. Keine Realdaten gelesen, keine API-, Persistenz-, Schema- oder Profilversionsaenderung; Phase 13A bleibt abgeschlossen. | | 2026-08-24 | Phase-7-Dashboard-IOE-Kennzahl als Filtereinstieg umgesetzt: `IOEs found` ist bei vorhandenem Trefferbestand anklickbar, setzt bestehende Dashboardfilter zurueck, uebergibt exakt die als explizite `IOE Found`/`IOEs found` gezaehlten Datensatz-IDs an die vorhandene Work-Teilmenge und oeffnet Work. Bei null Treffern bleibt das Steuerelement deaktiviert, damit kein ungefilterter Bestand erscheint. Zehn fokussierte Dashboard- und Filtertests sowie vollstaendiger Client-/Server-Produktionsbuild erfolgreich; keine Import-, API-, Persistenz- oder Schemaaenderung. | | 2026-08-24 | Phase-7-Dashboard-Ringspur nach Nutzerreferenz sichtbar gemacht: Der bislang mit dem Panel verschmelzende ungefuellte Anteil der oberen Statusringe verwendet nun den vorhandenen neutralgrauen Donut-Rahmentoken; Farbbogen, Geometrie, Werte und Interaktionen bleiben unveraendert. Sieben fokussierte Dashboardtests und vollstaendiger Client-/Server-Produktionsbuild erfolgreich; keine Import-, API-, Persistenz- oder Schemaaenderung. | | 2026-08-24 | Phase-7-Dashboard-Statusleiste nach Nutzerreferenz korrigiert: Die sechs gleichartigen Donutkarten sind durch eine fuenfteilige Leiste ersetzt. Compliance Score erhaelt den grossen Ring mit Formelhinweis; Deviation, Passed und Not Run verwenden kompakte Ringe mit realer Anzahl und Prozentwert. Not Applicable und Error stehen gemeinsam in einer Zusammenfassung neben `IOEs found`. Der IOE-Wert zaehlt ausschliesslich explizite `IOE Found`-/`IOEs found`-Quellenfelder und wird nicht aus Deviations abgeleitet; Statuskarten bleiben direkte Filtereinstiege in Work. 14 fokussierte Dashboard-, Donut- und Inset-Tests sowie vollstaendiger Client-/Server-Produktionsbuild erfolgreich. Der bekannte Designcheck bleibt ausschliesslich an den unveraenderten Schattenregeln in `datahub-redesign.css` Zeilen 39/42 rot; keine Import-, API-, Persistenz- oder Schemaaenderung. | diff --git a/package-lock.json b/package-lock.json index 3926613..e330a41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "assessor", "version": "0.1.0", "dependencies": { + "@fontsource-variable/inter": "^5.3.0", "jsdom": "^29.1.1", "pg": "^8.22.0", "react": "19.2.4", @@ -961,6 +962,15 @@ } } }, + "node_modules/@fontsource-variable/inter": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/inter/-/inter-5.3.0.tgz", + "integrity": "sha512-OupL48va4JNofb97w6NYeF9S7W/kHNKM0Er8Dem5nqi4jeOLrVJDoE8tZEpnMJmtkvNbB1EIPPwHcdkF6b1oUA==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", diff --git a/package.json b/package.json index 80f8862..17f90ee 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "test:watch": "vitest --config vitest.config.ts" }, "dependencies": { + "@fontsource-variable/inter": "^5.3.0", "jsdom": "^29.1.1", "pg": "^8.22.0", "react": "19.2.4", diff --git a/src/app/App.tsx b/src/app/App.tsx index d9c1a99..43c060e 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -39,7 +39,7 @@ function AreaIcon({ area }: { area: AppArea }) { const AREA_TOPBAR_TITLE: Record = { 'db-structure': 'DB Structure', 'data-hub': 'Data Hub', - dashboard: 'Dashboard', + dashboard: 'Client Info', graph: 'Pathfinder', work: 'Work', results: 'Result DB' @@ -52,7 +52,7 @@ const AREA_TOPBAR_TITLE: Record = { const NAV_AREAS = [ ['db-structure', 'DB Structure'], ['data-hub', 'Datahub'], - ['dashboard', 'Dashboard'], + ['dashboard', 'Client Info'], ['work', 'Work'], ['results', 'Result DB'], ['graph', 'Pathfinder'] diff --git a/src/components/DonutMetricCard/DonutMetricCard.tsx b/src/components/DonutMetricCard/DonutMetricCard.tsx index 7c0051a..83603d0 100644 --- a/src/components/DonutMetricCard/DonutMetricCard.tsx +++ b/src/components/DonutMetricCard/DonutMetricCard.tsx @@ -37,21 +37,23 @@ export function DonutMetricCard({ onClick }: DonutMetricCardProps) { const boundedPercentage = Math.min(100, Math.max(0, Math.round(percentage))); - const arcLength = boundedPercentage * 0.75; - const dashArray = String(arcLength) + ' ' + String(100 - arcLength); + const dashArray = String(boundedPercentage) + ' ' + String(100 - boundedPercentage); const progressStyle = { '--donut-progress-dash': dashArray } as CSSProperties; const classes = ['donut-metric-card', 'donut-metric-card--' + tone, className].filter(Boolean).join(' '); const ariaLabel = label + ': ' + value.toLocaleString('en-US') + ', ' + boundedPercentage + '%'; const content = <>

{label}

-
- {value.toLocaleString('en-US')}{valueSuffix} - {showPercentage && {boundedPercentage}%} +
= 100 ? ' is-compact' : ''}`}> + {showPercentage ? boundedPercentage : value.toLocaleString('en-US')}{showPercentage ? '' : valueSuffix} + {showPercentage && %} +
+ {showPercentage && {value.toLocaleString('en-US')}{valueSuffix}}
; diff --git a/src/components/ResizableResultTable/ResizableResultTable.tsx b/src/components/ResizableResultTable/ResizableResultTable.tsx index f22d3cf..8609bf4 100644 --- a/src/components/ResizableResultTable/ResizableResultTable.tsx +++ b/src/components/ResizableResultTable/ResizableResultTable.tsx @@ -21,6 +21,10 @@ function clampColumnWidth(width: number): number { return Math.max(MIN_COLUMN_WIDTH, Math.min(MAX_COLUMN_WIDTH, Math.round(width))); } +function columnWidthStyle(width: number): CSSProperties { + return { width: `${width}px` }; +} + function measuredColumnWidths(table: HTMLTableElement, columnCount: number): number[] { const headers = Array.from(table.tHead?.rows[0]?.cells ?? []); return Array.from({ length: columnCount }, (_, index) => clampColumnWidth(headers[index]?.getBoundingClientRect().width || MIN_COLUMN_WIDTH)); @@ -101,7 +105,7 @@ export function ResizableResultTable({ headers, children, enabled = false, class return ( {enabled && } - {appliedWidths.length > 0 && {appliedWidths.map((width, index) => )}} + {appliedWidths.length > 0 && {appliedWidths.map((width, index) => )}} {headers.map((header, index) =>
Column widths can be changed by dragging a header separator or fitted by double-clicking it.
{header}{enabled && index < columnCount - 1 && - {roundedPercentage}% + = 100 ? ' is-compact' : ''}`}>{roundedPercentage}%
{label} - {large ? `${roundedPercentage} %` : <>{value.toLocaleString('en-US')} · {roundedPercentage}%} + {large ? `${roundedPercentage} %` : <>{value.toLocaleString('en-US')}{roundedPercentage}%} {helper && {helper}}
; @@ -328,24 +328,27 @@ function RiskProfile({ sourceFacet, categoryFacet, severityFacet, records, profi ) :

No risk categories available.

}
{severities.length > 0 ? <> - - - {severities.map(severity => { - const percentage = share(severity.count, severityTotal); - const offset = severityOffset; - severityOffset += percentage; - return ; - })} - +
+ + 100% +
    {severities.map(severity => (
  • diff --git a/src/features/dataset-viewer/DatasetViewer.tsx b/src/features/dataset-viewer/DatasetViewer.tsx index 1211666..d5d85b1 100644 --- a/src/features/dataset-viewer/DatasetViewer.tsx +++ b/src/features/dataset-viewer/DatasetViewer.tsx @@ -34,10 +34,10 @@ import { toolIdForRecord } from '../../importers/status-normalization'; * zweizeilige Titel bleibt durch den Overscan unbemerkbar. */ const RECORD_ROW_HEIGHT = 78; -/** Mindestbreite einer Kachel, siehe `.tile-grid` in legacy-views.css. */ -const TILE_MIN_WIDTH = 280; -/** `gap` von `.tile-grid` (--panel-inset-Token, 20px). */ -const TILE_GAP = 20; +/** Verbindliche Inhaltskachelbreite, siehe `--tile-content-width`. */ +const TILE_MIN_WIDTH = 320; +/** Einheitlicher Segmentabstand, siehe `--segment-gap`. */ +const TILE_GAP = 8; /** `padding` von `.tile-grid` (--panel-inset-Token, 20px je Seite). */ const TILE_GRID_INSET = 20; /** Geschaetzte Kachelzeilenhoehe inklusive Zeilenabstand, aus demselben Grund @@ -316,7 +316,7 @@ export function DatasetViewer() { // literalen Inline-Style-Objekts, damit die Darstellung sonst in // src/styles bleibt (siehe FloatingResultsWindow.tsx fuer denselben // Kompromiss bei einer anderen laufzeitberechneten Position). - const tileGridStyle = { gridTemplateColumns: `repeat(${tileColumns}, 1fr)` }; + const tileGridStyle = { gridTemplateColumns: `repeat(${tileColumns}, minmax(var(--tile-content-width), 1fr))` }; const tileTopSpacerStyle = { gridColumn: '1 / -1', height: tileWindow.paddingTop }; const tileBottomSpacerStyle = { gridColumn: '1 / -1', height: tileWindow.paddingBottom }; const recordTopSpacerStyle = { height: recordWindow.paddingTop }; @@ -332,7 +332,7 @@ export function DatasetViewer() { {!records.length ? ( ) : view === 'columns' ? ( -
    +
    {tileWindow.paddingTop > 0 &&