'use client' import React, { useState, useEffect, useRef } from 'react' import { useSDK } from '@/lib/sdk' import { CompanyProfile, BusinessModel, OfferingType, TargetMarket, CompanySize, LegalForm, MachineBuilderProfile, MachineProductType, AIIntegrationType, HumanOversightLevel, CriticalSector, BUSINESS_MODEL_LABELS, OFFERING_TYPE_LABELS, TARGET_MARKET_LABELS, COMPANY_SIZE_LABELS, MACHINE_PRODUCT_TYPE_LABELS, AI_INTEGRATION_TYPE_LABELS, HUMAN_OVERSIGHT_LABELS, CRITICAL_SECTOR_LABELS, } from '@/lib/sdk/types' // ============================================================================= // WIZARD STEPS // ============================================================================= const BASE_WIZARD_STEPS = [ { id: 1, name: 'Basisinfos', description: 'Firmenname und Rechtsform' }, { id: 2, name: 'Geschaeftsmodell', description: 'B2B, B2C und Angebote' }, { id: 3, name: 'Firmengroesse', description: 'Mitarbeiter und Umsatz' }, { id: 4, name: 'Standorte', description: 'Hauptsitz und Zielmaerkte' }, { id: 5, name: 'Datenschutz', description: 'Rollen und DSB' }, { id: 6, name: 'Zertifizierungen & Kontakte', description: 'Bestehende und angestrebte Zertifizierungen' }, ] const MACHINE_BUILDER_STEP = { id: 7, name: 'Produkt & Maschine', description: 'Software, KI und CE in Ihrem Produkt' } function getWizardSteps(industry: string | string[]) { if (isMachineBuilderIndustry(industry)) { return [...BASE_WIZARD_STEPS, MACHINE_BUILDER_STEP] } return BASE_WIZARD_STEPS } // Keep WIZARD_STEPS for backwards compat in static references const WIZARD_STEPS = BASE_WIZARD_STEPS // ============================================================================= // LEGAL FORMS // ============================================================================= const LEGAL_FORM_LABELS: Record = { einzelunternehmen: 'Einzelunternehmen', gbr: 'GbR', ohg: 'OHG', kg: 'KG', gmbh: 'GmbH', ug: 'UG (haftungsbeschränkt)', ag: 'AG', gmbh_co_kg: 'GmbH & Co. KG', ev: 'e.V. (Verein)', stiftung: 'Stiftung', other: 'Sonstige', } // ============================================================================= // INDUSTRIES // ============================================================================= const INDUSTRIES = [ 'Technologie / IT', 'IT Dienstleistungen', 'E-Commerce / Handel', 'Finanzdienstleistungen', 'Versicherungen', 'Gesundheitswesen', 'Pharma', 'Bildung', 'Beratung / Consulting', 'Marketing / Agentur', 'Produktion / Industrie', 'Logistik / Transport', 'Immobilien', 'Bau', 'Energie', 'Automobil', 'Luft- und Raumfahrt', 'Maschinenbau', 'Anlagenbau', 'Automatisierung', 'Robotik', 'Messtechnik', 'Agrar', 'Chemie', 'Minen / Bergbau', 'Telekommunikation', 'Medien / Verlage', 'Gastronomie / Hotellerie', 'Recht / Kanzlei', 'Oeffentlicher Dienst', 'Sonstige', ] const MACHINE_BUILDER_INDUSTRIES = [ 'Maschinenbau', 'Anlagenbau', 'Automatisierung', 'Robotik', 'Messtechnik', ] const isMachineBuilderIndustry = (industry: string | string[]) => { const industries = Array.isArray(industry) ? industry : [industry] return industries.some(i => MACHINE_BUILDER_INDUSTRIES.includes(i)) } // ============================================================================= // STEP COMPONENTS // ============================================================================= function StepBasicInfo({ data, onChange, }: { data: Partial onChange: (updates: Partial) => void }) { return (
onChange({ companyName: e.target.value })} placeholder="Ihre Firma (ohne Rechtsform)" className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent" />

Mehrfachauswahl moeglich

