diff --git a/src/components/supply/PropertyIntelligenceCard.tsx b/src/components/supply/PropertyIntelligenceCard.tsx index efcf738..8f3c79e 100644 --- a/src/components/supply/PropertyIntelligenceCard.tsx +++ b/src/components/supply/PropertyIntelligenceCard.tsx @@ -1,6 +1,7 @@ import { memo } from 'react' -import { Box, Chip, Tooltip, Typography } from '@mui/material' -import { getAssetTypeColor, getAssetTypeLabel } from './propertyHelpers' +import { Box, Chip, LinearProgress, Tooltip, Typography } from '@mui/material' +import { MapPin } from 'lucide-react' +import { getAssetTypeColor, getAssetTypeLabel, getAvailabilityLabel } from './propertyHelpers' import { DS_TEXT, DS_BORDER } from '../../lib/ds' import type { Property } from '../../domain/property' @@ -9,24 +10,25 @@ interface Props { onSelect: (id: string) => void } -const FIT_OUT_LABELS: Record = { - SHELL: 'Rohbau', BASIC: 'Grundausbau', FULL: 'Vollausbau', PREMIUM: 'Premium-Ausbau', +const AVAIL_STYLE: Record = { + AVAILABLE_NOW: { bg: '#dcfce7', text: '#1d6342' }, + AVAILABLE_SOON: { bg: '#fef9c3', text: '#a16207' }, +} + +const FIT_OUT_META: Record = { + SHELL: { label: 'Rohbau', tip: 'Keine Einbauten — volle Ausbauinvestition.' }, + BASIC: { label: 'Grundausbau', tip: 'Grundinfrastruktur vorhanden.' }, + FULL: { label: 'Vollausbau', tip: 'Bezugsfertig ausgebaut.' }, + PREMIUM: { label: 'Premium-Ausbau', tip: 'Hochwertig, sofort bezugsfertig.' }, } export const PropertyIntelligenceCard = memo(function PropertyIntelligenceCard({ property: p, onSelect }: Props) { + const confPct = Math.round(p.confidenceScore * 100) const annualRent = Math.round(p.areaSqm * p.rentPricePerSqm / 1000) + const availStyle = AVAIL_STYLE[p.availabilityStatus] ?? { bg: '#f4f3f0', text: '#9a9a9a' } const assetColor = getAssetTypeColor(p.assetType) const hasImage = !!p.images?.[0] - // single compact facts line below price - const facts: string[] = [] - facts.push(`${p.areaSqm.toLocaleString('de-CH')} m²`) - if (p.availabilityDate) { - facts.push(`ab ${new Date(p.availabilityDate).toLocaleDateString('de-CH', { month: 'short', year: '2-digit' })}`) - } - if (p.hardFacts?.fitOut) facts.push(FIT_OUT_LABELS[p.hardFacts.fitOut] ?? p.hardFacts.fitOut) - if (p.contractDurationMonths) facts.push(`${p.contractDurationMonths}M Vertrag`) - return ( onSelect(p.id)} @@ -43,14 +45,10 @@ export const PropertyIntelligenceCard = memo(function PropertyIntelligenceCard({ }} > {/* ── Image ── */} - + {hasImage ? ( - + ) : ( )} - {/* Single small neutral asset-type tag */} - - + {/* Dark overlay for legibility */} + + + {/* Top badges — asset type + availability */} + + {getAssetTypeLabel(p.assetType)} - - - - {/* ── Content ── */} - - - {/* Location */} - - {p.location.city}{p.location.district ? ` · ${p.location.district}` : ''} - - - {/* Title */} - - {p.title} - - - {/* Price — hero element */} - - - CHF {annualRent}k - - - {p.rentPricePerSqm} CHF/m²/Jahr - + + + {getAvailabilityLabel(p.availabilityStatus)} + + - {/* Key facts — single compact line */} - - {facts.join(' · ')} - + {/* Bottom — CHF hero + per-sqm */} + + + + Jahresmiete ca. + + + CHF {annualRent}k + + + + {p.rentPricePerSqm} /m²/J + + + + + {/* ── Body ── */} + + + {/* Title + location */} + + + {p.title} + + + + + {p.location.city}{p.location.district ? ` · ${p.location.district}` : ''} + + + + + {/* Metric boxes */} + + {[ + { icon: '↗', label: 'FLÄCHE', value: `${p.areaSqm.toLocaleString('de-CH')} m²` }, + ...(p.availabilityDate ? [{ icon: '📅', label: 'AB', value: new Date(p.availabilityDate).toLocaleDateString('de-CH', { month: 'short', year: '2-digit' }) }] : []), + ...(p.softFactors?.prestige != null ? [{ icon: '↗', label: 'PRESTIGE', value: String(p.softFactors.prestige) }] : []), + ].map(({ label, value }) => ( + + + {label} + + + {value} + + + ))} + + + {/* Chips */} + + {p.softFactors?.publicTransportMinutes != null && ( + + )} + {p.hardFacts?.fitOut && (() => { + const meta = FIT_OUT_META[p.hardFacts!.fitOut!] ?? { label: p.hardFacts!.fitOut!, tip: '' } + return ( + + + + ) + })()} + {p.contractDurationMonths && ( + + )} + + + {/* Confidence bar */} + + + + Datenkonfidenz + + = 0.75 ? '#b8975a' : DS_TEXT.muted }}> + {confPct}% + + + + )