fix: clear stale search criteria on text edit, fix Zürich false-positive, improve cost breakdown and fit-out labels

- AISearch: clear parsed criteria when user manually edits text input so stale locations (e.g. Zürich) no longer persist after retyping
- needParser: fix ZURICH_SIGNALS substring bug — "kreis N" now uses word-boundary regex so "umkreis 22" no longer matches "kreis 2"
- CompareTableBody: row 9 shows full cost breakdown (Miete + NK + amortised fit-out) using FITOUT_AMORTIZATION_YEARS from constants
- FitOutCostPanel: replace local AMORTIZATION_YEARS with FITOUT_AMORTIZATION_YEARS from constants.ts (single source of truth)
- constants.ts: add FITOUT_AMORTIZATION_YEARS = 5 — change here to affect all cost calculations
- PropertyIntelligenceCard: translate raw fitOut enum to German labels (Rohbau/Grundausbau/Vollausbau/Premium-Ausbau) with explanatory tooltips; add tooltip on contract duration chip

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-06-09 17:32:25 +02:00
parent c6d3071010
commit 87ea4c4dfc
6 changed files with 97 additions and 20 deletions
@@ -1,5 +1,5 @@
import { memo } from 'react'
import { Box, Chip, LinearProgress, Typography } from '@mui/material'
import { Box, Chip, LinearProgress, Tooltip, Typography } from '@mui/material'
import { MapPin, Maximize2, TrendingUp, Calendar } from 'lucide-react'
import { getAssetTypeColor, getAssetTypeLabel, getAvailabilityLabel } from './propertyHelpers'
import type { Property } from '../../domain/property'
@@ -153,13 +153,26 @@ export const PropertyIntelligenceCard = memo(function PropertyIntelligenceCard({
<Chip label={`🚇 ${p.softFactors.publicTransportMinutes} min ÖV`} size="small"
sx={{ height: 20, fontSize: '0.65rem', bgcolor: '#f0f9ff', color: '#0369a1' }} />
)}
{p.hardFacts?.fitOut && (
<Chip label={p.hardFacts.fitOut} size="small"
sx={{ height: 20, fontSize: '0.65rem', bgcolor: '#f5f3ff', color: '#6d28d9' }} />
)}
{p.hardFacts?.fitOut && (() => {
const FIT_OUT_META: Record<string, { label: string; tip: string }> = {
SHELL: { label: 'Rohbau', tip: 'Keine Einbauten — volle Ausbauinvestition durch Mieter erforderlich (Böden, Decken, Trennwände, TGA).' },
BASIC: { label: 'Grundausbau', tip: 'Grundinfrastruktur vorhanden (Böden, Beleuchtung, WCs). Ausbau für Büro/Betrieb noch nötig.' },
FULL: { label: 'Vollausbau', tip: 'Bezugsfertig ausgebaut — keine Ausbauinvestition nötig. Direkt einzugsbereit.' },
PREMIUM: { label: 'Premium-Ausbau', tip: 'Hochwertig und repräsentativ ausgebaut. Sofort bezugsfertig ohne weiteren Ausbau.' },
}
const meta = FIT_OUT_META[p.hardFacts!.fitOut!] ?? { label: p.hardFacts!.fitOut!, tip: '' }
return (
<Tooltip title={meta.tip} placement="top" arrow>
<Chip label={meta.label} size="small"
sx={{ height: 20, fontSize: '0.65rem', bgcolor: '#f5f3ff', color: '#6d28d9', cursor: 'help' }} />
</Tooltip>
)
})()}
{p.contractDurationMonths && (
<Chip label={`${p.contractDurationMonths}M Vertrag`} size="small"
sx={{ height: 20, fontSize: '0.65rem', bgcolor: '#f1f5f9', color: '#475569' }} />
<Tooltip title={`Mindest-Vertragslaufzeit: ${p.contractDurationMonths} Monate (${Math.round(p.contractDurationMonths / 12)} Jahre)`} placement="top" arrow>
<Chip label={`${p.contractDurationMonths}M Vertrag`} size="small"
sx={{ height: 20, fontSize: '0.65rem', bgcolor: '#f1f5f9', color: '#475569', cursor: 'help' }} />
</Tooltip>
)}
</Box>
)}