{INDUSTRIES.map(ind => { const selected = (data.industry || []).includes(ind) return ( ) })}
{(data.industry || []).includes('Sonstige') && (
onChange({ industryOther: e.target.value })} placeholder="Ihre Branche eingeben..." className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent" />
)}
{ const val = parseInt(e.target.value) onChange({ foundedYear: isNaN(val) ? null : val }) }} onFocus={e => { if (!data.foundedYear) onChange({ foundedYear: 2000 }) }} placeholder="2020" min="1900" max={new Date().getFullYear()} className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent" />
) } // URL fields shown when specific offerings are selected const OFFERING_URL_CONFIG: Partial> = { website: { label: 'Website-Domain', placeholder: 'https://www.beispiel.de', hint: 'Ihre Unternehmenswebsite' }, webshop: { label: 'Online-Shop URL', placeholder: 'https://shop.beispiel.de', hint: 'URL zu Ihrem Online-Shop' }, app_mobile: { label: 'App-Store Links', placeholder: 'https://apps.apple.com/... oder https://play.google.com/...', hint: 'Apple App Store und/oder Google Play Store Link' }, software_saas: { label: 'SaaS-Portal URL', placeholder: 'https://app.beispiel.de', hint: 'Login-/Registrierungsseite Ihres Kundenportals' }, app_web: { label: 'Web-App URL', placeholder: 'https://app.beispiel.de', hint: 'URL zu Ihrer Web-Anwendung' }, } // Step-specific explanations for "Warum diese Fragen?" const STEP_EXPLANATIONS: Record = { 1: 'Rechtsform und Gründungsjahr bestimmen, welche Meldepflichten und Schwellenwerte für Ihr Unternehmen gelten (z.B. NIS2, AI Act).', 2: 'Ihr Geschäftsmodell und Ihre Angebote bestimmen, welche DSGVO-Pflichten greifen: B2C erfordert z.B. strengere Einwilligungsregeln, Webshops brauchen Cookie-Banner und Datenschutzerklärungen, SaaS-Angebote eine Auftragsverarbeitung.', 3: 'Die Unternehmensgröße bestimmt, ob Sie einen DSB benennen müssen (ab 20 MA), ob NIS2-Pflichten greifen und welche Audit-Anforderungen gelten.', 4: 'Standorte und Zielmärkte bestimmen, welche nationalen Datenschutzgesetze zusätzlich zur DSGVO greifen (z.B. BDSG, DSG-AT, UK GDPR, CCPA).', 5: 'Ob Sie Verantwortlicher oder Auftragsverarbeiter sind, bestimmt Ihre DSGVO-Pflichten grundlegend.', 6: 'Regulierungsrahmen und Prüfzyklen definieren, welche Compliance-Module für Sie aktiviert werden und in welchem Rhythmus Audits stattfinden.', 7: 'Als Maschinenbauer gelten zusätzliche Anforderungen: CE-Kennzeichnung, Maschinenverordnung, Produktsicherheit und ggf. Hochrisiko-KI im Sinne des AI Act.', } function StepBusinessModel({ data, onChange, }: { data: Partial onChange: (updates: Partial) => void }) { const toggleOffering = (offering: OfferingType) => { const current = data.offerings || [] if (current.includes(offering)) { // Remove offering and its URL const urls = { ...(data.offeringUrls || {}) } delete urls[offering] onChange({ offerings: current.filter(o => o !== offering), offeringUrls: urls }) } else { onChange({ offerings: [...current, offering] }) } } const updateOfferingUrl = (offering: string, url: string) => { onChange({ offeringUrls: { ...(data.offeringUrls || {}), [offering]: url } }) } // Offerings that are selected and have URL config const selectedWithUrls = (data.offerings || []).filter(o => o in OFFERING_URL_CONFIG) return (
{Object.entries(BUSINESS_MODEL_LABELS).map(([value, { short }]) => ( ))}
{data.businessModel && (

{BUSINESS_MODEL_LABELS[data.businessModel].description}

)}
{Object.entries(OFFERING_TYPE_LABELS).map(([value, { label, description }]) => ( ))}
{/* Hint when both webshop and SaaS are selected */} {(data.offerings || []).includes('webshop') && (data.offerings || []).includes('software_saas') && (

Hinweis: Wenn Sie reine Software verkaufen, genuegt SaaS/CloudOnline-Shop ist nur fuer physische Produkte oder Hardware mit Abo-Modell gedacht.

)}
{/* URL fields for selected offerings */} {selectedWithUrls.length > 0 && (
{selectedWithUrls.map(offering => { const config = OFFERING_URL_CONFIG[offering]! return (
updateOfferingUrl(offering, e.target.value)} placeholder={config.placeholder} className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent" />

{config.hint}

) })}
)}
) } function StepCompanySize({ data, onChange, }: { data: Partial onChange: (updates: Partial) => void }) { return (
{Object.entries(COMPANY_SIZE_LABELS).map(([value, label]) => ( ))}
{[ { value: '< 2 Mio', label: '< 2 Mio. Euro' }, { value: '2-10 Mio', label: '2-10 Mio. Euro' }, { value: '10-50 Mio', label: '10-50 Mio. Euro' }, { value: '> 50 Mio', label: '> 50 Mio. Euro' }, ].map(opt => ( ))}
{(data.companySize === 'medium' || data.companySize === 'large' || data.companySize === 'enterprise') && (

Geben Sie den konsolidierten Konzernumsatz an, wenn der Compliance-Check für Mutter- und Tochtergesellschaften gelten soll. Für eine einzelne Einheit eines Konzerns geben Sie nur deren Umsatz an.

)}
) } function StepLocations({ data, onChange, }: { data: Partial onChange: (updates: Partial) => void }) { const toggleMarket = (market: TargetMarket) => { const current = data.targetMarkets || [] if (current.includes(market)) { onChange({ targetMarkets: current.filter(m => m !== market) }) } else { onChange({ targetMarkets: [...current, market] }) } } const STATES_BY_COUNTRY: Record = { DE: { label: 'Bundesland', options: [ 'Baden-Württemberg', 'Bayern', 'Berlin', 'Brandenburg', 'Bremen', 'Hamburg', 'Hessen', 'Mecklenburg-Vorpommern', 'Niedersachsen', 'Nordrhein-Westfalen', 'Rheinland-Pfalz', 'Saarland', 'Sachsen', 'Sachsen-Anhalt', 'Schleswig-Holstein', 'Thüringen', ], }, AT: { label: 'Bundesland', options: [ 'Burgenland', 'Kärnten', 'Niederösterreich', 'Oberösterreich', 'Salzburg', 'Steiermark', 'Tirol', 'Vorarlberg', 'Wien', ], }, CH: { label: 'Kanton', options: [ 'Aargau', 'Appenzell Ausserrhoden', 'Appenzell Innerrhoden', 'Basel-Landschaft', 'Basel-Stadt', 'Bern', 'Freiburg', 'Genf', 'Glarus', 'Graubünden', 'Jura', 'Luzern', 'Neuenburg', 'Nidwalden', 'Obwalden', 'Schaffhausen', 'Schwyz', 'Solothurn', 'St. Gallen', 'Tessin', 'Thurgau', 'Uri', 'Waadt', 'Wallis', 'Zug', 'Zürich', ], }, } const countryStates = data.headquartersCountry ? STATES_BY_COUNTRY[data.headquartersCountry] : null const stateLabel = countryStates?.label || 'Region / Provinz' return (
{/* Country */}
{/* Other country free text */} {data.headquartersCountry === 'other' && (
onChange({ headquartersCountryOther: e.target.value })} placeholder="z.B. Vereinigtes Königreich" className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent" />
)} {/* Street + House Number */}
onChange({ headquartersStreet: e.target.value })} placeholder="Musterstraße 42" className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent" />
{/* PLZ + City */}
onChange({ headquartersZip: e.target.value })} placeholder="10115" className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent" />
onChange({ headquartersCity: e.target.value })} placeholder="Berlin" className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent" />
{/* State / Bundesland / Kanton */}
{countryStates ? ( ) : ( onChange({ headquartersState: e.target.value })} placeholder="Region / Provinz" className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent" /> )}
{Object.entries(TARGET_MARKET_LABELS).map(([value, { label, description }]) => ( ))}
) } function StepDataProtection({ data, onChange, }: { data: Partial onChange: (updates: Partial) => void }) { return (
onChange({ dpoName: e.target.value || null })} placeholder="Optional" className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent" />
onChange({ dpoEmail: e.target.value || null })} placeholder="dsb@firma.de" className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent" />
) } // ============================================================================= // STEP 6: RECHTLICHER RAHMEN (was Step 8) // ============================================================================= // NOTE: Verarbeitungstaetigkeiten (former Step 6) and KI-Systeme (former Step 7) // have been moved to Compliance Scope (Blocks 7 and 8). // DSGVO-Standard Datenkategorien — kept as shared reference for scope const ALL_DATA_CATEGORIES = [ { id: 'stammdaten', label: 'Stammdaten', desc: 'Name, Geburtsdatum, Geschlecht', info: 'Vor- und Nachname, Geburtsdatum, Geschlecht, Anrede, Titel, Familienstand, Staatsangehörigkeit, Personalnummer, Kundennummer' }, { id: 'kontaktdaten', label: 'Kontaktdaten', desc: 'E-Mail, Telefon, Adresse', info: 'E-Mail-Adresse, Telefonnummer, Mobilnummer, Postanschrift, Faxnummer, Messenger-IDs der betroffenen Personen' }, { id: 'vertragsdaten', label: 'Vertragsdaten', desc: 'Vertragsnummer, Laufzeit, Konditionen', info: 'Vertragsnummer, Vertragsbeginn/-ende, Laufzeit, Konditionen, Kündigungsfristen, Vertragsgegenstand, Bestellhistorie' }, { id: 'zahlungsdaten', label: 'Zahlungs-/Bankdaten', desc: 'IBAN, Kreditkarte, Rechnungen', info: 'IBAN, BIC, Kontoinhaber, Kreditkartennummer, Rechnungsbeträge, Zahlungshistorie, Steuer-ID, USt-IdNr.' }, { id: 'beschaeftigtendaten', label: 'Beschäftigtendaten', desc: 'Gehalt, Arbeitszeiten, Urlaub', info: 'Gehalt/Lohn, Steuerklasse, SV-Nummer, Krankenkasse (z.B. AOK, TK), Arbeitszeiten, Urlaubstage, Abwesenheiten, Beurteilungen, Eintrittsdatum. Aufbewahrung: i.d.R. 3 Jahre nach Austritt (§ 195 BGB), Lohndaten 8 Jahre (§ 147 AO)' }, { id: 'kommunikation', label: 'Kommunikationsdaten', desc: 'E-Mail-Inhalte, Chat-Verläufe', info: 'E-Mail-Inhalte und -Metadaten, Chat-Nachrichten, Gesprächsprotokolle, Support-Tickets, Briefkorrespondenz' }, { id: 'nutzungsdaten', label: 'Nutzungs-/Logdaten', desc: 'IP-Adressen, Login-Zeiten, Klicks', info: 'IP-Adressen, Login-Zeitpunkte, Seitenaufrufe, Klickverhalten, Geräteinformationen, Browser-Typ, Session-Dauer' }, { id: 'standortdaten', label: 'Standortdaten', desc: 'GPS, Check-in, Lieferadressen', info: 'GPS-Koordinaten, Check-in/Check-out-Zeiten, Lieferadressen, Reiserouten, WLAN-Standortbestimmung' }, { id: 'bilddaten', label: 'Bild-/Videodaten', desc: 'Fotos, Videoaufnahmen, Profilbilder', info: 'Profilfotos, Ausweiskopien, Videoaufnahmen (Überwachung), Bewerbungsfotos, Schulungsvideos' }, { id: 'bewerberdaten', label: 'Bewerberdaten', desc: 'Lebenslauf, Zeugnisse, Anschreiben', info: 'Lebenslauf, Anschreiben, Zeugnisse, Referenzen, Gehaltsvorstellungen, Verfügbarkeit, Bewerbungsquelle. Löschfrist bei Absage: max. 6 Monate (AGG §§ 15, 21)' }, { id: 'qualifikationsdaten', label: 'Qualifikations-/Schulungsdaten', desc: 'Fortbildungen, Zertifikate, Abschlüsse', info: 'Besuchte Seminare und Schulungen, Zertifikate, Abschlüsse, Qualifikationsnachweise, Schulungsdaten und -ergebnisse, Weiterbildungshistorie' }, ] as const const ALL_SPECIAL_CATEGORIES = [ { id: 'gesundheit', label: 'Gesundheitsdaten', desc: 'Krankheitstage, Atteste, Diagnosen', info: 'Krankheitstage, AU-Bescheinigungen, Diagnosen, Behinderungsgrad (GdB), BEM-Daten, arbeitsmedizinische Untersuchungen, Impfstatus, Allergien. Auch AU ohne Diagnose = Gesundheitsdatum (LDI NRW). Schwangerschaft, Allergien, Online-Arzneimittelbestellung (EuGH C-21/23). NICHT: Krankenkassenname (z.B. AOK, TK) — das sind normale Beschäftigtendaten.' }, { id: 'biometrie', label: 'Biometrische Daten', desc: 'Fingerabdruck, Gesichtserkennung', info: 'Fingerabdruck, Gesichtserkennung, Iris-Scan, Stimmerkennung, Handvenenscan. Nur wenn zur eindeutigen Identifizierung verwendet (ErwGr. 51). Einfaches Passfoto = kein biometrisches Datum.' }, { id: 'religion', label: 'Religion', desc: 'Konfession, Kirchensteuer', info: 'Konfession/Religionszugehörigkeit (relevant für Kirchensteuer auf Lohnabrechnung). Auch indirekt: Kantinenbestellung halal/koscher (EuGH C-184/20 weite Auslegung). Praktisch jedes Unternehmen mit Beschäftigten verarbeitet diese Daten über die Gehaltsabrechnung.' }, { id: 'gewerkschaft', label: 'Gewerkschaftszugehörigkeit', desc: 'Mitgliedschaft', info: 'Gewerkschaftsmitgliedschaft, Betriebsratszugehörigkeit, Tarifzugehörigkeit' }, { id: 'genetik', label: 'Genetische Daten', desc: 'DNA, Erbkrankheiten', info: 'DNA-Analysen, genetische Prädispositionen, Erbkrankheitsrisiken (nur in Spezialfällen relevant)' }, ] as const // Verarbeitungstätigkeiten mit aktivitätsspezifischen Datenkategorien // Hinweis: Rechtsgrundlage (legal_basis) wird hier im Profil nicht angezeigt — // die juristische Zuordnung erfolgt im VVT-Schritt (Art. 30 DSGVO). interface ActivityTemplate { id: string name: string purpose: string primary_categories: string[] // Sichtbar + vorausgewählt art9_relevant: string[] // Art. 9 Kategorien die plausibel relevant sind default_legal_basis: string legalHint?: string // Gesetzlicher Hinweis (z.B. ArbZG-Pflicht) hasServiceProvider?: boolean // Kann ein externer Dienstleister eingesetzt werden? categoryInfo?: Record // Override Info-Text pro Datenkategorie (kontextspezifisch) } interface ActivityDepartment { id: string name: string icon: string activities: ActivityTemplate[] } // ── Universelle Abteilungen (immer sichtbar) ── const UNIVERSAL_DEPARTMENTS: ActivityDepartment[] = [ { id: 'personal', name: 'Personal / HR', icon: '👥', activities: [ { id: 'personalverwaltung', name: 'Personalverwaltung', purpose: 'Verwaltung von Beschäftigtendaten für das Arbeitsverhältnis', primary_categories: ['stammdaten', 'kontaktdaten', 'beschaeftigtendaten', 'zahlungsdaten'], art9_relevant: ['gesundheit', 'religion', 'gewerkschaft'], default_legal_basis: 'contract', categoryInfo: { stammdaten: 'Vor-/Nachname, Geburtsdatum, Geschlecht, Familienstand, Staatsangehörigkeit, Personalnummer', kontaktdaten: 'Privat- und Dienstadresse, Telefonnummern, dienstliche E-Mail, Notfallkontakt', beschaeftigtendaten: 'Steuerklasse, SV-Nummer, Krankenkasse (z.B. AOK, TK — kein Gesundheitsdatum!), Eintrittsdatum, Arbeitszeit, Urlaubstage. Aufbewahrung: 3 Jahre nach Austritt (§ 195 BGB)', zahlungsdaten: 'IBAN für Gehaltsauszahlung, Vermögenswirksame Leistungen, Pfändungsdaten' } }, { id: 'lohnbuchhaltung', name: 'Lohn- und Gehaltsabrechnung', purpose: 'Berechnung und Auszahlung von Löhnen und Gehältern', primary_categories: ['beschaeftigtendaten', 'zahlungsdaten', 'stammdaten'], art9_relevant: [], default_legal_basis: 'legal', hasServiceProvider: true, categoryInfo: { beschaeftigtendaten: 'Gehalt, Zulagen, Prämien, Steuerklasse, SV-Nummer, Krankenkasse, Kirchensteuermerkmal. Aufbewahrung: Lohnabrechnungen 8 Jahre (§ 147 AO), Lohnsteuer 6 Jahre (§ 41 EStG). Hinweis: Gesundheits- und Religionsdaten werden bereits unter Personalverwaltung als Art. 9-Kategorien erfasst.', zahlungsdaten: 'IBAN, Bankverbindung, Gehaltsabrechnungen, Pfändungsbeträge. Aufbewahrung: 8 Jahre (§ 147 AO)' } }, { id: 'bewerbermanagement', name: 'Bewerbermanagement', purpose: 'Entgegennahme, Prüfung und Bearbeitung von Bewerbungen', primary_categories: ['bewerberdaten', 'stammdaten', 'kontaktdaten', 'kommunikation', 'qualifikationsdaten'], art9_relevant: ['gesundheit', 'religion'], default_legal_basis: 'consent', categoryInfo: { bewerberdaten: 'Lebenslauf, Anschreiben, Zeugnisse, Referenzen, Gehaltsvorstellungen. Löschfrist bei Absage: max. 6 Monate (AGG §§ 15, 21)', kontaktdaten: 'Privatadresse, E-Mail, Telefonnummer des Bewerbers', kommunikation: 'Bewerbungskorrespondenz, Einladungen, Absageschreiben' } }, { id: 'arbeitszeiterfassung', name: 'Arbeitszeiterfassung', purpose: 'Erfassung und Dokumentation der Arbeitszeiten', primary_categories: ['beschaeftigtendaten'], art9_relevant: [], default_legal_basis: 'legal', legalHint: 'Gesetzlich vorgeschrieben (§ 3 ArbZG). Fehlende Arbeitszeiterfassung ist ein Compliance-Risiko.', categoryInfo: { beschaeftigtendaten: 'Beginn/Ende der Arbeitszeit, Pausen, Überstunden, Ruhezeiten. Aufbewahrung: mind. 2 Jahre (§ 16 Abs. 2 ArbZG). Nicht für Leistungskontrolle verwenden!' } }, { id: 'weiterbildung', name: 'Fort- und Weiterbildung', purpose: 'Verwaltung von Schulungen und Weiterbildungsmaßnahmen', primary_categories: ['qualifikationsdaten', 'beschaeftigtendaten', 'stammdaten'], art9_relevant: [], default_legal_basis: 'contract' }, ], }, { id: 'finanzen', name: 'Finanzen / Buchhaltung', icon: '💰', activities: [ { id: 'finanzbuchhaltung', name: 'Finanzbuchhaltung', purpose: 'Buchführung, Rechnungsstellung, steuerliche Dokumentation', primary_categories: ['stammdaten', 'zahlungsdaten', 'vertragsdaten', 'kontaktdaten'], art9_relevant: [], default_legal_basis: 'legal', categoryInfo: { zahlungsdaten: 'Rechnungsbeträge, IBAN, Buchungsbelege, USt-IdNr. Aufbewahrung: 8 Jahre (§ 147 AO)', vertragsdaten: 'Vertragsnummer, Konditionen, Bestellhistorie. Aufbewahrung: Handelskorrespondenz 6 Jahre (§ 257 HGB)', kontaktdaten: 'Rechnungsadresse, Ansprechpartner in der Debitorenbuchhaltung' } }, { id: 'zahlungsverkehr', name: 'Zahlungsverkehr', purpose: 'Abwicklung von ein- und ausgehenden Zahlungen', primary_categories: ['zahlungsdaten', 'stammdaten', 'kontaktdaten', 'vertragsdaten'], art9_relevant: [], default_legal_basis: 'contract' }, { id: 'mahnwesen', name: 'Mahnwesen / Inkasso', purpose: 'Überwachung offener Forderungen und Mahnverfahren', primary_categories: ['stammdaten', 'kontaktdaten', 'zahlungsdaten', 'vertragsdaten'], art9_relevant: [], default_legal_basis: 'interest' }, { id: 'reisekostenabrechnung', name: 'Reisekostenabrechnung', purpose: 'Abrechnung und Erstattung von Dienstreisekosten', primary_categories: ['beschaeftigtendaten', 'zahlungsdaten', 'standortdaten'], art9_relevant: [], default_legal_basis: 'contract' }, ], }, { id: 'vertrieb', name: 'Vertrieb / Sales', icon: '📈', activities: [ { id: 'crm', name: 'CRM / Kundenverwaltung', purpose: 'Verwaltung von Kundenbeziehungen, Kontakthistorie, Verkaufschancen', primary_categories: ['stammdaten', 'kontaktdaten', 'kommunikation', 'vertragsdaten'], art9_relevant: [], default_legal_basis: 'contract', categoryInfo: { stammdaten: 'Firmenname, Ansprechpartner-Name, Titel, Position, Kundennummer', kontaktdaten: 'Geschäftliche E-Mail, Telefon, Büroadresse des Ansprechpartners. B2B-Kontaktdaten sind personenbezogene Daten — Art. 13 DSGVO Informationspflicht gilt!', kommunikation: 'E-Mail-Korrespondenz, Gesprächsnotizen, Support-Tickets, Meeting-Protokolle' } }, { id: 'angebotserstellung', name: 'Angebotserstellung', purpose: 'Erstellung und Nachverfolgung von Angeboten', primary_categories: ['stammdaten', 'kontaktdaten', 'vertragsdaten'], art9_relevant: [], default_legal_basis: 'contract' }, { id: 'vertragsmanagement', name: 'Vertragsmanagement', purpose: 'Verwaltung, Archivierung und Nachverfolgung von Verträgen', primary_categories: ['vertragsdaten', 'stammdaten', 'kontaktdaten'], art9_relevant: [], default_legal_basis: 'contract' }, ], }, { id: 'marketing', name: 'Marketing', icon: '📣', activities: [ { id: 'newsletter', name: 'Newsletter / E-Mail-Marketing', purpose: 'Versand von Newslettern und E-Mail-Marketing an Abonnenten', primary_categories: ['kontaktdaten', 'nutzungsdaten', 'stammdaten'], art9_relevant: [], default_legal_basis: 'consent' }, { id: 'website_tracking', name: 'Website-Tracking / Analytics', purpose: 'Analyse des Nutzerverhaltens auf der Website mittels Tracking-Tools', primary_categories: ['nutzungsdaten', 'standortdaten'], art9_relevant: [], default_legal_basis: 'consent' }, { id: 'social_media', name: 'Social-Media-Marketing', purpose: 'Betrieb von Unternehmensprofilen und Werbekampagnen', primary_categories: ['kontaktdaten', 'nutzungsdaten', 'kommunikation'], art9_relevant: [], default_legal_basis: 'consent' }, { id: 'consent_management', name: 'Consent-Management (Cookies)', purpose: 'Verwaltung der Einwilligungen für Cookies und Tracking', primary_categories: ['nutzungsdaten'], art9_relevant: [], default_legal_basis: 'consent' }, ], }, { id: 'it', name: 'IT / Administration', icon: '🖥️', activities: [ { id: 'zugangsverwaltung', name: 'Zugangsverwaltung (IAM)', purpose: 'Verwaltung von Benutzerkonten, Passwörtern und Zugriffsrechten', primary_categories: ['beschaeftigtendaten', 'stammdaten', 'nutzungsdaten'], art9_relevant: ['biometrie'], default_legal_basis: 'contract' }, { id: 'email_kommunikation', name: 'E-Mail-Kommunikation', purpose: 'Geschäftliche E-Mail-Korrespondenz', primary_categories: ['kontaktdaten', 'kommunikation', 'stammdaten'], art9_relevant: [], default_legal_basis: 'interest' }, { id: 'datensicherung', name: 'Datensicherung / Backup', purpose: 'Sicherung von Unternehmensdaten zum Schutz vor Datenverlust', primary_categories: ['nutzungsdaten', 'beschaeftigtendaten'], art9_relevant: [], default_legal_basis: 'interest' }, { id: 'website_betrieb', name: 'Website-Betrieb', purpose: 'Bereitstellung der Unternehmenswebsite und Kontaktformulare', primary_categories: ['nutzungsdaten', 'kontaktdaten', 'stammdaten'], art9_relevant: [], default_legal_basis: 'interest', hasServiceProvider: true, legalHint: 'Bei externem Website-Management: AVV nach Art. 28 DSGVO mit dem Dienstleister erforderlich. Cookies, Analytics und Kontaktformulare verarbeiten personenbezogene Daten — auch wenn der Dienstleister sie technisch betreibt, bleibt Ihr Unternehmen verantwortlich.' }, { id: 'it_sicherheit', name: 'IT-Sicherheit / Logging', purpose: 'Überwachung der IT-Sicherheit, Log-Analyse, Vorfallbehandlung', primary_categories: ['nutzungsdaten', 'beschaeftigtendaten'], art9_relevant: [], default_legal_basis: 'interest' }, ], }, { id: 'recht', name: 'Recht / Compliance', icon: '⚖️', activities: [ { id: 'datenschutzanfragen', name: 'Betroffenenrechte (DSGVO)', purpose: 'Bearbeitung von Auskunfts-, Lösch- und Berichtigungsanfragen', primary_categories: ['stammdaten', 'kontaktdaten', 'kommunikation'], art9_relevant: [], default_legal_basis: 'legal' }, { id: 'auftragsverarbeitung', name: 'Auftragsverarbeitung (AVV)', purpose: 'Dokumentation und Verwaltung von Auftragsverarbeitungsverhältnissen', primary_categories: ['stammdaten', 'kontaktdaten', 'vertragsdaten'], art9_relevant: [], default_legal_basis: 'legal' }, { id: 'whistleblowing', name: 'Hinweisgebersystem', purpose: 'Entgegennahme und Bearbeitung von Meldungen nach HinSchG', primary_categories: ['stammdaten', 'kontaktdaten', 'kommunikation'], art9_relevant: [], default_legal_basis: 'legal', categoryInfo: { stammdaten: 'Identität des Hinweisgebers (besonders schützenswert! § 8 HinSchG Vertraulichkeitsgebot)', kontaktdaten: 'Kontaktdaten nur für zuständige Meldestelle zugänglich', kommunikation: 'Meldungsinhalt, Kommunikationsverlauf, Zeugenaussagen. Löschfrist: 3 Jahre nach Abschluss (§ 11 Abs. 5 HinSchG)' } }, ], }, ] // ── Abteilungen die je nach Kontext relevant sind ── const OPTIONAL_DEPARTMENTS: ActivityDepartment[] = [ { id: 'einkauf', name: 'Einkauf / Beschaffung', icon: '🛒', activities: [ { id: 'lieferantenverwaltung', name: 'Lieferantenverwaltung', purpose: 'Erfassung und Pflege von Lieferantenstammdaten', primary_categories: ['stammdaten', 'kontaktdaten', 'vertragsdaten', 'zahlungsdaten'], art9_relevant: [], default_legal_basis: 'contract' }, { id: 'bestellwesen', name: 'Bestellwesen', purpose: 'Abwicklung von Bestellungen bei Lieferanten', primary_categories: ['stammdaten', 'vertragsdaten', 'zahlungsdaten'], art9_relevant: [], default_legal_basis: 'contract' }, { id: 'lieferantenbewertung', name: 'Lieferantenbewertung', purpose: 'Bewertung und Qualifizierung von Lieferanten', primary_categories: ['stammdaten', 'kontaktdaten'], art9_relevant: [], default_legal_basis: 'interest' }, ], }, { id: 'produktion', name: 'Produktion / Fertigung', icon: '🏭', activities: [ { id: 'produktionsplanung', name: 'Produktionsplanung', purpose: 'Planung und Steuerung von Produktionsprozessen inkl. Personalzuordnung', primary_categories: ['beschaeftigtendaten', 'stammdaten'], art9_relevant: [], default_legal_basis: 'contract' }, { id: 'qualitaetskontrolle', name: 'Qualitätskontrolle', purpose: 'Prüfung und Dokumentation der Produktqualität', primary_categories: ['beschaeftigtendaten', 'stammdaten'], art9_relevant: [], default_legal_basis: 'contract' }, { id: 'arbeitssicherheit', name: 'Arbeitssicherheit / Arbeitsschutz', purpose: 'Dokumentation von Arbeitsschutzmaßnahmen, Unfällen, Gefährdungsbeurteilungen', primary_categories: ['beschaeftigtendaten'], art9_relevant: ['gesundheit'], default_legal_basis: 'legal' }, { id: 'schichtplanung', name: 'Schichtplanung', purpose: 'Erstellung und Verwaltung von Schichtplänen', primary_categories: ['beschaeftigtendaten'], art9_relevant: [], default_legal_basis: 'contract' }, ], }, { id: 'logistik', name: 'Logistik / Versand', icon: '🚚', activities: [ { id: 'versandabwicklung', name: 'Versandabwicklung', purpose: 'Verarbeitung von Empfänger- und Versanddaten für den Warenversand', primary_categories: ['stammdaten', 'kontaktdaten', 'standortdaten'], art9_relevant: [], default_legal_basis: 'contract' }, { id: 'lieferverfolgung', name: 'Lieferverfolgung / Sendungstracking', purpose: 'Nachverfolgung von Sendungen und Zustellung', primary_categories: ['stammdaten', 'kontaktdaten', 'standortdaten'], art9_relevant: [], default_legal_basis: 'contract' }, { id: 'lagerverwaltung', name: 'Lagerverwaltung', purpose: 'Verwaltung von Lagerbeständen und Warenbewegungen', primary_categories: ['stammdaten', 'beschaeftigtendaten'], art9_relevant: [], default_legal_basis: 'contract' }, { id: 'retouren', name: 'Retourenmanagement', purpose: 'Bearbeitung von Warenrücksendungen', primary_categories: ['stammdaten', 'kontaktdaten', 'vertragsdaten'], art9_relevant: [], default_legal_basis: 'contract' }, ], }, { id: 'kundenservice', name: 'Kundenservice / Support', icon: '🎧', activities: [ { id: 'ticketsystem', name: 'Ticketsystem / Support', purpose: 'Erfassung und Bearbeitung von Kundenanfragen und Supportfällen', primary_categories: ['stammdaten', 'kontaktdaten', 'kommunikation'], art9_relevant: [], default_legal_basis: 'contract' }, { id: 'beschwerdemanagement', name: 'Beschwerdemanagement', purpose: 'Bearbeitung und Dokumentation von Kundenbeschwerden', primary_categories: ['stammdaten', 'kontaktdaten', 'kommunikation'], art9_relevant: [], default_legal_basis: 'contract' }, ], }, { id: 'facility', name: 'Facility Management', icon: '🏢', activities: [ { id: 'zutrittskontrolle', name: 'Zutrittskontrolle', purpose: 'Kontrolle und Protokollierung des Zutritts zu Gebäuden und Räumen', primary_categories: ['beschaeftigtendaten', 'stammdaten', 'bilddaten'], art9_relevant: ['biometrie'], default_legal_basis: 'interest' }, { id: 'videoueberwachung', name: 'Videoüberwachung', purpose: 'Überwachung von Gebäuden und Geländen mittels Videokameras', primary_categories: ['bilddaten', 'beschaeftigtendaten'], art9_relevant: ['biometrie'], default_legal_basis: 'interest', categoryInfo: { bilddaten: 'Videoaufzeichnungen von Kameras. Speicherdauer: empfohlen max. 72h (BeschDG-Entwurf). Datenschutzhinweis-Schilder (Art. 13 DSGVO) sind Pflicht. Betriebsrat hat Mitbestimmungsrecht (§ 87 Abs. 1 Nr. 6 BetrVG)' } }, { id: 'besuchermanagement', name: 'Besuchermanagement', purpose: 'Erfassung und Verwaltung von Besucherdaten', primary_categories: ['stammdaten', 'kontaktdaten'], art9_relevant: [], default_legal_basis: 'interest' }, ], }, ] // ── Branchenspezifische Abteilungen ── const INDUSTRY_DEPARTMENTS: Record = { 'E-Commerce / Handel': [{ id: 'ecommerce', name: 'E-Commerce / Webshop', icon: '🛍️', activities: [ { id: 'bestellabwicklung', name: 'Bestellabwicklung (Webshop)', purpose: 'Verarbeitung von Kundenbestellungen im Online-Shop', primary_categories: ['stammdaten', 'kontaktdaten', 'zahlungsdaten', 'vertragsdaten'], art9_relevant: [], default_legal_basis: 'contract' }, { id: 'kundenkonto', name: 'Kundenkonto-Verwaltung', purpose: 'Verwaltung registrierter Kundenkonten im Online-Shop', primary_categories: ['stammdaten', 'kontaktdaten', 'nutzungsdaten', 'zahlungsdaten'], art9_relevant: [], default_legal_basis: 'contract' }, { id: 'webshop_analyse', name: 'Webshop-Analyse / Conversion', purpose: 'Analyse des Kaufverhaltens und Conversion-Rates', primary_categories: ['nutzungsdaten', 'standortdaten'], art9_relevant: [], default_legal_basis: 'consent' }, { id: 'produktbewertungen', name: 'Produktbewertungen / Reviews', purpose: 'Verwaltung von Kundenrezensionen und Produktbewertungen', primary_categories: ['stammdaten', 'kontaktdaten', 'kommunikation'], art9_relevant: [], default_legal_basis: 'consent' }, ], }], 'Gesundheitswesen': [{ id: 'gesundheit_dept', name: 'Medizin / Patientenversorgung', icon: '🏥', activities: [ { id: 'patientenverwaltung', name: 'Patientenverwaltung', purpose: 'Verwaltung von Patientenstammdaten und Krankengeschichte', primary_categories: ['stammdaten', 'kontaktdaten', 'zahlungsdaten'], art9_relevant: ['gesundheit', 'genetik'], default_legal_basis: 'contract' }, { id: 'terminplanung_med', name: 'Terminplanung (Patienten)', purpose: 'Vergabe und Verwaltung von Patiententerminen', primary_categories: ['stammdaten', 'kontaktdaten'], art9_relevant: ['gesundheit'], default_legal_basis: 'contract' }, { id: 'kv_abrechnung', name: 'KV-Abrechnung', purpose: 'Abrechnung von Leistungen gegenüber Kassenärztlichen Vereinigungen', primary_categories: ['stammdaten', 'zahlungsdaten'], art9_relevant: ['gesundheit'], default_legal_basis: 'legal' }, { id: 'med_dokumentation', name: 'Medizinische Dokumentation', purpose: 'Dokumentation von Diagnosen, Therapien und Behandlungsverläufen', primary_categories: ['stammdaten'], art9_relevant: ['gesundheit', 'genetik'], default_legal_basis: 'legal' }, ], }], 'Finanzdienstleistungen': [{ id: 'finanz_dept', name: 'Regulatorik / Finanzaufsicht', icon: '🏦', activities: [ { id: 'kyc', name: 'Know Your Customer (KYC)', purpose: 'Identifizierung und Verifizierung von Kunden gemäß GwG', primary_categories: ['stammdaten', 'kontaktdaten', 'bilddaten'], art9_relevant: [], default_legal_basis: 'legal' }, { id: 'kontoverwaltung', name: 'Kontoverwaltung', purpose: 'Verwaltung von Kundenkonten und Kontobewegungen', primary_categories: ['stammdaten', 'kontaktdaten', 'zahlungsdaten'], art9_relevant: [], default_legal_basis: 'contract' }, { id: 'geldwaeschepraevention', name: 'Geldwäscheprävention (AML)', purpose: 'Überwachung verdächtiger Transaktionen nach GwG', primary_categories: ['stammdaten', 'zahlungsdaten'], art9_relevant: [], default_legal_basis: 'legal' }, ], }], 'Bildung': [{ id: 'bildung_dept', name: 'Bildung / Lehre', icon: '🎓', activities: [ { id: 'schuelerverwaltung', name: 'Schüler-/Teilnehmerverwaltung', purpose: 'Verwaltung von Lernenden, Noten, Anwesenheit', primary_categories: ['stammdaten', 'kontaktdaten', 'nutzungsdaten'], art9_relevant: ['gesundheit', 'religion'], default_legal_basis: 'contract' }, { id: 'lernplattform', name: 'Lernplattform / LMS', purpose: 'Bereitstellung und Nutzung digitaler Lernplattformen', primary_categories: ['stammdaten', 'nutzungsdaten', 'kommunikation'], art9_relevant: [], default_legal_basis: 'contract' }, { id: 'pruefungsverwaltung', name: 'Prüfungsverwaltung', purpose: 'Verwaltung und Dokumentation von Prüfungen und Noten', primary_categories: ['stammdaten'], art9_relevant: [], default_legal_basis: 'contract' }, ], }], 'Immobilien': [{ id: 'immobilien_dept', name: 'Immobilienverwaltung', icon: '🏠', activities: [ { id: 'mieterverwaltung', name: 'Mieterverwaltung', purpose: 'Verwaltung von Mietverträgen und Mieterdaten', primary_categories: ['stammdaten', 'kontaktdaten', 'vertragsdaten', 'zahlungsdaten'], art9_relevant: [], default_legal_basis: 'contract' }, { id: 'nebenkostenabrechnung', name: 'Nebenkostenabrechnung', purpose: 'Erstellung und Versand von Nebenkostenabrechnungen', primary_categories: ['stammdaten', 'zahlungsdaten'], art9_relevant: [], default_legal_basis: 'contract' }, ], }], } // Compute which departments to show based on company context function getRelevantDepartments(industry: string | string[], businessModel: string | undefined, companySize: string | undefined): ActivityDepartment[] { const departments: ActivityDepartment[] = [...UNIVERSAL_DEPARTMENTS] // Always show optional departments — user can choose departments.push(...OPTIONAL_DEPARTMENTS) // Add industry-specific departments (support multi-select) const industries = Array.isArray(industry) ? industry : [industry] const addedIds = new Set() for (const ind of industries) { const industryDepts = INDUSTRY_DEPARTMENTS[ind] if (industryDepts) { for (const dept of industryDepts) { if (!addedIds.has(dept.id)) { addedIds.add(dept.id) departments.push(dept) } } } } return departments } interface ProcessingActivity { id: string name: string purpose: string data_categories: string[] legal_basis: string department?: string custom?: boolean usesServiceProvider?: boolean serviceProviderName?: string } interface AISystem { id: string name: string vendor: string purpose: string purposes?: string[] processes_personal_data: boolean isCustom?: boolean notes?: string } // Helper: find template for an activity ID across all departments function findTemplate(departments: ActivityDepartment[], activityId: string): ActivityTemplate | null { for (const dept of departments) { const t = dept.activities.find(a => a.id === activityId) if (t) return t } return null } function StepProcessing({ data, onChange, }: { data: Partial & { processingSystems?: ProcessingActivity[] } onChange: (updates: Record) => void }) { const activities: ProcessingActivity[] = (data as any).processingSystems || [] const industry = data.industry || [] const [expandedActivity, setExpandedActivity] = useState(null) const [collapsedDepts, setCollapsedDepts] = useState>(new Set()) const [showExtraCategories, setShowExtraCategories] = useState>(new Set()) const [expandedInfoCat, setExpandedInfoCat] = useState(null) const departments = getRelevantDepartments(industry, data.businessModel, data.companySize) const activeIds = new Set(activities.map(a => a.id)) const toggleActivity = (template: ActivityTemplate, deptId: string) => { if (activeIds.has(template.id)) { onChange({ processingSystems: activities.filter(a => a.id !== template.id) }) } else { onChange({ processingSystems: [...activities, { id: template.id, name: template.name, purpose: template.purpose, data_categories: [...template.primary_categories], legal_basis: template.default_legal_basis, department: deptId, }], }) } } const updateActivity = (id: string, updates: Partial) => { onChange({ processingSystems: activities.map(a => a.id === id ? { ...a, ...updates } : a), }) } const toggleDataCategory = (activityId: string, categoryId: string) => { const activity = activities.find(a => a.id === activityId) if (!activity) return const cats = activity.data_categories.includes(categoryId) ? activity.data_categories.filter(c => c !== categoryId) : [...activity.data_categories, categoryId] updateActivity(activityId, { data_categories: cats }) } const toggleDeptCollapse = (deptId: string) => { setCollapsedDepts(prev => { const next = new Set(prev) if (next.has(deptId)) next.delete(deptId); else next.add(deptId) return next }) } const toggleExtraCategories = (activityId: string) => { setShowExtraCategories(prev => { const next = new Set(prev) if (next.has(activityId)) next.delete(activityId); else next.add(activityId) return next }) } const addCustomActivity = () => { const id = `custom_${Date.now()}` onChange({ processingSystems: [...activities, { id, name: '', purpose: '', data_categories: [], legal_basis: 'contract', custom: true, }], }) setExpandedActivity(id) } const removeActivity = (id: string) => { onChange({ processingSystems: activities.filter(a => a.id !== id) }) if (expandedActivity === id) setExpandedActivity(null) } // Count active activities per department const deptActivityCount = (dept: ActivityDepartment) => dept.activities.filter(a => activeIds.has(a.id)).length // Render a data category checkbox with info tooltip const renderCategoryCheckbox = (cat: { id: string; label: string; desc: string; info: string }, activity: ProcessingActivity, variant: 'normal' | 'extra' | 'art9' | 'art9-extra', template?: ActivityTemplate | null) => { const infoText = template?.categoryInfo?.[cat.id] || cat.info const isInfoExpanded = expandedInfoCat === `${activity.id}-${cat.id}` const colorClasses = variant.startsWith('art9') ? { check: 'text-red-600 focus:ring-red-500', hover: 'hover:bg-red-100', text: variant === 'art9-extra' ? 'text-gray-500' : 'text-gray-700' } : { check: 'text-purple-600 focus:ring-purple-500', hover: 'hover:bg-gray-100', text: variant === 'extra' ? 'text-gray-500' : 'text-gray-700' } // Split info text to highlight retention periods const aufbewahrungIdx = infoText.indexOf('Aufbewahrung:') const loeschfristIdx = infoText.indexOf('Löschfrist') const speicherdauerIdx = infoText.indexOf('Speicherdauer:') const retentionIdx = [aufbewahrungIdx, loeschfristIdx, speicherdauerIdx].filter(i => i >= 0).sort((a, b) => a - b)[0] ?? -1 const hasRetention = retentionIdx >= 0 const mainText = hasRetention ? infoText.slice(0, retentionIdx).replace(/\.\s*$/, '') : infoText const retentionText = hasRetention ? infoText.slice(retentionIdx) : '' return (
{isInfoExpanded && (
{hasRetention ? ( <> {mainText} 🕓{retentionText} ) : infoText}
)}
) } // Render activity detail panel (shared between template and custom) const renderActivityDetail = (activity: ProcessingActivity, template: ActivityTemplate | null) => { const primaryIds = new Set(template?.primary_categories || []) const art9Ids = new Set(template?.art9_relevant || []) const primaryCats = ALL_DATA_CATEGORIES.filter(c => primaryIds.has(c.id)) const extraCats = ALL_DATA_CATEGORIES.filter(c => !primaryIds.has(c.id)) const relevantArt9 = ALL_SPECIAL_CATEGORIES.filter(c => art9Ids.has(c.id)) const otherArt9 = ALL_SPECIAL_CATEGORIES.filter(c => !art9Ids.has(c.id)) const showingExtra = showExtraCategories.has(activity.id) // For custom activities, show all categories const isCustom = !template || activity.custom return (
{/* Legal hint (e.g. ArbZG for Arbeitszeiterfassung) */} {template?.legalHint && (
{template.legalHint}
)} {/* Custom: name + purpose fields */} {isCustom && (
updateActivity(activity.id, { name: e.target.value })} placeholder="Name der Verarbeitungstätigkeit" className="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent" /> updateActivity(activity.id, { purpose: e.target.value })} placeholder="Zweck der Verarbeitung" className="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent" />
)} {/* Service Provider option (e.g. for Lohn- und Gehaltsabrechnung) */} {template?.hasServiceProvider && (
{activity.usesServiceProvider && (
updateActivity(activity.id, { serviceProviderName: e.target.value })} placeholder="Name des Dienstleisters (optional)" className="w-full px-3 py-1.5 border border-blue-200 rounded text-xs focus:ring-2 focus:ring-blue-400 focus:border-transparent bg-white" />

Wird als Auftragsverarbeiter (AVV) im VVT erfasst.

)}
)} {/* Primary Data Categories */}
{(isCustom ? ALL_DATA_CATEGORIES : primaryCats).map(cat => renderCategoryCheckbox(cat, activity, 'normal', template) )}
{/* Extra categories (expandable, only for template-based) */} {!isCustom && extraCats.length > 0 && (
{showingExtra && (
{extraCats.map(cat => renderCategoryCheckbox(cat, activity, 'extra', template))}
)}
)} {/* Art. 9 Special Categories — only show if relevant for this activity */} {(isCustom ? ALL_SPECIAL_CATEGORIES.length > 0 : relevantArt9.length > 0) && (
{(isCustom ? ALL_SPECIAL_CATEGORIES : relevantArt9).map(cat => renderCategoryCheckbox(cat, activity, 'art9', template) )}
{/* Show remaining Art. 9 categories if expanded */} {!isCustom && otherArt9.length > 0 && showingExtra && (
{otherArt9.map(cat => renderCategoryCheckbox(cat, activity, 'art9-extra', template))}
)}
)}
) } return (
{/* Processing Activities grouped by department */}

Verarbeitungstätigkeiten

Wählen Sie pro Abteilung aus, welche Verarbeitungen stattfinden. Diese bilden die Grundlage für Ihr Verarbeitungsverzeichnis (VVT) nach Art. 30 DSGVO.

{departments.map(dept => { const isCollapsed = collapsedDepts.has(dept.id) const activeCount = deptActivityCount(dept) return (
{/* Department header */} {/* Department activities */} {!isCollapsed && (
{dept.activities.map(template => { const isActive = activeIds.has(template.id) const activity = activities.find(a => a.id === template.id) const isExpanded = expandedActivity === template.id return (
{ if (!isActive) { toggleActivity(template, dept.id) setExpandedActivity(template.id) } else { setExpandedActivity(isExpanded ? null : template.id) } }} > { e.stopPropagation(); toggleActivity(template, dept.id) }} className="w-4 h-4 text-purple-600 rounded focus:ring-purple-500 flex-shrink-0" />
{template.name} {template.legalHint && ( Pflicht )} {template.hasServiceProvider && ( AVV-relevant )}

{template.purpose}

{isActive && ( {activity?.data_categories.length || 0} Kat. )} {isActive && ( )}
{isActive && isExpanded && activity && renderActivityDetail(activity, template)}
) })}
)}
) })}
{/* Custom activities */} {activities.filter(a => a.custom).map(activity => (
setExpandedActivity(expandedActivity === activity.id ? null : activity.id)} > +
{activity.name || 'Neue Verarbeitungstätigkeit'}
{expandedActivity === activity.id && renderActivityDetail(activity, null)}
))} {/* Add custom activity button */}
) } // ============================================================================= // STEP 7: KI-SYSTEME // ============================================================================= interface AISystemTemplate { id: string name: string vendor: string category: string icon: string typicalPurposes: string[] dataWarning?: string processes_personal_data_likely: boolean } const AI_SYSTEM_TEMPLATES: { category: string; icon: string; systems: AISystemTemplate[] }[] = [ { category: 'Text-KI / Chatbots', icon: '\uD83D\uDCAC', systems: [ { id: 'chatgpt', name: 'ChatGPT', vendor: 'OpenAI', category: 'Text-KI / Chatbots', icon: '\uD83D\uDCAC', typicalPurposes: ['Textgenerierung', 'Kundensupport', 'Zusammenfassungen', 'Recherche'], dataWarning: 'Datenverarbeitung in den USA. Eingaben koennen fuer Training verwendet werden (opt-out moeglich).', processes_personal_data_likely: true }, { id: 'claude', name: 'Claude', vendor: 'Anthropic', category: 'Text-KI / Chatbots', icon: '\uD83D\uDCAC', typicalPurposes: ['Textgenerierung', 'Analyse', 'Zusammenfassungen', 'Code-Review'], dataWarning: 'Datenverarbeitung in den USA. Eingaben werden NICHT fuer Training verwendet.', processes_personal_data_likely: true }, { id: 'gemini', name: 'Google Gemini', vendor: 'Google', category: 'Text-KI / Chatbots', icon: '\uD83D\uDCAC', typicalPurposes: ['Textgenerierung', 'Recherche', 'Zusammenfassungen'], dataWarning: 'Datenverarbeitung in den USA/EU je nach Einstellung.', processes_personal_data_likely: true }, { id: 'perplexity', name: 'Perplexity', vendor: 'Perplexity AI', category: 'Text-KI / Chatbots', icon: '\uD83D\uDCAC', typicalPurposes: ['Websuche mit KI', 'Recherche', 'Zusammenfassungen'], dataWarning: 'Websuche + KI. Eingaben werden verarbeitet.', processes_personal_data_likely: false }, ], }, { category: 'Office / Produktivitaet', icon: '\uD83D\uDCCE', systems: [ { id: 'copilot365', name: 'Microsoft 365 Copilot', vendor: 'Microsoft', category: 'Office / Produktivitaet', icon: '\uD83D\uDCCE', typicalPurposes: ['E-Mail-Entwuerfe', 'Dokument-Zusammenfassung', 'Praesentationen', 'Excel-Analysen'], dataWarning: 'In M365-Tenant integriert. Daten bleiben im Tenant, aber: KI-Verarbeitung ggf. in den USA.', processes_personal_data_likely: true }, { id: 'google-workspace-ai', name: 'Google Workspace AI', vendor: 'Google', category: 'Office / Produktivitaet', icon: '\uD83D\uDCCE', typicalPurposes: ['E-Mail-Entwuerfe', 'Dokument-Zusammenfassung', 'Tabellen-Analysen'], dataWarning: 'Duet AI in Docs, Sheets, Gmail. Datenverarbeitung je nach Workspace-Region.', processes_personal_data_likely: true }, { id: 'notion-ai', name: 'Notion AI', vendor: 'Notion', category: 'Office / Produktivitaet', icon: '\uD83D\uDCCE', typicalPurposes: ['Texterstellung', 'Zusammenfassungen', 'Aufgabenverwaltung'], dataWarning: 'Datenverarbeitung in den USA.', processes_personal_data_likely: false }, { id: 'grammarly', name: 'Grammarly', vendor: 'Grammarly', category: 'Office / Produktivitaet', icon: '\uD83D\uDCCE', typicalPurposes: ['Textkorrektur', 'Stiloptimierung', 'Tonalitaet'], dataWarning: 'Textanalyse, Datenverarbeitung in den USA.', processes_personal_data_likely: false }, ], }, { category: 'Code-Assistenz', icon: '\uD83D\uDCBB', systems: [ { id: 'github-copilot', name: 'GitHub Copilot', vendor: 'Microsoft/GitHub', category: 'Code-Assistenz', icon: '\uD83D\uDCBB', typicalPurposes: ['Code-Vorschlaege', 'Code-Generierung', 'Dokumentation'], dataWarning: 'Code-Vorschlaege basierend auf Kontext. Code-Snippets werden verarbeitet.', processes_personal_data_likely: false }, { id: 'cursor', name: 'Cursor / Windsurf', vendor: 'Cursor Inc.', category: 'Code-Assistenz', icon: '\uD83D\uDCBB', typicalPurposes: ['Code-Generierung', 'Refactoring', 'Debugging'], dataWarning: 'KI-Code-Editor. Code wird an KI-Backend uebermittelt.', processes_personal_data_likely: false }, { id: 'codewhisperer', name: 'Amazon CodeWhisperer', vendor: 'AWS', category: 'Code-Assistenz', icon: '\uD83D\uDCBB', typicalPurposes: ['Code-Vorschlaege', 'Sicherheits-Scans'], dataWarning: 'Code-Vorschlaege. Opt-out fuer Code-Sharing moeglich.', processes_personal_data_likely: false }, ], }, { category: 'Bildgenerierung', icon: '\uD83C\uDFA8', systems: [ { id: 'dalle', name: 'DALL-E / ChatGPT Bildgenerierung', vendor: 'OpenAI', category: 'Bildgenerierung', icon: '\uD83C\uDFA8', typicalPurposes: ['Bildgenerierung', 'Marketing-Material', 'Illustrationen'], dataWarning: 'Bildgenerierung. Prompts werden verarbeitet.', processes_personal_data_likely: false }, { id: 'midjourney', name: 'Midjourney', vendor: 'Midjourney Inc.', category: 'Bildgenerierung', icon: '\uD83C\uDFA8', typicalPurposes: ['Bildgenerierung', 'Design-Konzepte', 'Illustrationen'], dataWarning: 'Bildgenerierung via Discord. Prompts sind oeffentlich sichtbar (ausser Pro-Plan).', processes_personal_data_likely: false }, { id: 'firefly', name: 'Adobe Firefly', vendor: 'Adobe', category: 'Bildgenerierung', icon: '\uD83C\uDFA8', typicalPurposes: ['Bildgenerierung', 'Bildbearbeitung', 'Design'], dataWarning: 'In Creative Cloud integriert. Trainiert auf lizenzierten Inhalten.', processes_personal_data_likely: false }, ], }, { category: 'Uebersetzung / Sprache', icon: '\uD83C\uDF10', systems: [ { id: 'deepl', name: 'DeepL', vendor: 'DeepL SE', category: 'Uebersetzung / Sprache', icon: '\uD83C\uDF10', typicalPurposes: ['Uebersetzung', 'Dokumentenuebersetzung'], dataWarning: 'Deutscher Anbieter, Server in EU. DeepL Pro: Texte werden NICHT gespeichert.', processes_personal_data_likely: false }, { id: 'deepl-write', name: 'DeepL Write', vendor: 'DeepL SE', category: 'Uebersetzung / Sprache', icon: '\uD83C\uDF10', typicalPurposes: ['Textoptimierung', 'Stilverbesserung'], dataWarning: 'Deutscher Anbieter, Server in EU. Gleiche Datenschutz-Bedingungen wie DeepL.', processes_personal_data_likely: false }, ], }, { category: 'CRM / Sales KI', icon: '\uD83D\uDCCA', systems: [ { id: 'salesforce-einstein', name: 'Salesforce Einstein', vendor: 'Salesforce', category: 'CRM / Sales KI', icon: '\uD83D\uDCCA', typicalPurposes: ['Lead-Scoring', 'Prognosen', 'Empfehlungen'], dataWarning: 'In Salesforce integriert. Verarbeitet CRM-Daten.', processes_personal_data_likely: true }, { id: 'hubspot-ai', name: 'HubSpot AI', vendor: 'HubSpot', category: 'CRM / Sales KI', icon: '\uD83D\uDCCA', typicalPurposes: ['E-Mail-Generierung', 'Lead-Scoring', 'Content-Erstellung'], dataWarning: 'KI-Features in HubSpot CRM. Datenverarbeitung in USA/EU.', processes_personal_data_likely: true }, ], }, { category: 'Interne / Eigene Systeme', icon: '\uD83C\uDFE2', systems: [ { id: 'internal-ai', name: 'Eigenes KI-System', vendor: 'Intern', category: 'Interne / Eigene Systeme', icon: '\uD83C\uDFE2', typicalPurposes: ['Interne Analyse', 'Automatisierung', 'Prozessoptimierung'], dataWarning: undefined, processes_personal_data_likely: false }, ], }, ] function StepAISystems({ data, onChange, }: { data: Partial & { aiSystems?: AISystem[] } onChange: (updates: Record) => void }) { const aiSystems: AISystem[] = (data as any).aiSystems || [] const [expandedSystem, setExpandedSystem] = useState(null) const [collapsedCategories, setCollapsedCategories] = useState>(new Set()) const activeIds = new Set(aiSystems.map(a => a.id)) const toggleTemplateSystem = (template: AISystemTemplate) => { if (activeIds.has(template.id)) { onChange({ aiSystems: aiSystems.filter(a => a.id !== template.id) }) if (expandedSystem === template.id) setExpandedSystem(null) } else { const newSystem: AISystem = { id: template.id, name: template.name, vendor: template.vendor, purpose: template.typicalPurposes.join(', '), purposes: [], processes_personal_data: template.processes_personal_data_likely, isCustom: false, } onChange({ aiSystems: [...aiSystems, newSystem] }) setExpandedSystem(template.id) } } const updateAISystem = (id: string, updates: Partial) => { onChange({ aiSystems: aiSystems.map(a => a.id === id ? { ...a, ...updates } : a), }) } const togglePurpose = (systemId: string, purpose: string) => { const system = aiSystems.find(a => a.id === systemId) if (!system) return const purposes = system.purposes || [] const updated = purposes.includes(purpose) ? purposes.filter(p => p !== purpose) : [...purposes, purpose] updateAISystem(systemId, { purposes: updated, purpose: updated.join(', ') }) } const addCustomSystem = () => { const id = `custom_ai_${Date.now()}` const newSystem: AISystem = { id, name: '', vendor: '', purpose: '', processes_personal_data: false, isCustom: true, } onChange({ aiSystems: [...aiSystems, newSystem] }) setExpandedSystem(id) } const removeSystem = (id: string) => { onChange({ aiSystems: aiSystems.filter(a => a.id !== id) }) if (expandedSystem === id) setExpandedSystem(null) } const toggleCategoryCollapse = (category: string) => { setCollapsedCategories(prev => { const next = new Set(prev) if (next.has(category)) next.delete(category); else next.add(category) return next }) } const categoryActiveCount = (systems: AISystemTemplate[]) => systems.filter(s => activeIds.has(s.id)).length return (

