import { useState } from 'react' import { useNavigate, useParams, useSearchParams } from 'react-router' import { Alert, Box, Button, Chip, CircularProgress, Divider, Paper, TextField, Typography, } from '@mui/material' import { ArrowLeft, Building2, Calendar, CheckCircle2, Clock, ExternalLink, Info, Layers, Mail, MapPin, ShieldCheck, Tag, Train, TrendingUp, } from 'lucide-react' import { usePropertyById } from '../../hooks/useProperties' import type { PropertyUnit } from '../../domain/property' // ── Helpers ─────────────────────────────────────────────────────────────────── const FLOOR_LABEL = (level: number) => level === 0 ? 'EG' : level < 0 ? `UG ${Math.abs(level)}` : `${level}.OG` const ASSET_LABELS: Record = { OFFICE: 'Büro', LOGISTICS: 'Lager / Logistik', RETAIL: 'Retail / Laden', PRODUCTION: 'Produktion', MIXED: 'Gewerbe (gemischt)', } const RISK_LABELS: Record = { LOW: 'Niedrig', MEDIUM: 'Mittel', HIGH: 'Hoch', } const SOURCE_LABELS: Record = { ERP_IMPORT: 'ERP-Import (intern)', IMMOSCOUT_SCRAPE: 'ImmoScout24', HOMEGATE_SCRAPE: 'Homegate', MATCHOFFICE_SCRAPE: 'MatchOffice', NEWHOME_SCRAPE: 'newhome.ch', AI_SIGNAL: 'KI-Signal', MANUAL: 'Manuell erfasst', } const PASSERBY_LABELS: Record = { LOW: 'Gering', MEDIUM: 'Mittel', HIGH: 'Hoch', VERY_HIGH: 'Sehr hoch', } // ── Sub-components ──────────────────────────────────────────────────────────── function KeyFactRow({ label, value }: { label: string; value?: string | null }) { if (!value) return null return ( {label} {value} ) } function UnitStatusChip({ unit }: { unit: PropertyUnit }) { if (unit.schattenmarktRelease?.enabled) { return ( } label="PRE-MARKET" sx={{ bgcolor: '#f0fdf4', color: '#1a7a4a', border: '1px solid #86efac', fontWeight: 700, fontSize: '0.68rem', height: 20 }} /> ) } if (unit.available) { return } return } function UnitRow({ unit, highlighted }: { unit: PropertyUnit; highlighted: boolean }) { const availableFrom = unit.schattenmarktRelease?.availableFrom ?? unit.leaseEndDate const monthlyRent = unit.rentPricePerSqm ? Math.round(unit.rentPricePerSqm / 12) : undefined return ( {FLOOR_LABEL(unit.floorLevel)}{unit.unitLabel ? ` · ${unit.unitLabel}` : ''} {unit.currentTenant && ( {unit.currentTenant} )} {unit.areaSqm.toLocaleString('de-CH')} m² {monthlyRent ? `CHF ${monthlyRent}/m²/Mt.` : '–'} {availableFrom ? new Date(availableFrom).toLocaleDateString('de-CH', { month: 'long', year: 'numeric' }) : '–'} ) } // ── Page ────────────────────────────────────────────────────────────────────── export default function PropertyDetail() { const { propertyId } = useParams<{ propertyId: string }>() const [searchParams] = useSearchParams() const highlightUnitId = searchParams.get('unit') const navigate = useNavigate() const { data: property, isLoading } = usePropertyById(propertyId ?? '') const [inquiryName, setInquiryName] = useState('') const [inquiryText, setInquiryText] = useState('') const [sent, setSent] = useState(false) if (isLoading) { return ( ) } if (!property) { return ( Objekt nicht gefunden. ) } const preMarketUnits = (property.units ?? []).filter(u => u.schattenmarktRelease?.enabled) const otherUnits = (property.units ?? []).filter(u => !u.schattenmarktRelease?.enabled) const flexibleUnits = (property.units ?? []).filter(u => u.isFlexible && u.minLettableSqm !== undefined) const monthlyPerSqm = Math.round(property.rentPricePerSqm / 12) const totalMonthly = Math.round(property.areaSqm * property.rentPricePerSqm / 12) const minLettable = property.areaSqmMin ?? (flexibleUnits.length > 0 ? Math.min(...flexibleUnits.map(u => u.minLettableSqm!)) : undefined) const sourceLabel = property.sourceLabel ?? property.sourceMeta?.sourceLabel ?? SOURCE_LABELS[property.sourceType] ?? property.sourceType function handleSendInquiry() { if (!inquiryName.trim() || !inquiryText.trim()) return setSent(true) } return ( {/* ── Back ── */} {/* ── Header ── */} {property.images?.[0] && ( )} {ASSET_LABELS[property.assetType] ?? property.assetType} {property.title} {property.address.street} {property.address.houseNumber}, {property.address.postalCode} {property.address.city} CHF {monthlyPerSqm.toLocaleString('de-CH')}/m²/Mt. {property.areaSqm.toLocaleString('de-CH')} m² total {preMarketUnits.length > 0 && ( {preMarketUnits.length} Einheit{preMarketUnits.length !== 1 ? 'en' : ''} für Pre-Market freigegeben — noch vor offizieller Insertion )} {/* ── Preis ── */} Preis {property.ancillaryCosts != null && ( )} {/* ── Hauptangaben ── */} Hauptangaben {minLettable != null && ( )} {property.contractDurationMonths != null && ( )} {property.floorLevel != null && ( )} {property.currentTenant && ( )} {property.leaseEndDate && ( )} {property.breakoutOption && ( )} {property.riskLevel && ( )} {property.expansionPotentialSqm != null && ( )} {/* ── Eigenschaften ── */} {property.softFactors && ( Eigenschaften {property.softFactors.publicTransportMinutes != null && ( } label={`ÖV: ${property.softFactors.publicTransportMinutes} Min. zu Fuss`} sx={{ bgcolor: '#eff6ff', color: '#1d4ed8', border: '1px solid #bfdbfe', fontWeight: 500 }} /> )} {property.softFactors.parkingSpots != null && property.softFactors.parkingSpots > 0 && ( )} {property.softFactors.prestige != null && property.softFactors.prestige >= 80 && ( )} {property.softFactors.visibilityScore != null && property.softFactors.visibilityScore >= 80 && ( )} {property.softFactors.passerbyFrequency && ( )} {property.softFactors.talentAccess != null && property.softFactors.talentAccess >= 80 && ( )} {property.softFactors.talentAccess != null && property.softFactors.talentAccess >= 80 && ( )} )} {/* ── Wegzeit ── */} {property.softFactors?.publicTransportMinutes != null && ( Wegzeit {property.softFactors.publicTransportMinutes} Min. zu Fuss Nächster ÖV-Anschluss — {property.location.city} {property.softFactors.infrastructureNotes && ( {property.softFactors.infrastructureNotes} )} Die Zeiten beziehen sich auf die Strecke zu Fuss. )} {/* ── Einheiten ── */} {(property.units ?? []).length > 0 && ( Einheiten {['Einheit', 'Fläche', 'Miete', 'Verfügbar ab', 'Status'].map(h => ( {h} ))} {preMarketUnits.map(u => ( ))} {otherUnits.map(u => ( ))} )} {/* ── Beschreibung ── */} {property.description && ( Beschreibung {property.description} )} {/* ── Quelle & Referenz ── */} Quelle & Referenz {property.propertyNumber && ( )} {property.importedFrom && ( )} {property.dataQuality.lastVerifiedAt && ( )} {property.sourceUrl && ( )} {/* ── Verwaltung kontaktieren ── */} Verwaltung kontaktieren {sent ? ( } severity="success" sx={{ borderRadius: 1 }} > Ihre Anfrage wurde übermittelt. Die Verwaltung meldet sich in Kürze. ) : ( Ihr Name setInquiryName(e.target.value)} /> Bezug u.id === highlightUnitId)?.unitLabel ?? 'Einheit') : property.title } slotProps={{ input: { readOnly: true } }} sx={{ '& .MuiInputBase-input': { color: 'text.secondary', fontSize: '0.85rem' } }} /> Ihre Nachricht setInquiryText(e.target.value)} /> Antwortzeit: typisch 1–2 Werktage )} Diese Fläche ist noch nicht offiziell auf dem Markt. Die Verwaltung hat sie explizit für qualifizierte Suchanfragen freigegeben. Ihre Anfrage wird vertraulich behandelt. ) }