feat: F006 supply dashboard — complete spec implementation
- dataQualityService with getPortfolioQualitySummary() - Service methods: getDashboardPropertiesSummary, getStrongMatches, getSignalSummary, getDashboardTasks added to respective services - dashboardService uses Promise.allSettled for partial error resilience - KpiCard: onClick + Tooltip support - KpiGrid: navigate actions and tooltips per card - StrongMatchMiniCard: Match Center, Prüfung, Vergleichen action buttons - FutureSignalWidget: time horizon distribution (0–6, 6–12, 12–24 Mo.) - SupplyDashboard: DashboardHeader, empty state, error state, partial widget errors, permission-based rendering per UserRole Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Card, CardContent, Chip, Typography } from '@mui/material'
|
||||
import { Card, CardContent, CardActionArea, Chip, Tooltip, Typography } from '@mui/material'
|
||||
import type { KpiCardData } from '../../domain/dashboard'
|
||||
|
||||
interface KpiCardProps {
|
||||
@@ -9,32 +9,53 @@ export function KpiCard({ card }: KpiCardProps) {
|
||||
const trendColor =
|
||||
card.trend === 'up' ? 'success' : card.trend === 'down' ? 'error' : 'default'
|
||||
|
||||
return (
|
||||
const content = (
|
||||
<CardContent sx={{ p: 2.5 }}>
|
||||
<Typography
|
||||
variant="overline"
|
||||
sx={{ color: 'text.secondary', lineHeight: 1.4, display: 'block' }}
|
||||
>
|
||||
{card.label}
|
||||
</Typography>
|
||||
<Typography variant="h4" sx={{ fontWeight: 700, mt: 0.5, mb: 0.5 }}>
|
||||
{card.value}
|
||||
</Typography>
|
||||
{card.trendLabel && (
|
||||
<Chip
|
||||
label={card.trendLabel}
|
||||
size="small"
|
||||
color={trendColor as 'success' | 'error' | 'default'}
|
||||
sx={{ mt: 0.5 }}
|
||||
/>
|
||||
)}
|
||||
</CardContent>
|
||||
)
|
||||
|
||||
const card_ = (
|
||||
<Card
|
||||
sx={{
|
||||
borderTop: card.accent ? `4px solid ${card.accent}` : '4px solid transparent',
|
||||
height: '100%',
|
||||
cursor: card.onClick ? 'pointer' : 'default',
|
||||
}}
|
||||
>
|
||||
<CardContent sx={{ p: 2.5 }}>
|
||||
<Typography
|
||||
variant="overline"
|
||||
sx={{ color: 'text.secondary', lineHeight: 1.4, display: 'block' }}
|
||||
>
|
||||
{card.label}
|
||||
</Typography>
|
||||
<Typography variant="h4" sx={{ fontWeight: 700, mt: 0.5, mb: 0.5 }}>
|
||||
{card.value}
|
||||
</Typography>
|
||||
{card.trendLabel && (
|
||||
<Chip
|
||||
label={card.trendLabel}
|
||||
size="small"
|
||||
color={trendColor as 'success' | 'error' | 'default'}
|
||||
sx={{ mt: 0.5 }}
|
||||
/>
|
||||
)}
|
||||
</CardContent>
|
||||
{card.onClick ? (
|
||||
<CardActionArea onClick={card.onClick} sx={{ height: '100%', alignItems: 'flex-start' }}>
|
||||
{content}
|
||||
</CardActionArea>
|
||||
) : (
|
||||
content
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
|
||||
if (card.tooltip) {
|
||||
return (
|
||||
<Tooltip title={card.tooltip} placement="top" arrow>
|
||||
<span style={{ display: 'contents' }}>{card_}</span>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
return card_
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user