diff --git a/admin-compliance/app/sdk/company-profile/page.tsx b/admin-compliance/app/sdk/company-profile/page.tsx index 972e3f5..7ac5177 100644 --- a/admin-compliance/app/sdk/company-profile/page.tsx +++ b/admin-compliance/app/sdk/company-profile/page.tsx @@ -34,12 +34,12 @@ const BASE_WIZARD_STEPS = [ { 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: 'Rechtlicher Rahmen', description: 'Regulierungen und Prüfzyklen' }, + { 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) { +function getWizardSteps(industry: string | string[]) { if (isMachineBuilderIndustry(industry)) { return [...BASE_WIZARD_STEPS, MACHINE_BUILDER_STEP] } @@ -73,20 +73,35 @@ const LEGAL_FORM_LABELS: Record = { 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', ] @@ -98,8 +113,10 @@ const MACHINE_BUILDER_INDUSTRIES = [ 'Messtechnik', ] -const isMachineBuilderIndustry = (industry: string) => - MACHINE_BUILDER_INDUSTRIES.includes(industry) +const isMachineBuilderIndustry = (industry: string | string[]) => { + const industries = Array.isArray(industry) ? industry : [industry] + return industries.some(i => MACHINE_BUILDER_INDUSTRIES.includes(i)) +} // ============================================================================= // STEP COMPONENTS @@ -146,23 +163,44 @@ function StepBasicInfo({
- + +

Mehrfachauswahl moeglich

- {INDUSTRIES.map(industry => ( - - ))} + {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" + /> +
+ )}
@@ -843,16 +881,25 @@ const INDUSTRY_DEPARTMENTS: Record = { } // Compute which departments to show based on company context -function getRelevantDepartments(industry: string, businessModel: string | undefined, companySize: string | undefined): ActivityDepartment[] { +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 - const industryDepts = INDUSTRY_DEPARTMENTS[industry] - if (industryDepts) { - departments.push(...industryDepts) + // 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 @@ -898,7 +945,7 @@ function StepProcessing({ onChange: (updates: Record) => void }) { const activities: ProcessingActivity[] = (data as any).processingSystems || [] - const industry = data.industry || '' + const industry = data.industry || [] const [expandedActivity, setExpandedActivity] = useState(null) const [collapsedDepts, setCollapsedDepts] = useState>(new Set()) const [showExtraCategories, setShowExtraCategories] = useState>(new Set()) @@ -1645,14 +1692,68 @@ function StepAISystems({ // 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: 'ce_marking', label: 'CE-Kennzeichnung', desc: 'EU-Konformitaetskennzeichnung fuer Produkte' }, + { id: 'other', label: 'Sonstige', desc: 'Andere Zertifizierungen' }, +] + +interface CertificationEntry { + certId: string + certifier?: string + lastDate?: string + customName?: string +} + function StepLegalFramework({ data, onChange, }: { - data: Partial & { subjectToNis2?: boolean; subjectToAiAct?: boolean; subjectToIso27001?: boolean; supervisoryAuthority?: string; reviewCycleMonths?: number; technicalContacts?: { name: string; role: string; email: string }[] } + 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: '' }] }) @@ -1668,77 +1769,144 @@ function StepLegalFramework({ return (
- {/* Regulatory Flags */} + {/* Bestehende Zertifizierungen */}
-

Regulatorischer Rahmen

-
- {[ - { key: 'subjectToNis2', label: 'NIS2-Richtlinie', desc: 'Ihr Unternehmen fällt unter die NIS2-Richtlinie (Netzwerk- und Informationssicherheit)' }, - { key: 'subjectToAiAct', label: 'EU AI Act', desc: 'Ihr Unternehmen setzt KI-Systeme ein, die unter den AI Act fallen' }, - { key: 'subjectToIso27001', label: 'ISO 27001', desc: 'Ihr Unternehmen strebt ISO 27001 Zertifizierung an oder ist bereits zertifiziert' }, - ].map(item => ( - - ))} +

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" + /> +
+
+ ) + })} +
+ )}
- {/* Supervisory Authority & Review Cycle */} -
-
- - + {/* 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" + /> +
+ )} +
+ + {/* Pruefzyklus */} +
+ +
+ {[ + { value: 3, label: 'Vierteljaehrlich', desc: '3 Monate' }, + { value: 6, label: 'Halbjaehrlich', desc: '6 Monate' }, + { value: 12, label: 'Jaehrlich', desc: '12 Monate' }, + { value: 24, label: 'Zweijaehrlich', desc: '24 Monate' }, + ].map(opt => { + const selected = ((data as any).reviewCycleMonths || 12) === opt.value + return ( + + ) + })}
@@ -2269,7 +2437,8 @@ export default function CompanyProfilePage() { const [formData, setFormData] = useState>({ companyName: '', legalForm: undefined, - industry: '', + industry: [], + industryOther: '', foundedYear: null, businessModel: undefined, offerings: [], @@ -2297,8 +2466,8 @@ export default function CompanyProfilePage() { completedAt: null, }) - const showMachineBuilderStep = isMachineBuilderIndustry(formData.industry || '') - const wizardSteps = getWizardSteps(formData.industry || '') + const showMachineBuilderStep = isMachineBuilderIndustry(formData.industry || []) + const wizardSteps = getWizardSteps(formData.industry || []) const totalSteps = wizardSteps.length const lastStep = wizardSteps[wizardSteps.length - 1].id @@ -2325,7 +2494,8 @@ export default function CompanyProfilePage() { const backendProfile: Partial = { companyName: data.company_name || '', legalForm: data.legal_form || undefined, - industry: data.industry || '', + industry: Array.isArray(data.industry) ? data.industry : (data.industry ? [data.industry] : []), + industryOther: data.industry_other || '', foundedYear: data.founded_year || undefined, businessModel: data.business_model || undefined, offerings: data.offerings || [], @@ -2352,10 +2522,9 @@ export default function CompanyProfilePage() { processingSystems: data.processing_systems || [], aiSystems: data.ai_systems || [], technicalContacts: data.technical_contacts || [], - subjectToNis2: data.subject_to_nis2 || false, - subjectToAiAct: data.subject_to_ai_act || false, - subjectToIso27001: data.subject_to_iso27001 || false, - supervisoryAuthority: data.supervisory_authority || '', + existingCertifications: data.existing_certifications || [], + targetCertifications: data.target_certifications || [], + targetCertificationOther: data.target_certification_other || '', reviewCycleMonths: data.review_cycle_months || 12, repos: data.repos || [], documentSources: data.document_sources || [], @@ -2395,7 +2564,8 @@ export default function CompanyProfilePage() { project_id: projectId || null, company_name: formData.companyName || '', legal_form: formData.legalForm || 'GmbH', - industry: formData.industry || '', + industry: formData.industry || [], + industry_other: formData.industryOther || '', founded_year: formData.foundedYear || null, business_model: formData.businessModel || 'B2B', offerings: formData.offerings || [], @@ -2422,10 +2592,9 @@ export default function CompanyProfilePage() { processing_systems: (formData as any).processingSystems || [], ai_systems: (formData as any).aiSystems || [], technical_contacts: (formData as any).technicalContacts || [], - subject_to_nis2: (formData as any).subjectToNis2 || false, - subject_to_ai_act: (formData as any).subjectToAiAct || false, - subject_to_iso27001: (formData as any).subjectToIso27001 || false, - supervisory_authority: (formData as any).supervisoryAuthority || '', + existing_certifications: (formData as any).existingCertifications || [], + target_certifications: (formData as any).targetCertifications || [], + target_certification_other: (formData as any).targetCertificationOther || '', review_cycle_months: (formData as any).reviewCycleMonths || 12, repos: (formData as any).repos || [], document_sources: (formData as any).documentSources || [], @@ -2542,7 +2711,8 @@ export default function CompanyProfilePage() { setFormData({ companyName: '', legalForm: undefined, - industry: '', + industry: [], + industryOther: '', foundedYear: null, businessModel: undefined, offerings: [], diff --git a/admin-compliance/lib/sdk/compliance-scope-profiling.ts b/admin-compliance/lib/sdk/compliance-scope-profiling.ts index 7e7882e..fa9bc49 100644 --- a/admin-compliance/lib/sdk/compliance-scope-profiling.ts +++ b/admin-compliance/lib/sdk/compliance-scope-profiling.ts @@ -702,10 +702,10 @@ export function getAutoFilledScoringAnswers( } // industry -> org_industry - if (profile.industry) { + if (profile.industry && profile.industry.length > 0) { answers.push({ questionId: 'org_industry', - value: profile.industry, + value: profile.industry.join(', '), }) } @@ -738,7 +738,7 @@ export function getProfileInfoForBlock( const items: { label: string; value: string }[] = [] if (blockId === 'organisation') { - if (profile.industry) items.push({ label: 'Branche', value: profile.industry }) + if (profile.industry && profile.industry.length > 0) items.push({ label: 'Branche', value: profile.industry.join(', ') }) if (profile.employeeCount) items.push({ label: 'Mitarbeiter', value: profile.employeeCount }) if (profile.annualRevenue) items.push({ label: 'Umsatz', value: profile.annualRevenue }) if (profile.businessModel) items.push({ label: 'Geschäftsmodell', value: profile.businessModel }) diff --git a/admin-compliance/lib/sdk/demo-data/index.ts b/admin-compliance/lib/sdk/demo-data/index.ts index 8b998b5..bf763b6 100644 --- a/admin-compliance/lib/sdk/demo-data/index.ts +++ b/admin-compliance/lib/sdk/demo-data/index.ts @@ -64,7 +64,8 @@ export function generateDemoState(tenantId: string, userId: string): Partial