Files
property-match/src/pages/ops/AIMonitoring.tsx
T
Benjamin Sutter e20e0e9a2e fix: resolve all MUI v9 TypeScript build errors
- Move system props (fontWeight, lineHeight, display, alignItems, etc.) into sx
- Fix Lucide icon color prop (no sx support)
- Merge duplicate sx attributes
- Remove unused variables (location, _needId, v)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 10:37:07 +02:00

281 lines
9.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
Box,
Card,
Chip,
Typography,
LinearProgress,
Stack,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Alert,
CircularProgress,
} from '@mui/material'
import { useQuery } from '@tanstack/react-query'
import { propertyService } from '../../services/propertyService'
interface MetricCard {
label: string
value: string
color: string
note: string
}
const HEALTH_METRICS: MetricCard[] = [
{ label: 'Extraktionsgenauigkeit', value: '94%', color: '#1a7a4a', note: 'Ø letzte 30 Tage' },
{ label: 'Konfidenz-Ø', value: '73%', color: '#d97706', note: 'Alle Objekte' },
{ label: 'Validierungsrate', value: '88%', color: '#1a7a4a', note: 'Menschliche Bestätigung' },
{ label: 'Fehlerrate', value: '2.1%', color: '#1a7a4a', note: 'Kritische Fehler' },
]
interface AIDecision {
timestamp: string
type: string
confidence: string
result: string
impact: string
}
const RECENT_DECISIONS: AIDecision[] = [
{
timestamp: '15.05.2025 14:32',
type: 'Bedarfsextraktion',
confidence: '91%',
result: 'Kriterien extrahiert',
impact: 'Suche ausgelöst',
},
{
timestamp: '15.05.2025 11:15',
type: 'Match-Scoring',
confidence: '88%',
result: '3 Matches berechnet',
impact: 'Review ausgelöst',
},
{
timestamp: '14.05.2025 16:40',
type: 'Signal-Erkennung',
confidence: '72%',
result: 'Expansion erkannt',
impact: 'Signal erstellt',
},
{
timestamp: '14.05.2025 09:00',
type: 'Datenqualitätsprüfung',
confidence: '95%',
result: '2 Warnungen erkannt',
impact: 'Meldung erstellt',
},
{
timestamp: '13.05.2025 15:22',
type: 'Match-Scoring',
confidence: '84%',
result: '2 Matches berechnet',
impact: 'Review ausgelöst',
},
{
timestamp: '12.05.2025 10:11',
type: 'Signal-Erkennung',
confidence: '65%',
result: 'Möglicher Auszug erkannt',
impact: 'Signal erstellt',
},
]
function getConfidenceBadge(pct: string) {
const n = parseInt(pct)
const color = n >= 85 ? '#1a7a4a' : n >= 70 ? '#d97706' : '#c0392b'
return (
<Chip
label={pct}
size="small"
sx={{ bgcolor: color, color: 'white', fontWeight: 700, fontSize: 11 }}
/>
)
}
export default function AIMonitoring() {
const { data: propResp, isLoading } = useQuery({
queryKey: ['properties'],
queryFn: () => propertyService.getAll(),
})
const properties = propResp?.data ?? []
const highConf = properties.filter(p => p.confidenceScore > 0.85).length
const midConf = properties.filter(p => p.confidenceScore >= 0.65 && p.confidenceScore <= 0.85).length
const lowConf = properties.filter(p => p.confidenceScore < 0.65).length
const total = properties.length || 1
return (
<Box>
{/* Page Header */}
<Box
sx={{
bgcolor: 'white',
borderBottom: '1px solid #e2e8f0',
px: 3,
py: 2,
display: 'flex',
alignItems: 'center',
gap: 2,
}}
>
<Box sx={{ flex: 1 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
<Typography variant="h5" sx={{ fontWeight: 700 }} color="text.primary">
AI Monitoring
</Typography>
<Chip
label="Live"
size="small"
sx={{
bgcolor: '#1a7a4a',
color: 'white',
fontWeight: 700,
fontSize: 11,
animation: 'pulse 2s ease-in-out infinite',
'@keyframes pulse': {
'0%, 100%': { opacity: 1 },
'50%': { opacity: 0.6 },
},
}}
/>
</Box>
<Typography variant="body2" color="text.secondary">
AI-Layer Gesundheit und Entscheidungsqualität
</Typography>
</Box>
</Box>
<Box sx={{ px: 3, py: 3 }}>
{/* Health Metrics */}
<Box className="grid grid-cols-4 gap-4" sx={{ mb: 3 }}>
{HEALTH_METRICS.map(m => (
<Card key={m.label} sx={{ p: 2.5 }}>
<Typography variant="overline" color="text.secondary" sx={{ lineHeight: 1.4, display: 'block' }}>
{m.label}
</Typography>
<Typography variant="h3" sx={{ fontWeight: 800, color: m.color }}>
{m.value}
</Typography>
<Typography variant="caption" color="text.secondary">
{m.note}
</Typography>
</Card>
))}
</Box>
<Box className="grid grid-cols-2 gap-4" sx={{ mb: 3 }}>
{/* Confidence Distribution */}
<Card sx={{ p: 2.5 }}>
<Typography variant="subtitle1" sx={{ fontWeight: 600, mb: 2 }}>
Konfidenzverteilung
</Typography>
{isLoading ? (
<Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
<CircularProgress size={32} />
</Box>
) : (
<Stack spacing={2}>
<Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 0.5 }}>
<Typography variant="body2" sx={{ fontWeight: 500 }}>Hoch (&gt;85%)</Typography>
<Typography variant="body2" color="text.secondary">{highConf} Objekte</Typography>
</Box>
<LinearProgress
variant="determinate"
value={(highConf / total) * 100}
color="success"
sx={{ height: 10, borderRadius: 5 }}
/>
</Box>
<Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 0.5 }}>
<Typography variant="body2" sx={{ fontWeight: 500 }}>Mittel (6585%)</Typography>
<Typography variant="body2" color="text.secondary">{midConf} Objekte</Typography>
</Box>
<LinearProgress
variant="determinate"
value={(midConf / total) * 100}
color="warning"
sx={{ height: 10, borderRadius: 5 }}
/>
</Box>
<Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 0.5 }}>
<Typography variant="body2" sx={{ fontWeight: 500 }}>Niedrig (&lt;65%)</Typography>
<Typography variant="body2" color="text.secondary">{lowConf} Objekte</Typography>
</Box>
<LinearProgress
variant="determinate"
value={(lowConf / total) * 100}
color="error"
sx={{ height: 10, borderRadius: 5 }}
/>
</Box>
</Stack>
)}
</Card>
{/* Anomaly Alerts */}
<Card sx={{ p: 2.5 }}>
<Typography variant="subtitle1" sx={{ fontWeight: 600, mb: 2 }}>
Anomalien
</Typography>
<Stack spacing={1.5}>
<Alert severity="warning">
Mietpreisangaben für prop-004 weichen von Marktdurchschnitt ab (±31%). Manuelle Prüfung empfohlen.
</Alert>
<Alert severity="success">
Keine kritischen Anomalien erkannt. System läuft stabil.
</Alert>
</Stack>
</Card>
</Box>
{/* Recent AI Decisions */}
<Card>
<Box sx={{ px: 2.5, py: 2, borderBottom: '1px solid #f1f5f9' }}>
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
Letzte KI-Entscheidungen
</Typography>
</Box>
<Table>
<TableHead>
<TableRow sx={{ bgcolor: '#f8fafc' }}>
<TableCell sx={{ fontSize: 12, fontWeight: 600, color: '#64748b' }}>Zeitpunkt</TableCell>
<TableCell sx={{ fontSize: 12, fontWeight: 600, color: '#64748b' }}>Entscheidungstyp</TableCell>
<TableCell sx={{ fontSize: 12, fontWeight: 600, color: '#64748b' }}>Konfidenz</TableCell>
<TableCell sx={{ fontSize: 12, fontWeight: 600, color: '#64748b' }}>Ergebnis</TableCell>
<TableCell sx={{ fontSize: 12, fontWeight: 600, color: '#64748b' }}>Einfluss</TableCell>
</TableRow>
</TableHead>
<TableBody>
{RECENT_DECISIONS.map((d, i) => (
<TableRow key={i} hover>
<TableCell>
<Typography variant="caption" color="text.secondary">{d.timestamp}</Typography>
</TableCell>
<TableCell>
<Typography variant="body2" sx={{ fontWeight: 500 }}>{d.type}</Typography>
</TableCell>
<TableCell>{getConfidenceBadge(d.confidence)}</TableCell>
<TableCell>
<Typography variant="body2">{d.result}</Typography>
</TableCell>
<TableCell>
<Typography variant="body2" color="text.secondary">{d.impact}</Typography>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Card>
</Box>
</Box>
)
}