Extract the monolithic company-profile wizard into _components/ and _hooks/ following Next.js 15 conventions from AGENTS.typescript.md: - _components/constants.ts: wizard steps, legal forms, industries, certifications - _components/types.ts: local interfaces (ProcessingActivity, AISystem, etc.) - _components/activity-data.ts: DSGVO data categories, department/activity templates - _components/ai-system-data.ts: AI system template catalog - _components/StepBasicInfo.tsx: step 1 (company name, legal form, industry) - _components/StepBusinessModel.tsx: step 2 (B2B/B2C, offerings) - _components/StepCompanySize.tsx: step 3 (size, revenue) - _components/StepLocations.tsx: step 4 (headquarters, target markets) - _components/StepDataProtection.tsx: step 5 (DSGVO roles, DPO) - _components/StepProcessing.tsx: processing activities with category checkboxes - _components/StepAISystems.tsx: AI system inventory - _components/StepLegalFramework.tsx: certifications and contacts - _components/StepMachineBuilder.tsx: machine builder profile (step 7) - _components/ProfileSummary.tsx: completion summary view - _hooks/useCompanyProfileForm.ts: form state, auto-save, navigation logic - page.tsx: thin orchestrator (160 LOC), imports and composes sections All 16 files are under 500 LOC (largest: StepProcessing at 343). Build verified: npx next build passes cleanly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
95 lines
4.6 KiB
TypeScript
95 lines
4.6 KiB
TypeScript
'use client'
|
|
|
|
import { CompanyProfile, BUSINESS_MODEL_LABELS, COMPANY_SIZE_LABELS, TARGET_MARKET_LABELS } from '@/lib/sdk/types'
|
|
import { LEGAL_FORM_LABELS } from './constants'
|
|
|
|
export function ProfileSummary({
|
|
formData,
|
|
onEdit,
|
|
onContinue,
|
|
}: {
|
|
formData: Partial<CompanyProfile>
|
|
onEdit: () => void
|
|
onContinue: () => void
|
|
}) {
|
|
const summaryItems = [
|
|
{ label: 'Firmenname', value: formData.companyName },
|
|
{ label: 'Rechtsform', value: formData.legalForm ? LEGAL_FORM_LABELS[formData.legalForm] : undefined },
|
|
{ label: 'Branche', value: formData.industry?.join(', ') },
|
|
{ label: 'Geschaeftsmodell', value: formData.businessModel ? BUSINESS_MODEL_LABELS[formData.businessModel]?.short : undefined },
|
|
{ label: 'Unternehmensgroesse', value: formData.companySize ? COMPANY_SIZE_LABELS[formData.companySize] : undefined },
|
|
{ label: 'Mitarbeiter', value: formData.employeeCount },
|
|
{ label: 'Hauptsitz', value: [formData.headquartersZip, formData.headquartersCity, formData.headquartersCountry === 'DE' ? 'Deutschland' : formData.headquartersCountry].filter(Boolean).join(', ') },
|
|
{ label: 'Zielmaerkte', value: formData.targetMarkets?.map(m => TARGET_MARKET_LABELS[m] || m).join(', ') },
|
|
{ label: 'Verantwortlicher', value: formData.isDataController ? 'Ja' : 'Nein' },
|
|
{ label: 'Auftragsverarbeiter', value: formData.isDataProcessor ? 'Ja' : 'Nein' },
|
|
{ label: 'DSB', value: formData.dpoName || 'Nicht angegeben' },
|
|
].filter(item => item.value && item.value.length > 0)
|
|
|
|
const missingFields: string[] = []
|
|
if (!formData.companyName) missingFields.push('Firmenname')
|
|
if (!formData.legalForm) missingFields.push('Rechtsform')
|
|
if (!formData.industry || formData.industry.length === 0) missingFields.push('Branche')
|
|
if (!formData.businessModel) missingFields.push('Geschaeftsmodell')
|
|
if (!formData.companySize) missingFields.push('Unternehmensgroesse')
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-50 py-8">
|
|
<div className="max-w-4xl mx-auto px-4">
|
|
<div className="mb-8">
|
|
<h1 className="text-3xl font-bold text-gray-900">Unternehmensprofil</h1>
|
|
</div>
|
|
|
|
{/* Success Banner */}
|
|
<div className={`rounded-xl border-2 p-6 mb-6 ${formData.isComplete ? 'bg-green-50 border-green-300' : 'bg-yellow-50 border-yellow-300'}`}>
|
|
<div className="flex items-start gap-4">
|
|
<div className={`flex-shrink-0 w-12 h-12 rounded-full flex items-center justify-center ${formData.isComplete ? 'bg-green-200' : 'bg-yellow-200'}`}>
|
|
<span className="text-2xl">{formData.isComplete ? '\u2713' : '!'}</span>
|
|
</div>
|
|
<div>
|
|
<h2 className={`text-xl font-bold ${formData.isComplete ? 'text-green-800' : 'text-yellow-800'}`}>
|
|
{formData.isComplete ? 'Profil erfolgreich abgeschlossen' : 'Profil unvollstaendig'}
|
|
</h2>
|
|
<p className={`mt-1 ${formData.isComplete ? 'text-green-700' : 'text-yellow-700'}`}>
|
|
{formData.isComplete
|
|
? 'Alle Angaben wurden gespeichert. Sie koennen jetzt mit der Scope-Analyse fortfahren.'
|
|
: `Es fehlen noch Angaben: ${missingFields.join(', ')}.`}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Profile Summary */}
|
|
<div className="bg-white rounded-xl border border-gray-200 p-6 mb-6">
|
|
<h3 className="text-lg font-semibold text-gray-900 mb-4">Zusammenfassung</h3>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
{summaryItems.map(item => (
|
|
<div key={item.label} className="flex flex-col">
|
|
<span className="text-xs font-medium text-gray-500 uppercase tracking-wide">{item.label}</span>
|
|
<span className="text-sm text-gray-900 mt-0.5">{item.value}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Actions */}
|
|
<div className="flex justify-between items-center">
|
|
<button onClick={onEdit} className="px-6 py-3 text-gray-600 hover:text-gray-900 border border-gray-300 rounded-lg hover:bg-gray-50">
|
|
Profil bearbeiten
|
|
</button>
|
|
|
|
{formData.isComplete ? (
|
|
<button onClick={onContinue} className="px-8 py-3 bg-purple-600 text-white rounded-lg hover:bg-purple-700 font-medium">
|
|
Weiter zu Scope
|
|
</button>
|
|
) : (
|
|
<button onClick={onEdit} className="px-8 py-3 bg-yellow-600 text-white rounded-lg hover:bg-yellow-700 font-medium">
|
|
Fehlende Angaben ergaenzen
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|