From cd31274738e394e3ec9d5ee09b50ce63a223381b Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 4 Jul 2026 09:16:14 +0000 Subject: [PATCH] feat(ai-act-kompass): i18n, national modules, task checklist, legal gate, new documents - Full i18n (DE/EN): UI, rules engine, content pack and generated documents; language switcher, browser-language default, architecture extensible to further EU languages via localized source texts - National country modules (opt-in per company profile): DE (KI-MIG/ BNetzA supervision, works council co-determination Sec. 87 BetrVG, GDPR/BDSG), AT (RTR AI service desk), IT (Law 132/2025: employer info duty, deepfake criminal liability, sector decrees), ES (AESIA, sandbox RD 817/2023), FR (CNIL guidance), NL (algorithm register); merged into obligation derivation, checklists and documents - New EU obligations: corrective actions (Art. 20/21), authorised representative (Art. 22), EU database registration for public-body deployers (Art. 49(3)), post-market monitoring (Art. 72), serious incident reporting (Art. 73), GDPR DPIA (Art. 35 GDPR / 26(9)) - New documents: AI procurement requirements specification (MUST/SHOULD matrix for vendors) and EU declaration of conformity draft (Annex V) - Cross-system checkable task list (due now / upcoming / done) with progress bar, one-tap toggling, per-system links - Legal safety: first-run acknowledgement gate (working aid, no legal services), legal notice panel in settings, result-screen disclaimer, disclaimers on every document - Responsive layout for phone/tablet (top-bar nav, scrollable tables, larger tap targets) - 12 new tests (44 total): EN pack parity, national merge, checklist grouping, new document generators Claude-Session: https://claude.ai/code/session_017YjohVNx1ZGNM4MX7tHpfv --- ai-act-kompass/README.md | 13 +- ai-act-kompass/src/content/pack.ts | 469 ++++-------------- ai-act-kompass/src/content/packCore.ts | 260 ++++++++++ ai-act-kompass/src/content/packNational.ts | 237 +++++++++ ai-act-kompass/src/content/packObligations.ts | 366 ++++++++++++++ ai-act-kompass/src/content/sourceTypes.ts | 37 ++ ai-act-kompass/src/engine/checklist.ts | 77 +++ ai-act-kompass/src/engine/classify.ts | 87 +++- ai-act-kompass/src/engine/docStrings.ts | 201 ++++++++ ai-act-kompass/src/engine/documents.test.ts | 1 + ai-act-kompass/src/engine/documents.ts | 245 ++++----- ai-act-kompass/src/engine/extraDocs.ts | 189 +++++++ ai-act-kompass/src/engine/i18nContent.test.ts | 158 ++++++ ai-act-kompass/src/engine/obligations.ts | 22 +- ai-act-kompass/src/engine/types.ts | 15 + ai-act-kompass/src/i18n/context.tsx | 47 ++ ai-act-kompass/src/i18n/types.ts | 48 ++ ai-act-kompass/src/i18n/ui.ts | 317 ++++++++++++ ai-act-kompass/src/main.tsx | 5 +- ai-act-kompass/src/store/storage.ts | 1 + ai-act-kompass/src/styles.css | 86 ++++ ai-act-kompass/src/ui/App.tsx | 71 ++- ai-act-kompass/src/ui/ChecklistView.tsx | 133 +++++ ai-act-kompass/src/ui/Dashboard.tsx | 113 ++--- ai-act-kompass/src/ui/DisclaimerGate.tsx | 47 ++ ai-act-kompass/src/ui/SettingsView.tsx | 150 ++++-- ai-act-kompass/src/ui/SystemDetail.tsx | 196 ++++---- ai-act-kompass/src/ui/Wizard.tsx | 129 +++-- ai-act-kompass/src/ui/common.tsx | 46 +- 29 files changed, 2948 insertions(+), 818 deletions(-) create mode 100644 ai-act-kompass/src/content/packCore.ts create mode 100644 ai-act-kompass/src/content/packNational.ts create mode 100644 ai-act-kompass/src/content/packObligations.ts create mode 100644 ai-act-kompass/src/content/sourceTypes.ts create mode 100644 ai-act-kompass/src/engine/checklist.ts create mode 100644 ai-act-kompass/src/engine/docStrings.ts create mode 100644 ai-act-kompass/src/engine/extraDocs.ts create mode 100644 ai-act-kompass/src/engine/i18nContent.test.ts create mode 100644 ai-act-kompass/src/i18n/context.tsx create mode 100644 ai-act-kompass/src/i18n/types.ts create mode 100644 ai-act-kompass/src/i18n/ui.ts create mode 100644 ai-act-kompass/src/ui/ChecklistView.tsx create mode 100644 ai-act-kompass/src/ui/DisclaimerGate.tsx diff --git a/ai-act-kompass/README.md b/ai-act-kompass/README.md index 0ca801e..ee4916a 100644 --- a/ai-act-kompass/README.md +++ b/ai-act-kompass/README.md @@ -1,15 +1,18 @@ -# 🧭 AI-Act-Kompass +# AI-Act-Kompass -**Standalone EU-AI-Act-Compliance-Toolkit fĂŒr KMU.** Local-first: keine Cloud, keine Telemetrie, keine DatenĂŒbertragung — alle Daten bleiben im Browser des Nutzers (localStorage, mit JSON-Export/-Import). +**Standalone EU-AI-Act-Compliance-Toolkit fĂŒr europĂ€ische Unternehmen (KMU-Fokus).** Local-first: keine Cloud, keine Telemetrie, keine DatenĂŒbertragung — alle Daten bleiben im Browser des Nutzers (localStorage, mit JSON-Export/-Import). Mehrsprachig (DE/EN, erweiterbar), responsiv fĂŒr Handy, Tablet und Desktop. -> ⚠ Strukturierte Arbeitshilfe auf Basis der Verordnung (EU) 2024/1689 — **keine Rechtsberatung**. +> ⚠ Strukturierte Arbeitshilfe auf Basis der Verordnung (EU) 2024/1689 — **keine Rechtsberatung** und keine Rechtsdienstleistung i. S. d. RDG. Beim ersten Start bestĂ€tigungspflichtiger rechtlicher Hinweis; Disclaimer in App und allen Dokumenten. ## Was es kann - **Bewertungs-Wizard**: erfasst ein KI-System und klassifiziert es automatisch nach der Systematik des AI Act — KI-Definition (Art. 3 Nr. 1) → verbotene Praktiken (Art. 5) → Hochrisiko (Art. 6, Annex I/III, inkl. Ausnahme Art. 6 Abs. 3 und Profiling-Sperre) → Transparenzpflichten (Art. 50) → minimales Risiko. GPAI-Pflichten (Art. 53 ff.) werden separat geflaggt. +- **Aufgaben & Fristen**: systemĂŒbergreifende, abhakbare Pflichtenliste („Jetzt fĂ€llig / Kommt auf Sie zu / Erledigt") mit Fortschrittsanzeige — mobiltauglich. - **KI-Inventar**: alle Systeme des Unternehmens mit Risikoklasse, Rolle und Verantwortlichen — exportierbar als Markdown (AI Asset Register). -- **Pflichten-Checklisten**: rollenspezifisch (Anbieter/Betreiber/EinfĂŒhrer/HĂ€ndler) abgeleitete Pflichten mit Rechtsgrundlage, Geltungsfrist und Umsetzungsstatus. -- **Dokumenten-Generator**: Risikoklassifizierungs-Bericht, Maßnahmenplan und Annex-IV-GrundgerĂŒst (technische Dokumentation) als Markdown. +- **Pflichten-Checklisten**: rollenspezifisch (Anbieter/Betreiber/EinfĂŒhrer/HĂ€ndler) abgeleitete EU-Pflichten (Art. 4–73) mit Rechtsgrundlage, Geltungsfrist und Umsetzungsstatus. +- **LĂ€nder-Module** (zuschaltbar): nationale Zusatzanforderungen fĂŒr DE (KI-MIG/BNetzA, § 87 BetrVG, DSGVO/BDSG), AT (RTR-KI-Servicestelle), IT (Legge 132/2025: Arbeitgeber-Info, Deepfake-Strafnormen, Sektorregeln), ES (AESIA, Sandbox RD 817/2023), FR (CNIL), NL (Algorithmenregister). Bei grenzĂŒberschreitender Zusammenarbeit mehrere LĂ€nder aktivieren. +- **Dokumenten-Generator** (Markdown + Druck/PDF mit Firmen-Briefkopf): Risikobericht, Maßnahmenplan, Annex-IV-GrundgerĂŒst, **Lastenheft KI-Beschaffung** (AI-Act-Anforderungen an Anbieter, MUSS/SOLL) und **EU-KonformitĂ€tserklĂ€rung** (Entwurf nach Art. 47/Annex V). +- **Firmenprofil**: Logo, Name, Kontaktdaten — erscheinen in Briefkopf und Fußzeile aller gedruckten Dokumente. - **Fristen-Übersicht**: alle Geltungsfristen des AI Act inkl. der Digital-Omnibus-Verschiebung (Annex III → 02.12.2027). ## Architektur diff --git a/ai-act-kompass/src/content/pack.ts b/ai-act-kompass/src/content/pack.ts index b3d5cf9..aead684 100644 --- a/ai-act-kompass/src/content/pack.ts +++ b/ai-act-kompass/src/content/pack.ts @@ -1,389 +1,100 @@ -import type { ContentPack } from '../engine/types'; +import type { + ContentItem, + ContentPack, + Deadline, + NationalPack, + Obligation, +} from '../engine/types'; +import type { Locale, LText } from '../i18n/types'; +import { + AI_DEFINITION_CRITERIA, + ANNEX_III_CATEGORIES, + DEADLINES, + PROHIBITED_PRACTICES, + TRANSPARENCY_TRIGGERS, +} from './packCore'; +import { NATIONAL_PACKS } from './packNational'; +import { OBLIGATIONS } from './packObligations'; +import type { ContentItemSrc, DeadlineSrc, NationalPackSrc, ObligationSrc } from './sourceTypes'; /** * Regulatorischer Content-Pack v2026.07. * * Kodifiziert den Stand der Verordnung (EU) 2024/1689 (AI Act) inkl. der - * durch das Digital-Omnibus-Paket verschobenen Annex-III-Fristen. - * Dieser Pack ist bewusst als Daten (nicht Code) modelliert, damit er - * spĂ€ter ĂŒber einen signierten Update-Feed ausgetauscht werden kann. + * durch das Digital-Omnibus-Paket verschobenen Annex-III-Fristen sowie + * nationale Zusatzmodule (DE, AT, IT, ES, FR, NL). Quelltexte sind + * lokalisiert (DE/EN) und werden hier je Sprache aufgelöst — vorbereitet + * fĂŒr einen spĂ€teren signierten Update-Feed. * * WICHTIG: Inhalte sind eine strukturierte Arbeitshilfe, keine Rechtsberatung. */ -export const CONTENT_PACK: ContentPack = { - version: '2026.07.0', - releasedAt: '2026-07-04', - legalBasis: 'Verordnung (EU) 2024/1689 (AI Act), Stand Juli 2026', +const VERSION = '2026.07.1'; +const RELEASED_AT = '2026-07-04'; - aiDefinitionCriteria: [ - { - id: 'def-autonomy', - label: 'Das System arbeitet mit einem gewissen Grad an Autonomie (nicht rein regelbasiert deterministisch durch den Nutzer gesteuert).', - legalRef: 'Art. 3 Nr. 1', - }, - { - id: 'def-inference', - label: 'Das System leitet aus Eingaben ab, wie Ausgaben (Vorhersagen, Inhalte, Empfehlungen, Entscheidungen) erzeugt werden.', - legalRef: 'Art. 3 Nr. 1', - }, - { - id: 'def-influence', - label: 'Die Ausgaben können physische oder virtuelle Umgebungen beeinflussen.', - legalRef: 'Art. 3 Nr. 1', - }, - ], - - prohibitedPractices: [ - { - id: 'proh-manipulation', - label: 'Unterschwellige oder gezielt manipulative Techniken, die Verhalten wesentlich verĂ€ndern und erheblichen Schaden verursachen (können).', - legalRef: 'Art. 5 Abs. 1 lit. a', - }, - { - id: 'proh-vulnerability', - label: 'Ausnutzung von SchwĂ€chen aufgrund von Alter, Behinderung oder sozialer/wirtschaftlicher Lage mit erheblichem Schadensrisiko.', - legalRef: 'Art. 5 Abs. 1 lit. b', - }, - { - id: 'proh-social-scoring', - label: 'Social Scoring: Bewertung von Personen anhand ihres Sozialverhaltens mit ungerechtfertigter Schlechterstellung.', - legalRef: 'Art. 5 Abs. 1 lit. c', - }, - { - id: 'proh-predictive-policing', - label: 'Risikobewertung von Straftaten allein auf Basis von Profiling oder Persönlichkeitsmerkmalen.', - legalRef: 'Art. 5 Abs. 1 lit. d', - }, - { - id: 'proh-face-scraping', - label: 'Ungezieltes Auslesen (Scraping) von Gesichtsbildern aus Internet oder Überwachungskameras zum Aufbau von Gesichtsdatenbanken.', - legalRef: 'Art. 5 Abs. 1 lit. e', - }, - { - id: 'proh-emotion-workplace', - label: 'Emotionserkennung am Arbeitsplatz oder in Bildungseinrichtungen (außer aus medizinischen oder SicherheitsgrĂŒnden).', - legalRef: 'Art. 5 Abs. 1 lit. f', - }, - { - id: 'proh-biometric-categorisation', - label: 'Biometrische Kategorisierung zur Ableitung sensibler Merkmale (z. B. Ethnie, politische Meinung, sexuelle Orientierung).', - legalRef: 'Art. 5 Abs. 1 lit. g', - }, - { - id: 'proh-rbi', - label: 'Biometrische Echtzeit-Fernidentifizierung in öffentlich zugĂ€nglichen RĂ€umen zu Strafverfolgungszwecken (enge Ausnahmen).', - legalRef: 'Art. 5 Abs. 1 lit. h', - }, - ], - - annexIIICategories: [ - { - id: 'a3-biometrics', - label: 'Biometrie: Fernidentifizierung, biometrische Kategorisierung, Emotionserkennung (soweit nicht verboten).', - legalRef: 'Annex III Nr. 1', - }, - { - id: 'a3-critical-infra', - label: 'Sicherheitskomponenten kritischer Infrastruktur (Verkehr, Wasser, Gas, WĂ€rme, Strom, digitale Infrastruktur).', - legalRef: 'Annex III Nr. 2', - }, - { - id: 'a3-education', - label: 'Bildung: Zulassung, Bewertung von Lernergebnissen, PrĂŒfungsĂŒberwachung.', - legalRef: 'Annex III Nr. 3', - }, - { - id: 'a3-employment', - label: 'BeschĂ€ftigung: Recruiting/Bewerberauswahl, Beförderung/KĂŒndigung, Aufgabenzuweisung, LeistungsĂŒberwachung.', - legalRef: 'Annex III Nr. 4', - hint: 'HĂ€ufigster KMU-Fall: KI-gestĂŒtztes Bewerber-Screening.', - }, - { - id: 'a3-essential-services', - label: 'Wesentliche Dienste: KreditwĂŒrdigkeitsprĂŒfung, Risiko-Pricing bei Lebens-/Krankenversicherung, Notruf-Triage, Sozialleistungen.', - legalRef: 'Annex III Nr. 5', - }, - { - id: 'a3-law-enforcement', - label: 'Strafverfolgung (Beweismittelbewertung, RĂŒckfallrisiko u. a.).', - legalRef: 'Annex III Nr. 6', - }, - { - id: 'a3-migration', - label: 'Migration, Asyl, Grenzkontrolle.', - legalRef: 'Annex III Nr. 7', - }, - { - id: 'a3-justice', - label: 'Rechtspflege und demokratische Prozesse (UnterstĂŒtzung richterlicher Entscheidungen, Wahlbeeinflussung).', - legalRef: 'Annex III Nr. 8', - }, - ], - - transparencyTriggers: [ - { - id: 'tr-chatbot', - label: 'Das System interagiert direkt mit Menschen (z. B. Chatbot, Voicebot).', - legalRef: 'Art. 50 Abs. 1', - hint: 'Nutzer mĂŒssen erkennen können, dass sie mit KI interagieren.', - }, - { - id: 'tr-synthetic-content', - label: 'Das System erzeugt synthetische Audio-, Bild-, Video- oder Textinhalte.', - legalRef: 'Art. 50 Abs. 2', - hint: 'Maschinenlesbare Kennzeichnung der Ausgaben erforderlich.', - }, - { - id: 'tr-emotion', - label: 'Emotionserkennung oder biometrische Kategorisierung wird eingesetzt (soweit erlaubt).', - legalRef: 'Art. 50 Abs. 3', - }, - { - id: 'tr-deepfake', - label: 'Das System erzeugt oder manipuliert Deepfakes (Bild/Audio/Video realer Personen, Orte oder Ereignisse).', - legalRef: 'Art. 50 Abs. 4', - }, - ], - - obligations: [ - // ── Alle Risikoklassen / alle Rollen ────────────────────────────── - { - id: 'ob-ai-literacy', - title: 'KI-Kompetenz des Personals sicherstellen', - articles: 'Art. 4', - description: - 'Mitarbeitende, die KI-Systeme betreiben oder nutzen, mĂŒssen ĂŒber ausreichende KI-Kompetenz verfĂŒgen (Schulungen, Richtlinien). Gilt seit 2. Februar 2025 fĂŒr alle Anbieter und Betreiber.', - deadline: '2025-02-02', - roles: ['provider', 'deployer', 'importer', 'distributor'], - riskClasses: ['high', 'limited', 'minimal'], - }, - // ── Hochrisiko: Anbieter ────────────────────────────────────────── - { - id: 'ob-risk-mgmt', - title: 'Risikomanagementsystem einrichten', - articles: 'Art. 9', - description: - 'Kontinuierlicher, iterativer Risikomanagementprozess ĂŒber den gesamten Lebenszyklus: Risiken identifizieren, bewerten, mindern, testen.', - deadline: '2027-12-02', - roles: ['provider'], - riskClasses: ['high'], - }, - { - id: 'ob-data-governance', - title: 'Daten-Governance fĂŒr Trainings-/Test-/Validierungsdaten', - articles: 'Art. 10', - description: - 'DatensĂ€tze mĂŒssen relevant, reprĂ€sentativ, möglichst fehlerfrei und vollstĂ€ndig sein; Bias-PrĂŒfung und geeignete Erhebungsverfahren dokumentieren.', - deadline: '2027-12-02', - roles: ['provider'], - riskClasses: ['high'], - }, - { - id: 'ob-tech-doc', - title: 'Technische Dokumentation (Annex IV) erstellen', - articles: 'Art. 11, Annex IV', - description: - 'VollstĂ€ndige technische Dokumentation vor Inverkehrbringen; fĂŒr KMU gelten vereinfachte Formulare. Der Dokumenten-Generator dieses Tools erzeugt das GrundgerĂŒst.', - deadline: '2027-12-02', - roles: ['provider'], - riskClasses: ['high'], - }, - { - id: 'ob-logging', - title: 'Protokollierung (Logging) technisch ermöglichen', - articles: 'Art. 12', - description: - 'Automatische Aufzeichnung von Ereignissen ĂŒber den Lebenszyklus, um RĂŒckverfolgbarkeit und MarktĂŒberwachung zu ermöglichen.', - deadline: '2027-12-02', - roles: ['provider'], - riskClasses: ['high'], - }, - { - id: 'ob-transparency-instructions', - title: 'Betriebsanleitung und Transparenzinformationen bereitstellen', - articles: 'Art. 13', - description: - 'Klare Gebrauchsanweisung: Zweckbestimmung, Genauigkeit, Grenzen, menschliche Aufsicht, erwartete Lebensdauer und Wartung.', - deadline: '2027-12-02', - roles: ['provider'], - riskClasses: ['high'], - }, - { - id: 'ob-human-oversight', - title: 'Menschliche Aufsicht konzipieren', - articles: 'Art. 14', - description: - 'Das System muss wirksam durch Menschen beaufsichtigt werden können (Eingriffs-/Abbruchmöglichkeiten, Automation-Bias-Vorkehrungen).', - deadline: '2027-12-02', - roles: ['provider'], - riskClasses: ['high'], - }, - { - id: 'ob-accuracy-robustness', - title: 'Genauigkeit, Robustheit und Cybersicherheit gewĂ€hrleisten', - articles: 'Art. 15', - description: - 'Angemessenes Niveau an Genauigkeit und WiderstandsfĂ€higkeit, inkl. Schutz gegen KI-spezifische Angriffe (Data Poisoning, Adversarial Examples, Prompt Injection).', - deadline: '2027-12-02', - roles: ['provider'], - riskClasses: ['high'], - }, - { - id: 'ob-qms', - title: 'QualitĂ€tsmanagementsystem einfĂŒhren', - articles: 'Art. 17', - description: - 'Dokumentiertes QMS: Compliance-Strategie, PrĂŒfverfahren, Datenmanagement, Risikomanagement, Meldeverfahren, Verantwortlichkeiten.', - deadline: '2027-12-02', - roles: ['provider'], - riskClasses: ['high'], - }, - { - id: 'ob-conformity', - title: 'KonformitĂ€tsbewertung durchfĂŒhren, CE-Kennzeichnung, EU-Datenbank-Registrierung', - articles: 'Art. 43, 48, 49', - description: - 'KonformitĂ€tsbewertungsverfahren abschließen, EU-KonformitĂ€tserklĂ€rung ausstellen, CE-Kennzeichnung anbringen und System in der EU-Datenbank registrieren.', - deadline: '2027-12-02', - roles: ['provider'], - riskClasses: ['high'], - }, - // ── Hochrisiko: Betreiber ───────────────────────────────────────── - { - id: 'ob-deployer-use', - title: 'Nutzung gemĂ€ĂŸ Betriebsanleitung sicherstellen', - articles: 'Art. 26 Abs. 1', - description: - 'Technische und organisatorische Maßnahmen, damit das System entsprechend der Gebrauchsanweisung des Anbieters genutzt wird.', - deadline: '2027-12-02', - roles: ['deployer'], - riskClasses: ['high'], - }, - { - id: 'ob-deployer-oversight', - title: 'Kompetente menschliche Aufsicht zuweisen', - articles: 'Art. 26 Abs. 2', - description: - 'Aufsicht durch Personen mit erforderlicher Kompetenz, Ausbildung und Befugnis.', - deadline: '2027-12-02', - roles: ['deployer'], - riskClasses: ['high'], - }, - { - id: 'ob-deployer-input', - title: 'Relevanz und ReprĂ€sentativitĂ€t der Eingabedaten sicherstellen', - articles: 'Art. 26 Abs. 4', - description: - 'Soweit der Betreiber die Eingabedaten kontrolliert, mĂŒssen diese fĂŒr die Zweckbestimmung relevant und hinreichend reprĂ€sentativ sein.', - deadline: '2027-12-02', - roles: ['deployer'], - riskClasses: ['high'], - }, - { - id: 'ob-deployer-monitoring', - title: 'Betrieb ĂŒberwachen, VorfĂ€lle melden, Logs aufbewahren', - articles: 'Art. 26 Abs. 5–6', - description: - 'Betrieb anhand der Anleitung ĂŒberwachen; bei Risiken Anbieter/Behörden informieren; automatisch erzeugte Logs mind. 6 Monate aufbewahren.', - deadline: '2027-12-02', - roles: ['deployer'], - riskClasses: ['high'], - }, - { - id: 'ob-deployer-workers', - title: 'BeschĂ€ftigte vor Einsatz am Arbeitsplatz informieren', - articles: 'Art. 26 Abs. 7', - description: - 'Vor Inbetriebnahme eines Hochrisiko-Systems am Arbeitsplatz sind Arbeitnehmervertreter und betroffene BeschĂ€ftigte zu informieren.', - deadline: '2027-12-02', - roles: ['deployer'], - riskClasses: ['high'], - }, - { - id: 'ob-fria', - title: 'Grundrechte-FolgenabschĂ€tzung (FRIA) prĂŒfen', - articles: 'Art. 27', - description: - 'Pflicht fĂŒr öffentliche Stellen und private Anbieter öffentlicher Dienste sowie bestimmte Annex-III-FĂ€lle (z. B. KreditwĂŒrdigkeit, Versicherung): Folgen fĂŒr Grundrechte vor Einsatz bewerten.', - deadline: '2027-12-02', - roles: ['deployer'], - riskClasses: ['high'], - }, - // ── Importeur / HĂ€ndler ─────────────────────────────────────────── - { - id: 'ob-importer-verify', - title: 'KonformitĂ€t vor Inverkehrbringen prĂŒfen', - articles: 'Art. 23', - description: - 'Importeure mĂŒssen prĂŒfen: KonformitĂ€tsbewertung durchgefĂŒhrt, technische Dokumentation vorhanden, CE-Kennzeichnung und Ansprechpartner benannt.', - deadline: '2027-12-02', - roles: ['importer'], - riskClasses: ['high'], - }, - { - id: 'ob-distributor-verify', - title: 'Sorgfaltspflichten der Vertriebskette erfĂŒllen', - articles: 'Art. 24', - description: - 'HĂ€ndler prĂŒfen CE-Kennzeichnung, KonformitĂ€tserklĂ€rung und Gebrauchsanweisung; bei Zweifeln keine Bereitstellung.', - deadline: '2027-12-02', - roles: ['distributor'], - riskClasses: ['high'], - }, - // ── Transparenzpflichten (limited) ──────────────────────────────── - { - id: 'ob-transparency-art50', - title: 'Transparenzpflichten nach Art. 50 umsetzen', - articles: 'Art. 50', - description: - 'Je nach Auslöser: KI-Interaktion offenlegen (Chatbots), synthetische Inhalte maschinenlesbar kennzeichnen, Deepfakes sichtbar kennzeichnen, ĂŒber Emotionserkennung informieren.', - deadline: '2026-08-02', - roles: ['provider', 'deployer'], - riskClasses: ['high', 'limited'], - }, - // ── GPAI ────────────────────────────────────────────────────────── - { - id: 'ob-gpai', - title: 'GPAI-Modellpflichten erfĂŒllen', - articles: 'Art. 53–55', - description: - 'FĂŒr Anbieter von GPAI-Modellen: technische Modell-Dokumentation, Informationen fĂŒr nachgelagerte Anbieter, Urheberrechts-Policy, Zusammenfassung der Trainingsdaten. Bei systemischem Risiko zusĂ€tzlich Evaluierungen und Incident-Meldungen.', - deadline: '2025-08-02', - roles: ['provider'], - riskClasses: ['high', 'limited', 'minimal'], - }, - ], - - deadlines: [ - { - id: 'dl-prohibitions', - date: '2025-02-02', - label: 'Verbote & KI-Kompetenz', - description: 'Verbotene Praktiken (Art. 5) und KI-Kompetenzpflicht (Art. 4) gelten. Bereits in Kraft.', - }, - { - id: 'dl-gpai', - date: '2025-08-02', - label: 'GPAI-Pflichten', - description: 'Pflichten fĂŒr Anbieter von GPAI-Modellen (Art. 53 ff.) gelten. Bereits in Kraft.', - }, - { - id: 'dl-general', - date: '2026-08-02', - label: 'Allgemeine Geltung', - description: 'Transparenzpflichten (Art. 50), Sanktionsregime und Governance-Struktur gelten.', - }, - { - id: 'dl-annex3', - date: '2027-12-02', - label: 'Hochrisiko (Annex III)', - description: 'Pflichten fĂŒr Annex-III-Hochrisiko-Systeme gelten (durch Digital Omnibus von Aug 2026 verschoben).', - }, - { - id: 'dl-annex1', - date: '2028-08-02', - label: 'Hochrisiko (Annex I)', - description: 'Pflichten fĂŒr KI als Sicherheitsbauteil regulierter Produkte (Annex I) gelten.', - }, - ], +const LEGAL_BASIS: LText = { + de: 'Verordnung (EU) 2024/1689 (AI Act) + nationale Module, Stand Juli 2026', + en: 'Regulation (EU) 2024/1689 (AI Act) + national modules, as of July 2026', }; + +function resolveItem(src: ContentItemSrc, locale: Locale): ContentItem { + return { + id: src.id, + label: src.label[locale], + legalRef: src.legalRef, + ...(src.hint ? { hint: src.hint[locale] } : {}), + }; +} + +function resolveObligation(src: ObligationSrc, locale: Locale): Obligation { + return { + id: src.id, + title: src.title[locale], + articles: src.articles, + description: src.description[locale], + deadline: src.deadline, + roles: src.roles, + riskClasses: src.riskClasses, + }; +} + +function resolveDeadline(src: DeadlineSrc, locale: Locale): Deadline { + return { + id: src.id, + date: src.date, + label: src.label[locale], + description: src.description[locale], + }; +} + +function resolveNational(src: NationalPackSrc, locale: Locale): NationalPack { + return { + code: src.code, + country: src.country[locale], + authority: src.authority[locale], + items: src.items.map((i) => resolveObligation(i, locale)), + }; +} + +/** + * Liefert den Content-Pack in der gewĂŒnschten Sprache. + * + * @param locale - Zielsprache (de/en) + * @returns VollstĂ€ndig aufgelöster Content-Pack + */ +export function getContentPack(locale: Locale): ContentPack { + return { + version: VERSION, + releasedAt: RELEASED_AT, + legalBasis: LEGAL_BASIS[locale], + aiDefinitionCriteria: AI_DEFINITION_CRITERIA.map((i) => resolveItem(i, locale)), + prohibitedPractices: PROHIBITED_PRACTICES.map((i) => resolveItem(i, locale)), + annexIIICategories: ANNEX_III_CATEGORIES.map((i) => resolveItem(i, locale)), + transparencyTriggers: TRANSPARENCY_TRIGGERS.map((i) => resolveItem(i, locale)), + obligations: OBLIGATIONS.map((o) => resolveObligation(o, locale)), + deadlines: DEADLINES.map((d) => resolveDeadline(d, locale)), + national: NATIONAL_PACKS.map((n) => resolveNational(n, locale)), + }; +} + +/** Deutscher Content-Pack (KompatibilitĂ€ts-Export, u. a. fĂŒr Tests). */ +export const CONTENT_PACK: ContentPack = getContentPack('de'); diff --git a/ai-act-kompass/src/content/packCore.ts b/ai-act-kompass/src/content/packCore.ts new file mode 100644 index 0000000..10e0b00 --- /dev/null +++ b/ai-act-kompass/src/content/packCore.ts @@ -0,0 +1,260 @@ +import { lt } from '../i18n/types'; +import type { ContentItemSrc, DeadlineSrc } from './sourceTypes'; + +/** Kriterien der KI-Definition (Art. 3 Nr. 1). */ +export const AI_DEFINITION_CRITERIA: readonly ContentItemSrc[] = [ + { + id: 'def-autonomy', + label: lt( + 'Das System arbeitet mit einem gewissen Grad an Autonomie (nicht rein regelbasiert deterministisch durch den Nutzer gesteuert).', + 'The system operates with a degree of autonomy (not purely rule-based and deterministically controlled by the user).', + ), + legalRef: 'Art. 3 Nr. 1', + }, + { + id: 'def-inference', + label: lt( + 'Das System leitet aus Eingaben ab, wie Ausgaben (Vorhersagen, Inhalte, Empfehlungen, Entscheidungen) erzeugt werden.', + 'The system infers from inputs how to generate outputs (predictions, content, recommendations, decisions).', + ), + legalRef: 'Art. 3 Nr. 1', + }, + { + id: 'def-influence', + label: lt( + 'Die Ausgaben können physische oder virtuelle Umgebungen beeinflussen.', + 'The outputs can influence physical or virtual environments.', + ), + legalRef: 'Art. 3 Nr. 1', + }, +]; + +/** Verbotene Praktiken (Art. 5). */ +export const PROHIBITED_PRACTICES: readonly ContentItemSrc[] = [ + { + id: 'proh-manipulation', + label: lt( + 'Unterschwellige oder gezielt manipulative Techniken, die Verhalten wesentlich verĂ€ndern und erheblichen Schaden verursachen (können).', + 'Subliminal or purposefully manipulative techniques that materially distort behaviour and cause (or are likely to cause) significant harm.', + ), + legalRef: 'Art. 5 Abs. 1 lit. a', + }, + { + id: 'proh-vulnerability', + label: lt( + 'Ausnutzung von SchwĂ€chen aufgrund von Alter, Behinderung oder sozialer/wirtschaftlicher Lage mit erheblichem Schadensrisiko.', + 'Exploitation of vulnerabilities due to age, disability or social/economic situation with a significant risk of harm.', + ), + legalRef: 'Art. 5 Abs. 1 lit. b', + }, + { + id: 'proh-social-scoring', + label: lt( + 'Social Scoring: Bewertung von Personen anhand ihres Sozialverhaltens mit ungerechtfertigter Schlechterstellung.', + 'Social scoring: evaluating persons based on social behaviour leading to unjustified detrimental treatment.', + ), + legalRef: 'Art. 5 Abs. 1 lit. c', + }, + { + id: 'proh-predictive-policing', + label: lt( + 'Risikobewertung von Straftaten allein auf Basis von Profiling oder Persönlichkeitsmerkmalen.', + 'Assessing the risk of criminal offences based solely on profiling or personality traits.', + ), + legalRef: 'Art. 5 Abs. 1 lit. d', + }, + { + id: 'proh-face-scraping', + label: lt( + 'Ungezieltes Auslesen (Scraping) von Gesichtsbildern aus Internet oder Überwachungskameras zum Aufbau von Gesichtsdatenbanken.', + 'Untargeted scraping of facial images from the internet or CCTV to build facial recognition databases.', + ), + legalRef: 'Art. 5 Abs. 1 lit. e', + }, + { + id: 'proh-emotion-workplace', + label: lt( + 'Emotionserkennung am Arbeitsplatz oder in Bildungseinrichtungen (außer aus medizinischen oder SicherheitsgrĂŒnden).', + 'Emotion recognition in the workplace or educational institutions (except for medical or safety reasons).', + ), + legalRef: 'Art. 5 Abs. 1 lit. f', + }, + { + id: 'proh-biometric-categorisation', + label: lt( + 'Biometrische Kategorisierung zur Ableitung sensibler Merkmale (z. B. Ethnie, politische Meinung, sexuelle Orientierung).', + 'Biometric categorisation to infer sensitive attributes (e.g. ethnicity, political opinion, sexual orientation).', + ), + legalRef: 'Art. 5 Abs. 1 lit. g', + }, + { + id: 'proh-rbi', + label: lt( + 'Biometrische Echtzeit-Fernidentifizierung in öffentlich zugĂ€nglichen RĂ€umen zu Strafverfolgungszwecken (enge Ausnahmen).', + 'Real-time remote biometric identification in publicly accessible spaces for law enforcement (narrow exceptions).', + ), + legalRef: 'Art. 5 Abs. 1 lit. h', + }, +]; + +/** Hochrisiko-Bereiche nach Annex III. */ +export const ANNEX_III_CATEGORIES: readonly ContentItemSrc[] = [ + { + id: 'a3-biometrics', + label: lt( + 'Biometrie: Fernidentifizierung, biometrische Kategorisierung, Emotionserkennung (soweit nicht verboten).', + 'Biometrics: remote identification, biometric categorisation, emotion recognition (where not prohibited).', + ), + legalRef: 'Annex III Nr. 1', + }, + { + id: 'a3-critical-infra', + label: lt( + 'Sicherheitskomponenten kritischer Infrastruktur (Verkehr, Wasser, Gas, WĂ€rme, Strom, digitale Infrastruktur).', + 'Safety components of critical infrastructure (traffic, water, gas, heating, electricity, digital infrastructure).', + ), + legalRef: 'Annex III Nr. 2', + }, + { + id: 'a3-education', + label: lt( + 'Bildung: Zulassung, Bewertung von Lernergebnissen, PrĂŒfungsĂŒberwachung.', + 'Education: admission, evaluation of learning outcomes, exam proctoring.', + ), + legalRef: 'Annex III Nr. 3', + }, + { + id: 'a3-employment', + label: lt( + 'BeschĂ€ftigung: Recruiting/Bewerberauswahl, Beförderung/KĂŒndigung, Aufgabenzuweisung, LeistungsĂŒberwachung.', + 'Employment: recruiting/candidate selection, promotion/termination, task allocation, performance monitoring.', + ), + legalRef: 'Annex III Nr. 4', + hint: lt( + 'HĂ€ufigster KMU-Fall: KI-gestĂŒtztes Bewerber-Screening.', + 'Most common SME case: AI-supported applicant screening.', + ), + }, + { + id: 'a3-essential-services', + label: lt( + 'Wesentliche Dienste: KreditwĂŒrdigkeitsprĂŒfung, Risiko-Pricing bei Lebens-/Krankenversicherung, Notruf-Triage, Sozialleistungen.', + 'Essential services: credit scoring, risk pricing in life/health insurance, emergency call triage, public benefits.', + ), + legalRef: 'Annex III Nr. 5', + }, + { + id: 'a3-law-enforcement', + label: lt( + 'Strafverfolgung (Beweismittelbewertung, RĂŒckfallrisiko u. a.).', + 'Law enforcement (evidence evaluation, recidivism risk, etc.).', + ), + legalRef: 'Annex III Nr. 6', + }, + { + id: 'a3-migration', + label: lt('Migration, Asyl, Grenzkontrolle.', 'Migration, asylum, border control.'), + legalRef: 'Annex III Nr. 7', + }, + { + id: 'a3-justice', + label: lt( + 'Rechtspflege und demokratische Prozesse (UnterstĂŒtzung richterlicher Entscheidungen, Wahlbeeinflussung).', + 'Administration of justice and democratic processes (supporting judicial decisions, influencing elections).', + ), + legalRef: 'Annex III Nr. 8', + }, +]; + +/** Auslöser der Transparenzpflichten (Art. 50). */ +export const TRANSPARENCY_TRIGGERS: readonly ContentItemSrc[] = [ + { + id: 'tr-chatbot', + label: lt( + 'Das System interagiert direkt mit Menschen (z. B. Chatbot, Voicebot).', + 'The system interacts directly with humans (e.g. chatbot, voicebot).', + ), + legalRef: 'Art. 50 Abs. 1', + hint: lt( + 'Nutzer mĂŒssen erkennen können, dass sie mit KI interagieren.', + 'Users must be able to recognise that they are interacting with AI.', + ), + }, + { + id: 'tr-synthetic-content', + label: lt( + 'Das System erzeugt synthetische Audio-, Bild-, Video- oder Textinhalte.', + 'The system generates synthetic audio, image, video or text content.', + ), + legalRef: 'Art. 50 Abs. 2', + hint: lt( + 'Maschinenlesbare Kennzeichnung der Ausgaben erforderlich.', + 'Machine-readable marking of outputs is required.', + ), + }, + { + id: 'tr-emotion', + label: lt( + 'Emotionserkennung oder biometrische Kategorisierung wird eingesetzt (soweit erlaubt).', + 'Emotion recognition or biometric categorisation is used (where permitted).', + ), + legalRef: 'Art. 50 Abs. 3', + }, + { + id: 'tr-deepfake', + label: lt( + 'Das System erzeugt oder manipuliert Deepfakes (Bild/Audio/Video realer Personen, Orte oder Ereignisse).', + 'The system generates or manipulates deepfakes (image/audio/video of real persons, places or events).', + ), + legalRef: 'Art. 50 Abs. 4', + }, +]; + +/** Geltungsfristen des AI Act. */ +export const DEADLINES: readonly DeadlineSrc[] = [ + { + id: 'dl-prohibitions', + date: '2025-02-02', + label: lt('Verbote & KI-Kompetenz', 'Prohibitions & AI literacy'), + description: lt( + 'Verbotene Praktiken (Art. 5) und KI-Kompetenzpflicht (Art. 4) gelten. Bereits in Kraft.', + 'Prohibited practices (Art. 5) and AI literacy duty (Art. 4) apply. Already in force.', + ), + }, + { + id: 'dl-gpai', + date: '2025-08-02', + label: lt('GPAI-Pflichten', 'GPAI obligations'), + description: lt( + 'Pflichten fĂŒr Anbieter von GPAI-Modellen (Art. 53 ff.) gelten. Bereits in Kraft.', + 'Obligations for providers of GPAI models (Art. 53 et seq.) apply. Already in force.', + ), + }, + { + id: 'dl-general', + date: '2026-08-02', + label: lt('Allgemeine Geltung', 'General application'), + description: lt( + 'Transparenzpflichten (Art. 50), Sanktionsregime und Governance-Struktur gelten.', + 'Transparency obligations (Art. 50), penalty regime and governance structure apply.', + ), + }, + { + id: 'dl-annex3', + date: '2027-12-02', + label: lt('Hochrisiko (Annex III)', 'High-risk (Annex III)'), + description: lt( + 'Pflichten fĂŒr Annex-III-Hochrisiko-Systeme gelten (durch Digital Omnibus von Aug 2026 verschoben).', + 'Obligations for Annex III high-risk systems apply (postponed from Aug 2026 by the Digital Omnibus).', + ), + }, + { + id: 'dl-annex1', + date: '2028-08-02', + label: lt('Hochrisiko (Annex I)', 'High-risk (Annex I)'), + description: lt( + 'Pflichten fĂŒr KI als Sicherheitsbauteil regulierter Produkte (Annex I) gelten.', + 'Obligations for AI as a safety component of regulated products (Annex I) apply.', + ), + }, +]; diff --git a/ai-act-kompass/src/content/packNational.ts b/ai-act-kompass/src/content/packNational.ts new file mode 100644 index 0000000..3d17c6a --- /dev/null +++ b/ai-act-kompass/src/content/packNational.ts @@ -0,0 +1,237 @@ +import { lt } from '../i18n/types'; +import type { NationalPackSrc } from './sourceTypes'; + +/** + * Nationale Zusatzanforderungen der EU-Mitgliedstaaten (Stand Juli 2026). + * + * Der AI Act ist eine Verordnung und gilt unmittelbar — die Mitgliedstaaten + * regeln aber Aufsicht, Sanktionen und teils zusĂ€tzliche Pflichten national. + * Diese Module sind zuschaltbar und werden mit dem Content-Pack gepflegt. + */ +export const NATIONAL_PACKS: readonly NationalPackSrc[] = [ + { + code: 'DE', + country: lt('Deutschland', 'Germany'), + authority: lt( + 'Bundesnetzagentur (BNetzA) — MarktĂŒberwachung, KI-MarktĂŒberwachungskammer (UKIM), KoKIVO-Service-Desk', + 'Federal Network Agency (BNetzA) — market surveillance, AI Market Surveillance Chamber (UKIM), KoKIVO service desk', + ), + items: [ + { + id: 'de-kimig-authority', + title: lt( + 'ZustĂ€ndigkeit und Meldewege nach KI-MIG kennen', + 'Know competences and reporting channels under the German AI Act implementation law', + ), + articles: 'KI-MIG (2026)', + description: lt( + 'Das deutsche DurchfĂŒhrungsgesetz benennt die Bundesnetzagentur als zentrale MarktĂŒberwachungs- und Anlaufstelle (KoKIVO als Service-Desk). Meldungen (z. B. Art. 73) laufen ĂŒber die BNetzA; sektorale Behörden (z. B. BaFin) bleiben teils zustĂ€ndig.', + 'The German implementation law designates the Federal Network Agency as central market surveillance body and contact point (KoKIVO service desk). Reports (e.g. Art. 73) go to BNetzA; sectoral authorities (e.g. BaFin) partly remain competent.', + ), + deadline: '2026-08-02', + roles: ['provider', 'deployer', 'importer', 'distributor'], + riskClasses: ['high', 'limited'], + }, + { + id: 'de-betrvg', + title: lt( + 'Mitbestimmung des Betriebsrats bei KI am Arbeitsplatz', + 'Works council co-determination for AI at the workplace', + ), + articles: '§ 87 Abs. 1 Nr. 6, § 90 BetrVG', + description: lt( + 'Kann das System Verhalten oder Leistung von BeschĂ€ftigten ĂŒberwachen, besteht ein Mitbestimmungsrecht des Betriebsrats; bei Planung von KI-Einsatz ist der Betriebsrat frĂŒhzeitig zu unterrichten (§ 90). Betriebsvereinbarung empfohlen.', + 'If the system can monitor employee behaviour or performance, the works council has a co-determination right; it must be informed early when AI use is planned (Sec. 90). A works agreement is recommended.', + ), + deadline: '2025-02-02', + roles: ['deployer'], + riskClasses: ['high', 'limited'], + }, + { + id: 'de-dsgvo-bdsg', + title: lt( + 'DSGVO/BDSG-KonformitĂ€t der KI-Datenverarbeitung sicherstellen', + 'Ensure GDPR/BDSG conformity of AI data processing', + ), + articles: 'DSGVO, BDSG', + description: lt( + 'Rechtsgrundlage der Verarbeitung, Informationspflichten, Betroffenenrechte und ggf. § 26 BDSG (BeschĂ€ftigtendaten) prĂŒfen; Datenschutzbeauftragten einbinden.', + 'Check legal basis of processing, information duties, data subject rights and, where relevant, Sec. 26 BDSG (employee data); involve the data protection officer.', + ), + deadline: '2025-02-02', + roles: ['provider', 'deployer'], + riskClasses: ['high', 'limited', 'minimal'], + }, + ], + }, + { + code: 'AT', + country: lt('Österreich', 'Austria'), + authority: lt( + 'KI-Servicestelle bei der RTR (Rundfunk und Telekom Regulierungs-GmbH); MarktĂŒberwachungsstruktur in Umsetzung', + 'AI service desk at RTR (Austrian Regulatory Authority for Broadcasting and Telecommunications); market surveillance structure being implemented', + ), + items: [ + { + id: 'at-servicestelle', + title: lt( + 'KI-Servicestelle als Anlaufstelle nutzen, nationale Umsetzung beobachten', + 'Use the AI service desk as contact point, monitor national implementation', + ), + articles: 'RTR KI-Servicestelle', + description: lt( + 'Österreich hat bei der RTR eine KI-Servicestelle fĂŒr Unternehmen eingerichtet; das nationale Begleitgesetz mit endgĂŒltiger Behördenstruktur und Sanktionen ist zu beobachten.', + 'Austria has set up an AI service desk at RTR for companies; the national implementing act with the final authority structure and penalties should be monitored.', + ), + deadline: '2026-08-02', + roles: ['provider', 'deployer'], + riskClasses: ['high', 'limited'], + }, + ], + }, + { + code: 'IT', + country: lt('Italien', 'Italy'), + authority: lt( + 'AgID (Agentur fĂŒr digitales Italien) und ACN (Nationale Cybersicherheitsagentur)', + 'AgID (Agency for Digital Italy) and ACN (National Cybersecurity Agency)', + ), + items: [ + { + id: 'it-l132-workers', + title: lt( + 'BeschĂ€ftigte ĂŒber KI-Einsatz informieren (Legge 132/2025)', + 'Inform employees about AI use (Law 132/2025)', + ), + articles: 'Legge 132/2025', + description: lt( + 'Italiens nationales KI-Gesetz verpflichtet Arbeitgeber, BeschĂ€ftigte ĂŒber den Einsatz von KI in Entscheidungsprozessen zu informieren — unabhĂ€ngig von der Risikoklasse des Systems.', + 'Italy’s national AI law obliges employers to inform employees about the use of AI in decision-making processes — regardless of the system’s risk class.', + ), + deadline: '2025-10-10', + roles: ['deployer'], + riskClasses: ['high', 'limited', 'minimal'], + }, + { + id: 'it-l132-deepfake', + title: lt( + 'Strafbarkeit rechtswidriger Deepfakes beachten', + 'Note criminal liability for unlawful deepfakes', + ), + articles: 'Legge 132/2025', + description: lt( + 'Die rechtswidrige Verbreitung schĂ€digender Deepfakes ist in Italien strafbewehrt (bis zu 5 Jahre Freiheitsstrafe). Kennzeichnungs- und Freigabeprozesse fĂŒr generierte Inhalte einrichten.', + 'Unlawful dissemination of harmful deepfakes is a criminal offence in Italy (up to 5 years’ imprisonment). Set up labelling and approval processes for generated content.', + ), + deadline: '2025-10-10', + roles: ['provider', 'deployer'], + riskClasses: ['high', 'limited'], + }, + { + id: 'it-l132-sector', + title: lt( + 'Sektorregeln (Gesundheit, Justiz, Arbeit) und DurchfĂŒhrungsdekrete beobachten', + 'Monitor sector rules (health, justice, labour) and implementing decrees', + ), + articles: 'Legge 132/2025', + description: lt( + 'Das Gesetz enthĂ€lt Sonderregeln u. a. fĂŒr Gesundheitswesen und Justiz; DurchfĂŒhrungsdekrete (inkl. Sanktionsregime) werden bis Ende 2026 erwartet.', + 'The law contains special rules for healthcare and justice, among others; implementing decrees (incl. penalty regime) are expected by the end of 2026.', + ), + deadline: '2026-10-10', + roles: ['provider', 'deployer'], + riskClasses: ['high'], + }, + ], + }, + { + code: 'ES', + country: lt('Spanien', 'Spain'), + authority: lt( + 'AESIA (Agencia Española de SupervisiĂłn de la Inteligencia Artificial)', + 'AESIA (Spanish Agency for the Supervision of Artificial Intelligence)', + ), + items: [ + { + id: 'es-aesia', + title: lt( + 'AESIA-Aufsicht und spanisches Sanktionsgesetz beachten', + 'Note AESIA supervision and the Spanish sanctions bill', + ), + articles: 'AESIA; Anteproyecto de Ley de gobernanza de la IA', + description: lt( + 'Spanien hat mit AESIA als erstes EU-Land eine eigene KI-Aufsichtsbehörde; ein nationales Gesetz zu Sanktionen und Governance ist im Verfahren. AESIA ist zentrale Anlauf- und Meldestelle.', + 'Spain was the first EU country with a dedicated AI supervisory agency (AESIA); a national law on sanctions and governance is in the legislative process. AESIA is the central contact and reporting point.', + ), + deadline: '2026-08-02', + roles: ['provider', 'deployer', 'importer', 'distributor'], + riskClasses: ['high', 'limited'], + }, + { + id: 'es-sandbox', + title: lt( + 'Regulatorischen Sandbox-Zugang prĂŒfen (RD 817/2023)', + 'Consider regulatory sandbox access (RD 817/2023)', + ), + articles: 'Real Decreto 817/2023', + description: lt( + 'Spanien betreibt eine KI-Regulierungs-Sandbox fĂŒr Hochrisiko-Systeme — fĂŒr Anbieter eine Möglichkeit, KonformitĂ€t unter Behördenbegleitung zu erproben.', + 'Spain operates an AI regulatory sandbox for high-risk systems — an opportunity for providers to test conformity under regulatory guidance.', + ), + deadline: '2027-12-02', + roles: ['provider'], + riskClasses: ['high'], + }, + ], + }, + { + code: 'FR', + country: lt('Frankreich', 'France'), + authority: lt( + 'Mehrbehördenmodell; CNIL federfĂŒhrend fĂŒr KI mit Personenbezug, DGCCRF u. a. fĂŒr MarktĂŒberwachung', + 'Multi-authority model; CNIL leads on AI involving personal data, DGCCRF and others for market surveillance', + ), + items: [ + { + id: 'fr-cnil', + title: lt( + 'CNIL-Empfehlungen zu KI und DSGVO umsetzen', + 'Implement CNIL recommendations on AI and GDPR', + ), + articles: 'CNIL — Recommandations IA', + description: lt( + 'Die CNIL hat detaillierte Leitlinien zur DSGVO-konformen Entwicklung und Nutzung von KI veröffentlicht (Rechtsgrundlagen, Trainingsdaten, Betroffenenrechte); bei Personenbezug maßgeblich.', + 'The CNIL has published detailed guidance on GDPR-compliant development and use of AI (legal bases, training data, data subject rights); authoritative where personal data is involved.', + ), + deadline: '2025-02-02', + roles: ['provider', 'deployer'], + riskClasses: ['high', 'limited', 'minimal'], + }, + ], + }, + { + code: 'NL', + country: lt('Niederlande', 'Netherlands'), + authority: lt( + 'Autoriteit Persoonsgegevens (AP) — Koordination Algorithmen-Aufsicht (DCA); RDI fĂŒr technische MarktĂŒberwachung', + 'Dutch Data Protection Authority (AP) — algorithm oversight coordination (DCA); RDI for technical market surveillance', + ), + items: [ + { + id: 'nl-algo-register', + title: lt( + 'Algorithmenregister fĂŒr öffentliche Stellen pflegen', + 'Maintain the algorithm register for public bodies', + ), + articles: 'Algoritmeregister (verplicht fĂŒr Behörden)', + description: lt( + 'NiederlĂ€ndische Behörden mĂŒssen eingesetzte (wirkungsrelevante) Algorithmen und KI-Systeme im öffentlichen Algorithmenregister dokumentieren; private Auftragnehmer öffentlicher Stellen sind mittelbar betroffen.', + 'Dutch public authorities must document deployed (impact-relevant) algorithms and AI systems in the public algorithm register; private contractors of public bodies are indirectly affected.', + ), + deadline: '2025-02-02', + roles: ['deployer'], + riskClasses: ['high', 'limited'], + }, + ], + }, +]; diff --git a/ai-act-kompass/src/content/packObligations.ts b/ai-act-kompass/src/content/packObligations.ts new file mode 100644 index 0000000..09ff085 --- /dev/null +++ b/ai-act-kompass/src/content/packObligations.ts @@ -0,0 +1,366 @@ +import { lt } from '../i18n/types'; +import type { ObligationSrc } from './sourceTypes'; + +/** EU-weite Pflichten aus dem AI Act (rollen- und risikoklassenspezifisch). */ +export const OBLIGATIONS: readonly ObligationSrc[] = [ + // ── Alle Risikoklassen / alle Rollen ────────────────────────────── + { + id: 'ob-ai-literacy', + title: lt('KI-Kompetenz des Personals sicherstellen', 'Ensure AI literacy of staff'), + articles: 'Art. 4', + description: lt( + 'Mitarbeitende, die KI-Systeme betreiben oder nutzen, mĂŒssen ĂŒber ausreichende KI-Kompetenz verfĂŒgen (Schulungen, Richtlinien). Gilt seit 2. Februar 2025 fĂŒr alle Anbieter und Betreiber.', + 'Staff operating or using AI systems must have sufficient AI literacy (training, policies). Applies since 2 February 2025 to all providers and deployers.', + ), + deadline: '2025-02-02', + roles: ['provider', 'deployer', 'importer', 'distributor'], + riskClasses: ['high', 'limited', 'minimal'], + }, + // ── Hochrisiko: Anbieter ────────────────────────────────────────── + { + id: 'ob-risk-mgmt', + title: lt('Risikomanagementsystem einrichten', 'Establish a risk management system'), + articles: 'Art. 9', + description: lt( + 'Kontinuierlicher, iterativer Risikomanagementprozess ĂŒber den gesamten Lebenszyklus: Risiken identifizieren, bewerten, mindern, testen.', + 'Continuous, iterative risk management process across the entire lifecycle: identify, assess, mitigate and test risks.', + ), + deadline: '2027-12-02', + roles: ['provider'], + riskClasses: ['high'], + }, + { + id: 'ob-data-governance', + title: lt( + 'Daten-Governance fĂŒr Trainings-/Test-/Validierungsdaten', + 'Data governance for training/testing/validation data', + ), + articles: 'Art. 10', + description: lt( + 'DatensĂ€tze mĂŒssen relevant, reprĂ€sentativ, möglichst fehlerfrei und vollstĂ€ndig sein; Bias-PrĂŒfung und geeignete Erhebungsverfahren dokumentieren.', + 'Datasets must be relevant, representative, as error-free and complete as possible; document bias examination and appropriate collection procedures.', + ), + deadline: '2027-12-02', + roles: ['provider'], + riskClasses: ['high'], + }, + { + id: 'ob-tech-doc', + title: lt('Technische Dokumentation (Annex IV) erstellen', 'Prepare technical documentation (Annex IV)'), + articles: 'Art. 11, Annex IV', + description: lt( + 'VollstĂ€ndige technische Dokumentation vor Inverkehrbringen; fĂŒr KMU gelten vereinfachte Formulare. Der Dokumenten-Generator dieses Tools erzeugt das GrundgerĂŒst.', + 'Complete technical documentation before placing on the market; simplified forms apply to SMEs. This tool generates the skeleton.', + ), + deadline: '2027-12-02', + roles: ['provider'], + riskClasses: ['high'], + }, + { + id: 'ob-logging', + title: lt('Protokollierung (Logging) technisch ermöglichen', 'Enable logging technically'), + articles: 'Art. 12', + description: lt( + 'Automatische Aufzeichnung von Ereignissen ĂŒber den Lebenszyklus, um RĂŒckverfolgbarkeit und MarktĂŒberwachung zu ermöglichen.', + 'Automatic recording of events across the lifecycle to enable traceability and market surveillance.', + ), + deadline: '2027-12-02', + roles: ['provider'], + riskClasses: ['high'], + }, + { + id: 'ob-transparency-instructions', + title: lt( + 'Betriebsanleitung und Transparenzinformationen bereitstellen', + 'Provide instructions for use and transparency information', + ), + articles: 'Art. 13', + description: lt( + 'Klare Gebrauchsanweisung: Zweckbestimmung, Genauigkeit, Grenzen, menschliche Aufsicht, erwartete Lebensdauer und Wartung.', + 'Clear instructions for use: intended purpose, accuracy, limitations, human oversight, expected lifetime and maintenance.', + ), + deadline: '2027-12-02', + roles: ['provider'], + riskClasses: ['high'], + }, + { + id: 'ob-human-oversight', + title: lt('Menschliche Aufsicht konzipieren', 'Design human oversight'), + articles: 'Art. 14', + description: lt( + 'Das System muss wirksam durch Menschen beaufsichtigt werden können (Eingriffs-/Abbruchmöglichkeiten, Automation-Bias-Vorkehrungen).', + 'The system must allow effective human oversight (intervention/stop mechanisms, safeguards against automation bias).', + ), + deadline: '2027-12-02', + roles: ['provider'], + riskClasses: ['high'], + }, + { + id: 'ob-accuracy-robustness', + title: lt( + 'Genauigkeit, Robustheit und Cybersicherheit gewĂ€hrleisten', + 'Ensure accuracy, robustness and cybersecurity', + ), + articles: 'Art. 15', + description: lt( + 'Angemessenes Niveau an Genauigkeit und WiderstandsfĂ€higkeit, inkl. Schutz gegen KI-spezifische Angriffe (Data Poisoning, Adversarial Examples, Prompt Injection).', + 'Appropriate level of accuracy and resilience, incl. protection against AI-specific attacks (data poisoning, adversarial examples, prompt injection).', + ), + deadline: '2027-12-02', + roles: ['provider'], + riskClasses: ['high'], + }, + { + id: 'ob-qms', + title: lt('QualitĂ€tsmanagementsystem einfĂŒhren', 'Implement a quality management system'), + articles: 'Art. 17', + description: lt( + 'Dokumentiertes QMS: Compliance-Strategie, PrĂŒfverfahren, Datenmanagement, Risikomanagement, Meldeverfahren, Verantwortlichkeiten.', + 'Documented QMS: compliance strategy, testing procedures, data management, risk management, reporting procedures, responsibilities.', + ), + deadline: '2027-12-02', + roles: ['provider'], + riskClasses: ['high'], + }, + { + id: 'ob-corrective', + title: lt( + 'Korrekturmaßnahmen und Informationspflichten organisieren', + 'Organise corrective actions and information duties', + ), + articles: 'Art. 20, 21', + description: lt( + 'Bei NichtkonformitĂ€t: unverzĂŒglich korrigieren, zurĂŒcknehmen oder zurĂŒckrufen; HĂ€ndler, Betreiber und Behörden informieren; mit zustĂ€ndigen Behörden kooperieren.', + 'In case of non-conformity: immediately correct, withdraw or recall; inform distributors, deployers and authorities; cooperate with competent authorities.', + ), + deadline: '2027-12-02', + roles: ['provider'], + riskClasses: ['high'], + }, + { + id: 'ob-authorized-rep', + title: lt( + 'BevollmĂ€chtigten in der EU benennen (Nicht-EU-Anbieter)', + 'Appoint an authorised representative in the EU (non-EU providers)', + ), + articles: 'Art. 22', + description: lt( + 'Anbieter mit Sitz außerhalb der EU mĂŒssen vor Bereitstellung einen in der EU niedergelassenen BevollmĂ€chtigten schriftlich mandatieren.', + 'Providers established outside the EU must appoint, by written mandate, an authorised representative established in the EU before making systems available.', + ), + deadline: '2027-12-02', + roles: ['provider'], + riskClasses: ['high'], + }, + { + id: 'ob-conformity', + title: lt( + 'KonformitĂ€tsbewertung durchfĂŒhren, CE-Kennzeichnung, EU-Datenbank-Registrierung', + 'Conduct conformity assessment, CE marking, EU database registration', + ), + articles: 'Art. 43, 47–49', + description: lt( + 'KonformitĂ€tsbewertungsverfahren abschließen, EU-KonformitĂ€tserklĂ€rung ausstellen, CE-Kennzeichnung anbringen und System in der EU-Datenbank registrieren.', + 'Complete the conformity assessment procedure, issue the EU declaration of conformity, affix CE marking and register the system in the EU database.', + ), + deadline: '2027-12-02', + roles: ['provider'], + riskClasses: ['high'], + }, + { + id: 'ob-postmarket', + title: lt( + 'Plan zur Beobachtung nach dem Inverkehrbringen aufstellen', + 'Establish a post-market monitoring plan', + ), + articles: 'Art. 72', + description: lt( + 'Systematische Sammlung und Auswertung von Betriebsdaten ĂŒber die gesamte Lebensdauer, um fortlaufende KonformitĂ€t sicherzustellen; Teil der technischen Dokumentation.', + 'Systematically collect and analyse operational data over the entire lifetime to ensure continued conformity; part of the technical documentation.', + ), + deadline: '2027-12-02', + roles: ['provider'], + riskClasses: ['high'], + }, + { + id: 'ob-incident', + title: lt( + 'Meldeprozess fĂŒr schwerwiegende VorfĂ€lle einrichten', + 'Set up a serious-incident reporting process', + ), + articles: 'Art. 73', + description: lt( + 'Schwerwiegende VorfĂ€lle sind der MarktĂŒberwachungsbehörde zu melden (grundsĂ€tzlich binnen 15 Tagen; bei TodesfĂ€llen/weitverbreiteten VerstĂ¶ĂŸen kĂŒrzere Fristen). Interner Melde- und Untersuchungsprozess erforderlich.', + 'Serious incidents must be reported to the market surveillance authority (generally within 15 days; shorter deadlines for deaths/widespread infringements). An internal reporting and investigation process is required.', + ), + deadline: '2027-12-02', + roles: ['provider'], + riskClasses: ['high'], + }, + // ── Hochrisiko: Betreiber ───────────────────────────────────────── + { + id: 'ob-deployer-use', + title: lt('Nutzung gemĂ€ĂŸ Betriebsanleitung sicherstellen', 'Ensure use according to instructions'), + articles: 'Art. 26 Abs. 1', + description: lt( + 'Technische und organisatorische Maßnahmen, damit das System entsprechend der Gebrauchsanweisung des Anbieters genutzt wird.', + 'Technical and organisational measures to ensure the system is used in accordance with the provider’s instructions for use.', + ), + deadline: '2027-12-02', + roles: ['deployer'], + riskClasses: ['high'], + }, + { + id: 'ob-deployer-oversight', + title: lt('Kompetente menschliche Aufsicht zuweisen', 'Assign competent human oversight'), + articles: 'Art. 26 Abs. 2', + description: lt( + 'Aufsicht durch Personen mit erforderlicher Kompetenz, Ausbildung und Befugnis.', + 'Oversight by persons with the necessary competence, training and authority.', + ), + deadline: '2027-12-02', + roles: ['deployer'], + riskClasses: ['high'], + }, + { + id: 'ob-deployer-input', + title: lt( + 'Relevanz und ReprĂ€sentativitĂ€t der Eingabedaten sicherstellen', + 'Ensure relevance and representativeness of input data', + ), + articles: 'Art. 26 Abs. 4', + description: lt( + 'Soweit der Betreiber die Eingabedaten kontrolliert, mĂŒssen diese fĂŒr die Zweckbestimmung relevant und hinreichend reprĂ€sentativ sein.', + 'Insofar as the deployer controls the input data, it must be relevant and sufficiently representative for the intended purpose.', + ), + deadline: '2027-12-02', + roles: ['deployer'], + riskClasses: ['high'], + }, + { + id: 'ob-deployer-monitoring', + title: lt( + 'Betrieb ĂŒberwachen, VorfĂ€lle melden, Logs aufbewahren', + 'Monitor operation, report incidents, retain logs', + ), + articles: 'Art. 26 Abs. 5–6', + description: lt( + 'Betrieb anhand der Anleitung ĂŒberwachen; bei Risiken Anbieter/Behörden informieren; automatisch erzeugte Logs mind. 6 Monate aufbewahren.', + 'Monitor operation based on the instructions; inform provider/authorities of risks; retain automatically generated logs for at least 6 months.', + ), + deadline: '2027-12-02', + roles: ['deployer'], + riskClasses: ['high'], + }, + { + id: 'ob-deployer-workers', + title: lt( + 'BeschĂ€ftigte vor Einsatz am Arbeitsplatz informieren', + 'Inform employees before workplace use', + ), + articles: 'Art. 26 Abs. 7', + description: lt( + 'Vor Inbetriebnahme eines Hochrisiko-Systems am Arbeitsplatz sind Arbeitnehmervertreter und betroffene BeschĂ€ftigte zu informieren.', + 'Before putting a high-risk system into service at the workplace, workers’ representatives and affected employees must be informed.', + ), + deadline: '2027-12-02', + roles: ['deployer'], + riskClasses: ['high'], + }, + { + id: 'ob-registration-deployer', + title: lt( + 'Registrierung in der EU-Datenbank prĂŒfen (öffentliche Stellen)', + 'Check EU database registration (public bodies)', + ), + articles: 'Art. 49 Abs. 3', + description: lt( + 'Betreiber, die Behörden oder öffentliche Einrichtungen sind (oder in deren Auftrag handeln), mĂŒssen sich vor Nutzung eines Annex-III-Systems in der EU-Datenbank registrieren.', + 'Deployers that are public authorities or bodies (or act on their behalf) must register in the EU database before using an Annex III system.', + ), + deadline: '2027-12-02', + roles: ['deployer'], + riskClasses: ['high'], + }, + { + id: 'ob-dsgvo-dpia', + title: lt( + 'Datenschutz-FolgenabschĂ€tzung (DSFA) durchfĂŒhren', + 'Carry out a data protection impact assessment (DPIA)', + ), + articles: 'DSGVO Art. 35 i. V. m. AI Act Art. 26 Abs. 9', + description: lt( + 'Verarbeitet das Hochrisiko-System personenbezogene Daten, ist regelmĂ€ĂŸig eine DSFA nach DSGVO erforderlich; die Anbieter-Informationen nach Art. 13 AI Act sind dafĂŒr zu nutzen.', + 'If the high-risk system processes personal data, a DPIA under the GDPR is regularly required; use the provider information under Art. 13 AI Act for this purpose.', + ), + deadline: '2027-12-02', + roles: ['deployer'], + riskClasses: ['high'], + }, + { + id: 'ob-fria', + title: lt( + 'Grundrechte-FolgenabschĂ€tzung (FRIA) prĂŒfen', + 'Assess fundamental rights impact (FRIA)', + ), + articles: 'Art. 27', + description: lt( + 'Pflicht fĂŒr öffentliche Stellen und private Anbieter öffentlicher Dienste sowie bestimmte Annex-III-FĂ€lle (z. B. KreditwĂŒrdigkeit, Versicherung): Folgen fĂŒr Grundrechte vor Einsatz bewerten.', + 'Required for public bodies and private providers of public services as well as certain Annex III cases (e.g. creditworthiness, insurance): assess fundamental rights impact before use.', + ), + deadline: '2027-12-02', + roles: ['deployer'], + riskClasses: ['high'], + }, + // ── Importeur / HĂ€ndler ─────────────────────────────────────────── + { + id: 'ob-importer-verify', + title: lt('KonformitĂ€t vor Inverkehrbringen prĂŒfen', 'Verify conformity before placing on the market'), + articles: 'Art. 23', + description: lt( + 'Importeure mĂŒssen prĂŒfen: KonformitĂ€tsbewertung durchgefĂŒhrt, technische Dokumentation vorhanden, CE-Kennzeichnung und Ansprechpartner benannt.', + 'Importers must verify: conformity assessment performed, technical documentation available, CE marking affixed and contact person designated.', + ), + deadline: '2027-12-02', + roles: ['importer'], + riskClasses: ['high'], + }, + { + id: 'ob-distributor-verify', + title: lt('Sorgfaltspflichten der Vertriebskette erfĂŒllen', 'Fulfil distribution chain due diligence'), + articles: 'Art. 24', + description: lt( + 'HĂ€ndler prĂŒfen CE-Kennzeichnung, KonformitĂ€tserklĂ€rung und Gebrauchsanweisung; bei Zweifeln keine Bereitstellung.', + 'Distributors check CE marking, declaration of conformity and instructions for use; do not make available in case of doubt.', + ), + deadline: '2027-12-02', + roles: ['distributor'], + riskClasses: ['high'], + }, + // ── Transparenzpflichten (limited) ──────────────────────────────── + { + id: 'ob-transparency-art50', + title: lt('Transparenzpflichten nach Art. 50 umsetzen', 'Implement transparency obligations under Art. 50'), + articles: 'Art. 50', + description: lt( + 'Je nach Auslöser: KI-Interaktion offenlegen (Chatbots), synthetische Inhalte maschinenlesbar kennzeichnen, Deepfakes sichtbar kennzeichnen, ĂŒber Emotionserkennung informieren.', + 'Depending on the trigger: disclose AI interaction (chatbots), machine-readably mark synthetic content, visibly label deepfakes, inform about emotion recognition.', + ), + deadline: '2026-08-02', + roles: ['provider', 'deployer'], + riskClasses: ['high', 'limited'], + }, + // ── GPAI ────────────────────────────────────────────────────────── + { + id: 'ob-gpai', + title: lt('GPAI-Modellpflichten erfĂŒllen', 'Fulfil GPAI model obligations'), + articles: 'Art. 53–55', + description: lt( + 'FĂŒr Anbieter von GPAI-Modellen: technische Modell-Dokumentation, Informationen fĂŒr nachgelagerte Anbieter, Urheberrechts-Policy, Zusammenfassung der Trainingsdaten. Bei systemischem Risiko zusĂ€tzlich Evaluierungen und Incident-Meldungen.', + 'For providers of GPAI models: technical model documentation, information for downstream providers, copyright policy, training data summary. For systemic risk additionally evaluations and incident reports.', + ), + deadline: '2025-08-02', + roles: ['provider'], + riskClasses: ['high', 'limited', 'minimal'], + }, +]; diff --git a/ai-act-kompass/src/content/sourceTypes.ts b/ai-act-kompass/src/content/sourceTypes.ts new file mode 100644 index 0000000..2fd4279 --- /dev/null +++ b/ai-act-kompass/src/content/sourceTypes.ts @@ -0,0 +1,37 @@ +import type { RiskClass, Role } from '../engine/types'; +import type { LText } from '../i18n/types'; + +/** Checklisten-Item in Quellform (lokalisiert). */ +export interface ContentItemSrc { + readonly id: string; + readonly label: LText; + readonly legalRef: string; + readonly hint?: LText; +} + +/** Pflicht in Quellform (lokalisiert). */ +export interface ObligationSrc { + readonly id: string; + readonly title: LText; + readonly articles: string; + readonly description: LText; + readonly deadline: string; + readonly roles: readonly Role[]; + readonly riskClasses: readonly RiskClass[]; +} + +/** Geltungsfrist in Quellform (lokalisiert). */ +export interface DeadlineSrc { + readonly id: string; + readonly date: string; + readonly label: LText; + readonly description: LText; +} + +/** Nationales Modul in Quellform (lokalisiert). */ +export interface NationalPackSrc { + readonly code: string; + readonly country: LText; + readonly authority: LText; + readonly items: readonly ObligationSrc[]; +} diff --git a/ai-act-kompass/src/engine/checklist.ts b/ai-act-kompass/src/engine/checklist.ts new file mode 100644 index 0000000..667b771 --- /dev/null +++ b/ai-act-kompass/src/engine/checklist.ts @@ -0,0 +1,77 @@ +import { deriveObligations } from './obligations'; +import type { AISystem, ContentPack, Obligation, ObligationStatus } from './types'; + +/** Ein Eintrag der systemĂŒbergreifenden Aufgabenliste. */ +export interface ChecklistEntry { + readonly systemId: string; + readonly systemName: string; + readonly obligation: Obligation; + readonly status: ObligationStatus; + /** Erledigt oder als nicht anwendbar markiert. */ + readonly resolved: boolean; + /** Frist liegt am oder vor dem Stichtag. */ + readonly due: boolean; +} + +/** Gruppierte Aufgabenliste mit Fortschritt. */ +export interface Checklist { + /** FĂ€llig (Frist erreicht/ĂŒberschritten) und noch nicht erledigt. */ + readonly dueNow: readonly ChecklistEntry[]; + /** Kommt auf das Unternehmen zu (Frist in der Zukunft), noch offen. */ + readonly upcoming: readonly ChecklistEntry[]; + /** Erledigt oder nicht anwendbar. */ + readonly resolved: readonly ChecklistEntry[]; + readonly total: number; + readonly resolvedCount: number; +} + +/** + * Baut die systemĂŒbergreifende, abhakbare Aufgabenliste. + * + * Jede Pflicht jedes Systems wird zu einem Eintrag; Sortierung innerhalb + * der Gruppen nach Frist aufsteigend, dann Systemname. + * + * @param systems - Alle registrierten KI-Systeme + * @param pack - Regulatorischer Content-Pack (Sprache des Aufrufers) + * @param countries - Aktivierte LĂ€nder-Module + * @param today - Stichtag im ISO-Format (YYYY-MM-DD) + */ +export function buildChecklist( + systems: readonly AISystem[], + pack: ContentPack, + countries: readonly string[], + today: string, +): Checklist { + const entries: ChecklistEntry[] = systems.flatMap((system) => + deriveObligations(system.classification, system.answers, pack, countries).map( + (obligation) => { + const status = system.obligationStatus[obligation.id] ?? 'open'; + const resolved = status === 'done' || status === 'not-applicable'; + return { + systemId: system.id, + systemName: system.name, + obligation, + status, + resolved, + due: obligation.deadline <= today, + }; + }, + ), + ); + + const byDeadline = (a: ChecklistEntry, b: ChecklistEntry): number => + a.obligation.deadline.localeCompare(b.obligation.deadline) || + a.systemName.localeCompare(b.systemName); + + const dueNow = entries.filter((e) => !e.resolved && e.due).sort(byDeadline); + const upcoming = entries.filter((e) => !e.resolved && !e.due).sort(byDeadline); + const resolved = entries.filter((e) => e.resolved).sort(byDeadline); + + return { + dueNow, + upcoming, + resolved, + total: entries.length, + resolvedCount: resolved.length, + }; +} diff --git a/ai-act-kompass/src/engine/classify.ts b/ai-act-kompass/src/engine/classify.ts index e553b10..f57f730 100644 --- a/ai-act-kompass/src/engine/classify.ts +++ b/ai-act-kompass/src/engine/classify.ts @@ -1,5 +1,50 @@ +import type { Locale } from '../i18n/types'; import type { AssessmentAnswers, Classification, ContentPack } from './types'; +interface ClassifyStrings { + readonly outOfScope: string; + readonly prohibited: (label: string, ref: string) => string; + readonly annexI: string; + readonly annexIII: (label: string, ref: string) => string; + readonly profilingBar: string; + readonly art63: string; + readonly transparency: (label: string, ref: string) => string; + readonly minimal: string; +} + +const STRINGS: Readonly> = { + de: { + outOfScope: + 'Das System erfĂŒllt die KI-Definition nach Art. 3 Nr. 1 nicht — der AI Act ist nicht anwendbar.', + prohibited: (label, ref) => `Verbotene Praktik: ${label} (${ref})`, + annexI: + 'Das System ist Sicherheitsbauteil eines in Annex I regulierten Produkts bzw. selbst ein solches Produkt (Art. 6 Abs. 1) — Hochrisiko.', + annexIII: (label, ref) => `Annex-III-Bereich: ${label} (${ref})`, + profilingBar: + 'Das System fĂŒhrt Profiling natĂŒrlicher Personen durch — die Ausnahme nach Art. 6 Abs. 3 ist damit ausgeschlossen.', + art63: + 'Ausnahme nach Art. 6 Abs. 3 in Anspruch genommen (eng begrenzte/vorbereitende Aufgabe ohne wesentliches Risiko). Die EinschĂ€tzung ist nach Art. 6 Abs. 4 zu dokumentieren.', + transparency: (label, ref) => `Transparenzpflicht: ${label} (${ref})`, + minimal: + 'Keine verbotene Praktik, kein Hochrisiko-Bereich, keine Transparenzpflicht-Auslöser — minimales Risiko. Es gelten die allgemeine KI-Kompetenzpflicht (Art. 4) und freiwillige Verhaltenskodizes.', + }, + en: { + outOfScope: + 'The system does not meet the AI definition under Art. 3(1) — the AI Act does not apply.', + prohibited: (label, ref) => `Prohibited practice: ${label} (${ref})`, + annexI: + 'The system is a safety component of a product regulated under Annex I, or such a product itself (Art. 6(1)) — high-risk.', + annexIII: (label, ref) => `Annex III area: ${label} (${ref})`, + profilingBar: + 'The system performs profiling of natural persons — the exception under Art. 6(3) is therefore excluded.', + art63: + 'Exception under Art. 6(3) claimed (narrow procedural/preparatory task without significant risk). The assessment must be documented under Art. 6(4).', + transparency: (label, ref) => `Transparency obligation: ${label} (${ref})`, + minimal: + 'No prohibited practice, no high-risk area, no transparency triggers — minimal risk. The general AI literacy duty (Art. 4) and voluntary codes of conduct apply.', + }, +}; + /** * Klassifiziert ein KI-System nach dem Risikomodell des EU AI Act. * @@ -13,16 +58,22 @@ import type { AssessmentAnswers, Classification, ContentPack } from './types'; * * @param answers - Antworten des Bewertungs-Wizards * @param pack - Regulatorischer Content-Pack (fĂŒr BegrĂŒndungstexte) + * @param locale - Sprache der BegrĂŒndungen (Default: Deutsch) * @returns UnverĂ€nderliches Klassifizierungsergebnis mit BegrĂŒndungen */ -export function classify(answers: AssessmentAnswers, pack: ContentPack): Classification { +export function classify( + answers: AssessmentAnswers, + pack: ContentPack, + locale: Locale = 'de', +): Classification { + const s = STRINGS[locale]; const reasons: string[] = []; const transparencyDuties = answers.transparencyTriggers.length > 0; if (!answers.isAISystem) { return { riskClass: 'out-of-scope', - reasons: ['Das System erfĂŒllt die KI-Definition nach Art. 3 Nr. 1 nicht — der AI Act ist nicht anwendbar.'], + reasons: [s.outOfScope], gpaiObligations: false, transparencyDuties: false, art64DocumentationDuty: false, @@ -33,7 +84,7 @@ export function classify(answers: AssessmentAnswers, pack: ContentPack): Classif for (const id of answers.prohibitedPractices) { const item = pack.prohibitedPractices.find((p) => p.id === id); if (item) { - reasons.push(`Verbotene Praktik: ${item.label} (${item.legalRef})`); + reasons.push(s.prohibited(item.label, item.legalRef)); } } return { @@ -46,9 +97,7 @@ export function classify(answers: AssessmentAnswers, pack: ContentPack): Classif } if (answers.isAnnexISafetyComponent) { - reasons.push( - 'Das System ist Sicherheitsbauteil eines in Annex I regulierten Produkts bzw. selbst ein solches Produkt (Art. 6 Abs. 1) — Hochrisiko.', - ); + reasons.push(s.annexI); return { riskClass: 'high', reasons, @@ -62,14 +111,12 @@ export function classify(answers: AssessmentAnswers, pack: ContentPack): Classif for (const id of answers.annexIIICategories) { const item = pack.annexIIICategories.find((c) => c.id === id); if (item) { - reasons.push(`Annex-III-Bereich: ${item.label} (${item.legalRef})`); + reasons.push(s.annexIII(item.label, item.legalRef)); } } if (answers.involvesProfiling) { - reasons.push( - 'Das System fĂŒhrt Profiling natĂŒrlicher Personen durch — die Ausnahme nach Art. 6 Abs. 3 ist damit ausgeschlossen.', - ); + reasons.push(s.profilingBar); return { riskClass: 'high', reasons, @@ -80,13 +127,11 @@ export function classify(answers: AssessmentAnswers, pack: ContentPack): Classif } if (answers.art63Exception) { - reasons.push( - 'Ausnahme nach Art. 6 Abs. 3 in Anspruch genommen (eng begrenzte/vorbereitende Aufgabe ohne wesentliches Risiko). Die EinschĂ€tzung ist nach Art. 6 Abs. 4 zu dokumentieren.', - ); + reasons.push(s.art63); return { riskClass: transparencyDuties ? 'limited' : 'minimal', reasons: transparencyDuties - ? [...reasons, ...transparencyReasons(answers, pack)] + ? [...reasons, ...transparencyReasons(answers, pack, s)] : reasons, gpaiObligations: answers.isGPAIModel, transparencyDuties, @@ -106,7 +151,7 @@ export function classify(answers: AssessmentAnswers, pack: ContentPack): Classif if (transparencyDuties) { return { riskClass: 'limited', - reasons: transparencyReasons(answers, pack), + reasons: transparencyReasons(answers, pack, s), gpaiObligations: answers.isGPAIModel, transparencyDuties: true, art64DocumentationDuty: false, @@ -115,9 +160,7 @@ export function classify(answers: AssessmentAnswers, pack: ContentPack): Classif return { riskClass: 'minimal', - reasons: [ - 'Keine verbotene Praktik, kein Hochrisiko-Bereich, keine Transparenzpflicht-Auslöser — minimales Risiko. Es gelten die allgemeine KI-Kompetenzpflicht (Art. 4) und freiwillige Verhaltenskodizes.', - ], + reasons: [s.minimal], gpaiObligations: answers.isGPAIModel, transparencyDuties: false, art64DocumentationDuty: false, @@ -125,9 +168,13 @@ export function classify(answers: AssessmentAnswers, pack: ContentPack): Classif } /** Erzeugt BegrĂŒndungstexte fĂŒr zutreffende Art.-50-Auslöser. */ -function transparencyReasons(answers: AssessmentAnswers, pack: ContentPack): string[] { +function transparencyReasons( + answers: AssessmentAnswers, + pack: ContentPack, + s: ClassifyStrings, +): string[] { return answers.transparencyTriggers.flatMap((id) => { const item = pack.transparencyTriggers.find((t) => t.id === id); - return item ? [`Transparenzpflicht: ${item.label} (${item.legalRef})`] : []; + return item ? [s.transparency(item.label, item.legalRef)] : []; }); } diff --git a/ai-act-kompass/src/engine/docStrings.ts b/ai-act-kompass/src/engine/docStrings.ts new file mode 100644 index 0000000..6b71ea1 --- /dev/null +++ b/ai-act-kompass/src/engine/docStrings.ts @@ -0,0 +1,201 @@ +import type { Locale } from '../i18n/types'; +import type { ObligationStatus, RiskClass, Role } from './types'; + +/** Anzeige-Labels und Textbausteine der Dokument-Generatoren je Sprache. */ +export interface DocStrings { + readonly risk: Readonly>; + readonly role: Readonly>; + readonly status: Readonly>; + readonly disclaimer: string; + readonly meta: { + readonly system: string; + readonly purpose: string; + readonly vendor: string; + readonly owner: string; + readonly role: string; + readonly riskClass: string; + readonly createdAt: string; + readonly contentPack: string; + readonly company: string; + readonly contact: string; + readonly contactPerson: string; + }; + readonly report: { + readonly title: string; + readonly result: string; + readonly reasons: string; + readonly gpai: string; + readonly gpaiText: string; + readonly art64: string; + readonly art64Text: string; + }; + readonly plan: { + readonly title: string; + readonly none: string; + readonly obligations: string; + readonly details: string; + readonly colObligation: string; + readonly colLegal: string; + readonly colDeadline: string; + readonly colStatus: string; + }; + readonly inventory: { + readonly title: string; + readonly asOf: string; + readonly count: string; + readonly colSystem: string; + readonly colPurpose: string; + readonly colRole: string; + readonly colRisk: string; + readonly colOwner: string; + }; + readonly todo: string; +} + +const DE: DocStrings = { + risk: { + 'out-of-scope': 'Nicht anwendbar (keine KI i. S. d. Art. 3 Nr. 1)', + prohibited: 'VERBOTEN (Art. 5)', + high: 'Hochrisiko', + limited: 'Begrenztes Risiko (Transparenzpflichten)', + minimal: 'Minimales Risiko', + }, + role: { + provider: 'Anbieter (Provider)', + deployer: 'Betreiber (Deployer)', + importer: 'EinfĂŒhrer (Importer)', + distributor: 'HĂ€ndler (Distributor)', + }, + status: { + open: 'Offen', + 'in-progress': 'In Umsetzung', + done: 'Erledigt', + 'not-applicable': 'Nicht anwendbar', + }, + disclaimer: + '> **Hinweis:** Dieses Dokument wurde mit dem AI-Act-Kompass erstellt und ist eine strukturierte Arbeitshilfe — **keine Rechtsberatung**. Die rechtliche Bewertung im Einzelfall obliegt dem Unternehmen bzw. seiner Rechtsberatung.', + meta: { + system: 'KI-System', + purpose: 'Zweckbestimmung', + vendor: 'Hersteller/Anbieter', + owner: 'Verantwortlich (intern)', + role: 'Rolle des Unternehmens', + riskClass: 'Risikoklasse', + createdAt: 'Erstellt am', + contentPack: 'Content-Pack', + company: 'Unternehmen', + contact: 'Kontakt', + contactPerson: 'Ansprechpartner', + }, + report: { + title: 'Risikoklassifizierung nach EU AI Act', + result: 'Ergebnis', + reasons: 'BegrĂŒndung', + gpai: 'GPAI', + gpaiText: 'FĂŒr dieses System sind zusĂ€tzlich GPAI-Modellpflichten (Art. 53 ff.) relevant.', + art64: 'Dokumentationspflicht Art. 6 Abs. 4', + art64Text: + 'Die Inanspruchnahme der Ausnahme nach Art. 6 Abs. 3 wurde dokumentiert. Diese EinschĂ€tzung ist auf Verlangen der MarktĂŒberwachung vorzulegen.', + }, + plan: { + title: 'Maßnahmenplan EU AI Act', + none: 'FĂŒr dieses System bestehen keine umsetzbaren Pflichten (verboten oder außerhalb des Anwendungsbereichs).', + obligations: 'Pflichten', + details: 'Details', + colObligation: 'Pflicht', + colLegal: 'Rechtsgrundlage', + colDeadline: 'Frist', + colStatus: 'Status', + }, + inventory: { + title: 'KI-Inventar (AI Asset Register)', + asOf: 'Stand', + count: 'Registrierte KI-Systeme', + colSystem: 'System', + colPurpose: 'Zweck', + colRole: 'Rolle', + colRisk: 'Risikoklasse', + colOwner: 'Verantwortlich', + }, + todo: '_[AUSFÜLLEN]_', +}; + +const EN: DocStrings = { + risk: { + 'out-of-scope': 'Not applicable (not AI within the meaning of Art. 3(1))', + prohibited: 'PROHIBITED (Art. 5)', + high: 'High-risk', + limited: 'Limited risk (transparency obligations)', + minimal: 'Minimal risk', + }, + role: { + provider: 'Provider', + deployer: 'Deployer', + importer: 'Importer', + distributor: 'Distributor', + }, + status: { + open: 'Open', + 'in-progress': 'In progress', + done: 'Done', + 'not-applicable': 'Not applicable', + }, + disclaimer: + '> **Note:** This document was created with AI-Act-Kompass and is a structured working aid — **not legal advice**. The legal assessment of the individual case remains with the company and its legal counsel.', + meta: { + system: 'AI system', + purpose: 'Intended purpose', + vendor: 'Manufacturer/provider', + owner: 'Responsible (internal)', + role: 'Company role', + riskClass: 'Risk class', + createdAt: 'Created on', + contentPack: 'Content pack', + company: 'Company', + contact: 'Contact', + contactPerson: 'Contact person', + }, + report: { + title: 'Risk classification under the EU AI Act', + result: 'Result', + reasons: 'Reasoning', + gpai: 'GPAI', + gpaiText: 'GPAI model obligations (Art. 53 et seq.) are additionally relevant for this system.', + art64: 'Documentation duty Art. 6(4)', + art64Text: + 'The reliance on the exception under Art. 6(3) has been documented. This assessment must be provided to market surveillance authorities upon request.', + }, + plan: { + title: 'EU AI Act action plan', + none: 'There are no implementable obligations for this system (prohibited or out of scope).', + obligations: 'Obligations', + details: 'Details', + colObligation: 'Obligation', + colLegal: 'Legal basis', + colDeadline: 'Deadline', + colStatus: 'Status', + }, + inventory: { + title: 'AI inventory (AI asset register)', + asOf: 'As of', + count: 'Registered AI systems', + colSystem: 'System', + colPurpose: 'Purpose', + colRole: 'Role', + colRisk: 'Risk class', + colOwner: 'Responsible', + }, + todo: '_[TO BE COMPLETED]_', +}; + +/** Liefert die Dokument-Strings fĂŒr die gewĂŒnschte Sprache. */ +export function docStrings(locale: Locale): DocStrings { + return locale === 'en' ? EN : DE; +} + +/** Formatiert ein ISO-Datum landesĂŒblich (DE: TT.MM.JJJJ, EN: ISO). */ +export function formatDocDate(iso: string, locale: Locale): string { + if (locale === 'en') return iso; + const [y, m, d] = iso.split('-'); + return d && m && y ? `${d}.${m}.${y}` : iso; +} diff --git a/ai-act-kompass/src/engine/documents.test.ts b/ai-act-kompass/src/engine/documents.test.ts index 8b14691..b59c6d5 100644 --- a/ai-act-kompass/src/engine/documents.test.ts +++ b/ai-act-kompass/src/engine/documents.test.ts @@ -74,6 +74,7 @@ describe('documents', () => { phone: '+49 69 000000', website: 'www.muster.de', logoDataUrl: '', + countries: [], }; const md = riskReport(system, CONTENT_PACK, TODAY, profile); expect(md).toContain('| **Unternehmen** | Muster GmbH |'); diff --git a/ai-act-kompass/src/engine/documents.ts b/ai-act-kompass/src/engine/documents.ts index 937654a..34203c2 100644 --- a/ai-act-kompass/src/engine/documents.ts +++ b/ai-act-kompass/src/engine/documents.ts @@ -1,46 +1,20 @@ +import type { Locale } from '../i18n/types'; +import { docStrings, formatDocDate } from './docStrings'; import type { AISystem, CompanyProfile, ContentPack, Obligation } from './types'; -const RISK_LABELS: Record = { - 'out-of-scope': 'Nicht anwendbar (keine KI i. S. d. Art. 3 Nr. 1)', - prohibited: 'VERBOTEN (Art. 5)', - high: 'Hochrisiko', - limited: 'Begrenztes Risiko (Transparenzpflichten)', - minimal: 'Minimales Risiko', -}; - -const ROLE_LABELS: Record = { - provider: 'Anbieter (Provider)', - deployer: 'Betreiber (Deployer)', - importer: 'EinfĂŒhrer (Importer)', - distributor: 'HĂ€ndler (Distributor)', -}; - -const STATUS_LABELS: Record = { - open: 'Offen', - 'in-progress': 'In Umsetzung', - done: 'Erledigt', - 'not-applicable': 'Nicht anwendbar', -}; - -const DISCLAIMER = - '> **Hinweis:** Dieses Dokument wurde mit dem AI-Act-Kompass erstellt und ist eine strukturierte Arbeitshilfe — **keine Rechtsberatung**. Die rechtliche Bewertung im Einzelfall obliegt dem Unternehmen bzw. seiner Rechtsberatung.'; - -/** Formatiert ein ISO-Datum als deutsches Datum (TT.MM.JJJJ). */ -function formatDate(iso: string): string { - const [y, m, d] = iso.split('-'); - return d && m && y ? `${d}.${m}.${y}` : iso; -} - -/** Kontaktzeile aus dem Firmenprofil (leer, wenn kein Profil gepflegt). */ -function companyRows(profile: CompanyProfile | undefined): readonly string[] { +/** Kontaktzeilen aus dem Firmenprofil (leer, wenn kein Profil gepflegt). */ +function companyRows(profile: CompanyProfile | undefined, locale: Locale): readonly string[] { + const s = docStrings(locale); if (!profile || profile.name.trim() === '') return []; const contact = [profile.street, profile.zipCity, profile.email, profile.phone, profile.website] .filter((p) => p.trim() !== '') .join(' · '); return [ - `| **Unternehmen** | ${profile.name} |`, - ...(contact ? [`| **Kontakt** | ${contact} |`] : []), - ...(profile.contactPerson.trim() ? [`| **Ansprechpartner** | ${profile.contactPerson} |`] : []), + `| **${s.meta.company}** | ${profile.name} |`, + ...(contact ? [`| **${s.meta.contact}** | ${contact} |`] : []), + ...(profile.contactPerson.trim() + ? [`| **${s.meta.contactPerson}** | ${profile.contactPerson} |`] + : []), ]; } @@ -49,24 +23,26 @@ function header( system: AISystem, pack: ContentPack, today: string, + locale: Locale, profile?: CompanyProfile, ): string { + const s = docStrings(locale); return [ `# ${title}`, '', `| | |`, `|---|---|`, - ...companyRows(profile), - `| **KI-System** | ${system.name} |`, - `| **Zweckbestimmung** | ${system.purpose || '—'} |`, - `| **Hersteller/Anbieter** | ${system.vendor || '—'} |`, - `| **Verantwortlich (intern)** | ${system.owner || '—'} |`, - `| **Rolle des Unternehmens** | ${ROLE_LABELS[system.answers.role] ?? system.answers.role} |`, - `| **Risikoklasse** | ${RISK_LABELS[system.classification.riskClass] ?? system.classification.riskClass} |`, - `| **Erstellt am** | ${formatDate(today)} |`, - `| **Content-Pack** | v${pack.version} (${pack.legalBasis}) |`, + ...companyRows(profile, locale), + `| **${s.meta.system}** | ${system.name} |`, + `| **${s.meta.purpose}** | ${system.purpose || '—'} |`, + `| **${s.meta.vendor}** | ${system.vendor || '—'} |`, + `| **${s.meta.owner}** | ${system.owner || '—'} |`, + `| **${s.meta.role}** | ${s.role[system.answers.role]} |`, + `| **${s.meta.riskClass}** | ${s.risk[system.classification.riskClass]} |`, + `| **${s.meta.createdAt}** | ${formatDocDate(today, locale)} |`, + `| **${s.meta.contentPack}** | v${pack.version} (${pack.legalBasis}) |`, '', - DISCLAIMER, + s.disclaimer, '', ].join('\n'); } @@ -78,34 +54,32 @@ function header( * @param pack - Regulatorischer Content-Pack * @param today - Stichtag im ISO-Format * @param profile - Firmenprofil fĂŒr den Berichtskopf (optional) + * @param locale - Dokumentsprache (Default: Deutsch) */ export function riskReport( system: AISystem, pack: ContentPack, today: string, profile?: CompanyProfile, + locale: Locale = 'de', ): string { - const lines = [header('Risikoklassifizierung nach EU AI Act', system, pack, today, profile)]; + const s = docStrings(locale); + const lines = [header(s.report.title, system, pack, today, locale, profile)]; - lines.push('## Ergebnis', ''); - lines.push(`**${RISK_LABELS[system.classification.riskClass] ?? system.classification.riskClass}**`, ''); + lines.push(`## ${s.report.result}`, ''); + lines.push(`**${s.risk[system.classification.riskClass]}**`, ''); - lines.push('## BegrĂŒndung', ''); + lines.push(`## ${s.report.reasons}`, ''); for (const reason of system.classification.reasons) { lines.push(`- ${reason}`); } lines.push(''); if (system.classification.gpaiObligations) { - lines.push('## GPAI', '', 'FĂŒr dieses System sind zusĂ€tzlich GPAI-Modellpflichten (Art. 53 ff.) relevant.', ''); + lines.push(`## ${s.report.gpai}`, '', s.report.gpaiText, ''); } if (system.classification.art64DocumentationDuty) { - lines.push( - '## Dokumentationspflicht Art. 6 Abs. 4', - '', - 'Die Inanspruchnahme der Ausnahme nach Art. 6 Abs. 3 wurde dokumentiert. Diese EinschĂ€tzung ist auf Verlangen der MarktĂŒberwachung vorzulegen.', - '', - ); + lines.push(`## ${s.report.art64}`, '', s.report.art64Text, ''); } return lines.join('\n'); @@ -119,6 +93,7 @@ export function riskReport( * @param pack - Regulatorischer Content-Pack * @param today - Stichtag im ISO-Format * @param profile - Firmenprofil fĂŒr den Berichtskopf (optional) + * @param locale - Dokumentsprache (Default: Deutsch) */ export function actionPlan( system: AISystem, @@ -126,22 +101,26 @@ export function actionPlan( pack: ContentPack, today: string, profile?: CompanyProfile, + locale: Locale = 'de', ): string { - const lines = [header('Maßnahmenplan EU AI Act', system, pack, today, profile)]; + const s = docStrings(locale); + const lines = [header(s.plan.title, system, pack, today, locale, profile)]; if (obligations.length === 0) { - lines.push('FĂŒr dieses System bestehen keine umsetzbaren Pflichten (verboten oder außerhalb des Anwendungsbereichs).'); + lines.push(s.plan.none); return lines.join('\n'); } - lines.push('## Pflichten', ''); - lines.push('| Pflicht | Rechtsgrundlage | Frist | Status |'); + lines.push(`## ${s.plan.obligations}`, ''); + lines.push(`| ${s.plan.colObligation} | ${s.plan.colLegal} | ${s.plan.colDeadline} | ${s.plan.colStatus} |`); lines.push('|---|---|---|---|'); for (const ob of obligations) { const status = system.obligationStatus[ob.id] ?? 'open'; - lines.push(`| ${ob.title} | ${ob.articles} | ${formatDate(ob.deadline)} | ${STATUS_LABELS[status]} |`); + lines.push( + `| ${ob.title} | ${ob.articles} | ${formatDocDate(ob.deadline, locale)} | ${s.status[status]} |`, + ); } - lines.push('', '## Details', ''); + lines.push('', `## ${s.plan.details}`, ''); for (const ob of obligations) { lines.push(`### ${ob.title} (${ob.articles})`, '', ob.description, ''); } @@ -149,6 +128,57 @@ export function actionPlan( return lines.join('\n'); } +const ANNEX_IV_SECTIONS: Readonly> = { + de: [ + ['1. Allgemeine Beschreibung des KI-Systems', [ + 'Zweckbestimmung', 'Anbieter', 'Versionen und Datum des Inverkehrbringens', + 'Hardware-Anforderungen / Bereitstellungsform', 'Interaktion mit externer Hard-/Software', + ]], + ['2. Detaillierte Beschreibung der Elemente und des Entwicklungsprozesses', [ + 'Entwicklungsmethoden und verwendete Werkzeuge', 'Systemarchitektur und Rechenressourcen', + 'Datenanforderungen (Trainings-, Validierungs-, Testdaten, Herkunft, Aufbereitung)', + 'Maßnahmen der menschlichen Aufsicht (Art. 14)', + 'Vorab festgelegte Änderungen und KontinuitĂ€t der KonformitĂ€t', + ]], + ['3. Überwachung, Funktionsweise und Kontrolle', [ + 'FĂ€higkeiten und Leistungsgrenzen (Genauigkeitsmetriken)', + 'Vorhersehbare unbeabsichtigte Ergebnisse und Risikoquellen', 'Eingabedaten-Spezifikation', + ]], + ['4. Risikomanagementsystem (Art. 9)', ['Beschreibung des Risikomanagementprozesses']], + ['5. Änderungen ĂŒber den Lebenszyklus', ['Änderungshistorie und Update-Prozess']], + ['6. Angewandte harmonisierte Normen und Spezifikationen', [ + 'Liste der Normen (z. B. ISO/IEC 42001, ISO/IEC 23894)', + ]], + ['7. EU-KonformitĂ€tserklĂ€rung', ['Kopie der KonformitĂ€tserklĂ€rung (Art. 47)']], + ['8. Bewertung nach dem Inverkehrbringen (Art. 72)', [ + 'Plan zur Beobachtung nach dem Inverkehrbringen', + ]], + ], + en: [ + ['1. General description of the AI system', [ + 'Intended purpose', 'Provider', 'Versions and date of placing on the market', + 'Hardware requirements / form of provision', 'Interaction with external hardware/software', + ]], + ['2. Detailed description of elements and development process', [ + 'Development methods and tools used', 'System architecture and computational resources', + 'Data requirements (training, validation, test data; provenance, preparation)', + 'Human oversight measures (Art. 14)', + 'Pre-determined changes and continuity of conformity', + ]], + ['3. Monitoring, functioning and control', [ + 'Capabilities and performance limitations (accuracy metrics)', + 'Foreseeable unintended outcomes and sources of risk', 'Input data specification', + ]], + ['4. Risk management system (Art. 9)', ['Description of the risk management process']], + ['5. Changes over the lifecycle', ['Change history and update process']], + ['6. Applied harmonised standards and specifications', [ + 'List of standards (e.g. ISO/IEC 42001, ISO/IEC 23894)', + ]], + ['7. EU declaration of conformity', ['Copy of the declaration of conformity (Art. 47)']], + ['8. Post-market evaluation (Art. 72)', ['Post-market monitoring plan']], + ], +}; + /** * Erzeugt das GrundgerĂŒst der technischen Dokumentation nach Annex IV. * @@ -159,61 +189,35 @@ export function actionPlan( * @param pack - Regulatorischer Content-Pack * @param today - Stichtag im ISO-Format * @param profile - Firmenprofil fĂŒr den Berichtskopf (optional) + * @param locale - Dokumentsprache (Default: Deutsch) */ export function annexIVSkeleton( system: AISystem, pack: ContentPack, today: string, profile?: CompanyProfile, + locale: Locale = 'de', ): string { - const todo = '_[AUSFÜLLEN]_'; - const lines = [ - header('Technische Dokumentation (Annex IV) — GrundgerĂŒst', system, pack, today, profile), - ]; + const s = docStrings(locale); + const title = + locale === 'en' + ? 'Technical documentation (Annex IV) — skeleton' + : 'Technische Dokumentation (Annex IV) — GrundgerĂŒst'; + const lines = [header(title, system, pack, today, locale, profile)]; - lines.push( - '## 1. Allgemeine Beschreibung des KI-Systems', - '', - `- **Zweckbestimmung:** ${system.purpose || todo}`, - `- **Anbieter:** ${system.vendor || todo}`, - `- **Versionen und Datum des Inverkehrbringens:** ${todo}`, - `- **Hardware-Anforderungen / Bereitstellungsform:** ${todo}`, - `- **Interaktion mit externer Hard-/Software:** ${todo}`, - '', - '## 2. Detaillierte Beschreibung der Elemente und des Entwicklungsprozesses', - '', - `- **Entwicklungsmethoden und verwendete Werkzeuge:** ${todo}`, - `- **Systemarchitektur und Rechenressourcen:** ${todo}`, - `- **Datenanforderungen (Trainings-, Validierungs-, Testdaten, Herkunft, Aufbereitung):** ${todo}`, - `- **Maßnahmen der menschlichen Aufsicht (Art. 14):** ${todo}`, - `- **Vorab festgelegte Änderungen und KontinuitĂ€t der KonformitĂ€t:** ${todo}`, - '', - '## 3. Überwachung, Funktionsweise und Kontrolle', - '', - `- **FĂ€higkeiten und Leistungsgrenzen (Genauigkeitsmetriken):** ${todo}`, - `- **Vorhersehbare unbeabsichtigte Ergebnisse und Risikoquellen:** ${todo}`, - `- **Eingabedaten-Spezifikation:** ${todo}`, - '', - '## 4. Risikomanagementsystem (Art. 9)', - '', - `- **Beschreibung des Risikomanagementprozesses:** ${todo}`, - '', - '## 5. Änderungen ĂŒber den Lebenszyklus', - '', - `- **Änderungshistorie und Update-Prozess:** ${todo}`, - '', - '## 6. Angewandte harmonisierte Normen und Spezifikationen', - '', - `- **Liste der Normen (z. B. ISO/IEC 42001, ISO/IEC 23894):** ${todo}`, - '', - '## 7. EU-KonformitĂ€tserklĂ€rung', - '', - `- **Kopie der KonformitĂ€tserklĂ€rung (Art. 47):** ${todo}`, - '', - '## 8. Bewertung nach dem Inverkehrbringen (Art. 72)', - '', - `- **Plan zur Beobachtung nach dem Inverkehrbringen:** ${todo}`, - ); + const prefilled: Readonly> = { + [ANNEX_IV_SECTIONS[locale][0]?.[1][0] ?? '']: system.purpose, + [ANNEX_IV_SECTIONS[locale][0]?.[1][1] ?? '']: system.vendor, + }; + + for (const [section, items] of ANNEX_IV_SECTIONS[locale]) { + lines.push(`## ${section}`, ''); + for (const item of items) { + const value = prefilled[item]; + lines.push(`- **${item}:** ${value && value.trim() !== '' ? value : s.todo}`); + } + lines.push(''); + } return lines.join('\n'); } @@ -225,31 +229,34 @@ export function annexIVSkeleton( * @param pack - Regulatorischer Content-Pack * @param today - Stichtag im ISO-Format * @param profile - Firmenprofil fĂŒr den Berichtskopf (optional) + * @param locale - Dokumentsprache (Default: Deutsch) */ export function inventoryReport( systems: readonly AISystem[], pack: ContentPack, today: string, profile?: CompanyProfile, + locale: Locale = 'de', ): string { + const s = docStrings(locale); const company = profile && profile.name.trim() !== '' ? `${profile.name} · ` : ''; const lines = [ - '# KI-Inventar (AI Asset Register)', + `# ${s.inventory.title}`, '', - `${company}Stand: ${formatDate(today)} · Content-Pack v${pack.version}`, + `${company}${s.inventory.asOf}: ${formatDocDate(today, locale)} · Content-Pack v${pack.version}`, '', - DISCLAIMER, + s.disclaimer, '', - `Registrierte KI-Systeme: **${systems.length}**`, + `${s.inventory.count}: **${systems.length}**`, '', - '| System | Zweck | Rolle | Risikoklasse | Verantwortlich |', + `| ${s.inventory.colSystem} | ${s.inventory.colPurpose} | ${s.inventory.colRole} | ${s.inventory.colRisk} | ${s.inventory.colOwner} |`, '|---|---|---|---|---|', ]; - for (const s of systems) { + for (const sys of systems) { lines.push( - `| ${s.name} | ${s.purpose || '—'} | ${ROLE_LABELS[s.answers.role] ?? s.answers.role} | ${ - RISK_LABELS[s.classification.riskClass] ?? s.classification.riskClass - } | ${s.owner || '—'} |`, + `| ${sys.name} | ${sys.purpose || '—'} | ${s.role[sys.answers.role]} | ${ + s.risk[sys.classification.riskClass] + } | ${sys.owner || '—'} |`, ); } return lines.join('\n'); diff --git a/ai-act-kompass/src/engine/extraDocs.ts b/ai-act-kompass/src/engine/extraDocs.ts new file mode 100644 index 0000000..eab87ec --- /dev/null +++ b/ai-act-kompass/src/engine/extraDocs.ts @@ -0,0 +1,189 @@ +import type { Locale } from '../i18n/types'; +import { docStrings, formatDocDate } from './docStrings'; +import type { AISystem, CompanyProfile, ContentPack } from './types'; + +/** Anforderung im Lastenheft. */ +interface Requirement { + readonly id: string; + readonly must: boolean; + readonly de: string; + readonly en: string; + /** Nur bei Hochrisiko aufnehmen. */ + readonly highOnly?: boolean; +} + +const REQUIREMENTS: readonly Requirement[] = [ + { id: 'REQ-01', must: true, highOnly: true, + de: 'CE-Kennzeichnung und EU-KonformitĂ€tserklĂ€rung (Art. 47, 48) liegen vor und werden mitgeliefert.', + en: 'CE marking and EU declaration of conformity (Art. 47, 48) exist and are supplied.' }, + { id: 'REQ-02', must: true, highOnly: true, + de: 'Registrierung des Systems in der EU-Datenbank (Art. 49) ist nachgewiesen.', + en: 'Registration of the system in the EU database (Art. 49) is evidenced.' }, + { id: 'REQ-03', must: true, + de: 'VollstĂ€ndige Betriebsanleitung nach Art. 13 (Zweckbestimmung, Genauigkeit, Grenzen, Aufsicht, Wartung) in der Vertragssprache.', + en: 'Complete instructions for use under Art. 13 (purpose, accuracy, limitations, oversight, maintenance) in the contract language.' }, + { id: 'REQ-04', must: true, highOnly: true, + de: 'Logging-FĂ€higkeit nach Art. 12: automatische Ereignisprotokolle sind fĂŒr den Betreiber zugĂ€nglich und mind. 6 Monate speicherbar.', + en: 'Logging capability under Art. 12: automatic event logs are accessible to the deployer and storable for at least 6 months.' }, + { id: 'REQ-05', must: true, highOnly: true, + de: 'Schnittstellen fĂŒr menschliche Aufsicht nach Art. 14 (Eingriff, Abbruch, Übersteuerung) sind vorhanden und dokumentiert.', + en: 'Human oversight interfaces under Art. 14 (intervention, stop, override) exist and are documented.' }, + { id: 'REQ-06', must: true, highOnly: true, + de: 'Nachweis von Genauigkeit, Robustheit und Cybersicherheit nach Art. 15 inkl. Schutz gegen Prompt Injection, Data Poisoning und Adversarial Attacks.', + en: 'Evidence of accuracy, robustness and cybersecurity under Art. 15 incl. protection against prompt injection, data poisoning and adversarial attacks.' }, + { id: 'REQ-07', must: true, + de: 'Transparenzfunktionen nach Art. 50 (KI-Offenlegung, maschinenlesbare Kennzeichnung synthetischer Inhalte) sind implementiert, soweit einschlĂ€gig.', + en: 'Transparency features under Art. 50 (AI disclosure, machine-readable marking of synthetic content) are implemented where applicable.' }, + { id: 'REQ-08', must: true, + de: 'UnterstĂŒtzung bei Vorfallmeldungen: Anbieter meldet schwerwiegende VorfĂ€lle (Art. 73) und informiert den Auftraggeber unverzĂŒglich.', + en: 'Incident reporting support: provider reports serious incidents (Art. 73) and informs the customer without undue delay.' }, + { id: 'REQ-09', must: true, + de: 'DSGVO-KonformitĂ€t: Auftragsverarbeitungsvertrag, Angaben zu DatenflĂŒssen, Speicherorten (EU), Unterauftragnehmern und Löschkonzept.', + en: 'GDPR conformity: data processing agreement, information on data flows, storage locations (EU), sub-processors and deletion concept.' }, + { id: 'REQ-10', must: false, + de: 'Update- und Support-Zusagen: Sicherheitsupdates, Re-Zertifizierung bei wesentlichen Änderungen, Reaktionszeiten.', + en: 'Update and support commitments: security updates, re-certification upon substantial modification, response times.' }, + { id: 'REQ-11', must: false, + de: 'Exit-Strategie: Datenexport in offenem Format, Übergabe von Konfiguration und Protokollen bei Vertragsende.', + en: 'Exit strategy: data export in an open format, handover of configuration and logs at contract end.' }, + { id: 'REQ-12', must: false, highOnly: true, + de: 'Einsicht in relevante Teile der technischen Dokumentation (Annex IV) unter Vertraulichkeit, soweit fĂŒr Betreiberpflichten erforderlich.', + en: 'Access to relevant parts of the technical documentation (Annex IV) under confidentiality, insofar as required for deployer duties.' }, +]; + +const PROC = { + de: { + title: 'Lastenheft KI-Beschaffung — AI-Act-Anforderungen an den Anbieter', + intro: + 'Dieses Lastenheft definiert die regulatorischen Mindestanforderungen (EU AI Act, DSGVO) an den Anbieter des unten bezeichneten KI-Systems. Es ist Grundlage fĂŒr Angebot, Pflichtenheft und Vertrag.', + colId: 'Nr.', colReq: 'Anforderung', colPrio: 'PrioritĂ€t', colFulfilled: 'ErfĂŒllt (Anbieter)', + must: 'MUSS', should: 'SOLL', fulfilled: 'JA / NEIN / TEILWEISE', + note: 'Hinweis: Bei Hochrisiko-Systemen gelten alle MUSS-Anforderungen zwingend; NichterfĂŒllung schließt die Beauftragung aus.', + }, + en: { + title: 'AI procurement requirements specification — AI Act requirements for the provider', + intro: + 'This requirements specification defines the minimum regulatory requirements (EU AI Act, GDPR) for the provider of the AI system identified below. It forms the basis for offer, implementation specification and contract.', + colId: 'No.', colReq: 'Requirement', colPrio: 'Priority', colFulfilled: 'Fulfilled (provider)', + must: 'MUST', should: 'SHOULD', fulfilled: 'YES / NO / PARTIALLY', + note: 'Note: For high-risk systems all MUST requirements are mandatory; non-fulfilment excludes the award.', + }, +} as const; + +/** + * Erzeugt das Lastenheft fĂŒr die KI-Beschaffung (Betreiber-Perspektive): + * AI-Act-Anforderungen, die der Anbieter erfĂŒllen und im Pflichtenheft + * beantworten muss. + * + * @param system - Bewertetes KI-System + * @param pack - Regulatorischer Content-Pack + * @param today - Stichtag im ISO-Format + * @param profile - Firmenprofil fĂŒr den Berichtskopf (optional) + * @param locale - Dokumentsprache (Default: Deutsch) + */ +export function procurementSpec( + system: AISystem, + pack: ContentPack, + today: string, + profile?: CompanyProfile, + locale: Locale = 'de', +): string { + const s = docStrings(locale); + const p = PROC[locale]; + const high = system.classification.riskClass === 'high'; + const requirements = REQUIREMENTS.filter((r) => high || !r.highOnly); + + const lines = [ + `# ${p.title}`, + '', + `| | |`, + `|---|---|`, + ...(profile && profile.name.trim() !== '' ? [`| **${s.meta.company}** | ${profile.name} |`] : []), + `| **${s.meta.system}** | ${system.name} |`, + `| **${s.meta.purpose}** | ${system.purpose || '—'} |`, + `| **${s.meta.riskClass}** | ${s.risk[system.classification.riskClass]} |`, + `| **${s.meta.createdAt}** | ${formatDocDate(today, locale)} |`, + `| **${s.meta.contentPack}** | v${pack.version} |`, + '', + s.disclaimer, + '', + p.intro, + '', + `| ${p.colId} | ${p.colReq} | ${p.colPrio} | ${p.colFulfilled} |`, + '|---|---|---|---|', + ]; + for (const r of requirements) { + lines.push(`| ${r.id} | ${r[locale]} | ${r.must ? p.must : p.should} | ${p.fulfilled} |`); + } + lines.push('', p.note); + return lines.join('\n'); +} + +const DECL = { + de: { + title: 'EU-KonformitĂ€tserklĂ€rung (Entwurf, Art. 47 / Annex V)', + intro: + 'Entwurf auf Basis von Annex V der Verordnung (EU) 2024/1689. Vor Verwendung rechtlich prĂŒfen und durch die GeschĂ€ftsleitung unterzeichnen.', + fields: [ + ['1. KI-System (Name, Typ, Version)', ''], + ['2. Anbieter (Name und Anschrift)', ''], + ['3. Verantwortung', 'Die alleinige Verantwortung fĂŒr die Ausstellung dieser KonformitĂ€tserklĂ€rung trĂ€gt der Anbieter.'], + ['4. Gegenstand der ErklĂ€rung', 'Das oben bezeichnete KI-System steht im Einklang mit der Verordnung (EU) 2024/1689 und ggf. weiteren einschlĂ€gigen Harmonisierungsvorschriften der Union.'], + ['5. Angewandte harmonisierte Normen / gemeinsame Spezifikationen', '_[AUSFÜLLEN]_'], + ['6. Notifizierte Stelle (falls einschlĂ€gig)', 'Name, Kennnummer, Bescheinigung: _[AUSFÜLLEN]_'], + ['7. DSGVO-ErklĂ€rung (falls personenbezogene Daten verarbeitet werden)', 'Das System entspricht den Verordnungen (EU) 2016/679 und (EU) 2018/1725 bzw. der Richtlinie (EU) 2016/680.'], + ['8. Ort, Datum der Ausstellung', '_[AUSFÜLLEN]_'], + ['9. Unterzeichnet fĂŒr und im Namen von', 'Name, Funktion, Unterschrift: _[AUSFÜLLEN]_'], + ], + }, + en: { + title: 'EU declaration of conformity (draft, Art. 47 / Annex V)', + intro: + 'Draft based on Annex V of Regulation (EU) 2024/1689. Have it legally reviewed and signed by management before use.', + fields: [ + ['1. AI system (name, type, version)', ''], + ['2. Provider (name and address)', ''], + ['3. Responsibility', 'This declaration of conformity is issued under the sole responsibility of the provider.'], + ['4. Object of the declaration', 'The AI system identified above is in conformity with Regulation (EU) 2024/1689 and, where applicable, other relevant Union harmonisation legislation.'], + ['5. Applied harmonised standards / common specifications', '_[TO BE COMPLETED]_'], + ['6. Notified body (where applicable)', 'Name, identification number, certificate: _[TO BE COMPLETED]_'], + ['7. GDPR statement (where personal data is processed)', 'The system complies with Regulations (EU) 2016/679 and (EU) 2018/1725 or Directive (EU) 2016/680.'], + ['8. Place and date of issue', '_[TO BE COMPLETED]_'], + ['9. Signed for and on behalf of', 'Name, function, signature: _[TO BE COMPLETED]_'], + ], + }, +} as const; + +/** + * Erzeugt den Entwurf der EU-KonformitĂ€tserklĂ€rung (Anbieter, Hochrisiko). + * + * @param system - Bewertetes KI-System + * @param pack - Regulatorischer Content-Pack + * @param today - Stichtag im ISO-Format + * @param profile - Firmenprofil (Anbieterangaben werden vorbefĂŒllt) + * @param locale - Dokumentsprache (Default: Deutsch) + */ +export function conformityDeclaration( + system: AISystem, + _pack: ContentPack, + today: string, + profile?: CompanyProfile, + locale: Locale = 'de', +): string { + const s = docStrings(locale); + const d = DECL[locale]; + const provider = + profile && profile.name.trim() !== '' + ? [profile.name, profile.street, profile.zipCity].filter((x) => x.trim() !== '').join(', ') + : s.todo; + + const lines = [`# ${d.title}`, '', s.disclaimer, '', d.intro, '']; + for (const [label, preset] of d.fields) { + let value: string = preset; + if (label.startsWith('1.')) value = `${system.name}${system.purpose ? ` — ${system.purpose}` : ''}`; + if (label.startsWith('2.')) value = provider; + if (label.startsWith('8.')) value = `${profile?.zipCity ?? ''} ${formatDocDate(today, locale)}`.trim(); + lines.push(`## ${label}`, '', value || s.todo, ''); + } + return lines.join('\n'); +} diff --git a/ai-act-kompass/src/engine/i18nContent.test.ts b/ai-act-kompass/src/engine/i18nContent.test.ts new file mode 100644 index 0000000..d4f1abd --- /dev/null +++ b/ai-act-kompass/src/engine/i18nContent.test.ts @@ -0,0 +1,158 @@ +import { describe, expect, it } from 'vitest'; +import { CONTENT_PACK, getContentPack } from '../content/pack'; +import { buildChecklist } from './checklist'; +import { classify } from './classify'; +import { conformityDeclaration, procurementSpec } from './extraDocs'; +import { deriveObligations } from './obligations'; +import type { AISystem, AssessmentAnswers, CompanyProfile } from './types'; + +const base: AssessmentAnswers = { + isAISystem: true, + role: 'deployer', + isGPAIModel: false, + prohibitedPractices: [], + annexIIICategories: [], + art63Exception: false, + involvesProfiling: false, + isAnnexISafetyComponent: false, + transparencyTriggers: [], +}; + +const profile: CompanyProfile = { + name: 'Muster GmbH', + street: 'Musterstraße 1', + zipCity: '60311 Frankfurt', + contactPerson: '', + email: '', + phone: '', + website: '', + logoDataUrl: '', + countries: ['DE', 'IT'], +}; + +function makeSystem(answers: AssessmentAnswers, locale: 'de' | 'en' = 'de'): AISystem { + return { + id: 'sys-1', + name: 'HR-Screening', + purpose: 'Bewerbervorauswahl', + vendor: 'Beispiel GmbH', + owner: 'Jane Doe', + createdAt: '2026-07-01T00:00:00.000Z', + updatedAt: '2026-07-01T00:00:00.000Z', + answers, + classification: classify(answers, getContentPack(locale), locale), + obligationStatus: {}, + }; +} + +describe('mehrsprachiger Content-Pack', () => { + it('liefert identische IDs und Strukturen in DE und EN', () => { + const de = getContentPack('de'); + const en = getContentPack('en'); + expect(en.obligations.map((o) => o.id)).toEqual(de.obligations.map((o) => o.id)); + expect(en.national.map((n) => n.code)).toEqual(de.national.map((n) => n.code)); + expect(en.prohibitedPractices).toHaveLength(de.prohibitedPractices.length); + }); + + it('ĂŒbersetzt Inhalte tatsĂ€chlich (kein deutscher Text im EN-Pack)', () => { + const en = getContentPack('en'); + const literacy = en.obligations.find((o) => o.id === 'ob-ai-literacy'); + expect(literacy?.title).toBe('Ensure AI literacy of staff'); + expect(en.national.find((n) => n.code === 'DE')?.country).toBe('Germany'); + }); + + it('klassifiziert mit englischen BegrĂŒndungen', () => { + const result = classify( + { ...base, annexIIICategories: ['a3-employment'] }, + getContentPack('en'), + 'en', + ); + expect(result.riskClass).toBe('high'); + expect(result.reasons[0]).toContain('Annex III area'); + }); +}); + +describe('nationale Module', () => { + const answers: AssessmentAnswers = { ...base, annexIIICategories: ['a3-employment'] }; + + it('ergĂ€nzt deutsche und italienische Pflichten bei aktivierten Modulen', () => { + const classification = classify(answers, CONTENT_PACK); + const ids = deriveObligations(classification, answers, CONTENT_PACK, ['DE', 'IT']).map((o) => o.id); + expect(ids).toContain('de-betrvg'); + expect(ids).toContain('it-l132-workers'); + expect(ids).not.toContain('es-aesia'); + }); + + it('liefert ohne LĂ€nderauswahl nur EU-Pflichten', () => { + const classification = classify(answers, CONTENT_PACK); + const ids = deriveObligations(classification, answers, CONTENT_PACK).map((o) => o.id); + expect(ids.some((id) => id.startsWith('de-') || id.startsWith('it-'))).toBe(false); + }); + + it('respektiert Rolle und Risikoklasse auch bei nationalen Pflichten', () => { + const providerAnswers: AssessmentAnswers = { ...answers, role: 'provider' }; + const classification = classify(providerAnswers, CONTENT_PACK); + const ids = deriveObligations(classification, providerAnswers, CONTENT_PACK, ['DE']).map((o) => o.id); + expect(ids).not.toContain('de-betrvg'); // nur Betreiber + expect(ids).toContain('de-kimig-authority'); + }); + + it('enthĂ€lt neue EU-Pflichten (Art. 72/73) fĂŒr Hochrisiko-Anbieter', () => { + const providerAnswers: AssessmentAnswers = { ...answers, role: 'provider' }; + const classification = classify(providerAnswers, CONTENT_PACK); + const ids = deriveObligations(classification, providerAnswers, CONTENT_PACK).map((o) => o.id); + expect(ids).toContain('ob-postmarket'); + expect(ids).toContain('ob-incident'); + expect(ids).toContain('ob-corrective'); + }); +}); + +describe('buildChecklist', () => { + it('gruppiert nach fĂ€llig / anstehend / erledigt und zĂ€hlt Fortschritt', () => { + const system = makeSystem({ ...base, annexIIICategories: ['a3-employment'] }); + const withStatus: AISystem = { + ...system, + obligationStatus: { 'ob-ai-literacy': 'done' }, + }; + const checklist = buildChecklist([withStatus], CONTENT_PACK, ['DE'], '2026-07-04'); + + expect(checklist.total).toBeGreaterThan(5); + expect(checklist.resolvedCount).toBe(1); + // KI-Kompetenz (2025) ist erledigt; BetrVG (2025) ist fĂ€llig + expect(checklist.dueNow.some((e) => e.obligation.id === 'de-betrvg')).toBe(true); + // Art.-26-Pflichten (2027) stehen an + expect(checklist.upcoming.some((e) => e.obligation.id === 'ob-deployer-oversight')).toBe(true); + expect(checklist.resolved[0]?.obligation.id).toBe('ob-ai-literacy'); + }); + + it('ist leer ohne Systeme', () => { + const checklist = buildChecklist([], CONTENT_PACK, [], '2026-07-04'); + expect(checklist.total).toBe(0); + }); +}); + +describe('Zusatzdokumente', () => { + it('Lastenheft enthĂ€lt MUSS-Anforderungen und Hochrisiko-Punkte', () => { + const system = makeSystem({ ...base, annexIIICategories: ['a3-employment'] }); + const md = procurementSpec(system, CONTENT_PACK, '2026-07-04', profile); + expect(md).toContain('Lastenheft'); + expect(md).toContain('REQ-01'); + expect(md).toContain('MUSS'); + expect(md).toContain('keine Rechtsberatung'); + }); + + it('Lastenheft lĂ€sst Hochrisiko-Anforderungen bei limited weg', () => { + const system = makeSystem({ ...base, transparencyTriggers: ['tr-chatbot'] }); + const md = procurementSpec(system, CONTENT_PACK, '2026-07-04', profile); + expect(md).not.toContain('REQ-01'); // CE-Kennzeichnung nur Hochrisiko + expect(md).toContain('REQ-03'); // Betriebsanleitung immer + }); + + it('KonformitĂ€tserklĂ€rung fĂŒllt Anbieter aus dem Profil vor (EN)', () => { + const system = makeSystem({ ...base, role: 'provider', annexIIICategories: ['a3-employment'] }, 'en'); + const md = conformityDeclaration(system, getContentPack('en'), '2026-07-04', profile, 'en'); + expect(md).toContain('EU declaration of conformity'); + expect(md).toContain('Muster GmbH, Musterstraße 1, 60311 Frankfurt'); + expect(md).toContain('not legal advice'); + }); +}); diff --git a/ai-act-kompass/src/engine/obligations.ts b/ai-act-kompass/src/engine/obligations.ts index 25ea576..24c34e5 100644 --- a/ai-act-kompass/src/engine/obligations.ts +++ b/ai-act-kompass/src/engine/obligations.ts @@ -5,35 +5,43 @@ import type { AssessmentAnswers, Classification, ContentPack, Obligation } from * * Auswahlkriterien je Pflicht: Risikoklasse und Rolle mĂŒssen passen; * GPAI- und Transparenzpflichten werden nur bei entsprechendem Flag - * aufgenommen. + * aufgenommen. ZusĂ€tzlich werden die Pflichten der aktivierten + * nationalen Module (LĂ€nderauswahl) einbezogen. * * @param classification - Ergebnis von {@link classify} * @param answers - Wizard-Antworten (Rolle, GPAI-Flag) * @param pack - Regulatorischer Content-Pack + * @param countries - Aktivierte LĂ€nder-Module (ISO-Codes, z. B. ["DE","IT"]) * @returns Deduplizierte, nach Frist sortierte Pflichtenliste */ export function deriveObligations( classification: Classification, answers: AssessmentAnswers, pack: ContentPack, + countries: readonly string[] = [], ): readonly Obligation[] { if (classification.riskClass === 'out-of-scope' || classification.riskClass === 'prohibited') { return []; } - const applicable = pack.obligations.filter((ob) => { + const matches = (ob: Obligation): boolean => { if (ob.id === 'ob-gpai') { return classification.gpaiObligations && answers.role === 'provider'; } if (ob.id === 'ob-transparency-art50') { return classification.transparencyDuties && ob.roles.includes(answers.role); } - return ( - ob.riskClasses.includes(classification.riskClass) && ob.roles.includes(answers.role) - ); - }); + return ob.riskClasses.includes(classification.riskClass) && ob.roles.includes(answers.role); + }; - return [...applicable].sort((a, b) => a.deadline.localeCompare(b.deadline)); + const euObligations = pack.obligations.filter(matches); + const nationalObligations = pack.national + .filter((n) => countries.includes(n.code)) + .flatMap((n) => n.items.filter(matches)); + + return [...euObligations, ...nationalObligations].sort((a, b) => + a.deadline.localeCompare(b.deadline), + ); } /** diff --git a/ai-act-kompass/src/engine/types.ts b/ai-act-kompass/src/engine/types.ts index 5cf1208..f0109c9 100644 --- a/ai-act-kompass/src/engine/types.ts +++ b/ai-act-kompass/src/engine/types.ts @@ -92,6 +92,8 @@ export interface CompanyProfile { readonly website: string; /** Logo als Data-URL (bleibt lokal im Browser), leer = kein Logo. */ readonly logoDataUrl: string; + /** ISO-LĂ€ndercodes der aktivierten nationalen Anforderungsmodule (z. B. "DE"). */ + readonly countries: readonly string[]; } /** Eintrag im regulatorischen Content-Pack (Checklisten-Item mit Rechtsgrundlage). */ @@ -110,6 +112,17 @@ export interface Deadline { readonly description: string; } +/** Nationales Anforderungsmodul eines EU-Mitgliedstaats. */ +export interface NationalPack { + /** ISO-3166-LĂ€ndercode, z. B. "DE". */ + readonly code: string; + readonly country: string; + /** ZustĂ€ndige Aufsichts-/MarktĂŒberwachungsbehörde(n). */ + readonly authority: string; + /** ZusĂ€tzliche nationale Pflichten (Rechtsgrundlage im Feld articles). */ + readonly items: readonly Obligation[]; +} + /** Versionierter regulatorischer Content-Pack. */ export interface ContentPack { readonly version: string; @@ -121,4 +134,6 @@ export interface ContentPack { readonly aiDefinitionCriteria: readonly ContentItem[]; readonly obligations: readonly Obligation[]; readonly deadlines: readonly Deadline[]; + /** Nationale Zusatzanforderungen der EU-Mitgliedstaaten. */ + readonly national: readonly NationalPack[]; } diff --git a/ai-act-kompass/src/i18n/context.tsx b/ai-act-kompass/src/i18n/context.tsx new file mode 100644 index 0000000..115580d --- /dev/null +++ b/ai-act-kompass/src/i18n/context.tsx @@ -0,0 +1,47 @@ +import { createContext, useContext, useMemo, useState } from 'react'; +import type { ReactNode } from 'react'; +import { getContentPack } from '../content/pack'; +import type { ContentPack } from '../engine/types'; +import { loadLocale, saveLocale } from './types'; +import type { Locale } from './types'; +import { uiStrings } from './ui'; +import type { UIStrings } from './ui'; + +interface I18nValue { + readonly locale: Locale; + readonly t: UIStrings; + /** Content-Pack in der aktuellen Sprache. */ + readonly pack: ContentPack; + readonly setLocale: (locale: Locale) => void; +} + +const I18nContext = createContext(null); + +/** Stellt Sprache, UI-Wörterbuch und lokalisierten Content-Pack bereit. */ +export function I18nProvider({ children }: { readonly children: ReactNode }): JSX.Element { + const [locale, setLocaleState] = useState(() => loadLocale()); + + const value = useMemo( + () => ({ + locale, + t: uiStrings(locale), + pack: getContentPack(locale), + setLocale: (next: Locale) => { + saveLocale(next); + setLocaleState(next); + }, + }), + [locale], + ); + + return {children}; +} + +/** Zugriff auf Sprache, Wörterbuch und Content-Pack. */ +export function useI18n(): I18nValue { + const ctx = useContext(I18nContext); + if (!ctx) { + throw new Error('useI18n muss innerhalb von verwendet werden.'); + } + return ctx; +} diff --git a/ai-act-kompass/src/i18n/types.ts b/ai-act-kompass/src/i18n/types.ts new file mode 100644 index 0000000..cbc3e7d --- /dev/null +++ b/ai-act-kompass/src/i18n/types.ts @@ -0,0 +1,48 @@ +/** + * Mehrsprachigkeits-Grundlagen. + * + * Die App ist fĂŒr europĂ€ische Unternehmen gedacht — alle Inhalte + * (UI, Regel-Engine, Content-Pack, Dokumente) sind lokalisierbar. + * Aktuell vollstĂ€ndig: Deutsch und Englisch; weitere EU-Sprachen + * werden durch Erweitern von {@link LText} und den WörterbĂŒchern ergĂ€nzt. + */ + +/** UnterstĂŒtzte Sprachen. */ +export type Locale = 'de' | 'en'; + +/** Alle unterstĂŒtzten Sprachen in Anzeigereihenfolge. */ +export const LOCALES: readonly Locale[] = ['de', 'en']; + +/** Lokalisierter Text — eine Fassung je unterstĂŒtzter Sprache. */ +export interface LText { + readonly de: string; + readonly en: string; +} + +/** Kurzform zum Anlegen lokalisierter Texte. */ +export function lt(de: string, en: string): LText { + return { de, en }; +} + +/** Löst einen lokalisierten Text in der gewĂŒnschten Sprache auf. */ +export function resolveText(text: LText, locale: Locale): string { + return text[locale]; +} + +const LOCALE_KEY = 'ai-act-kompass/locale/v1'; + +/** LĂ€dt die gespeicherte Sprache (Fallback: Browser-Sprache, sonst Deutsch). */ +export function loadLocale(): Locale { + try { + const stored = localStorage.getItem(LOCALE_KEY); + if (stored === 'de' || stored === 'en') return stored; + return navigator.language.toLowerCase().startsWith('de') ? 'de' : 'en'; + } catch { + return 'de'; + } +} + +/** Persistiert die gewĂ€hlte Sprache. */ +export function saveLocale(locale: Locale): void { + localStorage.setItem(LOCALE_KEY, locale); +} diff --git a/ai-act-kompass/src/i18n/ui.ts b/ai-act-kompass/src/i18n/ui.ts new file mode 100644 index 0000000..7e78bed --- /dev/null +++ b/ai-act-kompass/src/i18n/ui.ts @@ -0,0 +1,317 @@ +import type { Locale } from './types'; + +/* eslint-disable max-lines */ + +const DE = { + brandTag: 'EU AI Act Compliance fĂŒr KMU · local-first', + navDashboard: 'Dashboard & Inventar', + navTasks: 'Aufgaben & Fristen', + navNew: 'Neues System bewerten', + navSettings: 'Einstellungen & Backup', + footPrivacy: 'Keine Cloud. Keine Telemetrie.', + footLocal: 'Alle Daten bleiben auf diesem GerĂ€t.', + footNoAdvice: 'Keine Rechtsberatung.', + + dashTitle: 'Dashboard', + dashSub: 'EU-AI-Act-Compliance auf einen Blick. Alle Daten bleiben lokal auf diesem GerĂ€t.', + statSystems: 'KI-Systeme im Inventar', + statHigh: 'Hochrisiko-Systeme', + statProhibited: 'Verbotene Praktiken', + statOpen: 'Offene Aufgaben', + deadlinesTitle: 'Geltungsfristen des AI Act', + inventoryTitle: 'KI-Inventar', + inventoryPrint: 'Inventar drucken / PDF', + newSystem: '+ System bewerten', + emptyInventory: + 'Noch keine KI-Systeme erfasst. Starten Sie mit der Bewertung Ihres ersten Systems — z. B. Ihres Chatbots, Ihres Bewerber-Screenings oder eines eingekauften KI-Tools.', + colSystem: 'System', + colRole: 'Rolle', + colRisk: 'Risikoklasse', + colOwner: 'Verantwortlich', + colUpdated: 'Zuletzt bewertet', + disclaimerLine: + 'Der AI-Act-Kompass ist eine strukturierte Arbeitshilfe auf Basis der Verordnung (EU) 2024/1689 — keine Rechtsberatung. Content-Pack v{version}.', + + tasksTitle: 'Aufgaben & Fristen', + tasksSub: 'Alle Pflichten aller Systeme als abhakbare Liste — was erledigt ist, was jetzt fĂ€llig ist und was noch kommt.', + tasksProgress: '{done} von {total} Aufgaben erledigt', + tasksDueNow: 'Jetzt fĂ€llig', + tasksUpcoming: 'Kommt auf Sie zu', + tasksResolved: 'Erledigt', + tasksEmpty: 'Keine Aufgaben — bewerten Sie zuerst ein KI-System.', + tasksAllDone: 'Alles erledigt in dieser Gruppe.', + tasksDue: 'Frist', + + wizardTitleNew: 'Neues KI-System bewerten', + wizardTitleRe: 'Neubewertung: {name}', + wizardSub: 'Beantworten Sie die Fragen — die Risikoklasse wird automatisch nach der Systematik des AI Act ermittelt.', + stepBasics: 'Grunddaten', + stepDefinition: 'KI-Definition', + stepProhibited: 'Verbote', + stepHighRisk: 'Hochrisiko', + stepTransparency: 'Transparenz & GPAI', + stepResult: 'Ergebnis', + fieldName: 'Name des Systems *', + fieldNamePh: 'z. B. Bewerber-Screening-Tool', + fieldPurpose: 'Zweckbestimmung', + fieldPurposePh: 'WofĂŒr wird das System eingesetzt?', + fieldVendor: 'Hersteller / Anbieter', + fieldOwner: 'Verantwortliche Person (intern)', + fieldRole: 'Rolle Ihres Unternehmens', + roleHint: 'Anbieter = entwickelt/vertreibt das System unter eigenem Namen. Betreiber = setzt ein fremdes System ein (hĂ€ufigster KMU-Fall).', + defTitle: 'ErfĂŒllt das System die KI-Definition (Art. 3 Nr. 1)?', + defHint: 'Nur wenn alle Kriterien zutreffen, fĂ€llt das System unter den AI Act.', + defWarn: 'Nicht alle Kriterien erfĂŒllt → das System gilt nicht als KI-System, der AI Act ist nicht anwendbar.', + prohTitle: 'Verbotene Praktiken (Art. 5)', + prohHint: 'Trifft einer dieser Punkte zu, ist der Einsatz seit 2. Februar 2025 verboten.', + hrTitle: 'Hochrisiko-Einstufung (Art. 6)', + annexILabel: 'Das System ist Sicherheitsbauteil eines regulierten Produkts (z. B. Medizinprodukt, Maschine, Aufzug) oder selbst ein solches Produkt.', + annexIIITitle: 'Einsatzbereiche nach Annex III', + exceptionTitle: 'Mögliche Ausnahme (Art. 6 Abs. 3)', + profilingLabel: 'Das System fĂŒhrt Profiling natĂŒrlicher Personen durch.', + profilingRef: 'Profiling schließt die Ausnahme aus — immer Hochrisiko.', + art63Label: 'Das System erfĂŒllt nur eine eng begrenzte verfahrenstechnische oder vorbereitende Aufgabe bzw. verbessert lediglich das Ergebnis einer zuvor abgeschlossenen menschlichen TĂ€tigkeit — ohne wesentliches Risiko fĂŒr Gesundheit, Sicherheit oder Grundrechte.', + art63Ref: 'Art. 6 Abs. 3 — die EinschĂ€tzung muss dokumentiert werden (Art. 6 Abs. 4).', + trTitle: 'Transparenzpflichten (Art. 50)', + gpaiTitle: 'General-Purpose AI', + gpaiLabel: 'Wir stellen ein eigenes GPAI-Modell bereit (z. B. selbst trainiertes oder wesentlich verĂ€ndertes Basismodell).', + gpaiRef: 'Art. 51 ff. — die bloße Nutzung von z. B. GPT/Claude ĂŒber API zĂ€hlt nicht.', + resultTitle: 'Ergebnis:', + resultGpai: 'ZusĂ€tzlich gelten GPAI-Modellpflichten (Art. 53 ff.).', + resultProhibited: 'Der Einsatz dieses Systems ist nach Art. 5 verboten (Bußgeld bis 35 Mio. € oder 7 % des weltweiten Jahresumsatzes). Einsatz einstellen und rechtlichen Rat einholen.', + btnCancel: 'Abbrechen', + btnBack: 'ZurĂŒck', + btnNext: 'Weiter', + btnSave: 'Speichern', + + detailBack: '← ZurĂŒck zum Dashboard', + detailNoPurpose: 'Keine Zweckbestimmung erfasst', + detailReasons: 'BegrĂŒndung der Einstufung', + detailReassess: 'Neu bewerten', + detailDelete: 'System löschen', + detailObligations: 'Pflichten', + detailNational: 'inkl. LĂ€nder-Module: {codes}', + detailDocs: 'Dokumente generieren', + docShow: 'ansehen', + docHide: 'ausblenden', + docPrint: 'Drucken / PDF', + docRisk: 'Risikobericht', + docPlan: 'Maßnahmenplan', + docAnnexIV: 'Annex-IV-Dokumentation', + docProcurement: 'Lastenheft KI-Beschaffung', + docConformity: 'EU-KonformitĂ€tserklĂ€rung', + confirmDeleteSystem: '„{name}" wirklich löschen?', + systemNotFound: 'System nicht gefunden.', + statusOpen: 'Offen', + statusInProgress: 'In Umsetzung', + statusDone: 'Erledigt', + statusNA: 'Nicht anwendbar', + + settingsTitle: 'Einstellungen', + settingsSub: 'Datenhoheit: Alles liegt lokal in diesem Browser — kein Cloud-Sync, keine Telemetrie.', + langTitle: 'Sprache / Language', + langHint: 'BenutzeroberflĂ€che, Inhalte und generierte Dokumente. Weitere EU-Sprachen folgen ĂŒber den Content-Feed.', + profileTitle: 'Firmenprofil (Briefkopf fĂŒr Berichte & PDF-Druck)', + profileHint: 'Diese Angaben erscheinen im Kopf und in der Fußzeile aller generierten und gedruckten Dokumente. Das Logo bleibt lokal gespeichert und verlĂ€sst das GerĂ€t nicht.', + pfName: 'Firmenname', pfStreet: 'Straße & Hausnummer', pfZip: 'PLZ & Ort', + pfContact: 'Ansprechpartner', pfEmail: 'E-Mail', pfPhone: 'Telefon', pfWeb: 'Website', + logoNone: 'Kein Logo hinterlegt.', + logoUpload: 'Logo hochladen', + logoRemove: 'Logo entfernen', + logoTaken: 'Logo ĂŒbernommen.', + logoTooBig: 'Logo zu groß — bitte eine Datei unter 300 KB verwenden.', + logoNotImage: 'Bitte eine Bilddatei wĂ€hlen (PNG, JPG oder SVG).', + countriesTitle: 'LĂ€nder-Module (nationale Zusatzanforderungen)', + countriesHint: 'Aktivieren Sie die EU-LĂ€nder, in denen Ihr Unternehmen KI-Systeme anbietet oder betreibt. Die nationalen Pflichten (Aufsicht, Arbeitsrecht, Sondergesetze) erscheinen dann in Checklisten und Dokumenten. Bei grenzĂŒberschreitender Zusammenarbeit: alle beteiligten LĂ€nder aktivieren.', + authorityLabel: 'Aufsicht', + packTitle: 'Regulatorischer Content-Pack', + packStand: 'Stand', + packHint: 'Der Content-Pack (Verbote, Annex-III-Bereiche, EU-Pflichten, LĂ€nder-Module, Fristen) ist als austauschbares Datenpaket modelliert. Updates erscheinen mit neuen App-Versionen bzw. kĂŒnftig ĂŒber einen signierten Update-Feed.', + backupTitle: 'Datensicherung', + backupExport: 'Backup exportieren (JSON)', + backupImport: 'Backup importieren', + importOk: 'Import abgeschlossen: {count} System(e){profile}.', + importProfileSuffix: ' inkl. Firmenprofil', + importFail: 'Import fehlgeschlagen: {error}', + deleteTitle: 'Alle Daten löschen', + deleteHint: 'Entfernt alle erfassten KI-Systeme und das Firmenprofil unwiderruflich aus diesem Browser.', + deleteBtn: 'Alles löschen', + deleteConfirm: 'Wirklich alle lokalen Daten unwiderruflich löschen?', + deleteDone: 'Alle Daten gelöscht.', + + legalGateTitle: 'Wichtiger rechtlicher Hinweis', + legalGateBody1: + 'Der AI-Act-Kompass ist eine strukturierte Arbeitshilfe, die die Umsetzung der Verordnung (EU) 2024/1689 (EU AI Act) und einschlĂ€giger nationaler Vorschriften erleichtern soll.', + legalGateBody2: + 'Diese Anwendung erbringt keine Rechtsdienstleistung im Sinne des Rechtsdienstleistungsgesetzes (RDG) und stellt keine Rechtsberatung, Rechtsauskunft oder rechtsverbindliche Belehrung dar. Alle Einstufungen, Checklisten und generierten Dokumente sind unverbindliche ErsteinschĂ€tzungen und EntwĂŒrfe ohne GewĂ€hr fĂŒr Richtigkeit, VollstĂ€ndigkeit oder AktualitĂ€t.', + legalGateBody3: + 'Die verbindliche Auslegung des EU AI Act obliegt allein den zustĂ€ndigen Behörden und Gerichten. Die rechtliche Bewertung Ihres Einzelfalls ersetzt dieses Tool nicht — ziehen Sie hierfĂŒr qualifizierte Rechtsberatung (RechtsanwĂ€ltin/Rechtsanwalt) hinzu. Die Nutzung erfolgt auf eigenes Risiko; eine Haftung fĂŒr Entscheidungen auf Grundlage dieses Tools ist ausgeschlossen, soweit gesetzlich zulĂ€ssig.', + legalGateAccept: 'Verstanden — als Arbeitshilfe nutzen', + legalNoticeTitle: 'Rechtlicher Hinweis (keine Rechtsberatung)', + resultDisclaimer: + 'Diese Einstufung ist eine unverbindliche, automatisierte ErsteinschĂ€tzung auf Basis Ihrer Angaben — keine Rechtsberatung. Lassen Sie das Ergebnis vor weitreichenden Entscheidungen rechtlich prĂŒfen.', +}; + +export type UIStrings = typeof DE; + +const EN: UIStrings = { + brandTag: 'EU AI Act compliance for SMEs · local-first', + navDashboard: 'Dashboard & inventory', + navTasks: 'Tasks & deadlines', + navNew: 'Assess new system', + navSettings: 'Settings & backup', + footPrivacy: 'No cloud. No telemetry.', + footLocal: 'All data stays on this device.', + footNoAdvice: 'Not legal advice.', + + dashTitle: 'Dashboard', + dashSub: 'EU AI Act compliance at a glance. All data stays locally on this device.', + statSystems: 'AI systems in inventory', + statHigh: 'High-risk systems', + statProhibited: 'Prohibited practices', + statOpen: 'Open tasks', + deadlinesTitle: 'AI Act application deadlines', + inventoryTitle: 'AI inventory', + inventoryPrint: 'Print inventory / PDF', + newSystem: '+ Assess system', + emptyInventory: + 'No AI systems recorded yet. Start by assessing your first system — e.g. your chatbot, your applicant screening or a purchased AI tool.', + colSystem: 'System', + colRole: 'Role', + colRisk: 'Risk class', + colOwner: 'Responsible', + colUpdated: 'Last assessed', + disclaimerLine: + 'AI-Act-Kompass is a structured working aid based on Regulation (EU) 2024/1689 — not legal advice. Content pack v{version}.', + + tasksTitle: 'Tasks & deadlines', + tasksSub: 'All obligations of all systems as a checkable list — what is done, what is due now and what is coming up.', + tasksProgress: '{done} of {total} tasks done', + tasksDueNow: 'Due now', + tasksUpcoming: 'Coming up', + tasksResolved: 'Done', + tasksEmpty: 'No tasks — assess an AI system first.', + tasksAllDone: 'Everything done in this group.', + tasksDue: 'Deadline', + + wizardTitleNew: 'Assess a new AI system', + wizardTitleRe: 'Reassessment: {name}', + wizardSub: 'Answer the questions — the risk class is determined automatically following the AI Act’s system.', + stepBasics: 'Basics', + stepDefinition: 'AI definition', + stepProhibited: 'Prohibitions', + stepHighRisk: 'High-risk', + stepTransparency: 'Transparency & GPAI', + stepResult: 'Result', + fieldName: 'System name *', + fieldNamePh: 'e.g. applicant screening tool', + fieldPurpose: 'Intended purpose', + fieldPurposePh: 'What is the system used for?', + fieldVendor: 'Manufacturer / provider', + fieldOwner: 'Responsible person (internal)', + fieldRole: 'Your company’s role', + roleHint: 'Provider = develops/markets the system under its own name. Deployer = uses a third-party system (most common SME case).', + defTitle: 'Does the system meet the AI definition (Art. 3(1))?', + defHint: 'Only if all criteria apply does the system fall under the AI Act.', + defWarn: 'Not all criteria met → the system does not qualify as an AI system; the AI Act does not apply.', + prohTitle: 'Prohibited practices (Art. 5)', + prohHint: 'If any of these apply, use has been prohibited since 2 February 2025.', + hrTitle: 'High-risk classification (Art. 6)', + annexILabel: 'The system is a safety component of a regulated product (e.g. medical device, machine, lift) or such a product itself.', + annexIIITitle: 'Areas of use under Annex III', + exceptionTitle: 'Possible exception (Art. 6(3))', + profilingLabel: 'The system performs profiling of natural persons.', + profilingRef: 'Profiling excludes the exception — always high-risk.', + art63Label: 'The system only performs a narrow procedural or preparatory task, or merely improves the result of a previously completed human activity — without significant risk to health, safety or fundamental rights.', + art63Ref: 'Art. 6(3) — the assessment must be documented (Art. 6(4)).', + trTitle: 'Transparency obligations (Art. 50)', + gpaiTitle: 'General-purpose AI', + gpaiLabel: 'We provide our own GPAI model (e.g. self-trained or substantially modified base model).', + gpaiRef: 'Art. 51 et seq. — merely using e.g. GPT/Claude via API does not count.', + resultTitle: 'Result:', + resultGpai: 'GPAI model obligations (Art. 53 et seq.) apply additionally.', + resultProhibited: 'Use of this system is prohibited under Art. 5 (fines up to €35m or 7% of global annual turnover). Cease use and seek legal advice.', + btnCancel: 'Cancel', + btnBack: 'Back', + btnNext: 'Next', + btnSave: 'Save', + + detailBack: '← Back to dashboard', + detailNoPurpose: 'No intended purpose recorded', + detailReasons: 'Reasoning for the classification', + detailReassess: 'Reassess', + detailDelete: 'Delete system', + detailObligations: 'Obligations', + detailNational: 'incl. country modules: {codes}', + detailDocs: 'Generate documents', + docShow: 'view', + docHide: 'hide', + docPrint: 'Print / PDF', + docRisk: 'Risk report', + docPlan: 'Action plan', + docAnnexIV: 'Annex IV documentation', + docProcurement: 'AI procurement specification', + docConformity: 'EU declaration of conformity', + confirmDeleteSystem: 'Really delete “{name}”?', + systemNotFound: 'System not found.', + statusOpen: 'Open', + statusInProgress: 'In progress', + statusDone: 'Done', + statusNA: 'Not applicable', + + settingsTitle: 'Settings', + settingsSub: 'Data sovereignty: everything stays locally in this browser — no cloud sync, no telemetry.', + langTitle: 'Sprache / Language', + langHint: 'User interface, content and generated documents. More EU languages will follow via the content feed.', + profileTitle: 'Company profile (letterhead for reports & PDF printing)', + profileHint: 'These details appear in the header and footer of all generated and printed documents. The logo is stored locally and never leaves the device.', + pfName: 'Company name', pfStreet: 'Street & number', pfZip: 'Postcode & city', + pfContact: 'Contact person', pfEmail: 'Email', pfPhone: 'Phone', pfWeb: 'Website', + logoNone: 'No logo stored.', + logoUpload: 'Upload logo', + logoRemove: 'Remove logo', + logoTaken: 'Logo applied.', + logoTooBig: 'Logo too large — please use a file under 300 KB.', + logoNotImage: 'Please choose an image file (PNG, JPG or SVG).', + countriesTitle: 'Country modules (national additional requirements)', + countriesHint: 'Activate the EU countries in which your company offers or operates AI systems. National obligations (supervision, labour law, special laws) then appear in checklists and documents. For cross-border cooperation: activate all countries involved.', + authorityLabel: 'Supervision', + packTitle: 'Regulatory content pack', + packStand: 'As of', + packHint: 'The content pack (prohibitions, Annex III areas, EU obligations, country modules, deadlines) is modelled as a replaceable data package. Updates ship with new app versions and, in future, via a signed update feed.', + backupTitle: 'Backup', + backupExport: 'Export backup (JSON)', + backupImport: 'Import backup', + importOk: 'Import completed: {count} system(s){profile}.', + importProfileSuffix: ' incl. company profile', + importFail: 'Import failed: {error}', + deleteTitle: 'Delete all data', + deleteHint: 'Irreversibly removes all recorded AI systems and the company profile from this browser.', + deleteBtn: 'Delete everything', + deleteConfirm: 'Really delete all local data irreversibly?', + deleteDone: 'All data deleted.', + + legalGateTitle: 'Important legal notice', + legalGateBody1: + 'AI-Act-Kompass is a structured working aid intended to facilitate the implementation of Regulation (EU) 2024/1689 (EU AI Act) and relevant national provisions.', + legalGateBody2: + 'This application does not provide legal services and does not constitute legal advice, legal information or legally binding instruction. All classifications, checklists and generated documents are non-binding preliminary assessments and drafts, without warranty as to accuracy, completeness or currency.', + legalGateBody3: + 'The binding interpretation of the EU AI Act rests solely with the competent authorities and courts. This tool does not replace the legal assessment of your individual case — consult qualified legal counsel for that purpose. Use is at your own risk; liability for decisions based on this tool is excluded to the extent permitted by law.', + legalGateAccept: 'Understood — use as a working aid', + legalNoticeTitle: 'Legal notice (not legal advice)', + resultDisclaimer: + 'This classification is a non-binding, automated preliminary assessment based on your input — not legal advice. Have the result legally reviewed before far-reaching decisions.', +}; + +const DICTS: Readonly> = { de: DE, en: EN }; + +/** Liefert das UI-Wörterbuch fĂŒr die Sprache. */ +export function uiStrings(locale: Locale): UIStrings { + return DICTS[locale]; +} + +/** Ersetzt {platzhalter} in einem UI-String. */ +export function fmt(template: string, params: Readonly>): string { + return template.replace(/\{(\w+)\}/g, (_, key: string) => String(params[key] ?? '')); +} diff --git a/ai-act-kompass/src/main.tsx b/ai-act-kompass/src/main.tsx index 394bd63..5067f21 100644 --- a/ai-act-kompass/src/main.tsx +++ b/ai-act-kompass/src/main.tsx @@ -1,5 +1,6 @@ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; +import { I18nProvider } from './i18n/context'; import { App } from './ui/App'; import './styles.css'; @@ -10,6 +11,8 @@ if (!container) { createRoot(container).render( - + + + , ); diff --git a/ai-act-kompass/src/store/storage.ts b/ai-act-kompass/src/store/storage.ts index b910e2f..463cc71 100644 --- a/ai-act-kompass/src/store/storage.ts +++ b/ai-act-kompass/src/store/storage.ts @@ -30,6 +30,7 @@ export function emptyProfile(): CompanyProfile { phone: '', website: '', logoDataUrl: '', + countries: [], }; } diff --git a/ai-act-kompass/src/styles.css b/ai-act-kompass/src/styles.css index f87cf51..8a61ca5 100644 --- a/ai-act-kompass/src/styles.css +++ b/ai-act-kompass/src/styles.css @@ -289,3 +289,89 @@ input:focus, select:focus, textarea:focus { outline: 2px solid var(--accent); ou .toolbar { display: flex; gap: 8px; flex-wrap: wrap; margin: 10px 0 16px; } .empty { text-align: center; color: var(--muted); padding: 44px 0; font-size: 14.5px; font-style: italic; } +.panel-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; } +.table-scroll { overflow-x: auto; } + +/* ── Sprachumschalter ─────────────────────────────────────────────── */ +.lang-switch { display: flex; gap: 0; margin-top: 18px; border: 1px solid var(--ink); align-self: flex-start; } +.lang-switch button { + border: none; border-right: 1px solid var(--ink); border-radius: 0; + padding: 6px 12px; background: var(--panel); color: var(--ink); + font-family: var(--mono); font-size: 11px; font-weight: 700; letter-spacing: 0.06em; cursor: pointer; +} +.lang-switch button:last-child { border-right: none; } +.lang-switch button.active { background: var(--ink); color: var(--paper); } + +/* ── Aufgabenliste (abhakbar) ─────────────────────────────────────── */ +.task-list { list-style: none; padding: 0; margin: 0; } +.task { + display: flex; gap: 12px; align-items: flex-start; + padding: 11px 4px; border-bottom: 1px solid var(--hairline); +} +.task:last-child { border-bottom: none; } +.task.done .task-title { text-decoration: line-through; color: var(--muted); } +.task-check input { width: 19px; height: 19px; margin-top: 2px; accent-color: var(--accent); cursor: pointer; } +.task-body { flex: 1; min-width: 0; } +.task-title { font-size: 14.5px; font-weight: 600; } +.task-meta { + display: flex; gap: 12px; flex-wrap: wrap; margin-top: 3px; + font-family: var(--mono); font-size: 11px; color: var(--muted); +} +.task-system { + border: none; background: none; padding: 0; cursor: pointer; + font-family: var(--mono); font-size: 11px; color: var(--accent); + text-decoration: underline; text-underline-offset: 2px; +} + +.progress-label { font-family: var(--mono); font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 8px; } +.progress-track { height: 12px; border: 1px solid var(--ink); background: var(--panel); } +.progress-fill { height: 100%; background: var(--ink); transition: width 0.3s ease; } + +/* ── Rechtlicher Hinweis (Erststart) ──────────────────────────────── */ +.gate-backdrop { + position: fixed; inset: 0; z-index: 100; + background: rgba(20, 18, 14, 0.6); + display: flex; align-items: center; justify-content: center; padding: 20px; +} +.gate { + background: var(--paper); border: 2px solid var(--ink); + max-width: 620px; max-height: 90vh; overflow-y: auto; padding: 26px 28px; +} +.gate h1 { border-bottom: 3px double var(--ink); padding-bottom: 10px; } +.gate p { font-size: 14.5px; line-height: 1.6; } +.gate button { margin-top: 10px; } + +/* ── Responsiv: Tablet & Handy ────────────────────────────────────── */ +@media (max-width: 900px) { + .layout { flex-direction: column; } + .sidebar { + width: 100%; border-right: none; border-bottom: 2px solid var(--ink); + padding: 14px 16px; flex-direction: column; + } + .sidebar .brand { font-size: 17px; padding-bottom: 8px; } + .sidebar nav { + flex-direction: row; overflow-x: auto; margin-top: 10px; gap: 0; + -webkit-overflow-scrolling: touch; + } + .sidebar nav button { + white-space: nowrap; border-bottom: none; border-right: 1px solid var(--hairline); + padding: 9px 12px; font-size: 13.5px; + } + .sidebar button.active { border-left: none; padding-left: 12px; background: var(--ink); color: var(--paper); } + .sidebar .foot { display: none; } + .lang-switch { position: absolute; top: 14px; right: 16px; margin-top: 0; } + .sidebar { position: relative; } + .main { padding: 18px 14px; } + h1 { font-size: 21px; } + .panel { padding: 14px 14px; } + .grid { grid-template-columns: repeat(2, 1fr); gap: 12px 0; } + .grid .stat:nth-child(odd) { border-left: none; padding-left: 0; } + .grid .stat:nth-child(even) { padding-left: 14px; } + .stat .num { font-size: 26px; } + .wizard-steps { border: none; gap: 4px; } + .wizard-steps .step { border: 1px solid var(--hairline); } + .deadline-list li { flex-direction: column; gap: 2px; } + button.primary, button.ghost, button.danger { padding: 10px 14px; } + .task { padding: 12px 2px; } + .task-check input { width: 22px; height: 22px; } +} diff --git a/ai-act-kompass/src/ui/App.tsx b/ai-act-kompass/src/ui/App.tsx index c4c9d75..b94755d 100644 --- a/ai-act-kompass/src/ui/App.tsx +++ b/ai-act-kompass/src/ui/App.tsx @@ -1,19 +1,26 @@ import { useEffect, useState } from 'react'; -import type { AISystem, CompanyProfile } from '../engine/types'; +import type { AISystem, CompanyProfile, ObligationStatus } from '../engine/types'; +import { useI18n } from '../i18n/context'; +import { LOCALES } from '../i18n/types'; +import { fmt } from '../i18n/ui'; import { loadProfile, loadSystems, saveProfile, saveSystems } from '../store/storage'; +import { ChecklistView } from './ChecklistView'; import { Dashboard } from './Dashboard'; +import { DisclaimerGate } from './DisclaimerGate'; import { SettingsView } from './SettingsView'; import { SystemDetail } from './SystemDetail'; import { Wizard } from './Wizard'; type View = | { readonly kind: 'dashboard' } + | { readonly kind: 'tasks' } | { readonly kind: 'wizard'; readonly systemId: string | null } | { readonly kind: 'detail'; readonly systemId: string } | { readonly kind: 'settings' }; /** App-Shell: Navigation, State-Verwaltung und Persistenz. */ export function App(): JSX.Element { + const { t, locale, setLocale } = useI18n(); const [systems, setSystems] = useState(() => loadSystems()); const [profile, setProfile] = useState(() => loadProfile()); const [view, setView] = useState({ kind: 'dashboard' }); @@ -40,40 +47,73 @@ export function App(): JSX.Element { setView({ kind: 'dashboard' }); }; + const setObligationStatus = ( + systemId: string, + obligationId: string, + status: ObligationStatus, + ): void => { + const system = findSystem(systemId); + if (!system) return; + upsert({ + ...system, + updatedAt: new Date().toISOString(), + obligationStatus: { ...system.obligationStatus, [obligationId]: status }, + }); + }; + return (
+ @@ -87,6 +127,17 @@ export function App(): JSX.Element { /> )} + {view.kind === 'tasks' && ( + + setObligationStatus(systemId, obligationId, done ? 'done' : 'open') + } + onOpenSystem={(id) => setView({ kind: 'detail', systemId: id })} + /> + )} + {view.kind === 'wizard' && ( { const system = findSystem(view.systemId); if (!system) { - return
System nicht gefunden.
; + return
{t.systemNotFound}
; } return ( setView({ kind: 'wizard', systemId: system.id })} onDelete={() => { - if (window.confirm(`„${system.name}" wirklich löschen?`)) { + if (window.confirm(fmt(t.confirmDeleteSystem, { name: system.name }))) { remove(system.id); } }} diff --git a/ai-act-kompass/src/ui/ChecklistView.tsx b/ai-act-kompass/src/ui/ChecklistView.tsx new file mode 100644 index 0000000..d95504a --- /dev/null +++ b/ai-act-kompass/src/ui/ChecklistView.tsx @@ -0,0 +1,133 @@ +import { useMemo } from 'react'; +import { buildChecklist } from '../engine/checklist'; +import type { ChecklistEntry } from '../engine/checklist'; +import type { AISystem, CompanyProfile } from '../engine/types'; +import { useI18n } from '../i18n/context'; +import { fmt } from '../i18n/ui'; +import { todayISO, uiDate } from './common'; + +interface ChecklistViewProps { + readonly systems: readonly AISystem[]; + readonly profile: CompanyProfile; + readonly onToggle: (systemId: string, obligationId: string, done: boolean) => void; + readonly onOpenSystem: (systemId: string) => void; +} + +function Group({ + title, + entries, + emptyText, + onToggle, + onOpenSystem, +}: { + readonly title: string; + readonly entries: readonly ChecklistEntry[]; + readonly emptyText: string; + readonly onToggle: (systemId: string, obligationId: string, done: boolean) => void; + readonly onOpenSystem: (systemId: string) => void; +}): JSX.Element { + const { locale, t } = useI18n(); + return ( +
+

+ {title} ({entries.length}) +

+ {entries.length === 0 ? ( +

{emptyText}

+ ) : ( +
    + {entries.map((e) => ( +
  • + +
    +
    {e.obligation.title}
    +
    + + {e.obligation.articles} + + {t.tasksDue}: {uiDate(e.obligation.deadline, locale)} + +
    +
    +
  • + ))} +
+ )} +
+ ); +} + +/** + * SystemĂŒbergreifende, abhakbare Aufgabenliste: was erledigt ist, + * was jetzt fĂ€llig ist und was noch kommt — mobiltauglich. + */ +export function ChecklistView({ + systems, + profile, + onToggle, + onOpenSystem, +}: ChecklistViewProps): JSX.Element { + const { t, pack } = useI18n(); + const today = todayISO(); + + const checklist = useMemo( + () => buildChecklist(systems, pack, profile.countries, today), + [systems, pack, profile.countries, today], + ); + + const pct = checklist.total === 0 ? 0 : Math.round((checklist.resolvedCount / checklist.total) * 100); + + return ( +
+

{t.tasksTitle}

+

{t.tasksSub}

+ + {checklist.total === 0 ? ( +
+
{t.tasksEmpty}
+
+ ) : ( + <> +
+
+ {fmt(t.tasksProgress, { done: checklist.resolvedCount, total: checklist.total })} — {pct} % +
+
+
+
+
+ + + + + + )} +
+ ); +} diff --git a/ai-act-kompass/src/ui/Dashboard.tsx b/ai-act-kompass/src/ui/Dashboard.tsx index 7a30dce..a219dab 100644 --- a/ai-act-kompass/src/ui/Dashboard.tsx +++ b/ai-act-kompass/src/ui/Dashboard.tsx @@ -1,8 +1,10 @@ -import { CONTENT_PACK } from '../content/pack'; +import { buildChecklist } from '../engine/checklist'; import { inventoryReport } from '../engine/documents'; import type { AISystem, CompanyProfile } from '../engine/types'; +import { useI18n } from '../i18n/context'; +import { fmt } from '../i18n/ui'; import { printDocument } from '../print/print'; -import { downloadText, RiskBadge, ROLE_LABELS, todayISO } from './common'; +import { downloadText, RiskBadge, ROLE_UI, todayISO, uiDate } from './common'; interface DashboardProps { readonly systems: readonly AISystem[]; @@ -13,51 +15,44 @@ interface DashboardProps { /** Übersichtsseite: Kennzahlen, Fristen und das KI-Inventar. */ export function Dashboard({ systems, profile, onOpen, onNew }: DashboardProps): JSX.Element { + const { t, locale, pack } = useI18n(); const today = todayISO(); const count = (risk: string): number => systems.filter((s) => s.classification.riskClass === risk).length; - const openObligations = systems.reduce((acc, s) => { - const done = Object.values(s.obligationStatus).filter( - (st) => st === 'done' || st === 'not-applicable', - ).length; - return acc + Math.max(0, Object.keys(s.obligationStatus).length - done); - }, 0); + const checklist = buildChecklist(systems, pack, profile.countries, today); + const openTasks = checklist.total - checklist.resolvedCount; return (
-

Dashboard

-

- EU-AI-Act-Compliance auf einen Blick. Alle Daten bleiben lokal auf diesem GerÀt. -

+

{t.dashTitle}

+

{t.dashSub}

{systems.length}
-
KI-Systeme im Inventar
+
{t.statSystems}
{count('high')}
-
Hochrisiko-Systeme
+
{t.statHigh}
{count('prohibited')}
-
Verbotene Praktiken
+
{t.statProhibited}
-
{openObligations}
-
Offene Pflichten (begonnen)
+
{openTasks}
+
{t.statOpen}
-

Geltungsfristen des AI Act

+

{t.deadlinesTitle}

    - {CONTENT_PACK.deadlines.map((d) => ( + {pack.deadlines.map((d) => (
  • - - {d.date.split('-').reverse().join('.')} - + {uiDate(d.date, locale)} {d.label} — {d.description} @@ -67,8 +62,8 @@ export function Dashboard({ systems, profile, onOpen, onNew }: DashboardProps):
-
-

KI-Inventar

+
+

{t.inventoryTitle}

{systems.length > 0 && ( <> @@ -77,19 +72,19 @@ export function Dashboard({ systems, profile, onOpen, onNew }: DashboardProps): onClick={() => printDocument( `ki-inventar-${today}`, - inventoryReport(systems, CONTENT_PACK, today, profile), + inventoryReport(systems, pack, today, profile, locale), profile, ) } > - Inventar drucken / PDF + {t.inventoryPrint}
{systems.length === 0 ? ( -
- Noch keine KI-Systeme erfasst. Starten Sie mit der Bewertung Ihres ersten Systems — - z. B. Ihres Chatbots, Ihres Bewerber-Screenings oder eines eingekauften KI-Tools. -
+
{t.emptyInventory}
) : ( - - - - - - - - - - - - {systems.map((s) => ( - onOpen(s.id)}> - - - - - +
+
SystemRolleRisikoklasseVerantwortlichZuletzt bewertet
- {s.name} - {ROLE_LABELS[s.answers.role]} - - {s.owner || '—'}{s.updatedAt.slice(0, 10).split('-').reverse().join('.')}
+ + + + + + + - ))} - -
{t.colSystem}{t.colRole}{t.colRisk}{t.colOwner}{t.colUpdated}
+ + + {systems.map((s) => ( + onOpen(s.id)}> + + {s.name} + + {ROLE_UI[locale][s.answers.role]} + + + + {s.owner || '—'} + {uiDate(s.updatedAt.slice(0, 10), locale)} + + ))} + + +
)}
-

- Der AI-Act-Kompass ist eine strukturierte Arbeitshilfe auf Basis der Verordnung (EU) 2024/1689 — - keine Rechtsberatung. Content-Pack v{CONTENT_PACK.version}. -

+

{fmt(t.disclaimerLine, { version: pack.version })}

); } diff --git a/ai-act-kompass/src/ui/DisclaimerGate.tsx b/ai-act-kompass/src/ui/DisclaimerGate.tsx new file mode 100644 index 0000000..fa1195f --- /dev/null +++ b/ai-act-kompass/src/ui/DisclaimerGate.tsx @@ -0,0 +1,47 @@ +import { useState } from 'react'; +import { useI18n } from '../i18n/context'; + +const ACK_KEY = 'ai-act-kompass/legal-ack/v1'; + +function hasAcknowledged(): boolean { + try { + return localStorage.getItem(ACK_KEY) !== null; + } catch { + return false; + } +} + +/** + * Rechtlicher Hinweis beim ersten Start: Die Nutzung ist erst nach + * ausdrĂŒcklicher BestĂ€tigung möglich, dass das Tool nur eine Arbeitshilfe + * und keine Rechtsberatung ist. Die BestĂ€tigung wird mit Zeitstempel + * lokal gespeichert. + */ +export function DisclaimerGate(): JSX.Element | null { + const { t } = useI18n(); + const [acknowledged, setAcknowledged] = useState(() => hasAcknowledged()); + + if (acknowledged) return null; + + return ( +
+
+

{t.legalGateTitle}

+

{t.legalGateBody1}

+

+ {t.legalGateBody2} +

+

{t.legalGateBody3}

+ +
+
+ ); +} diff --git a/ai-act-kompass/src/ui/SettingsView.tsx b/ai-act-kompass/src/ui/SettingsView.tsx index 6927898..c0f7e7b 100644 --- a/ai-act-kompass/src/ui/SettingsView.tsx +++ b/ai-act-kompass/src/ui/SettingsView.tsx @@ -1,6 +1,8 @@ import { useRef, useState } from 'react'; -import { CONTENT_PACK } from '../content/pack'; import type { AISystem, CompanyProfile } from '../engine/types'; +import { useI18n } from '../i18n/context'; +import { LOCALES } from '../i18n/types'; +import { fmt } from '../i18n/ui'; import { clearAll, exportData, importData } from '../store/storage'; import { downloadText, todayISO } from './common'; @@ -14,17 +16,9 @@ interface SettingsViewProps { const MAX_LOGO_BYTES = 300_000; -const PROFILE_FIELDS: readonly { key: keyof CompanyProfile; label: string; placeholder: string }[] = [ - { key: 'name', label: 'Firmenname', placeholder: 'Muster GmbH' }, - { key: 'street', label: 'Straße & Hausnummer', placeholder: 'Musterstraße 1' }, - { key: 'zipCity', label: 'PLZ & Ort', placeholder: '60311 Frankfurt am Main' }, - { key: 'contactPerson', label: 'Ansprechpartner', placeholder: 'Max Mustermann' }, - { key: 'email', label: 'E-Mail', placeholder: 'compliance@muster.de' }, - { key: 'phone', label: 'Telefon', placeholder: '+49 69 000000' }, - { key: 'website', label: 'Website', placeholder: 'www.muster.de' }, -]; +type ProfileTextKey = Exclude; -/** Einstellungen: Firmenprofil (Briefkopf), Datensicherung, Löschen, Content-Pack-Info. */ +/** Einstellungen: Sprache, Firmenprofil, LĂ€nder-Module, Backup, Rechtshinweis. */ export function SettingsView({ systems, profile, @@ -32,52 +26,110 @@ export function SettingsView({ onImport, onClear, }: SettingsViewProps): JSX.Element { + const { t, locale, pack, setLocale } = useI18n(); const fileInput = useRef(null); const logoInput = useRef(null); const [message, setMessage] = useState(''); const [logoMessage, setLogoMessage] = useState(''); + const profileFields: readonly { key: ProfileTextKey; label: string; placeholder: string }[] = [ + { key: 'name', label: t.pfName, placeholder: 'Muster GmbH' }, + { key: 'street', label: t.pfStreet, placeholder: 'Musterstraße 1' }, + { key: 'zipCity', label: t.pfZip, placeholder: '60311 Frankfurt am Main' }, + { key: 'contactPerson', label: t.pfContact, placeholder: 'Max Mustermann' }, + { key: 'email', label: t.pfEmail, placeholder: 'compliance@muster.de' }, + { key: 'phone', label: t.pfPhone, placeholder: '+49 69 000000' }, + { key: 'website', label: t.pfWeb, placeholder: 'www.muster.de' }, + ]; + const handleImport = async (file: File): Promise => { try { const result = importData(await file.text()); onImport(result.systems, result.profile); - setMessage(`Import abgeschlossen: ${result.systems.length} System(e)${result.profile ? ' inkl. Firmenprofil' : ''}.`); + setMessage( + fmt(t.importOk, { + count: result.systems.length, + profile: result.profile ? t.importProfileSuffix : '', + }), + ); } catch (err) { - setMessage(`Import fehlgeschlagen: ${err instanceof Error ? err.message : String(err)}`); + setMessage(fmt(t.importFail, { error: err instanceof Error ? err.message : String(err) })); } }; const handleLogo = (file: File): void => { if (!file.type.startsWith('image/')) { - setLogoMessage('Bitte eine Bilddatei wĂ€hlen (PNG, JPG oder SVG).'); + setLogoMessage(t.logoNotImage); return; } const reader = new FileReader(); reader.onload = () => { const dataUrl = typeof reader.result === 'string' ? reader.result : ''; if (dataUrl.length > MAX_LOGO_BYTES * 1.4) { - setLogoMessage('Logo zu groß — bitte eine Datei unter 300 KB verwenden.'); + setLogoMessage(t.logoTooBig); return; } onProfileChange({ ...profile, logoDataUrl: dataUrl }); - setLogoMessage('Logo ĂŒbernommen.'); + setLogoMessage(t.logoTaken); }; reader.readAsDataURL(file); }; + const toggleCountry = (code: string): void => { + const countries = profile.countries.includes(code) + ? profile.countries.filter((c) => c !== code) + : [...profile.countries, code]; + onProfileChange({ ...profile, countries }); + }; + return (
-

Einstellungen

-

Datenhoheit: Alles liegt lokal in diesem Browser — kein Cloud-Sync, keine Telemetrie.

+

{t.settingsTitle}

+

{t.settingsSub}

-

Firmenprofil (Briefkopf fĂŒr Berichte & PDF-Druck)

-

- Diese Angaben erscheinen im Kopf und in der Fußzeile aller generierten und gedruckten - Dokumente. Das Logo bleibt lokal gespeichert und verlĂ€sst das GerĂ€t nicht. -

+

{t.langTitle}

+
+ {LOCALES.map((l) => ( + + ))} +
+

{t.langHint}

+
+ +
+

{t.countriesTitle}

+

{t.countriesHint}

+ {pack.national.map((n) => ( + + ))} +
+ +
+

{t.profileTitle}

+

{t.profileHint}

- {PROFILE_FIELDS.map((f) => ( + {profileFields.map((f) => (