Some checks failed
Build + Deploy / build-admin-compliance (push) Successful in 1m30s
Build + Deploy / build-backend-compliance (push) Successful in 13s
Build + Deploy / build-ai-sdk (push) Failing after 29s
Build + Deploy / build-developer-portal (push) Successful in 6s
Build + Deploy / build-tts (push) Successful in 6s
Build + Deploy / build-document-crawler (push) Successful in 6s
Build + Deploy / build-dsms-gateway (push) Successful in 6s
Build + Deploy / trigger-orca (push) Has been skipped
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 12s
CI / secret-scan (push) Has been skipped
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 2m18s
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-go (push) Failing after 29s
CI / test-python-backend (push) Successful in 34s
CI / test-python-document-crawler (push) Successful in 23s
CI / test-python-dsms-gateway (push) Successful in 19s
CI / validate-canonical-controls (push) Successful in 30s
Merged feature/fisa-702-drittland-risiko in den refakturierten main-Branch. Konflikte in 8 Dateien aufgelöst — neue Features in die aufgesplittete Modulstruktur integriert. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
439 lines
18 KiB
TypeScript
439 lines
18 KiB
TypeScript
'use client'
|
|
|
|
import React, { useState, useEffect, Suspense } from 'react'
|
|
import { useRouter, useSearchParams } from 'next/navigation'
|
|
import Link from 'next/link'
|
|
import { useSDK } from '@/lib/sdk'
|
|
import type { AdvisoryForm } from './_types'
|
|
import { industryToDomain } from './_data'
|
|
import { StepIndicator } from './_components/StepIndicator'
|
|
import { Step1Basics } from './_components/Step1Basics'
|
|
import { Step2DataCategories } from './_components/Step2DataCategories'
|
|
import { Step3Purposes } from './_components/Step3Purposes'
|
|
import { Step4Automation } from './_components/Step4Automation'
|
|
import { Step5Hosting } from './_components/Step5Hosting'
|
|
import { Step6Transfer } from './_components/Step6Transfer'
|
|
import { Step7Retention } from './_components/Step7Retention'
|
|
import { Step8Contracts } from './_components/Step8Contracts'
|
|
import { NavigationButtons } from './_components/NavigationButtons'
|
|
import { ResultView } from './_components/ResultView'
|
|
|
|
// =============================================================================
|
|
// MAIN COMPONENT
|
|
// =============================================================================
|
|
|
|
function AdvisoryBoardPageInner() {
|
|
const router = useRouter()
|
|
const searchParams = useSearchParams()
|
|
const { state: sdkState } = useSDK()
|
|
const editId = searchParams.get('edit')
|
|
const isEditMode = !!editId
|
|
|
|
const [currentStep, setCurrentStep] = useState(1)
|
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
|
const [editLoading, setEditLoading] = useState(false)
|
|
const [result, setResult] = useState<unknown>(null)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
// Derive domain from company profile industry
|
|
const profileIndustry = sdkState.companyProfile?.industry
|
|
const derivedDomain = industryToDomain(
|
|
Array.isArray(profileIndustry) ? profileIndustry : profileIndustry ? [profileIndustry] : []
|
|
)
|
|
|
|
// Form state — tile-based multi-select via arrays
|
|
const [form, setForm] = useState<AdvisoryForm>({
|
|
title: '',
|
|
use_case_text: '',
|
|
domain: 'general',
|
|
category: '',
|
|
data_categories: [],
|
|
custom_data_types: [],
|
|
purposes: [],
|
|
automation: '',
|
|
// BetrVG / works council
|
|
employee_monitoring: false,
|
|
hr_decision_support: false,
|
|
works_council_consulted: false,
|
|
// Domain-specific contexts (Annex III)
|
|
hr_automated_screening: false,
|
|
hr_automated_rejection: false,
|
|
hr_candidate_ranking: false,
|
|
hr_bias_audits: false,
|
|
hr_agg_visible: false,
|
|
hr_human_review: false,
|
|
hr_performance_eval: false,
|
|
edu_grade_influence: false,
|
|
edu_exam_evaluation: false,
|
|
edu_student_selection: false,
|
|
edu_minors: false,
|
|
edu_teacher_review: false,
|
|
hc_diagnosis: false,
|
|
hc_treatment: false,
|
|
hc_triage: false,
|
|
hc_patient_data: false,
|
|
hc_medical_device: false,
|
|
hc_clinical_validation: false,
|
|
// Legal
|
|
leg_legal_advice: false, leg_court_prediction: false, leg_client_confidential: false,
|
|
// Public Sector
|
|
pub_admin_decision: false, pub_benefit_allocation: false, pub_transparency: false,
|
|
// Critical Infrastructure
|
|
crit_grid_control: false, crit_safety_critical: false, crit_redundancy: false,
|
|
// Automotive
|
|
auto_autonomous: false, auto_safety: false, auto_functional_safety: false,
|
|
// Retail
|
|
ret_pricing: false, ret_profiling: false, ret_credit_scoring: false, ret_dark_patterns: false,
|
|
// IT Security
|
|
its_surveillance: false, its_threat_detection: false, its_data_retention: false,
|
|
// Logistics
|
|
log_driver_tracking: false, log_workload_scoring: false,
|
|
// Construction
|
|
con_tenant_screening: false, con_worker_safety: false,
|
|
// Marketing
|
|
mkt_deepfake: false, mkt_minors: false, mkt_targeting: false, mkt_labeled: false,
|
|
// Manufacturing
|
|
mfg_machine_safety: false, mfg_ce_required: false, mfg_validated: false,
|
|
// Agriculture
|
|
agr_pesticide: false, agr_animal_welfare: false, agr_environmental: false,
|
|
// Social Services
|
|
soc_vulnerable: false, soc_benefit: false, soc_case_mgmt: false,
|
|
// Hospitality
|
|
hos_guest_profiling: false, hos_dynamic_pricing: false, hos_review_manipulation: false,
|
|
// Insurance
|
|
ins_risk_class: false, ins_claims: false, ins_premium: false, ins_fraud: false,
|
|
// Investment
|
|
inv_algo_trading: false, inv_advice: false, inv_robo: false,
|
|
// Defense
|
|
def_dual_use: false, def_export: false, def_classified: false,
|
|
// Supply Chain
|
|
sch_supplier: false, sch_human_rights: false, sch_environmental: false,
|
|
// Facility
|
|
fac_access: false, fac_occupancy: false, fac_energy: false,
|
|
// Sports
|
|
spo_athlete: false, spo_fan: false, spo_doping: false,
|
|
// Finance / Banking
|
|
fin_credit_scoring: false, fin_aml_kyc: false, fin_algo_decisions: false, fin_customer_profiling: false,
|
|
// General
|
|
gen_affects_people: false, gen_automated_decisions: false, gen_sensitive_data: false,
|
|
hosting_provider: '',
|
|
hosting_region: '',
|
|
model_usage: [],
|
|
transfer_targets: [],
|
|
transfer_countries: [],
|
|
transfer_mechanism: '',
|
|
retention_period: '',
|
|
retention_purpose: '',
|
|
contracts: [],
|
|
subprocessors: '',
|
|
})
|
|
|
|
const updateForm = (updates: Partial<AdvisoryForm>) => {
|
|
setForm(prev => ({ ...prev, ...updates }))
|
|
}
|
|
|
|
// Auto-set domain from profile
|
|
useEffect(() => {
|
|
if (!isEditMode && derivedDomain !== 'general') {
|
|
updateForm({ domain: derivedDomain })
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [derivedDomain])
|
|
|
|
// Pre-fill form when in edit mode
|
|
useEffect(() => {
|
|
if (!editId) return
|
|
setEditLoading(true)
|
|
fetch(`/api/sdk/v1/ucca/assessments/${editId}`)
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
const intake = data.intake || {}
|
|
setForm({
|
|
title: data.title || '',
|
|
use_case_text: intake.use_case_text || '',
|
|
domain: data.domain || 'general',
|
|
category: data.category || intake.category || '',
|
|
data_categories: intake.data_categories || [],
|
|
custom_data_types: intake.data_types?.custom_data_types || intake.custom_data_types || [],
|
|
purposes: intake.purposes || [],
|
|
automation: intake.automation || '',
|
|
hosting_provider: intake.hosting?.provider || '',
|
|
hosting_region: intake.hosting?.region || '',
|
|
model_usage: intake.model_usage_list || [],
|
|
transfer_targets: intake.transfer_targets || [],
|
|
transfer_countries: intake.international_transfer?.countries || intake.transfer_countries || [],
|
|
transfer_mechanism: intake.transfer_mechanism || intake.international_transfer?.mechanism || '',
|
|
retention_period: intake.retention_period || '',
|
|
retention_purpose: intake.retention?.purpose || intake.retention_purpose || '',
|
|
contracts: intake.contracts_list || [],
|
|
subprocessors: intake.contracts?.subprocessors || intake.subprocessors || '',
|
|
})
|
|
})
|
|
.catch(() => {})
|
|
.finally(() => setEditLoading(false))
|
|
}, [editId])
|
|
|
|
const handleSubmit = async () => {
|
|
setIsSubmitting(true)
|
|
setError(null)
|
|
try {
|
|
const intake = {
|
|
title: form.title,
|
|
use_case_text: form.use_case_text,
|
|
domain: form.domain,
|
|
category: form.category,
|
|
data_categories: form.data_categories,
|
|
custom_data_types: form.custom_data_types.filter(s => s.trim()),
|
|
purposes: form.purposes,
|
|
automation: form.automation,
|
|
hosting: {
|
|
provider: form.hosting_provider,
|
|
region: form.hosting_region,
|
|
},
|
|
model_usage_list: form.model_usage,
|
|
transfer_targets: form.transfer_targets,
|
|
transfer_countries: form.transfer_countries,
|
|
transfer_mechanism: form.transfer_mechanism,
|
|
retention_period: form.retention_period,
|
|
retention_purpose: form.retention_purpose,
|
|
contracts_list: form.contracts,
|
|
subprocessors: form.subprocessors,
|
|
employee_monitoring: form.employee_monitoring,
|
|
hr_decision_support: form.hr_decision_support,
|
|
works_council_consulted: form.works_council_consulted,
|
|
// Domain-specific contexts
|
|
hr_context: ['hr', 'recruiting'].includes(form.domain) ? {
|
|
automated_screening: form.hr_automated_screening,
|
|
automated_rejection: form.hr_automated_rejection,
|
|
candidate_ranking: form.hr_candidate_ranking,
|
|
bias_audits_done: form.hr_bias_audits,
|
|
agg_categories_visible: form.hr_agg_visible,
|
|
human_review_enforced: form.hr_human_review,
|
|
performance_evaluation: form.hr_performance_eval,
|
|
} : undefined,
|
|
education_context: ['education', 'higher_education', 'vocational_training', 'research'].includes(form.domain) ? {
|
|
grade_influence: form.edu_grade_influence,
|
|
exam_evaluation: form.edu_exam_evaluation,
|
|
student_selection: form.edu_student_selection,
|
|
minors_involved: form.edu_minors,
|
|
teacher_review_required: form.edu_teacher_review,
|
|
} : undefined,
|
|
healthcare_context: ['healthcare', 'medical_devices', 'pharma', 'elderly_care'].includes(form.domain) ? {
|
|
diagnosis_support: form.hc_diagnosis,
|
|
treatment_recommendation: form.hc_treatment,
|
|
triage_decision: form.hc_triage,
|
|
patient_data_processed: form.hc_patient_data,
|
|
medical_device: form.hc_medical_device,
|
|
clinical_validation: form.hc_clinical_validation,
|
|
} : undefined,
|
|
legal_context: ['legal', 'consulting', 'tax_advisory'].includes(form.domain) ? {
|
|
legal_advice: form.leg_legal_advice,
|
|
court_prediction: form.leg_court_prediction,
|
|
client_confidential: form.leg_client_confidential,
|
|
} : undefined,
|
|
public_sector_context: ['public_sector', 'defense', 'justice'].includes(form.domain) ? {
|
|
admin_decision: form.pub_admin_decision,
|
|
benefit_allocation: form.pub_benefit_allocation,
|
|
transparency_ensured: form.pub_transparency,
|
|
} : undefined,
|
|
critical_infra_context: ['energy', 'utilities', 'oil_gas'].includes(form.domain) ? {
|
|
grid_control: form.crit_grid_control,
|
|
safety_critical: form.crit_safety_critical,
|
|
redundancy_exists: form.crit_redundancy,
|
|
} : undefined,
|
|
automotive_context: ['automotive', 'aerospace'].includes(form.domain) ? {
|
|
autonomous_driving: form.auto_autonomous,
|
|
safety_relevant: form.auto_safety,
|
|
functional_safety: form.auto_functional_safety,
|
|
} : undefined,
|
|
retail_context: ['retail', 'ecommerce', 'wholesale'].includes(form.domain) ? {
|
|
pricing_personalized: form.ret_pricing,
|
|
credit_scoring: form.ret_credit_scoring,
|
|
dark_patterns: form.ret_dark_patterns,
|
|
} : undefined,
|
|
it_security_context: ['it_services', 'cybersecurity', 'telecom'].includes(form.domain) ? {
|
|
employee_surveillance: form.its_surveillance,
|
|
threat_detection: form.its_threat_detection,
|
|
data_retention_logs: form.its_data_retention,
|
|
} : undefined,
|
|
logistics_context: ['logistics'].includes(form.domain) ? {
|
|
driver_tracking: form.log_driver_tracking,
|
|
workload_scoring: form.log_workload_scoring,
|
|
} : undefined,
|
|
construction_context: ['construction', 'real_estate', 'facility_management'].includes(form.domain) ? {
|
|
tenant_screening: form.con_tenant_screening,
|
|
worker_safety: form.con_worker_safety,
|
|
} : undefined,
|
|
marketing_context: ['marketing', 'media', 'entertainment'].includes(form.domain) ? {
|
|
deepfake_content: form.mkt_deepfake,
|
|
behavioral_targeting: form.mkt_targeting,
|
|
minors_targeted: form.mkt_minors,
|
|
ai_content_labeled: form.mkt_labeled,
|
|
} : undefined,
|
|
manufacturing_context: ['mechanical_engineering', 'electrical_engineering', 'plant_engineering', 'chemicals', 'food_beverage'].includes(form.domain) ? {
|
|
machine_safety: form.mfg_machine_safety,
|
|
ce_marking_required: form.mfg_ce_required,
|
|
safety_validated: form.mfg_validated,
|
|
} : undefined,
|
|
agriculture_context: ['agriculture', 'forestry', 'fishing'].includes(form.domain) ? {
|
|
pesticide_ai: form.agr_pesticide,
|
|
animal_welfare: form.agr_animal_welfare,
|
|
environmental_data: form.agr_environmental,
|
|
} : undefined,
|
|
social_services_context: ['social_services', 'nonprofit'].includes(form.domain) ? {
|
|
vulnerable_groups: form.soc_vulnerable,
|
|
benefit_decision: form.soc_benefit,
|
|
case_management: form.soc_case_mgmt,
|
|
} : undefined,
|
|
hospitality_context: ['hospitality', 'tourism'].includes(form.domain) ? {
|
|
guest_profiling: form.hos_guest_profiling,
|
|
dynamic_pricing: form.hos_dynamic_pricing,
|
|
review_manipulation: form.hos_review_manipulation,
|
|
} : undefined,
|
|
insurance_context: ['insurance'].includes(form.domain) ? {
|
|
risk_classification: form.ins_risk_class,
|
|
claims_automation: form.ins_claims,
|
|
premium_calculation: form.ins_premium,
|
|
fraud_detection: form.ins_fraud,
|
|
} : undefined,
|
|
investment_context: ['investment'].includes(form.domain) ? {
|
|
algo_trading: form.inv_algo_trading,
|
|
investment_advice: form.inv_advice,
|
|
robo_advisor: form.inv_robo,
|
|
} : undefined,
|
|
defense_context: ['defense'].includes(form.domain) ? {
|
|
dual_use: form.def_dual_use,
|
|
export_controlled: form.def_export,
|
|
classified_data: form.def_classified,
|
|
} : undefined,
|
|
supply_chain_context: ['textiles', 'packaging'].includes(form.domain) ? {
|
|
supplier_monitoring: form.sch_supplier,
|
|
human_rights_check: form.sch_human_rights,
|
|
environmental_impact: form.sch_environmental,
|
|
} : undefined,
|
|
facility_context: ['facility_management'].includes(form.domain) ? {
|
|
access_control_ai: form.fac_access,
|
|
occupancy_tracking: form.fac_occupancy,
|
|
energy_optimization: form.fac_energy,
|
|
} : undefined,
|
|
sports_context: ['sports'].includes(form.domain) ? {
|
|
athlete_tracking: form.spo_athlete,
|
|
fan_profiling: form.spo_fan,
|
|
} : undefined,
|
|
store_raw_text: true,
|
|
// Finance/Banking and General don't need separate context structs —
|
|
// their fields are evaluated via existing FinancialContext or generic rules
|
|
}
|
|
|
|
const url = isEditMode
|
|
? `/api/sdk/v1/ucca/assessments/${editId}`
|
|
: '/api/sdk/v1/ucca/assess'
|
|
const method = isEditMode ? 'PUT' : 'POST'
|
|
|
|
const response = await fetch(url, {
|
|
method,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(intake),
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const errData = await response.json().catch(() => null)
|
|
throw new Error(errData?.error || `HTTP ${response.status}`)
|
|
}
|
|
|
|
if (isEditMode) {
|
|
router.push(`/sdk/use-cases/${editId}`)
|
|
return
|
|
}
|
|
|
|
const data = await response.json()
|
|
setResult(data)
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Fehler bei der Bewertung')
|
|
} finally {
|
|
setIsSubmitting(false)
|
|
}
|
|
}
|
|
|
|
// If we have a result, show it
|
|
if (result) {
|
|
return (
|
|
<ResultView
|
|
result={result}
|
|
onGoToAssessment={(id) => router.push(`/sdk/use-cases/${id}`)}
|
|
onGoToOverview={() => router.push('/sdk/use-cases')}
|
|
/>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="max-w-3xl mx-auto space-y-6">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-gray-900">
|
|
{isEditMode ? 'Assessment bearbeiten' : 'Use Case Workshop'}
|
|
</h1>
|
|
<p className="mt-1 text-gray-500">
|
|
{isEditMode
|
|
? 'Angaben anpassen und Assessment neu bewerten'
|
|
: 'Erfassen Sie Ihren KI-Anwendungsfall Schritt fuer Schritt'}
|
|
</p>
|
|
</div>
|
|
<Link
|
|
href="/sdk/advisory-board/documentation"
|
|
className="inline-flex items-center gap-2 px-4 py-2 text-sm text-purple-600 hover:text-purple-700 hover:bg-purple-50 border border-purple-300 rounded-lg transition-colors"
|
|
>
|
|
UCCA-Dokumentation
|
|
</Link>
|
|
</div>
|
|
|
|
{/* Edit loading indicator */}
|
|
{editLoading && (
|
|
<div className="bg-purple-50 border border-purple-200 rounded-lg p-4 text-purple-700 text-sm">
|
|
Lade Assessment-Daten...
|
|
</div>
|
|
)}
|
|
|
|
<StepIndicator currentStep={currentStep} onStepClick={setCurrentStep} />
|
|
|
|
{/* Error */}
|
|
{error && (
|
|
<div className="bg-red-50 border border-red-200 rounded-lg p-4 text-red-700">{error}</div>
|
|
)}
|
|
|
|
{/* Step Content */}
|
|
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
|
{currentStep === 1 && (
|
|
<Step1Basics form={form} updateForm={updateForm} profileIndustry={profileIndustry} />
|
|
)}
|
|
{currentStep === 2 && <Step2DataCategories form={form} updateForm={updateForm} />}
|
|
{currentStep === 3 && <Step3Purposes form={form} updateForm={updateForm} />}
|
|
{currentStep === 4 && <Step4Automation form={form} updateForm={updateForm} />}
|
|
{currentStep === 5 && <Step5Hosting form={form} updateForm={updateForm} />}
|
|
{currentStep === 6 && <Step6Transfer form={form} updateForm={updateForm} />}
|
|
{currentStep === 7 && <Step7Retention form={form} updateForm={updateForm} />}
|
|
{currentStep === 8 && <Step8Contracts form={form} updateForm={updateForm} />}
|
|
</div>
|
|
|
|
<NavigationButtons
|
|
currentStep={currentStep}
|
|
isSubmitting={isSubmitting}
|
|
isEditMode={isEditMode}
|
|
titleEmpty={!form.title}
|
|
onBack={() => currentStep > 1 ? setCurrentStep(currentStep - 1) : router.push('/sdk/use-cases')}
|
|
onNext={() => setCurrentStep(currentStep + 1)}
|
|
onSubmit={handleSubmit}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function AdvisoryBoardPage() {
|
|
return (
|
|
<Suspense fallback={<div className="flex items-center justify-center h-64 text-gray-500">Lade...</div>}>
|
|
<AdvisoryBoardPageInner />
|
|
</Suspense>
|
|
)
|
|
}
|