KI-Systeme im Einsatz

Waehlen Sie die KI-Systeme aus, die in Ihrem Unternehmen eingesetzt werden. Dies dient der Erfassung fuer den EU AI Act und die DSGVO-Dokumentation.

{/* Template categories */}
{AI_SYSTEM_TEMPLATES.map(group => { const isCollapsed = collapsedCategories.has(group.category) const activeCount = categoryActiveCount(group.systems) return (
{/* Category header */} {/* Systems in category */} {!isCollapsed && (
{group.systems.map(template => { const isActive = activeIds.has(template.id) const system = aiSystems.find(a => a.id === template.id) const isExpanded = expandedSystem === template.id return (
{ if (!isActive) { toggleTemplateSystem(template) } else { setExpandedSystem(isExpanded ? null : template.id) } }} > { e.stopPropagation(); toggleTemplateSystem(template) }} className="w-4 h-4 text-purple-600 rounded focus:ring-purple-500 flex-shrink-0" />
{template.name}

{template.vendor}

{isActive && ( )}
{/* Detail panel */} {isActive && isExpanded && system && (
{/* Purposes as chips */}
{template.typicalPurposes.map(purpose => ( ))}
updateAISystem(template.id, { notes: e.target.value })} placeholder="Weitere Einsatzzwecke / Anmerkungen..." className="mt-2 w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent" />
{/* Data warning */} {template.dataWarning && (
{template.dataWarning.includes('EU') || template.dataWarning.includes('Deutscher Anbieter') ? '\u2139\uFE0F' : '\u26A0\uFE0F'} {template.dataWarning}
)} {/* Personal data checkbox */}
)}
) })}
)}
) })}
{/* Custom AI systems */} {aiSystems.filter(a => a.isCustom).map(system => (
setExpandedSystem(expandedSystem === system.id ? null : system.id)} > +
{system.name || 'Neues KI-System'} {system.vendor && ({system.vendor})}
{expandedSystem === system.id && (
updateAISystem(system.id, { name: e.target.value })} placeholder="Name (z.B. ChatGPT, Copilot)" className="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent" /> updateAISystem(system.id, { vendor: e.target.value })} placeholder="Anbieter (z.B. OpenAI, Microsoft)" className="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent" />
updateAISystem(system.id, { purpose: e.target.value })} placeholder="Einsatzzweck (z.B. Kundensupport, Code-Assistenz)" className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent" />
)}
))} {/* Add custom system button */} {/* AI Act module link */}
{'\u2139\uFE0F'}

