import { Alert, Box, Button, Chip, Divider, LinearProgress, Tooltip, Typography } from '@mui/material' import { BarChart2, Briefcase, Building2, CheckCircle2, ExternalLink, FileCheck, FileText, Newspaper, ShieldCheck, TrendingUp, User, Zap, } from 'lucide-react' import { LocationPreview } from '../shared/LocationPreview' import { getScoreTier, SCORE_THEME } from '../shared/scoreTheme' import type { MatchCardViewModel } from './MatchCardViewModel' // ── Constants ───────────────────────────────────────────────────────────────── const RESULT_TYPE_META: Record = { VERIFIED_PORTFOLIO: { label: 'Verified Portfolio', color: '#1e3a5f' }, EXTERNAL_MARKET: { label: 'Direktinserat', color: '#d97706' }, MAISON_WORK: { label: 'Maison Work', color: '#0369a1' }, FUTURE_AVAILABILITY: { label: 'Schattenmarkt', color: '#7c3aed' }, } const SOURCE_META: Record = { JOB_POSTING: { label: 'Stelleninserate', icon: }, PRESS: { label: 'Pressebericht', icon: }, CONSTRUCTION_PERMIT:{ label: 'Baubewilligung', icon: }, COMPANY_REPORT: { label: 'Geschäftsbericht', icon: }, MARKET_DATA: { label: 'Marktdaten', icon: }, MANUAL: { label: 'Analyst', icon: }, LEASE_CONTRACT: { label: 'Vertrag verifiziert', icon: }, } const SIGNAL_TYPE_LABELS: Record = { EXPANSION: 'Expansion', POSSIBLE_MOVE_OUT: 'Möglicher Auszug', CONSTRUCTION_PROJECT:'Bauprojekt', RESTRUCTURING: 'Restrukturierung', PROJECT_DEVELOPMENT: 'Projektentwicklung', SPACE_CONSOLIDATION: 'Flächenkonsolidierung', LEASE_EXPIRY: 'Vertragsende', } // Human-readable explanation of WHY a signal type is relevant to a space search const SIGNAL_RELEVANCE_BRIDGE: Record = { EXPANSION: 'Neuer Flächenbedarf entsteht — Fläche wird gesucht', POSSIBLE_MOVE_OUT: 'Fläche könnte frei werden — frühzeitig beobachten', CONSTRUCTION_PROJECT:'Neubau geplant — zukünftige Verfügbarkeit möglich', RESTRUCTURING: 'Flächenveränderung durch Restrukturierung möglich', PROJECT_DEVELOPMENT: 'Projektentwicklung — neue Flächen in Planung', SPACE_CONSOLIDATION: 'Konsolidierung — Teilflächen könnten verfügbar werden', } const CREDIBILITY_META: Record = { HIGH: { label: 'Hohe Quellenqualität', color: '#1a7a4a' }, MEDIUM: { label: 'Mittlere Quellenqualität', color: '#d97706' }, LOW: { label: 'Niedrige Quellenqualität', color: '#c0392b' }, } // ── Schattenmarkt hero zone ─────────────────────────────────────────────────── function SignalHero({ vm }: { vm: MatchCardViewModel }) { const tier = getScoreTier(vm.matchScore) const theme = SCORE_THEME[tier] const signalLabel = vm.signalType ? (SIGNAL_TYPE_LABELS[vm.signalType] ?? vm.signalType) : 'Signal' const relevanceBridge = vm.signalType ? (SIGNAL_RELEVANCE_BRIDGE[vm.signalType] ?? null) : null const initials = (vm.title ?? '?') .split(/\s+/) .slice(0, 2) .map(w => w[0]) .join('') .toUpperCase() return ( {/* Gradient hero background */} {/* Subtle pattern overlay */} {/* Company initials */} {initials} {/* Signal type label */} {signalLabel} {/* Relevance bridge — why this appears in results */} {relevanceBridge && ( {relevanceBridge} )} {/* Verified badge */} {vm.signalIsVerified && ( Verifiziert )} {/* Score badge — overlaid top-left */} {vm.matchScore}% {tier !== 'bronze' && ( {theme.label} )} {/* Schattenmarkt chip — top-right */} ) } // ── Signal metadata strip ───────────────────────────────────────────────────── function SignalMetaStrip({ vm }: { vm: MatchCardViewModel }) { const sourceMeta = vm.signalSourceType ? SOURCE_META[vm.signalSourceType] : null const credMeta = vm.signalSourceCredibility ? CREDIBILITY_META[vm.signalSourceCredibility] : null const probPct = vm.signalProbability ? Math.round(vm.signalProbability * 100) : null return ( {/* Source + credibility row */} {sourceMeta && ( vm.signalSourceUrl ? ( {sourceMeta.icon} {sourceMeta.label} ) : ( {sourceMeta.icon} {sourceMeta.label} ) )} {credMeta && ( {vm.signalSourceCredibility === 'HIGH' ? 'Hoch' : vm.signalSourceCredibility === 'MEDIUM' ? 'Mittel' : 'Niedrig'} )} {/* Probability bar */} {probPct !== null && ( Eintretenswahrscheinlichkeit = 70 ? '#1a7a4a' : probPct >= 50 ? '#d97706' : '#c0392b' }}> {probPct}% = 70 ? '#1a7a4a' : probPct >= 50 ? '#d97706' : '#c0392b', borderRadius: 2, }, }} /> )} {/* Market indicator */} {vm.signalMarketIndicator && ( {vm.signalMarketIndicator} )} ) } // ── Main component ──────────────────────────────────────────────────────────── interface Props { vm: MatchCardViewModel imageUrl?: string lat?: number lng?: number cityLabel?: string } export function IntelligenceMatchCard({ vm, imageUrl, lat, lng, cityLabel }: Props) { const tier = getScoreTier(vm.matchScore) const theme = SCORE_THEME[tier] const rt = RESULT_TYPE_META[vm.resultType] ?? { label: vm.resultType, color: '#64748b' } const isFuture = vm.resultType === 'FUTURE_AVAILABILITY' return ( {/* ── Hero zone ── */} {isFuture ? ( ) : ( {vm.matchScore}% {tier !== 'bronze' && ( {theme.label} )} {vm.resultType === 'VERIFIED_PORTFOLIO' && ( } label="Ihr Objekt" size="small" sx={{ bgcolor: 'rgba(30,58,95,0.85)', color: 'white', fontWeight: 700, fontSize: 9, height: 18, '& .MuiChip-icon': { ml: 0.5 } }} /> )} )} {/* ── Signal metadata (Schattenmarkt only) ── */} {isFuture && } {/* ── Card body ── */} {vm.title} {[vm.locationLabel, vm.availabilityLabel].filter(Boolean).join(' · ')} {/* AI Signal Summary (Schattenmarkt) */} {isFuture && vm.aiSignalSummary && ( <> {vm.aiSignalSummary} )} {/* Regular explainability summary (non-future) */} {!isFuture && vm.explainabilitySummary && ( <> {vm.explainabilitySummary} )} {vm.reasons.length > 0 && ( <> {vm.reasons.slice(0, 3).map((r, i) => ( {r.label} {r.explanation} ))} )} {/* Actions */} {vm.actions.map(a => ( ))} {/* Disclaimer */} {vm.disclaimer && ( {vm.disclaimer} )} ) }