feat: F012 match detail view
Full decision analysis page at /demand/results/:matchId: - MatchDetailHeader: score, badges, CTA (compare/shortlist), back button - ExecutiveSummaryPanel: 4-row summary (fit, top reason, tradeoff, next step) - PropertyOverviewPanel: key facts grid; signal-aware for FUTURE_AVAILABILITY - NeedAlignmentPanel: comparison table with fit indicators (MATCH/PARTIAL/NO_MATCH) - ScoreBreakdownPanel: hard/soft scores + modifiers + total (sidebar) - TradeoffPanel: severity-coded list with mitigation hints - RiskPanel: risks sorted CRITICAL→LOW with category chips - MissingInformationPanel: priority-grouped with per-item CTAs - SourceProvenancePanel: source type, label, URL, freshness - FutureAvailabilityContextPanel: mandatory disclaimer + signal metadata - NextActionsPanel: engine-ranked actions + standard actions (sidebar) - Details button on result cards now navigates to match detail Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import { Box, Chip, Paper, Typography } from '@mui/material'
|
||||
import { ArrowLeftRight } from 'lucide-react'
|
||||
import type { Match } from '../../domain/match'
|
||||
|
||||
const SEVERITY_META: Record<string, { label: string; color: 'error' | 'warning' | 'default' }> = {
|
||||
HIGH: { label: 'Hoch', color: 'error' },
|
||||
MEDIUM: { label: 'Mittel', color: 'warning' },
|
||||
LOW: { label: 'Gering', color: 'default' },
|
||||
}
|
||||
|
||||
interface Props {
|
||||
match: Match
|
||||
}
|
||||
|
||||
export function TradeoffPanel({ match }: Props) {
|
||||
const tradeoffs = match.tradeoffs ?? []
|
||||
if (tradeoffs.length === 0) return null
|
||||
|
||||
return (
|
||||
<Paper sx={{ p: 2.5, mb: 2 }}>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700, mb: 1.5 }}>Abwägungen</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
||||
{tradeoffs.map((t, i) => {
|
||||
const meta = SEVERITY_META[t.severity] ?? SEVERITY_META.LOW
|
||||
return (
|
||||
<Box
|
||||
key={i}
|
||||
sx={{ p: 1.5, bgcolor: '#fffbeb', border: '1px solid #fde68a', borderRadius: 1 }}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 0.75 }}>
|
||||
<ArrowLeftRight size={15} color="#d97706" />
|
||||
<Typography variant="body2" sx={{ fontWeight: 600, flex: 1 }}>
|
||||
{t.criterion}
|
||||
</Typography>
|
||||
<Chip label={meta.label} size="small" color={meta.color} variant="outlined" />
|
||||
</Box>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 0.5 }}>
|
||||
{t.concern}
|
||||
</Typography>
|
||||
{t.mitigation && (
|
||||
<Typography variant="caption" color="text.secondary" sx={{ fontStyle: 'italic' }}>
|
||||
Mitigation: {t.mitigation}
|
||||
</Typography>
|
||||
)}
|
||||
{t.impactOnScore !== undefined && (
|
||||
<Typography variant="caption" sx={{ display: 'block', mt: 0.5, color: '#c0392b' }}>
|
||||
Score-Einfluss: {t.impactOnScore} Punkte
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
})}
|
||||
</Box>
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user