feat: F006 supply dashboard — KPI grid, strong matches, data quality, future signals, review tasks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-15 16:08:49 +02:00
parent c65e565dc7
commit 46acca9e62
15 changed files with 833 additions and 280 deletions
@@ -0,0 +1,59 @@
import { Box, Card, CardContent, Chip, Typography } from '@mui/material'
import type { StrongMatchItem } from '../../domain/dashboard'
interface StrongMatchMiniCardProps {
match: StrongMatchItem
}
function scoreColor(score: number): string {
if (score >= 80) return '#1a7a4a'
if (score >= 60) return '#d97706'
return '#c0392b'
}
export function StrongMatchMiniCard({ match }: StrongMatchMiniCardProps) {
return (
<Card variant="outlined" sx={{ mb: 1 }}>
<CardContent sx={{ p: 2, '&:last-child': { pb: 2 } }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', mb: 0.5 }}>
<Typography variant="body2" sx={{ fontWeight: 600, flex: 1 }}>
{match.propertyTitle}
</Typography>
<Chip
label={`${match.matchScore}`}
size="small"
sx={{
ml: 1,
fontWeight: 700,
bgcolor: scoreColor(match.matchScore),
color: 'white',
flexShrink: 0,
}}
/>
</Box>
<Typography variant="caption" sx={{ color: 'text.secondary', display: 'block' }}>
{match.propertyAddress}
</Typography>
<Typography variant="caption" sx={{ color: 'text.secondary', display: 'block', mt: 0.5 }}>
{match.topReason}
</Typography>
<Box sx={{ display: 'flex', gap: 1, mt: 1, flexWrap: 'wrap' }}>
{match.missingDataCount > 0 && (
<Chip
label={`${match.missingDataCount} Felder fehlen`}
size="small"
color="warning"
/>
)}
{match.nextBestAction !== '' && (
<Chip
label={match.nextBestAction}
size="small"
variant="outlined"
/>
)}
</Box>
</CardContent>
</Card>
)
}