AI Act Risikoeinstufung

Die detaillierte Risikoeinstufung Ihrer KI-Systeme nach EU AI Act (verboten / hochriskant / begrenzt / minimal) erfolgt automatisch im AI-Act-Modul.

Zum AI-Act-Modul
) } // ============================================================================= // STEP 6: RECHTLICHER RAHMEN (was Step 8, renumbered) // ============================================================================= const CERTIFICATIONS = [ { id: 'iso27001', label: 'ISO 27001', desc: 'Informationssicherheits-Managementsystem' }, { id: 'iso27701', label: 'ISO 27701', desc: 'Datenschutz-Managementsystem' }, { id: 'iso9001', label: 'ISO 9001', desc: 'Qualitaetsmanagement' }, { id: 'iso14001', label: 'ISO 14001', desc: 'Umweltmanagement' }, { id: 'iso22301', label: 'ISO 22301', desc: 'Business Continuity Management' }, { id: 'iso42001', label: 'ISO 42001', desc: 'KI-Managementsystem' }, { id: 'tisax', label: 'TISAX', desc: 'Trusted Information Security Assessment Exchange (Automotive)' }, { id: 'soc2', label: 'SOC 2', desc: 'Service Organization Controls (Typ I/II)' }, { id: 'c5', label: 'C5', desc: 'Cloud Computing Compliance Criteria Catalogue (BSI)' }, { id: 'bsi_grundschutz', label: 'BSI IT-Grundschutz', desc: 'IT-Grundschutz-Zertifikat oder Testat' }, { id: 'pci_dss', label: 'PCI DSS', desc: 'Payment Card Industry Data Security Standard' }, { id: 'hipaa', label: 'HIPAA', desc: 'Health Insurance Portability and Accountability Act' }, { id: 'other', label: 'Sonstige', desc: 'Andere Zertifizierungen' }, ] interface CertificationEntry { certId: string certifier?: string lastDate?: string customName?: string } function StepLegalFramework({ data, onChange, }: { data: Partial onChange: (updates: Record) => void }) { const contacts = (data as any).technicalContacts || [] const existingCerts: CertificationEntry[] = (data as any).existingCertifications || [] const targetCerts: string[] = (data as any).targetCertifications || [] const targetCertOther: string = (data as any).targetCertificationOther || '' // Toggle existing certification const toggleExistingCert = (certId: string) => { const exists = existingCerts.find((c: CertificationEntry) => c.certId === certId) if (exists) { onChange({ existingCertifications: existingCerts.filter((c: CertificationEntry) => c.certId !== certId) }) } else { onChange({ existingCertifications: [...existingCerts, { certId }] }) } } const updateExistingCert = (certId: string, updates: Partial) => { onChange({ existingCertifications: existingCerts.map((c: CertificationEntry) => c.certId === certId ? { ...c, ...updates } : c ), }) } // Toggle target certification const toggleTargetCert = (certId: string) => { if (targetCerts.includes(certId)) { onChange({ targetCertifications: targetCerts.filter((c: string) => c !== certId) }) } else { onChange({ targetCertifications: [...targetCerts, certId] }) } } const addContact = () => { onChange({ technicalContacts: [...contacts, { name: '', role: '', email: '' }] }) } const removeContact = (i: number) => { onChange({ technicalContacts: contacts.filter((_: { name: string; role: string; email: string }, idx: number) => idx !== i) }) } const updateContact = (i: number, updates: Partial<{ name: string; role: string; email: string }>) => { const updated = [...contacts] updated[i] = { ...updated[i], ...updates } onChange({ technicalContacts: updated }) } return (
{/* Bestehende Zertifizierungen */}

