feat(F020): frontend infrastructure foundation

- src/lib/constants.ts: centralized route paths, labels, thresholds
- src/lib/utils.ts: formatting, color helpers, math utils
- src/hooks/: useProperties, useMatches, useFutureSignals, useNeeds
- src/components/scores/MatchScoreRing
- src/components/data-quality/DataQualityBar
- src/components/future-signals/SignalTypeBadge
- src/components/cards/SourceTypeBadge
- src/provider/AuthProvider (placeholder, swappable)
- main.tsx: AuthProvider + centralized stale time

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-15 10:50:26 +02:00
parent e20e0e9a2e
commit 3b45b0c121
17 changed files with 600 additions and 2 deletions
+57
View File
@@ -0,0 +1,57 @@
import { Box, Typography } from '@mui/material'
import { matchScoreColor } from '../../lib/utils'
import type { MatchStrength } from '../../domain/enums'
import { MATCH_STRENGTH_LABELS } from '../../lib/constants'
interface MatchScoreRingProps {
score: number
strength: MatchStrength
size?: 'sm' | 'md' | 'lg'
showLabel?: boolean
}
const SIZE_MAP = {
sm: { ring: 48, font: '0.875rem', label: '0.625rem' },
md: { ring: 64, font: '1.125rem', label: '0.75rem' },
lg: { ring: 80, font: '1.5rem', label: '0.8125rem' },
}
const COLOR_MAP = {
success: '#1a7a4a',
warning: '#d97706',
error: '#c0392b',
}
export function MatchScoreRing({ score, strength, size = 'md', showLabel = true }: MatchScoreRingProps) {
const dim = SIZE_MAP[size]
const colorKey = matchScoreColor(score)
const color = COLOR_MAP[colorKey]
const bgColor = colorKey === 'success' ? 'rgba(26,122,74,0.08)' : colorKey === 'warning' ? 'rgba(217,119,6,0.08)' : 'rgba(192,57,43,0.08)'
return (
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 0.5 }}>
<Box
sx={{
width: dim.ring,
height: dim.ring,
borderRadius: '50%',
border: `3px solid ${color}`,
bgcolor: bgColor,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0,
}}
>
<Typography sx={{ fontSize: dim.font, fontWeight: 700, color, lineHeight: 1 }}>
{score}
</Typography>
</Box>
{showLabel && (
<Typography sx={{ fontSize: dim.label, fontWeight: 600, color, textTransform: 'uppercase', letterSpacing: '0.06em' }}>
{MATCH_STRENGTH_LABELS[strength]}
</Typography>
)}
</Box>
)
}
+1
View File
@@ -0,0 +1 @@
export { MatchScoreRing } from './MatchScoreRing'