da50f3b5ea
- Design tokens (ds.ts, theme.ts, scoreTheme.ts): new warm palette (#152642 navy, #f9f8f6 warm white, #e8e7e4 borders, #b8975a gold accent), flat score tier badges replacing CSS gradients, Inter + DM Serif Display typography - Card components: white-background cards, DM Serif score numbers, max-2 badge chips with +N overflow tooltip, editorial score badge positioning - Layout shell: gold left-accent nav active state, 64px top bar, outlined workspace chip, DM Serif page titles - Shared atoms: GenericBadge (outlined/solid variants), ResultFilterBar (simplified chip styles), DecisionContextPanel (dot metrics, no left accent) - Global replacement (118 files): #1e3a5f→#152642, #e2e8f0→#e8e7e4, #f4f6f9→#f9f8f6 — all handled via Node.js for proper UTF-8 safety Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
101 lines
5.1 KiB
TypeScript
101 lines
5.1 KiB
TypeScript
import { Box, Card, Chip, Table, TableBody, TableCell, TableHead, TableRow, Typography } from '@mui/material'
|
|
import { Trophy, CheckCircle2 } from 'lucide-react'
|
|
import { LABEL_SX, DATA_SX, CRITERION_ALIASES, getTitle, scoreBar } from './compareUtils'
|
|
import type { WeightingKey } from '../../domain/needBuilder'
|
|
import { MissingDataCell } from './CompareCell'
|
|
import type { UnifiedMatchResult } from '../../domain/unifiedResult'
|
|
|
|
interface Props {
|
|
compareItems: UnifiedMatchResult[]
|
|
relevantCriteria: Array<{ key: string; label: string; weight: number }>
|
|
overallWinnerIdx: number
|
|
activeNeed: { companyName: string }
|
|
}
|
|
|
|
export function CompareCriteriaCard({ compareItems, relevantCriteria, overallWinnerIdx, activeNeed }: Props) {
|
|
return (
|
|
<Card sx={{ mb: 3, overflow: 'hidden' }}>
|
|
<Box sx={{ p: 2.5, borderBottom: '1px solid #e2e8f0', bgcolor: '#f8fafc', display: 'flex', alignItems: 'center', gap: 2 }}>
|
|
<Trophy size={18} color="#d4920e" />
|
|
<Box>
|
|
<Typography variant="h6" sx={{ fontWeight: 700, lineHeight: 1.2 }}>Vergleich nach Suchkriterien</Typography>
|
|
<Typography variant="caption" color="text.secondary">Basierend auf der Suche: {activeNeed.companyName}</Typography>
|
|
</Box>
|
|
{overallWinnerIdx !== -1 && (
|
|
<Box sx={{ ml: 'auto', bgcolor: '#fffbeb', border: '1px solid #fbbf24', borderRadius: 2, px: 2, py: 1, display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
<Trophy size={16} color="#d4920e" />
|
|
<Box>
|
|
<Typography variant="caption" sx={{ color: '#92400e', fontWeight: 700, display: 'block', lineHeight: 1 }}>Gesamtsieger</Typography>
|
|
<Typography variant="body2" sx={{ fontWeight: 800, color: '#7a4f00' }}>{getTitle(compareItems[overallWinnerIdx])}</Typography>
|
|
</Box>
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
<Box sx={{ overflowX: 'auto' }}>
|
|
<Table sx={{ tableLayout: 'auto' }}>
|
|
<TableHead>
|
|
<TableRow sx={{ bgcolor: '#f8fafc' }}>
|
|
<TableCell sx={{ ...LABEL_SX, bgcolor: '#f8fafc', fontSize: 11 }}>Kriterium</TableCell>
|
|
{compareItems.map(item => (
|
|
<TableCell key={item.matchId} sx={{ ...DATA_SX, bgcolor: '#f8fafc' }}>
|
|
<Typography variant="caption" sx={{ fontWeight: 700, color: '#152642' }} noWrap>
|
|
{getTitle(item)}
|
|
</Typography>
|
|
</TableCell>
|
|
))}
|
|
</TableRow>
|
|
</TableHead>
|
|
<TableBody>
|
|
{relevantCriteria.map(({ key, label }) => {
|
|
const allCriteria = (item: typeof compareItems[0]) =>
|
|
item.match.allFactors ?? [...item.match.positiveFactors, ...item.match.negativeFactors]
|
|
|
|
const factors = compareItems.map(item =>
|
|
allCriteria(item).find(f => CRITERION_ALIASES[key as WeightingKey].some(a => a.toLowerCase() === f.criterion.toLowerCase()))
|
|
)
|
|
const numericScores = factors.map(f => f?.score ?? null)
|
|
const presentScores = numericScores.filter((s): s is number => s !== null)
|
|
const maxScore = presentScores.length > 0 ? Math.max(...presentScores) : 0
|
|
const winnerIdx = compareItems.length > 1 && presentScores.length > 1 && numericScores.filter(s => s === maxScore).length === 1
|
|
? numericScores.indexOf(maxScore)
|
|
: -1
|
|
|
|
return (
|
|
<TableRow key={key} hover>
|
|
<TableCell sx={LABEL_SX}>
|
|
<Typography variant="body2" sx={{ fontWeight: 600 }}>{label}</Typography>
|
|
</TableCell>
|
|
{factors.map((factor, idx) => (
|
|
<TableCell key={idx} sx={{ ...DATA_SX, bgcolor: idx === winnerIdx ? 'rgba(26,122,74,0.05)' : undefined }}>
|
|
{factor ? (
|
|
<Box>
|
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 0.5 }}>
|
|
{scoreBar(factor.score / 100)}
|
|
{idx === winnerIdx && (
|
|
<Chip label="Besser" size="small"
|
|
icon={<CheckCircle2 size={10} />}
|
|
sx={{ height: 18, fontSize: 9, bgcolor: '#f0fdf4', color: '#166534',
|
|
'& .MuiChip-icon': { color: '#1a7a4a', ml: 0.5 },
|
|
'& .MuiChip-label': { px: 0.75 } }} />
|
|
)}
|
|
</Box>
|
|
<Typography variant="caption" color="text.secondary"
|
|
sx={{ display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden', lineHeight: 1.4 }}>
|
|
{factor.explanation}
|
|
</Typography>
|
|
</Box>
|
|
) : (
|
|
<MissingDataCell reason="Kein Score für dieses Kriterium" />
|
|
)}
|
|
</TableCell>
|
|
))}
|
|
</TableRow>
|
|
)
|
|
})}
|
|
</TableBody>
|
|
</Table>
|
|
</Box>
|
|
</Card>
|
|
)
|
|
}
|