feat: F017 data quality system — trust layer across all decision surfaces
New components: DataQualityBadge, DataQualityPanel, DataQualityProgress, FreshnessIndicator, CriticalFieldWarning, MissingDataList, ProvenancePanel Service: calculatePropertyQuality, getMissingCriticalFields, getQualityWarnings, getRecommendedActions with field→action mapping PropertyDetailView: tab 5/6 now use DataQualityPanel + ProvenancePanel Datenpflege page: DataQualityBadge, FreshnessIndicator, filter by level, recommended action column showing highest-priority next step per property Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import { Box, Button, Divider, Paper, Typography } from '@mui/material'
|
||||
import { DataQualityProgress } from './DataQualityProgress'
|
||||
import { CriticalFieldWarning } from './CriticalFieldWarning'
|
||||
import { MissingDataList } from './MissingDataList'
|
||||
import { ProvenancePanel } from './ProvenancePanel'
|
||||
import { DataQualityBadge } from './DataQualityBadge'
|
||||
import { getRecommendedActions } from '../../services/dataQualityService'
|
||||
import type { Property } from '../../domain/property'
|
||||
|
||||
interface DataQualityPanelProps {
|
||||
property: Property
|
||||
}
|
||||
|
||||
function SectionLabel({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<Typography variant="caption" sx={{
|
||||
fontWeight: 700,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
color: '#64748b',
|
||||
display: 'block',
|
||||
mb: 1,
|
||||
}}>
|
||||
{children}
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
|
||||
export function DataQualityPanel({ property }: DataQualityPanelProps) {
|
||||
const q = property.dataQuality
|
||||
const actions = getRecommendedActions(q, q.freshness)
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{/* ── Score & Dimensions ─────────────────────────────────────────── */}
|
||||
<Paper variant="outlined" sx={{ p: 2, mb: 2, bgcolor: '#fafafa' }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', mb: 1.5 }}>
|
||||
<Box>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 700, mb: 0.5 }}>Datenqualität</Typography>
|
||||
<DataQualityBadge quality={q} showLabel size="medium" />
|
||||
</Box>
|
||||
<Box sx={{ textAlign: 'right' }}>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ display: 'block' }}>
|
||||
{q.missingCriticalFields.length === 0 ? 'Alle Pflichtfelder vorhanden' : `${q.missingCriticalFields.length} Pflichtfeld(er) fehlen`}
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{q.missingOptionalFields.length} optionale Felder fehlen
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<DataQualityProgress property={property} />
|
||||
</Paper>
|
||||
|
||||
{/* ── Critical warnings ──────────────────────────────────────────── */}
|
||||
<CriticalFieldWarning
|
||||
fields={q.missingCriticalFields}
|
||||
warnings={q.warnings}
|
||||
/>
|
||||
|
||||
{/* ── Missing data with actions ──────────────────────────────────── */}
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<SectionLabel>Fehlende Daten & Massnahmen</SectionLabel>
|
||||
<MissingDataList
|
||||
criticalFields={q.missingCriticalFields}
|
||||
optionalFields={q.missingOptionalFields}
|
||||
recommendedActions={actions}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Divider sx={{ mb: 2 }} />
|
||||
|
||||
{/* ── Provenance ─────────────────────────────────────────────────── */}
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<SectionLabel>Datenherkunft & Verifikation</SectionLabel>
|
||||
<ProvenancePanel property={property} />
|
||||
</Box>
|
||||
|
||||
{/* ── Action button ──────────────────────────────────────────────── */}
|
||||
<Box sx={{ pt: 1 }}>
|
||||
<Button variant="outlined" size="small" sx={{ textTransform: 'none', mr: 1 }}>
|
||||
Datenaktualisierung anfragen
|
||||
</Button>
|
||||
{q.missingCriticalFields.length > 0 && (
|
||||
<Button variant="contained" size="small" sx={{ textTransform: 'none', bgcolor: '#1e3a5f' }}>
|
||||
Fehlende Felder ergänzen
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user