250 lines
8.9 KiB
TypeScript
250 lines
8.9 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: '',
|
|
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,
|
|
store_raw_text: true,
|
|
}
|
|
|
|
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>
|
|
)
|
|
}
|