import { Box, Chip, MenuItem, TextField, Typography } from '@mui/material' import type { ExposeField, ExposeSection } from '../../lib/exposeFields' import type { ExposeValues } from '../../domain/expose' import { DS_SLATE, DS_TEXT } from '../../lib/ds' interface Props { section: ExposeSection values: ExposeValues missing: Set onChange: (key: string, value: string) => void /** Zusatzinhalt am Ende des Abschnitts, z. B. der KI-Entwurfsknopf. */ footer?: React.ReactNode } /** Mehrfachauswahl als Wert: kommagetrennt, damit ein flaches Wörterbuch reicht. */ function toggleChip(current: string, option: string): string { const set = new Set(current.split(',').map(s => s.trim()).filter(Boolean)) if (set.has(option)) set.delete(option) else set.add(option) return [...set].join(', ') } function isSelected(current: string, option: string): boolean { return current.split(',').map(s => s.trim()).includes(option) } function FieldControl({ field, value, isMissing, onChange, }: { field: ExposeField value: string isMissing: boolean onChange: (value: string) => void }) { const label = field.suffix ? `${field.label} (${field.suffix})` : field.label if (field.type === 'chips') { return ( {label} {(field.options ?? []).map(o => { const selected = isSelected(value, o) return ( onChange(toggleChip(value, o))} sx={{ cursor: 'pointer', bgcolor: selected ? '#152642' : 'transparent', color: selected ? 'white' : DS_TEXT.secondary, borderColor: DS_SLATE[200], }} /> ) })} ) } return ( onChange(e.target.value)} sx={{ '& .MuiOutlinedInput-notchedOutline': isMissing ? { borderColor: DS_TEXT.error, borderWidth: 2 } : {}, }} > {field.type === 'select' && (field.options ?? []).map(o => {o})} ) } /** * Ein fachlicher Bereich des Exposé-Dossiers. * * Rendert generisch aus `lib/exposeFields.ts` — die Bereiche sind Daten, keine * elf handgeschriebenen Formulare. Leere Pflichtfelder sind rot umrandet * (Runde 4, §8.5.2); sie werden nicht mit plausiblen Werten gefüllt. */ export function ExposeFieldGroup({ section, values, missing, onChange, footer }: Props) { return ( {section.title} {section.description && ( {section.description} )} {section.fields.map(field => ( onChange(field.key, v)} /> ))} {footer && {footer}} ) }