diff --git a/admin-compliance/app/layout.tsx b/admin-compliance/app/layout.tsx index 53bcf349..ccf2f814 100644 --- a/admin-compliance/app/layout.tsx +++ b/admin-compliance/app/layout.tsx @@ -1,13 +1,8 @@ import type { Metadata } from 'next' -import { Inter, Public_Sans, Source_Serif_4, IBM_Plex_Mono } from 'next/font/google' +import { Inter } from 'next/font/google' import './globals.css' const inter = Inter({ subsets: ['latin'] }) -// Redesign fonts (design/redesign) — exposed as CSS variables; the new -// design-language components opt in via Tailwind font-publicSans/-sourceSerif/-plexMono. -const publicSans = Public_Sans({ subsets: ['latin'], weight: ['400', '500', '600', '700', '800'], variable: '--font-public-sans' }) -const sourceSerif = Source_Serif_4({ subsets: ['latin'], weight: ['400', '500', '600'], style: ['normal', 'italic'], variable: '--font-source-serif' }) -const plexMono = IBM_Plex_Mono({ subsets: ['latin'], weight: ['400', '500', '600'], variable: '--font-plex-mono' }) export const metadata: Metadata = { title: 'BreakPilot Admin Compliance', @@ -20,7 +15,7 @@ export default function RootLayout({ children: React.ReactNode }) { return ( - + {children} ) diff --git a/admin-compliance/app/sdk/design-system/page.tsx b/admin-compliance/app/sdk/design-system/page.tsx deleted file mode 100644 index 8905e110..00000000 --- a/admin-compliance/app/sdk/design-system/page.tsx +++ /dev/null @@ -1,114 +0,0 @@ -'use client' - -// Design-system showcase (Schritt A verification). Renders the redesign tokens + -// chips so the design language can be reviewed in isolation. Internal reference -// page (not in the customer sidebar). - -import { GeltungChip, SeverityChip, DomainTag, MonoId } from '@/components/redesign/Chips' -import { COLORS, DOMAIN } from '@/components/redesign/tokens' - -function Section({ title, children }: { title: string; children: React.ReactNode }) { - return ( -
-

{title}

