5035445d2e
All page-level headers now use fontSize:1.125rem, fontWeight:700, color:#0f172a, container py:2.5, borderBottom:#e8e7e4 — matching the standard established in PageHeader. Removes variant="h5"/h6" inconsistencies across Supply, Demand, and shared components. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
147 lines
4.9 KiB
TypeScript
147 lines
4.9 KiB
TypeScript
import {
|
|
Alert,
|
|
Box,
|
|
Button,
|
|
Card,
|
|
Chip,
|
|
Stack,
|
|
Table,
|
|
TableCell,
|
|
TableHead,
|
|
TableRow,
|
|
Typography,
|
|
} from '@mui/material'
|
|
import { useNavigate } from 'react-router'
|
|
import { useCompareStore } from '../../stores/compareStore'
|
|
import {
|
|
CompareEmptyState,
|
|
CompareColumnHeader,
|
|
AICompareSummary,
|
|
CompareCriteriaCard,
|
|
CompareTableBody,
|
|
} from '../../components/compare'
|
|
import { AddToPipelineDialog } from '../../components/shortlist'
|
|
import {
|
|
LABEL_SX,
|
|
DATA_SX,
|
|
} from '../../components/compare/compareUtils'
|
|
import { useCompareData } from '../../hooks/useCompareData'
|
|
|
|
// ── Main component ────────────────────────────────────────────────────────────
|
|
|
|
export default function Compare() {
|
|
const navigate = useNavigate()
|
|
const { compareItems, removeFromCompare, clearCompare } = useCompareStore()
|
|
|
|
const {
|
|
aiSummary,
|
|
aiLoading,
|
|
activeNeed,
|
|
relevantCriteria,
|
|
overallWinnerIdx,
|
|
bestScoreIdx,
|
|
worstConfIdx,
|
|
worstDQIdx,
|
|
missingCriticalCounts,
|
|
maxMissingCritical,
|
|
} = useCompareData(compareItems)
|
|
|
|
if (compareItems.length === 0) {
|
|
return (
|
|
<Box>
|
|
<Box sx={{ bgcolor: 'white', borderBottom: '1px solid #e8e7e4', px: 3, py: 2.5 }}>
|
|
<Typography sx={{ fontWeight: 700, fontSize: '1.125rem', lineHeight: 1.3, color: '#0f172a' }}>Vergleich</Typography>
|
|
</Box>
|
|
<CompareEmptyState />
|
|
</Box>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<Box>
|
|
<AddToPipelineDialog />
|
|
|
|
{/* Page header */}
|
|
<Box sx={{ bgcolor: 'white', borderBottom: '1px solid #e8e7e4', px: 3, py: 2.5, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
|
<Typography sx={{ fontWeight: 700, fontSize: '1.125rem', lineHeight: 1.3, color: '#0f172a' }}>Vergleich</Typography>
|
|
<Chip label={`${compareItems.length} Ergebnisse`} size="small" />
|
|
</Box>
|
|
<Button variant="outlined" size="small" color="error" onClick={clearCompare}>
|
|
Vergleich leeren
|
|
</Button>
|
|
</Box>
|
|
|
|
{/* Mobile notice */}
|
|
<Box sx={{ display: { xs: 'block', md: 'none' }, p: 3 }}>
|
|
<Alert severity="info">
|
|
Die Vergleichsansicht ist für Desktop optimiert. Für beste Erfahrung auf einem grösseren Bildschirm öffnen.
|
|
</Alert>
|
|
</Box>
|
|
|
|
{/* Desktop table */}
|
|
<Box sx={{ display: { xs: 'none', md: 'block' }, px: 3, py: 3, pb: 10 }}>
|
|
|
|
{/* AI Summary */}
|
|
<AICompareSummary summary={aiSummary} isLoading={aiLoading && compareItems.length >= 2} />
|
|
|
|
{/* Criteria Head-to-Head */}
|
|
{activeNeed && relevantCriteria.length > 0 && (
|
|
<CompareCriteriaCard
|
|
compareItems={compareItems}
|
|
relevantCriteria={relevantCriteria}
|
|
overallWinnerIdx={overallWinnerIdx}
|
|
activeNeed={activeNeed}
|
|
/>
|
|
)}
|
|
|
|
<Card sx={{ overflowX: 'auto' }}>
|
|
<Table sx={{ tableLayout: 'auto' }}>
|
|
|
|
{/* Column headers */}
|
|
<TableHead>
|
|
<TableRow sx={{ bgcolor: '#f8fafc' }}>
|
|
<TableCell sx={{ ...LABEL_SX, bgcolor: '#f8fafc', zIndex: 2, fontSize: 11, color: '#64748b', fontWeight: 600 }}>
|
|
Kriterium
|
|
</TableCell>
|
|
{compareItems.map(item => (
|
|
<TableCell key={item.matchId} sx={{ ...DATA_SX, bgcolor: '#f8fafc', verticalAlign: 'top' }}>
|
|
<CompareColumnHeader item={item} onRemove={() => removeFromCompare(item.matchId)} />
|
|
</TableCell>
|
|
))}
|
|
</TableRow>
|
|
</TableHead>
|
|
|
|
<CompareTableBody
|
|
compareItems={compareItems}
|
|
bestScoreIdx={bestScoreIdx}
|
|
worstConfIdx={worstConfIdx}
|
|
worstDQIdx={worstDQIdx}
|
|
missingCriticalCounts={missingCriticalCounts}
|
|
maxMissingCritical={maxMissingCritical}
|
|
/>
|
|
|
|
</Table>
|
|
</Card>
|
|
|
|
{/* Add more prompt */}
|
|
{compareItems.length < 4 && (
|
|
<Card sx={{ p: 2.5, mt: 2, border: '2px dashed #e2e8f0', boxShadow: 'none' }}>
|
|
<Stack direction="row" sx={{ alignItems: 'center', justifyContent: 'space-between' }}>
|
|
<Box>
|
|
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>Weiteres Ergebnis hinzufügen</Typography>
|
|
<Typography variant="caption" color="text.secondary">
|
|
Bis zu {4 - compareItems.length} weitere{4 - compareItems.length === 1 ? 's' : ''} Ergebnis{4 - compareItems.length === 1 ? '' : 'se'} möglich
|
|
</Typography>
|
|
</Box>
|
|
<Button variant="outlined" size="small" onClick={() => navigate('/demand/results')}>
|
|
Zur Suche
|
|
</Button>
|
|
</Stack>
|
|
</Card>
|
|
)}
|
|
</Box>
|
|
</Box>
|
|
)
|
|
}
|