ad84fe8289
.ts files cannot contain JSX — Vite/oxc parse error at the <Box> on line 83. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
92 lines
3.9 KiB
TypeScript
92 lines
3.9 KiB
TypeScript
import type { ReactNode } from 'react'
|
|
import { Box, LinearProgress, Typography } from '@mui/material'
|
|
import type { WeightingKey } from '../../domain/needBuilder'
|
|
import type { UnifiedMatchResult, VerifiedPortfolioResult, ExternalMarketResult, FutureAvailabilityResult } from '../../domain/unifiedResult'
|
|
|
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
|
|
export const HARD_CRITERIA = new Set(['area', 'location', 'budget', 'timing'])
|
|
export const SCORE_COLOR = (s: number) => s >= 78 ? '#1a7a4a' : s >= 52 ? '#d97706' : '#c0392b'
|
|
|
|
export function getProp(item: UnifiedMatchResult) {
|
|
return item.resultType !== 'FUTURE_AVAILABILITY'
|
|
? (item as VerifiedPortfolioResult | ExternalMarketResult).property
|
|
: null
|
|
}
|
|
|
|
export function getSig(item: UnifiedMatchResult) {
|
|
return item.resultType === 'FUTURE_AVAILABILITY'
|
|
? (item as FutureAvailabilityResult).signal
|
|
: null
|
|
}
|
|
|
|
export const TYPE_META: Record<string, { label: string; color: string }> = {
|
|
VERIFIED_PORTFOLIO: { label: 'Plattform', color: '#1e3a5f' },
|
|
EXTERNAL_MARKET: { label: 'Plattform', color: '#1e3a5f' },
|
|
MAISON_WORK: { label: 'Maison Work', color: '#0369a1' },
|
|
FUTURE_AVAILABILITY: { label: 'Zukunftssignal', color: '#7c3aed' },
|
|
}
|
|
|
|
export const RISK_LEVEL_ORDER: Record<string, number> = { CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3 }
|
|
|
|
export const CRITERION_ALIASES: Record<WeightingKey, string[]> = {
|
|
area: ['area', 'Fläche', 'fläche'],
|
|
location: ['location', 'Standort', 'standort'],
|
|
budget: ['budget', 'Budget', 'Mietpreis', 'mietpreis'],
|
|
timing: ['timing', 'Verfügbarkeit', 'verfügbarkeit'],
|
|
prestige: ['prestige', 'Prestige'],
|
|
accessibility: ['accessibility', 'ÖV-Anbindung', 'ÖV', 'Erreichbarkeit'],
|
|
expansionPotential:['expansionPotential', 'Expansionspotenzial'],
|
|
flexibility: ['flexibility', 'Flexibilität'],
|
|
visibility: ['visibility', 'Sichtbarkeit', 'visibilityScore'],
|
|
footfall: ['footfall', 'Passantenfrequenz', 'passerbyFrequency'],
|
|
talentAccess: ['talentAccess', 'Talent-Zugang', 'Talente'],
|
|
esg: ['esg', 'ESG', 'Nachhaltigkeit'],
|
|
taxEnvironment: ['taxEnvironment', 'Steuerlast', 'Steuerumfeld'],
|
|
}
|
|
|
|
export function getTitle(item: UnifiedMatchResult): string {
|
|
const prop = getProp(item)
|
|
const sig = getSig(item)
|
|
return prop?.title ?? sig?.companyName ?? sig?.locationHint ?? item.matchId.slice(0, 8)
|
|
}
|
|
|
|
// ── Row label cell ────────────────────────────────────────────────────────────
|
|
|
|
export const LABEL_SX = {
|
|
position: 'sticky' as const,
|
|
left: 0,
|
|
bgcolor: 'white',
|
|
zIndex: 1,
|
|
width: 200,
|
|
minWidth: 200,
|
|
color: '#64748b',
|
|
fontSize: 13,
|
|
fontWeight: 600,
|
|
borderRight: '1px solid #e2e8f0',
|
|
verticalAlign: 'top',
|
|
py: 1.5,
|
|
}
|
|
|
|
export const DATA_SX = {
|
|
borderLeft: '1px solid #f1f5f9',
|
|
minWidth: 220,
|
|
verticalAlign: 'top',
|
|
py: 1.5,
|
|
}
|
|
|
|
// ── Score bar helper ──────────────────────────────────────────────────────────
|
|
|
|
export function scoreBar(value: number, label?: string): ReactNode {
|
|
const color = value >= 0.8 ? '#1a7a4a' : value >= 0.6 ? '#d97706' : '#c0392b'
|
|
return (
|
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
<Box sx={{ width: 70 }}>
|
|
<LinearProgress variant="determinate" value={value * 100}
|
|
sx={{ height: 6, borderRadius: 3, '& .MuiLinearProgress-bar': { bgcolor: color } }} />
|
|
</Box>
|
|
<Typography variant="caption" sx={{ color }}>{label ?? `${Math.round(value * 100)}%`}</Typography>
|
|
</Box>
|
|
)
|
|
}
|