-
{children}
-
- ) -} - -function Swatch({ name, hex }: { name: string; hex: string }) { - return ( -
-
-
{name}
-
{hex}
-
- ) -} - -export default function DesignSystemPage() { - return ( -
-
-

- Design-Sprache -

-

- Schritt A — Tokens & Bausteine des Redesigns (Geltung, Severity, Domänen, Typografie). - Referenz: design/redesign/HANDOFF_README.md. -

- -
-
- - - - + Klartext-Maßnahme - M542 -
-
- -
-
- - - - -
-
- -
-
- - - -
-
- {(['safety', 'cyber', 'bridge'] as const).map((k) => ( -
-
{DOMAIN[k].label}
-
{DOMAIN[k].accent}
-
- ))} -
-
- -
-
-
Public Sans — UI & Überschriften (800)
-
Source Serif 4 — Normzitate / rechtliche Texte (kursiv)
-
IBM Plex Mono — interne IDs · CRA-AI-8 · R = S × (F + W + P)
-
-
- -
-
- - - - - -
-
- -
-
-
    - {[ - '3 Ebenen pro Screen: Überblick → Cyber×Safety → Technik (eingeklappt).', - 'Klartext führt. Interne IDs nur in Monospace nachgestellt.', - 'Co-Pilot statt Roboter-Anwalt — keine Panik-Rot-Blöcke.', - 'Pflicht / Empfehlung / Kann immer visuell getrennt.', - ].map((t, i) => ( -
  1. - {i + 1} - {t} -
  2. - ))} -
-
-
-
-
- ) -} diff --git a/admin-compliance/app/sdk/iace/[projectId]/cra/_components/CyberMeetsSafety.tsx b/admin-compliance/app/sdk/iace/[projectId]/cra/_components/CyberMeetsSafety.tsx deleted file mode 100644 index 42457ed6..00000000 --- a/admin-compliance/app/sdk/iace/[projectId]/cra/_components/CyberMeetsSafety.tsx +++ /dev/null @@ -1,189 +0,0 @@ -'use client' - -// Ebene 2 — "Cyber trifft Safety" (Redesign-Herzstück / USP). -// Macht sichtbar, wo ein Cyber-Befund eine bereits mechanisch gemilderte -// CE-Gefährdung wieder aufreißt. Bindet an die ECHTEN Bridge-Daten (cross_links) -// + findings + open_measures aus useCRA. Design nach design/redesign/HANDOFF_README.md. -// Additiv: ersetzt den bestehenden CRACyberView NICHT. - -import { useState } from 'react' -import { CRADemo, CrossLink, CRAFinding, Measure } from '../_hooks/useCRADemo' -import { GeltungChip, MonoId } from '@/components/redesign/Chips' -import { COLORS, DOMAIN, Geltung } from '@/components/redesign/tokens' - -// Maßnahme → Geltung: technische Schutzmaßnahmen = Pflicht; Info/Hardening-Guides -// = Empfehlung. Heuristik (kein Geltung-Feld in den Daten); nie still „Kann". -function measureGeltung(name: string, id: string): Geltung { - const hay = `${id} ${name}`.toLowerCase() - return /info|guide|hardening|dokumentation|beilegen|hinweis|schulung/.test(hay) ? 'empfehlung' : 'pflicht' -} - -function ChainNode({ tone, marker, label, text }: { - tone: 'ce' | 'cyber' | 'residual'; marker: string; label: string; text: string -}) { - const s = tone === 'ce' - ? { bg: DOMAIN.safety.tint, border: DOMAIN.safety.border, accent: DOMAIN.safety.accent } - : tone === 'cyber' - ? { bg: DOMAIN.cyber.tint, border: DOMAIN.cyber.border, accent: DOMAIN.cyber.accent } - : { bg: '#FBECEA', border: '#F3D2CC', accent: '#A23323' } - return ( -
-
- {marker}{label} -
-
{text}
-
- ) -} - -function HazardCard({ link, findings, measures, defaultOpen }: { - link: CrossLink; findings: CRAFinding[]; measures: Measure[]; defaultOpen: boolean -}) { - const [open, setOpen] = useState(defaultOpen) - const triggers = findings.filter((f) => link.cyber_finding_ids.includes(f.id)) - const cyberText = triggers.map((f) => f.title).join(' · ') || link.cyber_finding_ids.join(', ') - const measureIds = Array.from(new Set(triggers.flatMap((f) => f.measures))) - const measureObjs = measureIds.map((id) => measures.find((m) => m.id === id) || { id, name: id, description: '', norm_refs: [] }) - const normPills = Array.from(new Set(triggers.flatMap((f) => [f.annex_anchor, ...f.requirement_ids, ...f.iso27001_ref]).filter(Boolean))) - - return ( -
-
-
-
Mechanische Gefährdung
-

{link.safety_hazard}

-
- - - Restrisiko: wieder offen - -
- -
- - - - - -
- -
- Warum: - {link.cyber_breaks_it} -
- - {measureObjs.length > 0 && ( -
-
Empfohlene Maßnahmen
-
    - {measureObjs.map((m) => ( -
  • - - {m.name} - {m.id} -
  • - ))} -
-
- )} - -
- - {open && ( -
-
-
Auslösende Cyber-Befunde
-
- {triggers.map((f) => {f.location || f.id})} -
-
-
-
Norm- & Annex-Bezug
-
- {normPills.map((n) => ( - {n} - ))} -
-
-
- )} -
-
- ) -} - -export function CyberMeetsSafety({ data }: { data: CRADemo }) { - const links = data.cross_links || [] - const measures = data.open_measures || [] - const [filter, setFilter] = useState<'alle' | 'pflicht' | 'empfehlung'>('alle') - - if (links.length === 0) { - return ( -
-
Cyber trifft Safety
-

- Aktuell keine Cyber-Befunde, die eine CE-Gefährdung wieder öffnen. Sobald Befunde vorliegen, erscheinen sie hier. -

-
- ) - } - - const backlog = measures.map((m) => ({ m, g: measureGeltung(m.name, m.id) })) - .filter((x) => filter === 'alle' || x.g === filter) - - return ( -
- {/* Domänen-Bar */} -
-
-
Safety (Maschine / CE)
-
Mechanisch gemilderte Gefährdungen
-
-
-
-
{links.length} wieder geöffnet
-
-
-
Cyber (CRA)
-
Befunde, die Schutzfunktionen aushebeln
-
-
- - {links.map((link, i) => ( - - ))} - - {/* Maßnahmen-Backlog */} -
-
-
- Maßnahmen-Backlog · {measures.length} Maßnahmen · nach Geltung -
-
- {(['alle', 'pflicht', 'empfehlung'] as const).map((f) => ( - - ))} -
-
-
    - {backlog.map(({ m, g }) => ( -
  • - - {m.name} - {m.norm_refs?.[0] || 'Sicherheit'} - {m.id} -
  • - ))} - {backlog.length === 0 &&
  • Keine Maßnahmen in diesem Filter.
  • } -
-
-
- ) -} diff --git a/admin-compliance/app/sdk/iace/[projectId]/cra/page.tsx b/admin-compliance/app/sdk/iace/[projectId]/cra/page.tsx index 0693183b..61c0b968 100644 --- a/admin-compliance/app/sdk/iace/[projectId]/cra/page.tsx +++ b/admin-compliance/app/sdk/iace/[projectId]/cra/page.tsx @@ -3,7 +3,6 @@ import { useParams } from 'next/navigation' import { useCRA } from './_hooks/useCRA' import { CRACyberView } from './_components/CRACyberView' -import { CyberMeetsSafety } from './_components/CyberMeetsSafety' import { WeightsControl } from './_components/WeightsControl' import { SnapshotPanel } from './_components/SnapshotPanel' import { ScannerRepoPicker } from './_components/ScannerRepoPicker' @@ -32,22 +31,7 @@ export default function CRAPage() { )} - - {/* Ebene 2 — Cyber trifft Safety (Redesign, neue Design-Sprache) */} -
-

