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>
108 lines
3.8 KiB
TypeScript
108 lines
3.8 KiB
TypeScript
'use client'
|
|
|
|
import { CompanyProfile, LegalForm } from '@/lib/sdk/types'
|
|
import { INDUSTRIES, LEGAL_FORM_LABELS } from './constants'
|
|
|
|
export function StepBasicInfo({
|
|
data,
|
|
onChange,
|
|
}: {
|
|
data: Partial<CompanyProfile>
|
|
onChange: (updates: Partial<CompanyProfile>) => void
|
|
}) {
|
|
return (
|
|
<div className="space-y-6">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Firmenname <span className="text-red-500">*</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={data.companyName || ''}
|
|
onChange={e => 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"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Rechtsform <span className="text-red-500">*</span>
|
|
</label>
|
|
<select
|
|
value={data.legalForm || ''}
|
|
onChange={e => onChange({ legalForm: e.target.value as LegalForm })}
|
|
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
|
|
>
|
|
<option value="">Bitte wählen...</option>
|
|
{Object.entries(LEGAL_FORM_LABELS).map(([value, label]) => (
|
|
<option key={value} value={value}>
|
|
{label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Branche(n)</label>
|
|
<p className="text-sm text-gray-500 mb-3">Mehrfachauswahl moeglich</p>
|
|
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
|
{INDUSTRIES.map(ind => {
|
|
const selected = (data.industry || []).includes(ind)
|
|
return (
|
|
<button
|
|
key={ind}
|
|
type="button"
|
|
onClick={() => {
|
|
const current = data.industry || []
|
|
const updated = selected
|
|
? current.filter(i => i !== ind)
|
|
: [...current, ind]
|
|
onChange({ industry: updated })
|
|
}}
|
|
className={`p-3 rounded-lg border-2 text-sm text-left transition-all ${
|
|
selected
|
|
? 'border-purple-500 bg-purple-50 text-purple-700 font-medium'
|
|
: 'border-gray-200 hover:border-purple-300 text-gray-700'
|
|
}`}
|
|
>
|
|
{ind}
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
{(data.industry || []).includes('Sonstige') && (
|
|
<div className="mt-3">
|
|
<input
|
|
type="text"
|
|
value={data.industryOther || ''}
|
|
onChange={e => 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"
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">Gründungsjahr</label>
|
|
<input
|
|
type="number"
|
|
value={data.foundedYear || ''}
|
|
onChange={e => {
|
|
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"
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|