Bestehende Zertifizierungen

Ueber welche Zertifizierungen verfuegt Ihr Unternehmen aktuell? Mehrfachauswahl moeglich.

{CERTIFICATIONS.map(cert => { const selected = existingCerts.some((c: CertificationEntry) => c.certId === cert.id) return ( ) })}
{/* Details fuer ausgewaehlte Zertifizierungen */} {existingCerts.length > 0 && (
{existingCerts.map((entry: CertificationEntry) => { const cert = CERTIFICATIONS.find(c => c.id === entry.certId) const label = cert?.label || entry.certId return (
{entry.certId === 'other' ? 'Sonstige Zertifizierung' : label}
{entry.certId === 'other' && ( updateExistingCert(entry.certId, { customName: e.target.value })} placeholder="Name der Zertifizierung" className="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent" /> )} updateExistingCert(entry.certId, { certifier: e.target.value })} placeholder="Zertifizierer (z.B. TÜV, DEKRA)" className="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent" /> updateExistingCert(entry.certId, { lastDate: e.target.value })} title="Datum der letzten Zertifizierung" className="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent" />
) })}
)}
{/* Angestrebte Zertifizierungen */}

Streben Sie eine Zertifizierung an?

Welche Zertifizierungen planen Sie? Mehrfachauswahl moeglich.

