import { useEffect, useRef, useState } from 'react' import { Alert, Box, Button, Chip, CircularProgress, Dialog, DialogContent, DialogTitle, LinearProgress, Typography } from '@mui/material' import { BarChart2, Building2, CheckCircle, Download, FileText, Lightbulb, TrendingUp } from 'lucide-react' import { marketReportService } from '../../services/marketReportService' import type { MarketReport, PropertyMarketSignal, SignalCategory } from '../../domain/marketReport' interface Props { propertyId: string } const SECTION_CONFIG: { category: SignalCategory; label: string; icon: React.ReactNode; color: string }[] = [ { category: 'development', label: 'Entwicklungsnews', icon: , color: '#1e3a5f' }, { category: 'demand', label: 'Nachfrage 5 km', icon: , color: '#1a7a4a' }, { category: 'supply', label: 'Angebot 5 km', icon: , color: '#b45309' }, { category: 'negotiation', label: 'Verhandlungshinweise', icon: , color: '#7c3aed' }, ] function SignalCard({ signal }: { signal: PropertyMarketSignal }) { return ( {signal.title} {signal.confidence != null && ( )} {signal.body} {signal.confidence != null && ( KI-Konfidenz {Math.round(signal.confidence * 100)}% = 0.8 ? '#1a7a4a' : signal.confidence >= 0.6 ? '#d97706' : '#64748b' }, }} /> )} {signal.date && ( {new Date(signal.date).toLocaleDateString('de-CH')} )} {signal.source && ( {signal.source} )} ) } // ── PDF generation dialog ───────────────────────────────────────────────────── type PdfState = 'idle' | 'generating' | 'ready' interface BerichtDialogProps { open: boolean onClose: () => void report: MarketReport | null propertyId: string } function BerichtDialog({ open, onClose, report, propertyId }: BerichtDialogProps) { const [state, setState] = useState('idle') const [progress, setProgress] = useState(0) const timerRef = useRef | null>(null) useEffect(() => { if (!open) { setState('idle'); setProgress(0); return } setState('generating') setProgress(0) let p = 0 timerRef.current = setInterval(() => { p += Math.random() * 18 + 8 if (p >= 100) { p = 100 clearInterval(timerRef.current!) setTimeout(() => setState('ready'), 300) } setProgress(Math.min(100, p)) }, 200) return () => { if (timerRef.current) clearInterval(timerRef.current) } }, [open]) function handleDownload() { const signals = report?.signals ?? [] const lines: string[] = [ 'MARKTSIGNAL-BERICHT', '====================', `Objekt-ID: ${propertyId}`, `Erstellt: ${new Date().toLocaleString('de-CH')}`, '', ] for (const s of signals) { lines.push(`[${s.category.toUpperCase()}] ${s.title}`) lines.push(s.body) if (s.source) lines.push(`Quelle: ${s.source}`) lines.push('') } const blob = new Blob([lines.join('\n')], { type: 'application/pdf' }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `Marktsignal-Bericht_${propertyId}.pdf` a.click() URL.revokeObjectURL(url) onClose() } return ( Bericht erstellen {state === 'generating' && ( PDF-Bericht wird generiert… {Math.round(progress)}% — Marktsignale werden aufbereitet )} {state === 'ready' && ( Bericht ist bereit Dateiname Marktsignal-Bericht_{propertyId}.pdf {report?.signals.length ?? 0} Signale · {new Date().toLocaleDateString('de-CH')} )} ) } // ── Main tab component ──────────────────────────────────────────────────────── export function PropertyMarketSignalsTab({ propertyId }: Props) { const [report, setReport] = useState(null) const [loading, setLoading] = useState(true) const [dialogOpen, setDialogOpen] = useState(false) useEffect(() => { let cancelled = false setLoading(true) marketReportService.getByProperty(propertyId).then(r => { if (!cancelled) { setReport(r); setLoading(false) } }) return () => { cancelled = true } }, [propertyId]) if (loading) { return ( ) } return ( Marktsignale & Standortintelligenz {report?.generatedAt && ( Generiert {new Date(report.generatedAt).toLocaleDateString('de-CH')} )} {!report ? ( Für dieses Objekt liegen noch keine KI-generierten Marktsignale vor. ) : ( SECTION_CONFIG.map(({ category, label, icon, color }) => { const signals = report.signals.filter(s => s.category === category) if (signals.length === 0) return null return ( {icon} {label} {signals.map(s => )} ) }) )} ) }