'use client' /** * UCCA - Use-Case Compliance & Feasibility Advisor * * Wizard-based intake for AI use cases with result dashboard * Supports Normal Mode (simple language) and Expert Mode (technical terms) */ import { useState, useEffect } from 'react' import { PagePurpose } from '@/components/common/PagePurpose' import Link from 'next/link' // ============================================================================ // Types // ============================================================================ type WizardMode = 'normal' | 'expert' interface DataTypes { personal_data: boolean article_9_data: boolean minor_data: boolean license_plates: boolean images: boolean audio: boolean location_data: boolean biometric_data: boolean financial_data: boolean employee_data: boolean customer_data: boolean public_data: boolean } interface Purpose { customer_support: boolean marketing: boolean analytics: boolean automation: boolean evaluation_scoring: boolean decision_making: boolean profiling: boolean research: boolean internal_tools: boolean public_service: boolean } interface Outputs { recommendations_to_users: boolean rankings_or_scores: boolean legal_effects: boolean access_decisions: boolean content_generation: boolean data_export: boolean } interface Hosting { provider: string region: string data_residency: string } interface ModelUsage { rag: boolean finetune: boolean training: boolean inference: boolean } interface Retention { store_prompts: boolean store_responses: boolean retention_days: number anonymize_after_use: boolean } interface UseCaseIntake { use_case_text: string domain: string title: string data_types: DataTypes purpose: Purpose automation: string outputs: Outputs hosting: Hosting model_usage: ModelUsage retention: Retention store_raw_text: boolean } interface TriggeredRule { code: string category: string title: string description: string severity: 'INFO' | 'WARN' | 'BLOCK' score_delta: number gdpr_ref: string rationale: string } interface RequiredControl { id: string title: string description: string severity: string category: string gdpr_ref: string } interface PatternRecommendation { pattern_id: string title: string description: string rationale: string priority: number } interface ForbiddenPattern { pattern_id: string title: string description: string reason: string gdpr_ref: string } interface ExampleMatch { example_id: string title: string description: string similarity: number outcome: string lessons: string } interface AssessmentResult { feasibility: 'YES' | 'CONDITIONAL' | 'NO' risk_level: 'MINIMAL' | 'LOW' | 'MEDIUM' | 'HIGH' | 'UNACCEPTABLE' complexity: 'LOW' | 'MEDIUM' | 'HIGH' risk_score: number triggered_rules: TriggeredRule[] required_controls: RequiredControl[] recommended_architecture: PatternRecommendation[] forbidden_patterns: ForbiddenPattern[] example_matches: ExampleMatch[] dsfa_recommended: boolean art22_risk: boolean training_allowed: string summary: string recommendation: string alternative_approach?: string } interface Assessment { id: string title: string created_at: string feasibility: string risk_level: string risk_score: number domain: string explanation_text?: string } // Problem-Solution types from API interface ProblemTrigger { rule: string without_control?: string } interface Solution { id: string title: string pattern?: string control?: string removes_problem: boolean team_question: string } interface ProblemSolution { problem_id: string title: string triggers: ProblemTrigger[] solutions: Solution[] } // ============================================================================ // Domain Configuration with Keywords for Auto-Detection // ============================================================================ interface DomainConfig { value: string label: string labelExpert?: string keywords: string[] category: string } const DOMAINS: DomainConfig[] = [ // Industrie & Produktion { value: 'automotive', label: 'Automobil & Fahrzeugbau', keywords: ['auto', 'fahrzeug', 'kfz', 'pkw', 'lkw', 'motor', 'antrieb', 'karosserie', 'zulieferer', 'oem', 'tier1', 'tier2', 'werkstatt', 'autohaus'], category: 'Industrie' }, { value: 'mechanical_engineering', label: 'Maschinenbau', keywords: ['maschine', 'maschinenbau', 'anlage', 'fertigung', 'produktion', 'werkzeug', 'cnc', 'roboter', 'automatisierung'], category: 'Industrie' }, { value: 'plant_engineering', label: 'Anlagenbau', keywords: ['anlagenbau', 'grossanlage', 'industrieanlage', 'kraftwerk', 'raffinerie', 'chemieanlage'], category: 'Industrie' }, { value: 'electrical_engineering', label: 'Elektrotechnik', keywords: ['elektro', 'elektronik', 'schaltung', 'steuerung', 'sps', 'antrieb', 'motor', 'transformator'], category: 'Industrie' }, { value: 'aerospace', label: 'Luft- & Raumfahrt', keywords: ['flugzeug', 'luftfahrt', 'raumfahrt', 'satellit', 'rakete', 'aviation', 'aerospace', 'airline'], category: 'Industrie' }, { value: 'chemicals', label: 'Chemie & Pharma', keywords: ['chemie', 'pharma', 'labor', 'wirkstoff', 'medikament', 'arzneimittel', 'kosmetik', 'kunststoff'], category: 'Industrie' }, { value: 'food_beverage', label: 'Lebensmittel & Getraenke', keywords: ['lebensmittel', 'nahrung', 'getraenk', 'brauerei', 'baeckerei', 'fleisch', 'molkerei', 'food'], category: 'Industrie' }, { value: 'textiles', label: 'Textil & Bekleidung', keywords: ['textil', 'bekleidung', 'mode', 'fashion', 'stoff', 'naeherei', 'konfektion'], category: 'Industrie' }, { value: 'packaging', label: 'Verpackung', keywords: ['verpackung', 'packaging', 'karton', 'folie', 'etikette', 'abfuellung'], category: 'Industrie' }, // Energie & Versorgung { value: 'utilities', label: 'Stadtwerke & Versorgung', keywords: ['stadtwerk', 'versorgung', 'wasser', 'gas', 'strom', 'fernwaerme', 'entsorgung', 'abfall', 'muell'], category: 'Energie' }, { value: 'energy', label: 'Energie & Kraftwerke', keywords: ['energie', 'kraftwerk', 'solar', 'wind', 'photovoltaik', 'erneuerbar', 'netz', 'einspeise'], category: 'Energie' }, { value: 'oil_gas', label: 'Oel & Gas', keywords: ['oel', 'gas', 'pipeline', 'raffinerie', 'tankstelle', 'bohren', 'foerder'], category: 'Energie' }, // Land- & Forstwirtschaft { value: 'agriculture', label: 'Landwirtschaft & Agrar', keywords: ['agrar', 'landwirt', 'bauer', 'acker', 'ernte', 'saat', 'duenger', 'pflanzenschutz', 'traktor', 'wetterstation', 'precision farming', 'smart farming'], category: 'Agrar' }, { value: 'forestry', label: 'Forstwirtschaft', keywords: ['forst', 'wald', 'holz', 'saege', 'foerster', 'jagd'], category: 'Agrar' }, { value: 'fishing', label: 'Fischerei & Aquakultur', keywords: ['fisch', 'aquakultur', 'meeresfruchte', 'fischzucht', 'trawler'], category: 'Agrar' }, // Bau & Immobilien { value: 'construction', label: 'Bauwesen & Tiefbau', keywords: ['bau', 'baustelle', 'hochbau', 'tiefbau', 'architekt', 'statik', 'beton', 'fundament'], category: 'Bau' }, { value: 'real_estate', label: 'Immobilien', keywords: ['immobilie', 'makler', 'wohnung', 'miete', 'vermietung', 'hausverwaltung', 'wohnungsbau'], category: 'Bau' }, { value: 'facility_management', label: 'Facility Management', keywords: ['facility', 'gebaeudemanagement', 'hausmeister', 'reinigung', 'wartung', 'instandhaltung'], category: 'Bau' }, // Gesundheit & Soziales { value: 'healthcare', label: 'Gesundheit & Medizin', keywords: ['gesundheit', 'medizin', 'arzt', 'krankenhaus', 'klinik', 'praxis', 'patient', 'diagnose', 'therapie', 'pflege'], category: 'Gesundheit' }, { value: 'medical_devices', label: 'Medizintechnik', keywords: ['medizintechnik', 'medtech', 'implantat', 'roentgen', 'mrt', 'ct', 'ultraschall', 'beatmung', 'dialyse'], category: 'Gesundheit' }, { value: 'pharma', label: 'Pharmaindustrie', keywords: ['pharma', 'arzneimittel', 'medikament', 'zulassung', 'klinische studie', 'wirkstoff'], category: 'Gesundheit' }, { value: 'elderly_care', label: 'Altenpflege & Betreuung', keywords: ['altenpflege', 'seniorenheim', 'pflegeheim', 'betreuung', 'demenz', 'ambulante pflege'], category: 'Gesundheit' }, { value: 'social_services', label: 'Soziale Dienste', keywords: ['sozial', 'jugendamt', 'beratung', 'hilfe', 'integration', 'behinderung', 'inklusion'], category: 'Gesundheit' }, // Bildung & Forschung { value: 'education', label: 'Bildung & Schule', keywords: ['schule', 'bildung', 'lehrer', 'schueler', 'unterricht', 'abitur', 'klausur', 'gymnasium', 'grundschule'], category: 'Bildung' }, { value: 'higher_education', label: 'Hochschule & Universitaet', keywords: ['universitaet', 'hochschule', 'studium', 'student', 'professor', 'forschung', 'promotion'], category: 'Bildung' }, { value: 'vocational_training', label: 'Berufsausbildung', keywords: ['ausbildung', 'azubi', 'lehrling', 'berufsschule', 'ihk', 'handwerkskammer', 'meister'], category: 'Bildung' }, { value: 'research', label: 'Forschung & Entwicklung', keywords: ['forschung', 'entwicklung', 'f&e', 'r&d', 'labor', 'innovation', 'patent'], category: 'Bildung' }, // Finanzen & Versicherung { value: 'finance', label: 'Finanzen & Buchhaltung', keywords: ['finanzen', 'buchhaltung', 'controlling', 'bilanz', 'rechnungswesen', 'steuer', 'wirtschaftspruef'], category: 'Finanzen' }, { value: 'banking', label: 'Banken & Kreditinstitute', keywords: ['bank', 'kredit', 'darlehen', 'hypothek', 'sparkasse', 'volksbank', 'girokonto', 'depot'], category: 'Finanzen' }, { value: 'insurance', label: 'Versicherungen', keywords: ['versicherung', 'police', 'schaden', 'regulierung', 'praemie', 'lebensversicherung', 'krankenversicherung'], category: 'Finanzen' }, { value: 'investment', label: 'Investment & Vermoegensverwaltung', keywords: ['investment', 'fonds', 'aktie', 'wertpapier', 'portfolio', 'asset', 'vermoegen'], category: 'Finanzen' }, // Handel & Logistik { value: 'retail', label: 'Einzelhandel', keywords: ['einzelhandel', 'geschaeft', 'laden', 'filiale', 'supermarkt', 'kasse', 'warenhaus'], category: 'Handel' }, { value: 'ecommerce', label: 'E-Commerce & Online-Handel', keywords: ['ecommerce', 'online', 'shop', 'webshop', 'bestellung', 'versand', 'retoure', 'warenkorb'], category: 'Handel' }, { value: 'wholesale', label: 'Grosshandel', keywords: ['grosshandel', 'b2b', 'distributor', 'haendler', 'lieferant', 'einkauf'], category: 'Handel' }, { value: 'logistics', label: 'Logistik & Transport', keywords: ['logistik', 'transport', 'spedition', 'lager', 'lieferkette', 'supply chain', 'fracht', 'versand'], category: 'Handel' }, // IT & Telekommunikation { value: 'it_services', label: 'IT & Software', keywords: ['it', 'software', 'entwicklung', 'programmierung', 'cloud', 'saas', 'rechenzentrum', 'server'], category: 'IT' }, { value: 'telecom', label: 'Telekommunikation', keywords: ['telekom', 'mobilfunk', 'netz', 'provider', 'glasfaser', 'breitband', 'telefon'], category: 'IT' }, { value: 'cybersecurity', label: 'IT-Sicherheit', keywords: ['sicherheit', 'security', 'cyber', 'firewall', 'penetration', 'hacking', 'datenschutz'], category: 'IT' }, // Recht & Beratung { value: 'legal', label: 'Recht & Anwaelte', keywords: ['recht', 'anwalt', 'kanzlei', 'vertrag', 'klage', 'gericht', 'notar', 'jurist'], category: 'Beratung' }, { value: 'consulting', label: 'Unternehmensberatung', keywords: ['beratung', 'consulting', 'strategie', 'management', 'transformation', 'prozess'], category: 'Beratung' }, { value: 'tax_advisory', label: 'Steuerberatung', keywords: ['steuerberater', 'steuerkanzlei', 'finanzamt', 'steuererklaerung', 'lohnsteuer', 'umsatzsteuer'], category: 'Beratung' }, // Oeffentlicher Sektor { value: 'public_sector', label: 'Behoerden & Verwaltung', keywords: ['behoerde', 'verwaltung', 'amt', 'rathaus', 'buergeramt', 'gemeinde', 'kommune', 'oeffentlich'], category: 'Oeffentlich' }, { value: 'defense', label: 'Verteidigung & Sicherheit', keywords: ['bundeswehr', 'militaer', 'verteidigung', 'ruestung', 'polizei', 'sicherheitsbehoerde'], category: 'Oeffentlich' }, { value: 'justice', label: 'Justiz', keywords: ['justiz', 'gericht', 'staatsanwalt', 'richter', 'strafverfolgung', 'jva'], category: 'Oeffentlich' }, // Marketing & Medien { value: 'marketing', label: 'Werbung & Marketing', keywords: ['marketing', 'werbung', 'kampagne', 'marke', 'brand', 'agentur', 'media', 'seo', 'social media'], category: 'Medien' }, { value: 'media', label: 'Medien & Verlage', keywords: ['medien', 'verlag', 'zeitung', 'magazin', 'redaktion', 'journalist', 'presse', 'rundfunk', 'tv', 'radio'], category: 'Medien' }, { value: 'entertainment', label: 'Unterhaltung & Gaming', keywords: ['unterhaltung', 'spiel', 'gaming', 'film', 'musik', 'event', 'konzert', 'kino'], category: 'Medien' }, // HR & Personal { value: 'hr', label: 'Personalwesen', keywords: ['personal', 'hr', 'human resources', 'mitarbeiter', 'recruiting', 'bewerbung', 'gehalt', 'lohn', 'personalentwicklung'], category: 'HR' }, { value: 'recruiting', label: 'Recruiting & Stellenvermittlung', keywords: ['recruiting', 'headhunter', 'stellenvermittlung', 'jobboerse', 'bewerber', 'talent'], category: 'HR' }, // Tourismus & Gastronomie { value: 'hospitality', label: 'Hotellerie & Gastronomie', keywords: ['hotel', 'restaurant', 'gastronomie', 'gastro', 'kueche', 'koch', 'rezeption', 'zimmer', 'buchung'], category: 'Tourismus' }, { value: 'tourism', label: 'Tourismus & Reise', keywords: ['tourismus', 'reise', 'urlaub', 'reisebuero', 'flug', 'pauschalreise', 'kreuzfahrt', 'tourist'], category: 'Tourismus' }, // Sonstige { value: 'nonprofit', label: 'Gemeinnuetzig & NGO', keywords: ['gemeinnuetzig', 'ngo', 'verein', 'stiftung', 'spende', 'ehrenamt', 'wohltaetig'], category: 'Sonstige' }, { value: 'sports', label: 'Sport & Fitness', keywords: ['sport', 'fitness', 'verein', 'trainer', 'athlet', 'stadion', 'wettkampf'], category: 'Sonstige' }, { value: 'general', label: 'Allgemein / Branchenuebergreifend', keywords: ['allgemein', 'sonstig', 'andere', 'uebergreifend'], category: 'Sonstige' }, ] // Group domains by category for the dropdown const DOMAIN_CATEGORIES = [...new Set(DOMAINS.map(d => d.category))] /** * Analyzes text and suggests matching domains based on keywords */ function suggestDomainsFromText(text: string): { value: string; label: string; score: number }[] { if (!text || text.length < 3) return [] const normalizedText = text.toLowerCase() const suggestions: { value: string; label: string; score: number }[] = [] for (const domain of DOMAINS) { let score = 0 for (const keyword of domain.keywords) { if (normalizedText.includes(keyword.toLowerCase())) { // Longer keywords get higher scores (more specific) score += keyword.length } } if (score > 0) { suggestions.push({ value: domain.value, label: domain.label, score }) } } // Sort by score descending, take top 3 return suggestions.sort((a, b) => b.score - a.score).slice(0, 3) } // ============================================================================ // Label Maps for Normal vs Expert Mode // ============================================================================ const LABELS = { // Domains - now dynamically generated from DOMAINS array domains: { normal: DOMAINS.map(d => ({ value: d.value, label: d.label, category: d.category })), expert: DOMAINS.map(d => ({ value: d.value, label: d.labelExpert || d.label, category: d.category })), }, // Data Types dataTypes: { normal: [ { key: 'personal_data', label: 'Namen, E-Mails, Adressen', hint: 'Alles womit man Personen identifizieren kann' }, { key: 'article_9_data', label: 'Gesundheit, Religion, politische Meinung', hint: 'Besonders geschuetzte Daten', risk: true }, { key: 'minor_data', label: 'Daten von Kindern/Jugendlichen', hint: 'Unter 18 Jahre', risk: true }, { key: 'license_plates', label: 'Auto-Kennzeichen', hint: 'KFZ-Nummernschilder' }, { key: 'images', label: 'Fotos von Personen', hint: 'Gesichter erkennbar' }, { key: 'audio', label: 'Sprachaufnahmen', hint: 'Gespraeche, Telefonate' }, { key: 'location_data', label: 'Standorte & Bewegungsdaten', hint: 'GPS, Aufenthaltsorte' }, { key: 'biometric_data', label: 'Fingerabdruecke, Gesichtserkennung', hint: 'Koerperliche Merkmale', risk: true }, { key: 'financial_data', label: 'Gehaelter, Kontodaten', hint: 'Finanzielle Informationen' }, { key: 'employee_data', label: 'Mitarbeiter-Informationen', hint: 'Personalakten, Bewertungen' }, { key: 'customer_data', label: 'Kundendaten', hint: 'Bestellungen, Kontaktdaten' }, { key: 'public_data', label: 'Nur oeffentliche Daten', hint: 'Keine personenbezogenen Daten', safe: true }, ], expert: [ { key: 'personal_data', label: 'Personenbezogene Daten', hint: 'Art. 4(1) DSGVO' }, { key: 'article_9_data', label: 'Besondere Kategorien (Art. 9)', hint: 'Gesundheit, Religion, etc.', risk: true }, { key: 'minor_data', label: 'Daten von Minderjaehrigen', hint: 'Art. 8 DSGVO', risk: true }, { key: 'license_plates', label: 'KFZ-Kennzeichen', hint: 'Personenbezug moeglich' }, { key: 'images', label: 'Bilder von Personen', hint: 'Biometrische Daten moeglich' }, { key: 'audio', label: 'Audioaufnahmen', hint: 'Stimmprofile moeglich' }, { key: 'location_data', label: 'Standortdaten', hint: 'Bewegungsprofile' }, { key: 'biometric_data', label: 'Biometrische Daten', hint: 'Art. 9(1) DSGVO', risk: true }, { key: 'financial_data', label: 'Finanzdaten', hint: 'Bankdaten, Gehaelter' }, { key: 'employee_data', label: 'Mitarbeiterdaten', hint: 'Beschaeftigtendatenschutz' }, { key: 'customer_data', label: 'Kundendaten', hint: 'B2C/B2B Kontakte' }, { key: 'public_data', label: 'Nur oeffentliche Daten', hint: 'Kein Personenbezug', safe: true }, ], }, // Purpose purpose: { normal: [ { key: 'customer_support', label: 'Kundenservice & Support', hint: 'Fragen beantworten, Hilfe anbieten' }, { key: 'marketing', label: 'Werbung & Newsletter', hint: 'Kunden ansprechen, Kampagnen' }, { key: 'analytics', label: 'Auswertungen & Berichte', hint: 'Zahlen analysieren, Trends erkennen' }, { key: 'automation', label: 'Arbeitsablaeufe automatisieren', hint: 'Routineaufgaben von KI erledigen lassen' }, { key: 'evaluation_scoring', label: 'Personen bewerten oder einstufen', hint: 'Noten, Scores, Rankings', risk: true }, { key: 'decision_making', label: 'Entscheidungen treffen', hint: 'Genehmigungen, Ablehnungen', risk: true }, { key: 'profiling', label: 'Personenprofile erstellen', hint: 'Verhaltensmuster analysieren', risk: true }, { key: 'research', label: 'Forschung & Entwicklung', hint: 'Neue Erkenntnisse gewinnen' }, { key: 'internal_tools', label: 'Interne Werkzeuge', hint: 'Nur fuer Mitarbeiter' }, { key: 'public_service', label: 'Service fuer Buerger/Kunden', hint: 'Oeffentlich zugaenglich' }, ], expert: [ { key: 'customer_support', label: 'Kundenservice', hint: 'Support, Chatbots' }, { key: 'marketing', label: 'Marketing', hint: 'Kampagnen, Targeting' }, { key: 'analytics', label: 'Analyse', hint: 'Business Intelligence' }, { key: 'automation', label: 'Automatisierung', hint: 'Prozessautomation' }, { key: 'evaluation_scoring', label: 'Bewertung / Scoring', hint: 'Art. 22 relevant', risk: true }, { key: 'decision_making', label: 'Entscheidungsfindung', hint: 'Automated Decision Making', risk: true }, { key: 'profiling', label: 'Profiling', hint: 'Art. 4(4) DSGVO', risk: true }, { key: 'research', label: 'Forschung', hint: 'Art. 89 DSGVO' }, { key: 'internal_tools', label: 'Interne Tools', hint: 'Mitarbeiter-only' }, { key: 'public_service', label: 'Oeffentlicher Service', hint: 'Extern zugaenglich' }, ], }, // Automation Levels automation: { normal: [ { value: 'assistive', label: 'KI macht Vorschlaege', description: 'Wie eine Rechtschreibpruefung: Sie sehen Vorschlaege und entscheiden selbst', example: 'Beispiel: KI schlaegt Antworten vor, Mitarbeiter waehlt aus', safe: true, risk: false, }, { value: 'semi_automated', label: 'KI filtert vor, Mensch prueft', description: 'Wie ein Spam-Filter: KI sortiert vor, Sie schauen drueber', example: 'Beispiel: KI ordnet Anfragen ein, Sachbearbeiter bestaetigt', safe: false, risk: false, }, { value: 'fully_automated', label: 'KI entscheidet alleine', description: 'Wie automatische Kreditpruefung: Keine menschliche Pruefung mehr', example: 'Beispiel: KI lehnt Antrag ab ohne dass jemand draufschaut', safe: false, risk: true, }, ], expert: [ { value: 'assistive', label: 'Assistierend', description: 'KI macht Vorschlaege, Mensch entscheidet', example: 'Human-in-the-loop garantiert', safe: false, risk: false, }, { value: 'semi_automated', label: 'Teilautomatisiert', description: 'KI trifft Vorentscheidungen, Mensch prueft', example: 'Human-on-the-loop', safe: false, risk: false, }, { value: 'fully_automated', label: 'Vollautomatisiert', description: 'KI entscheidet ohne menschliche Pruefung', example: 'Art. 22 DSGVO beachten!', safe: false, risk: true, }, ], }, // Outputs outputs: { normal: [ { key: 'recommendations_to_users', label: 'Empfehlungen an Nutzer', hint: 'z.B. Produktvorschlaege' }, { key: 'rankings_or_scores', label: 'Punkte, Noten oder Ranglisten', hint: 'Personen werden eingestuft', risk: true }, { key: 'legal_effects', label: 'Rechtliche Auswirkungen', hint: 'Vertraege, Kuendigungen', risk: true }, { key: 'access_decisions', label: 'Zugang erlauben/verweigern', hint: 'Tueroeffnung, Login', risk: true }, { key: 'content_generation', label: 'Texte oder Bilder erstellen', hint: 'Automatisch generierte Inhalte' }, { key: 'data_export', label: 'Daten exportieren', hint: 'Berichte, Downloads' }, ], expert: [ { key: 'recommendations_to_users', label: 'Empfehlungen an Nutzer', hint: 'Recommendation Systems' }, { key: 'rankings_or_scores', label: 'Rankings oder Scores', hint: 'Scoring-Systeme', risk: true }, { key: 'legal_effects', label: 'Rechtliche Auswirkungen', hint: 'Art. 22(1) DSGVO', risk: true }, { key: 'access_decisions', label: 'Zugriffsentscheidungen', hint: 'Access Control', risk: true }, { key: 'content_generation', label: 'Content-Generierung', hint: 'Generative AI' }, { key: 'data_export', label: 'Datenexport', hint: 'Reporting' }, ], }, // Hosting Region hosting: { normal: [ { value: 'eu', label: 'In Deutschland / EU', description: 'Daten bleiben in Europa - einfachste Loesung', safe: true, risk: false, }, { value: 'third_country', label: 'Ausserhalb der EU (z.B. USA)', description: 'Zusaetzliche Vertraege und Schutzmassnahmen noetig', safe: false, risk: true, }, { value: 'on_prem', label: 'Auf unseren eigenen Servern', description: 'Volle Kontrolle, aber mehr technischer Aufwand', safe: false, risk: false, }, ], expert: [ { value: 'eu', label: 'EU/EWR', description: 'Hosting innerhalb der EU', safe: false, risk: false, }, { value: 'third_country', label: 'Drittland', description: 'Hosting ausserhalb der EU (Art. 44ff DSGVO)', safe: false, risk: true, }, { value: 'on_prem', label: 'On-Premise', description: 'Eigene Server / Private Cloud', safe: false, risk: false, }, ], }, // Model Usage - This is the key difference! modelUsage: { normal: [ { id: 'search_only', label: 'KI durchsucht meine Dokumente', description: 'Die KI findet Informationen in Ihren Unterlagen und formuliert Antworten. Ihre Daten werden NICHT zum Training verwendet.', example: 'Wie eine intelligente Suchmaschine fuer Ihre Dateien', maps_to: { rag: true, finetune: false, training: false, inference: true }, safe: true, }, { id: 'use_only', label: 'KI nur nutzen (ohne meine Daten)', description: 'Sie nutzen eine fertige KI wie ChatGPT. Keine Ihrer Daten fliessen in das Training.', example: 'Wie ein Taschenrechner: Sie geben etwas ein, bekommen Ergebnis', maps_to: { rag: false, finetune: false, training: false, inference: true }, safe: true, }, { id: 'learn_from_data', label: 'KI soll aus meinen Daten lernen', description: 'Die KI wird mit Ihren Daten trainiert und passt ihr Verhalten an. Hoeheres Datenschutz-Risiko!', example: 'Die KI "merkt" sich Muster aus Ihren Daten', maps_to: { rag: false, finetune: true, training: true, inference: true }, risk: true, }, ], expert: [ { key: 'rag', label: 'RAG (Retrieval-Augmented Generation)', description: 'Anreicherung mit Kontextdaten aus Vektordatenbank' }, { key: 'finetune', label: 'Fine-Tuning', description: 'Modell mit eigenen Daten anpassen (Transfer Learning)' }, { key: 'training', label: 'Vollstaendiges Training', description: 'Modell von Grund auf trainieren' }, { key: 'inference', label: 'Nur Inferenz', description: 'Nur Nutzung des bestehenden Modells ohne Training' }, ], }, // Retention retention: { normal: { store_prompts: { label: 'Anfragen speichern', hint: 'Was Nutzer der KI schreiben' }, store_responses: { label: 'Antworten speichern', hint: 'Was die KI zurueckgibt' }, retention_days: { label: 'Wie lange aufbewahren?', hint: 'Anzahl Tage, dann automatisch loeschen' }, }, expert: { store_prompts: { label: 'Prompts speichern', hint: 'User-Eingaben persistieren' }, store_responses: { label: 'Responses speichern', hint: 'Model-Outputs persistieren' }, retention_days: { label: 'Aufbewahrungsdauer (Tage)', hint: 'Retention Period' }, }, }, } // ============================================================================ // Constants // ============================================================================ const SDK_BASE_URL = process.env.NEXT_PUBLIC_SDK_URL || 'https://macmini:8093/sdk/v1' // Default UUIDs for development/testing when localStorage is empty const DEFAULT_TENANT_ID = 'e4ed7450-ad19-4be3-bea9-83aab6e1c21d' const DEFAULT_USER_ID = '00000000-0000-0000-0000-000000000001' // Helper to get tenant/user IDs with fallbacks const getTenantId = () => { if (typeof window === 'undefined') return DEFAULT_TENANT_ID return localStorage.getItem('bp_tenant_id') || DEFAULT_TENANT_ID } const getUserId = () => { if (typeof window === 'undefined') return DEFAULT_USER_ID return localStorage.getItem('bp_user_id') || DEFAULT_USER_ID } // ============================================================================ // Initial State // ============================================================================ const initialIntake: UseCaseIntake = { use_case_text: '', domain: 'general', title: '', data_types: { personal_data: false, article_9_data: false, minor_data: false, license_plates: false, images: false, audio: false, location_data: false, biometric_data: false, financial_data: false, employee_data: false, customer_data: false, public_data: false, }, purpose: { customer_support: false, marketing: false, analytics: false, automation: false, evaluation_scoring: false, decision_making: false, profiling: false, research: false, internal_tools: false, public_service: false, }, automation: 'assistive', outputs: { recommendations_to_users: false, rankings_or_scores: false, legal_effects: false, access_decisions: false, content_generation: false, data_export: false, }, hosting: { provider: '', region: 'eu', data_residency: '', }, model_usage: { rag: true, finetune: false, training: false, inference: true, }, retention: { store_prompts: false, store_responses: false, retention_days: 0, anonymize_after_use: false, }, store_raw_text: false, } // ============================================================================ // Main Component // ============================================================================ export default function AdvisoryBoardPage() { const [mode, setMode] = useState('normal') const [step, setStep] = useState(0) // 0 = overview, 1-5 = wizard steps, 6 = result const [intake, setIntake] = useState(initialIntake) const [result, setResult] = useState(null) const [assessmentId, setAssessmentId] = useState(null) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [history, setHistory] = useState([]) const [loadingHistory, setLoadingHistory] = useState(true) const [expandedRuleCategory, setExpandedRuleCategory] = useState(null) const [generatingExplanation, setGeneratingExplanation] = useState(false) const [explanation, setExplanation] = useState(null) const [problemSolutions, setProblemSolutions] = useState([]) const [matchedProblems, setMatchedProblems] = useState([]) // For Normal mode Step 5: which simplified option is selected const [normalModelChoice, setNormalModelChoice] = useState('search_only') // Load assessment history on mount useEffect(() => { loadHistory() fetchProblemSolutions() }, []) // Sync normalModelChoice with intake.model_usage when switching modes useEffect(() => { if (mode === 'normal') { // Determine which normal option matches current state if (intake.model_usage.finetune || intake.model_usage.training) { setNormalModelChoice('learn_from_data') } else if (intake.model_usage.rag) { setNormalModelChoice('search_only') } else { setNormalModelChoice('use_only') } } }, [mode]) const loadHistory = async () => { try { const res = await fetch(`${SDK_BASE_URL}/ucca/assessments`, { headers: { 'X-Tenant-ID': getTenantId(), 'X-User-ID': getUserId(), } }) if (res.ok) { const data = await res.json() setHistory(data.assessments || []) } } catch (err) { console.error('Failed to load history:', err) } finally { setLoadingHistory(false) } } // Fetch problem-solutions catalog from API const fetchProblemSolutions = async () => { try { const res = await fetch(`${SDK_BASE_URL}/ucca/problem-solutions`, { headers: { 'X-Tenant-ID': getTenantId(), 'X-User-ID': getUserId(), }, }) if (res.ok) { const data = await res.json() setProblemSolutions(data.problem_solutions || []) } } catch (err) { console.error('Failed to load problem-solutions:', err) } } // Match triggered rules with problems and find applicable solutions const matchProblemsToResult = (triggeredRules: TriggeredRule[], requiredControls: RequiredControl[]) => { if (!problemSolutions.length || !triggeredRules.length) { setMatchedProblems([]) return } const triggeredRuleIds = new Set(triggeredRules.map(r => r.code)) const controlIds = new Set(requiredControls.map(c => c.id)) const matched = problemSolutions.filter(ps => { // Check if any trigger condition matches return ps.triggers.some(trigger => { // Rule must be triggered if (!triggeredRuleIds.has(trigger.rule)) { return false } // If without_control is specified, that control must NOT be present if (trigger.without_control && controlIds.has(trigger.without_control)) { return false } return true }) }) setMatchedProblems(matched) } const submitAssessment = async () => { setLoading(true) setError(null) try { const res = await fetch(`${SDK_BASE_URL}/ucca/assess`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Tenant-ID': getTenantId(), 'X-User-ID': getUserId(), }, body: JSON.stringify(intake), }) if (!res.ok) { const errData = await res.json() throw new Error(errData.error || 'Assessment failed') } const data = await res.json() setResult(data.result) setAssessmentId(data.assessment.id) setStep(6) loadHistory() // Match problems to triggered rules matchProblemsToResult(data.result.triggered_rules, data.result.required_controls) } catch (err) { setError(err instanceof Error ? err.message : 'Unknown error') } finally { setLoading(false) } } const generateExplanation = async () => { if (!assessmentId) return setGeneratingExplanation(true) try { const res = await fetch(`${SDK_BASE_URL}/ucca/assessments/${assessmentId}/explain`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Tenant-ID': getTenantId(), 'X-User-ID': getUserId(), }, body: JSON.stringify({ language: 'de' }), }) if (res.ok) { const data = await res.json() setExplanation(data.explanation_text) } } catch (err) { console.error('Failed to generate explanation:', err) } finally { setGeneratingExplanation(false) } } const exportAssessment = async (format: 'json' | 'md') => { if (!assessmentId) return const res = await fetch(`${SDK_BASE_URL}/ucca/export/${assessmentId}?format=${format}`, { headers: { 'X-Tenant-ID': getTenantId(), 'X-User-ID': getUserId(), } }) if (res.ok) { const blob = await res.blob() const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `ucca_assessment_${assessmentId.slice(0, 8)}.${format}` a.click() } } const startNew = () => { setStep(1) setIntake(initialIntake) setResult(null) setAssessmentId(null) setExplanation(null) setNormalModelChoice('search_only') } const loadAssessment = async (id: string) => { try { const res = await fetch(`${SDK_BASE_URL}/ucca/assessments/${id}`, { headers: { 'X-Tenant-ID': getTenantId(), 'X-User-ID': getUserId(), } }) if (res.ok) { const assessment = await res.json() setResult({ feasibility: assessment.feasibility, risk_level: assessment.risk_level, complexity: assessment.complexity, risk_score: assessment.risk_score, triggered_rules: assessment.triggered_rules || [], required_controls: assessment.required_controls || [], recommended_architecture: assessment.recommended_architecture || [], forbidden_patterns: assessment.forbidden_patterns || [], example_matches: assessment.example_matches || [], dsfa_recommended: assessment.dsfa_recommended, art22_risk: assessment.art22_risk, training_allowed: assessment.training_allowed, summary: '', recommendation: '', }) setAssessmentId(assessment.id) setExplanation(assessment.explanation_text || null) setStep(6) } } catch (err) { console.error('Failed to load assessment:', err) } } // Handle Normal mode model choice change const handleNormalModelChoice = (choiceId: string) => { setNormalModelChoice(choiceId) const choice = LABELS.modelUsage.normal.find(c => c.id === choiceId) if (choice) { setIntake({ ...intake, model_usage: choice.maps_to }) } } // ============================================================================ // Render Functions // ============================================================================ const renderModeToggle = () => (
Ansicht:
) const renderOverview = () => (

Advisory Board

📋 Dokumentation
{/* Quick Stats */}
{history.length}
Assessments gesamt
{history.filter(a => a.feasibility === 'YES').length}
Zulaessig
{history.filter(a => a.feasibility === 'CONDITIONAL').length}
Mit Auflagen
{history.filter(a => a.feasibility === 'NO').length}
Nicht zulaessig
{/* History */}

Bisherige Assessments

{loadingHistory ? (
Lade...
) : history.length === 0 ? (
Noch keine Assessments vorhanden. Starten Sie Ihr erstes Assessment!
) : (
{history.map((assessment) => (
loadAssessment(assessment.id)} >
{assessment.title}
{new Date(assessment.created_at).toLocaleDateString('de-DE')} - {assessment.domain}
{assessment.feasibility === 'YES' ? 'Zulaessig' : assessment.feasibility === 'CONDITIONAL' ? 'Mit Auflagen' : 'Nicht zulaessig'} Score: {assessment.risk_score}
))}
)}
) const renderStep1 = () => { const domains = LABELS.domains[mode] const domainSuggestions = suggestDomainsFromText(intake.use_case_text) const currentDomain = DOMAINS.find(d => d.value === intake.domain) return (

Schritt 1: {mode === 'normal' ? 'Was moechten Sie mit KI machen?' : 'Use Case beschreiben'}