- Cyber trifft Safety -

- -
- - {/* Bisherige Detailansicht (bleibt erhalten, bis das Redesign 100% abdeckt) */} -
- Bisherige Detailansicht (CRACyberView) -
- -
-
+
) diff --git a/admin-compliance/components/redesign/Chips.tsx b/admin-compliance/components/redesign/Chips.tsx deleted file mode 100644 index aded5fa8..00000000 --- a/admin-compliance/components/redesign/Chips.tsx +++ /dev/null @@ -1,64 +0,0 @@ -'use client' - -// Reusable design-language chips. Geltung (Pflicht/Empfehlung/Kann) and Severity -// follow the handoff specs exactly (bg/text/border + marker glyph). Klartext label -// leads; internal IDs are never shown by the chip itself. See ./tokens. - -import { - GELTUNG, PFLICHT_MARKER, SEVERITY, DOMAIN, - normalizeGeltung, normalizeSeverity, Geltung, Severity, Domain, -} from './tokens' - -function GeltungMarker({ g }: { g: Geltung }) { - const m = GELTUNG[g].marker - if (m === 'square') { - return - } - // diamond ◇ (Empfehlung) / circle ○ (Kann) — open glyphs in the chip text color - return {m === 'diamond' ? '◇' : '○'} -} - -export function GeltungChip({ value, className = '' }: { value: Geltung | string; className?: string }) { - const g = normalizeGeltung(value) - const t = GELTUNG[g] - return ( - - - {t.label} - - ) -} - -export function SeverityChip({ value, className = '' }: { value: Severity | string; className?: string }) { - const s = normalizeSeverity(value) - const t = SEVERITY[s] - return ( - - {t.label} - - ) -} - -// Small domain tag (Safety / Cyber / Schnittstelle) — tinted, not neon. -export function DomainTag({ value, label, className = '' }: { value: Domain; label?: string; className?: string }) { - const d = DOMAIN[value] - return ( - - {label || d.label} - - ) -} - -// Monospace internal ID — IDs are ALWAYS secondary/nachgestellt, never a heading. -export function MonoId({ children, className = '' }: { children: React.ReactNode; className?: string }) { - return {children} -} diff --git a/admin-compliance/components/redesign/tokens.ts b/admin-compliance/components/redesign/tokens.ts deleted file mode 100644 index c2ff19b3..00000000 --- a/admin-compliance/components/redesign/tokens.ts +++ /dev/null @@ -1,55 +0,0 @@ -// Design-language tokens — single source of truth for the redesign. -// Mirrors design/redesign/HANDOFF_README.md (Claude Design handoff). The same -// values are mirrored into tailwind.config.ts (namespaces re/geltung/severity/domain) -// for utility-class use; components that need the exact chip look import from here. - -export const COLORS = { - page: '#EDEFF3', surface: '#FFFFFF', border: '#E4E7EE', borderSoft: '#F0F1F5', - ink: '#1A1D29', muted: '#5A6273', muted2: '#6B7184', faint: '#8089A0', fainter: '#9AA1B2', - brand: '#4338CA', brandText: '#3B36B0', brandTint: '#EEF0FF', brandTint2: '#F6F4FF', - panel: '#15182A', panelText: '#E8EAF2', panelText2: '#C7CBDA', panelAccent: '#9B8BF5', -} as const - -// --- Geltung: Pflicht / Empfehlung / Kann (the core 3-tier obligation system) --- -export type Geltung = 'pflicht' | 'empfehlung' | 'kann' -export const GELTUNG: Record = { - pflicht: { label: 'Pflicht', bg: '#FBECEA', text: '#A23323', border: '#F3D2CC', marker: 'square' }, - empfehlung: { label: 'Empfehlung', bg: '#EEF0FF', text: '#3B36B0', border: '#DAD9F7', marker: 'diamond' }, - kann: { label: 'Kann', bg: '#F1F3F7', text: '#5A6273', border: '#E2E6EE', marker: 'circle' }, -} -export const PFLICHT_MARKER = '#C0362C' // filled square color - -export function normalizeGeltung(v: string | Geltung): Geltung { - const s = String(v || '').toLowerCase() - if (['pflicht', 'mandatory', 'required', 'must', 'core'].includes(s)) return 'pflicht' - if (['empfehlung', 'recommended', 'should', 'review'].includes(s)) return 'empfehlung' - if (['kann', 'optional', 'may', 'can'].includes(s)) return 'kann' - return 'empfehlung' // unknown → recommendation (never silently drop; never over-state as Pflicht) -} - -// --- Severity (Dringlichkeit) --- -export type Severity = 'kritisch' | 'hoch' | 'mittel' | 'niedrig' -export const SEVERITY: Record = { - kritisch: { label: 'Kritisch', bg: '#FBE9E7', text: '#B5362A' }, - hoch: { label: 'Hoch', bg: '#FBF1E0', text: '#9A6410' }, - mittel: { label: 'Mittel', bg: '#FAF6DD', text: '#897209' }, - niedrig: { label: 'Niedrig', bg: '#E9F5EF', text: '#2C7A52' }, -} -export function normalizeSeverity(v: string | Severity): Severity { - const s = String(v || '').toLowerCase() - if (['kritisch', 'critical'].includes(s)) return 'kritisch' - if (['hoch', 'high'].includes(s)) return 'hoch' - if (['mittel', 'medium', 'moderate'].includes(s)) return 'mittel' - if (['niedrig', 'low', 'minor'].includes(s)) return 'niedrig' - return 'mittel' -} - -// --- Domains (Safety / Cyber / Schnittstelle) --- -export type Domain = 'safety' | 'cyber' | 'bridge' -export const DOMAIN: Record = { - safety: { label: 'Safety (Maschine/CE)', accent: '#0E8A66', tint: '#F3FAF7', border: '#D7ECE3' }, - cyber: { label: 'Cyber (CRA)', accent: '#6A43D6', tint: '#F6F1FE', border: '#E4D8F7' }, - bridge: { label: 'Cyber × Safety', accent: '#BE7714', tint: '#FCF6EF', border: '#F2E6D5', warn: '#9A6410' }, -} diff --git a/admin-compliance/tailwind.config.ts b/admin-compliance/tailwind.config.ts index a92c4ea1..0532d33c 100644 --- a/admin-compliance/tailwind.config.ts +++ b/admin-compliance/tailwind.config.ts @@ -48,39 +48,9 @@ const config: Config = { 900: '#0c4a6e', 950: '#082f49', }, - - // === Redesign design-language tokens (2026-06, see design/redesign) === - // Additive + namespaced ('re', 'geltung', 'severity', 'domain') so nothing - // existing is overridden. Single source of truth: components/redesign/tokens.ts. - re: { - page: '#EDEFF3', surface: '#FFFFFF', border: '#E4E7EE', 'border-soft': '#F0F1F5', - ink: '#1A1D29', muted: '#5A6273', 'muted-2': '#6B7184', faint: '#8089A0', fainter: '#9AA1B2', - brand: '#4338CA', 'brand-text': '#3B36B0', 'brand-tint': '#EEF0FF', 'brand-tint-2': '#F6F4FF', - panel: '#15182A', 'panel-text': '#E8EAF2', 'panel-text-2': '#C7CBDA', 'panel-accent': '#9B8BF5', - }, - geltung: { - pflicht: { bg: '#FBECEA', text: '#A23323', border: '#F3D2CC', marker: '#C0362C' }, - empfehlung: { bg: '#EEF0FF', text: '#3B36B0', border: '#DAD9F7' }, - kann: { bg: '#F1F3F7', text: '#5A6273', border: '#E2E6EE' }, - }, - severity: { - kritisch: { bg: '#FBE9E7', text: '#B5362A' }, - hoch: { bg: '#FBF1E0', text: '#9A6410' }, - mittel: { bg: '#FAF6DD', text: '#897209' }, - niedrig: { bg: '#E9F5EF', text: '#2C7A52' }, - }, - domain: { - safety: '#0E8A66', 'safety-tint': '#F3FAF7', 'safety-border': '#D7ECE3', - cyber: '#6A43D6', 'cyber-tint': '#F6F1FE', 'cyber-border': '#E4D8F7', - bridge: '#BE7714', 'bridge-tint': '#FCF6EF', 'bridge-border': '#F2E6D5', 'bridge-warn': '#9A6410', - }, }, fontFamily: { sans: ['Inter', 'system-ui', 'sans-serif'], - // Redesign fonts (loaded via next/font in app/layout.tsx as CSS variables). - publicSans: ['var(--font-public-sans)', 'Inter', 'system-ui', 'sans-serif'], - sourceSerif: ['var(--font-source-serif)', 'Georgia', 'serif'], - plexMono: ['var(--font-plex-mono)', 'ui-monospace', 'SFMono-Regular', 'monospace'], }, }, },