{CERTIFICATIONS.map(cert => { const selected = targetCerts.includes(cert.id) // Bereits bestehende Zertifizierungen ausgrauen const alreadyHas = existingCerts.some((c: CertificationEntry) => c.certId === cert.id) return ( ) })}
{targetCerts.includes('other') && (
onChange({ targetCertificationOther: e.target.value })} placeholder="Name der angestrebten Zertifizierung" className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent" />
)}
{/* Technical Contacts */}

Technische Ansprechpartner

CISO, IT-Manager, DSB etc.

{contacts.length === 0 && (
Noch keine Kontakte
)}
{contacts.map((c: { name: string; role: string; email: string }, i: number) => (
updateContact(i, { name: e.target.value })} placeholder="Name" className="flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent" /> updateContact(i, { role: e.target.value })} placeholder="Rolle (z.B. CISO)" className="flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent" /> updateContact(i, { email: e.target.value })} placeholder="E-Mail" className="flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent" />
))}
) } // ============================================================================= // STEP 7: PRODUKT & MASCHINE (nur fuer Maschinenbauer, was Step 9) // ============================================================================= const EMPTY_MACHINE_BUILDER: MachineBuilderProfile = { productTypes: [], productDescription: '', productPride: '', containsSoftware: false, containsFirmware: false, containsAI: false, aiIntegrationType: [], hasSafetyFunction: false, safetyFunctionDescription: '', autonomousBehavior: false, humanOversightLevel: 'full', isNetworked: false, hasRemoteAccess: false, hasOTAUpdates: false, updateMechanism: '', exportMarkets: [], criticalSectorClients: false, criticalSectors: [], oemClients: false, ceMarkingRequired: false, existingCEProcess: false, hasRiskAssessment: false, } function StepMachineBuilder({ data, onChange, }: { data: Partial onChange: (updates: Partial) => void }) { const mb = data.machineBuilder || EMPTY_MACHINE_BUILDER const updateMB = (updates: Partial) => { onChange({ machineBuilder: { ...mb, ...updates } }) } const toggleProductType = (type: MachineProductType) => { const current = mb.productTypes || [] if (current.includes(type)) { updateMB({ productTypes: current.filter(t => t !== type) }) } else { updateMB({ productTypes: [...current, type] }) } } const toggleAIType = (type: AIIntegrationType) => { const current = mb.aiIntegrationType || [] if (current.includes(type)) { updateMB({ aiIntegrationType: current.filter(t => t !== type) }) } else { updateMB({ aiIntegrationType: [...current, type] }) } } const toggleCriticalSector = (sector: CriticalSector) => { const current = mb.criticalSectors || [] if (current.includes(sector)) { updateMB({ criticalSectors: current.filter(s => s !== sector) }) } else { updateMB({ criticalSectors: [...current, sector] }) } } return (
{/* Block 1: Erzaehlen Sie uns von Ihrer Anlage */}

Erzaehlen Sie uns von Ihrer Anlage

Je besser wir Ihr Produkt verstehen, desto praeziser koennen wir die relevanten Vorschriften identifizieren.