import { useState } from 'react' import { Box, Card, Chip, LinearProgress, MenuItem, Select, Table, TableBody, TableCell, TableHead, TableRow, Tooltip, Typography, } from '@mui/material' import { useQuery } from '@tanstack/react-query' import { ErrorState, LoadingPage, SectionContainer } from '../../components/ui' import { DataQualityBadge, FreshnessIndicator } from '../../components/data-quality' import { propertyService } from '../../services/propertyService' import { getRecommendedActions } from '../../services/dataQualityService' import { DataFreshness } from '../../domain/enums' type QualityFilter = '' | 'HIGH' | 'MEDIUM' | 'LOW' | 'INCOMPLETE' function getQualityColor(score: number): 'success' | 'warning' | 'error' { if (score >= 0.8) return 'success' if (score >= 0.6) return 'warning' return 'error' } export default function DataQuality() { const [qualityFilter, setQualityFilter] = useState('') const { data: resp, isLoading, error } = useQuery({ queryKey: ['properties'], queryFn: () => propertyService.getAll(), }) if (isLoading) return if (error) return const properties = resp?.data ?? [] const avgScore = properties.length ? properties.reduce((sum, p) => sum + p.dataQuality.score, 0) / properties.length : 0 const criticalIssues = properties.filter(p => p.dataQuality.missingCriticalFields.length > 0) const staleData = properties.filter( p => p.dataQuality.freshness === DataFreshness.STALE || p.dataQuality.freshness === DataFreshness.OUTDATED, ) const highQuality = properties.filter(p => p.dataQuality.score >= 0.8) const medQuality = properties.filter(p => p.dataQuality.score >= 0.6 && p.dataQuality.score < 0.8) const lowQuality = properties.filter(p => p.dataQuality.score < 0.6) const filtered = [...properties] .filter(p => { if (!qualityFilter) return true if (qualityFilter === 'INCOMPLETE') return p.dataQuality.missingCriticalFields.length > 0 if (qualityFilter === 'HIGH') return p.dataQuality.score >= 0.8 && p.dataQuality.missingCriticalFields.length === 0 if (qualityFilter === 'MEDIUM') return p.dataQuality.score >= 0.6 && p.dataQuality.score < 0.8 if (qualityFilter === 'LOW') return p.dataQuality.score < 0.6 return true }) .sort((a, b) => a.dataQuality.score - b.dataQuality.score) return ( {/* Page Header */} Datenpflege Vollständigkeit, Aktualität und Vertrauen der Objektdaten {/* Summary Stats */} Ø Qualitätsscore = 0.8 ? '#1a7a4a' : avgScore >= 0.6 ? '#d97706' : '#c0392b' }}> {Math.round(avgScore * 100)}% Pflichtfelder fehlen 2 ? '#c0392b' : criticalIssues.length > 0 ? '#d97706' : '#1a7a4a' }}> {criticalIssues.length} von {properties.length} Objekten Veraltete Daten 2 ? '#c0392b' : staleData.length > 0 ? '#d97706' : '#1a7a4a' }}> {staleData.length} von {properties.length} Objekten {/* Quality Distribution */} {[ { label: 'Hoch (≥80%)', count: highQuality.length, color: 'success' as const }, { label: 'Mittel (60–79%)', count: medQuality.length, color: 'warning' as const }, { label: 'Niedrig (<60%)', count: lowQuality.length, color: 'error' as const }, ].map(row => ( {row.label} 0 ? (row.count / properties.length) * 100 : 0} color={row.color} sx={{ height: 10, borderRadius: 5 }} /> {properties.length > 0 ? Math.round((row.count / properties.length) * 100) : 0}% ))} {/* Objects Table */} {/* Filter bar */} {filtered.length} von {properties.length} Objekten Objekt Qualität Aktualität Pflichtfelder Nächste Massnahme {filtered.map(property => { const q = property.dataQuality const hasCritical = q.missingCriticalFields.length > 0 const actions = getRecommendedActions(q, q.freshness) const topAction = actions[0] ?? null return ( {property.title} {property.location.city} {q.missingCriticalFields.length === 0 ? ( ) : ( )} {topAction ? ( {topAction.label} {topAction.detail} ) : ( )} ) })}
) }