import { useState } from 'react' import { Box, Card, Chip, Slider, Stack, TextField, Typography } from '@mui/material' import type { ParsedNeedCriteria } from '../../domain/needBuilder' import { AssetType } from '../../domain/enums' import { NeedExtendedRequirements } from './NeedExtendedRequirements' interface Props { criteria: ParsedNeedCriteria onCriteriaChange: (c: ParsedNeedCriteria) => void } const ASSET_OPTIONS = [ { label: 'Büro', value: AssetType.OFFICE }, { label: 'Retail', value: AssetType.RETAIL }, { label: 'Logistik', value: AssetType.LOGISTICS }, { label: 'Produktion', value: AssetType.PRODUCTION }, { label: 'Gewerbe', value: AssetType.LIGHT_INDUSTRIAL }, { label: 'Gemischt', value: AssetType.MIXED }, ] const AREA_PRESETS = [ { label: '200–500 m²', min: 200, max: 500 }, { label: '500–1000 m²', min: 500, max: 1000 }, { label: '1000–2000 m²', min: 1000, max: 2000 }, { label: '2000–5000 m²', min: 2000, max: 5000 }, ] const BUDGET_PRESETS = [ { label: '300 CHF/m²/J', value: 300 }, { label: '420 CHF/m²/J', value: 420 }, { label: '540 CHF/m²/J', value: 540 }, { label: '780 CHF/m²/J', value: 780 }, ] const LOCATION_PRESETS = ['Zürich', 'Zürich-West', 'Zürich-Nord', 'Bern', 'Basel', 'Luzern'] const RADIUS_MARKS = [5, 10, 20, 30, 50, 100].map(v => ({ value: v, label: v === 5 || v === 100 ? `${v} km` : `${v}` })) function FieldLabel({ children }: { children: React.ReactNode }) { return ( {children} ) } export function NeedInput({ criteria: c, onCriteriaChange: set }: Props) { const [locationDraft, setLocationDraft] = useState('') const [mustHaveDraft, setMustHaveDraft] = useState('') function addLocations(raw: string) { const tokens = raw.split(',').map(x => x.trim()).filter(Boolean) if (!tokens.length) return set({ ...c, preferredLocations: [...new Set([...(c.preferredLocations ?? []), ...tokens])] }) setLocationDraft('') } function addMustHaves(raw: string) { const tokens = raw.split(',').map(x => x.trim()).filter(Boolean) if (!tokens.length) return set({ ...c, mustHaveCriteria: [...new Set([...(c.mustHaveCriteria ?? []), ...tokens])] }) setMustHaveDraft('') } return ( Kriterien verfeinern Ergänzen oder korrigieren Sie die extrahierten Felder. {/* Asset Type */} Nutzungstyp {ASSET_OPTIONS.map(opt => ( set({ ...c, assetType: c.assetType === opt.value ? undefined : opt.value })} sx={c.assetType === opt.value ? { bgcolor: '#152642', color: 'white', '& .MuiChip-label': { color: 'white' } } : {}} /> ))} {/* Area */} Fläche (m²) set({ ...c, areaRange: { min: parseInt(e.target.value) || 0, max: c.areaRange?.max ?? 0 } })} sx={{ width: 100 }} slotProps={{ htmlInput: { min: 0 } }} /> set({ ...c, areaRange: { min: c.areaRange?.min ?? 0, max: parseInt(e.target.value) || 0 } })} sx={{ width: 100 }} slotProps={{ htmlInput: { min: 0 } }} /> {AREA_PRESETS.map(p => ( set({ ...c, areaRange: { min: p.min, max: p.max } })} sx={{ fontSize: '0.68rem', height: 20, color: '#64748b', borderColor: '#cbd5e1' }} /> ))} {/* Location */} Standort {LOCATION_PRESETS.map(loc => ( { const already = c.preferredLocations?.includes(loc) set({ ...c, preferredLocations: already ? (c.preferredLocations ?? []).filter(l => l !== loc) : [...new Set([...(c.preferredLocations ?? []), loc])] }) }} sx={c.preferredLocations?.includes(loc) ? { fontSize: '0.68rem', height: 22, bgcolor: '#152642', color: 'white' } : { fontSize: '0.68rem', height: 22, color: '#64748b', borderColor: '#cbd5e1' } } /> ))} setLocationDraft(e.target.value)} onKeyDown={e => { if (e.key === 'Enter' && locationDraft.trim()) addLocations(locationDraft) }} onBlur={() => { if (locationDraft.trim()) addLocations(locationDraft) }} sx={{ mb: 0.75 }} /> {(c.preferredLocations?.filter(l => !LOCATION_PRESETS.includes(l)).length ?? 0) > 0 && ( {c.preferredLocations!.filter(l => !LOCATION_PRESETS.includes(l)).map(loc => ( set({ ...c, preferredLocations: c.preferredLocations!.filter(l => l !== loc) })} /> ))} )} {(c.preferredLocations?.filter(l => !LOCATION_PRESETS.includes(l)).length ?? 0) === 0 && } {/* Radius */} Suchradius {c.searchRadius ?? 30} km set({ ...c, searchRadius: v as number })} size="small" sx={{ color: '#152642', '& .MuiSlider-markLabel': { fontSize: '0.62rem' } }} /> {/* Budget */} Budget (max CHF/m²/Jahr) {BUDGET_PRESETS.map(p => ( set({ ...c, budgetRange: { maxPerSqm: p.value, currency: 'CHF' } })} sx={c.budgetRange?.maxPerSqm === p.value ? { fontSize: '0.68rem', height: 22, bgcolor: '#152642', color: 'white' } : { fontSize: '0.68rem', height: 22, color: '#64748b', borderColor: '#cbd5e1' } } /> ))} set({ ...c, budgetRange: { maxPerSqm: parseInt(e.target.value) || 0, currency: 'CHF' } })} sx={{ width: 180, mb: 2.5 }} slotProps={{ htmlInput: { min: 0 } }} /> {/* Timing */} Verfügbar ab set({ ...c, timing: { earliestMoveIn: e.target.value, latestMoveIn: e.target.value, flexibleTiming: true } })} sx={{ width: 220, mb: 2.5 }} /> {/* Must-haves */} Must-haves setMustHaveDraft(e.target.value)} onKeyDown={e => { if (e.key === 'Enter' && mustHaveDraft.trim()) addMustHaves(mustHaveDraft) }} onBlur={() => { if (mustHaveDraft.trim()) addMustHaves(mustHaveDraft) }} sx={{ mb: 0.75 }} /> {(c.mustHaveCriteria?.length ?? 0) > 0 && ( {c.mustHaveCriteria!.map(item => ( set({ ...c, mustHaveCriteria: c.mustHaveCriteria!.filter(m => m !== item) })} /> ))} )} ) }