feat: score transparency on all cards + budget parser fix
- Single scoring system: MockupNeedProvider rewrites matches via calculateScore exclusively — allFactors always populated, no more dual-system (computeScore removed), weights from weightingProfile reflected in every breakdown - ScoreInlineBreakdown: new component shows hard/soft criteria with importance labels (Entscheidend/Sehr wichtig/…) and formula on compact + expanded cards - MatchCardAdapter: passes scoreBreakdown + allFactors to ViewModel - MatchDetail: 'Zukunftssignal' label replaced with 'Future Availability' - aiService budget parser: values < 100 treated as monthly and multiplied by 12 to produce correct annual CHF/m²/year value — fixes 0-result searches Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Box, Divider, LinearProgress, Link, Paper, Typography } from '@mui/material'
|
||||
import { CheckCircle2, ExternalLink, ShieldCheck, X } from 'lucide-react'
|
||||
import type { Match } from '../../domain/match'
|
||||
import { CheckCircle2, ShieldCheck, X } from 'lucide-react'
|
||||
import { ExternalLink } from 'lucide-react'
|
||||
import type { Match, ScoreFactor } from '../../domain/match'
|
||||
import type { FutureSignal } from '../../domain/futureSignal'
|
||||
|
||||
// ── Shared helpers ─────────────────────────────────────────────────────────────
|
||||
@@ -9,49 +10,205 @@ function scoreColor(v: number): 'success' | 'warning' | 'error' {
|
||||
return v >= 70 ? 'success' : v >= 50 ? 'warning' : 'error'
|
||||
}
|
||||
|
||||
function scoreTextColor(v: number): string {
|
||||
return v >= 70 ? '#1a7a4a' : v >= 50 ? '#d97706' : '#c0392b'
|
||||
}
|
||||
|
||||
const CREDIBILITY_LABELS: Record<string, string> = {
|
||||
HIGH: 'Hohe Quellenqualität',
|
||||
MEDIUM: 'Mittlere Quellenqualität',
|
||||
LOW: 'Niedrige Quellenqualität',
|
||||
}
|
||||
|
||||
// ── Standard breakdown (VERIFIED_PORTFOLIO / EXTERNAL / MAISON) ──────────────
|
||||
|
||||
interface BreakdownRowProps {
|
||||
label: string
|
||||
value: number
|
||||
max: number
|
||||
description: string
|
||||
color?: 'success' | 'warning' | 'error' | 'primary'
|
||||
modifier?: boolean
|
||||
const CRITERION_LABEL: Record<string, string> = {
|
||||
area: 'Fläche', location: 'Standort', budget: 'Budget', timing: 'Verfügbarkeit',
|
||||
prestige: 'Prestige', accessibility: 'Erreichbarkeit', expansionPotential: 'Expansion',
|
||||
flexibility: 'Flexibilität', visibility: 'Sichtbarkeit', footfall: 'Passantenfrequenz',
|
||||
talentAccess: 'Talent-Zugang', esg: 'ESG / Nachhaltigkeit', taxEnvironment: 'Steuerumfeld',
|
||||
}
|
||||
|
||||
function BreakdownRow({ label, value, max, description, color = 'primary', modifier }: BreakdownRowProps) {
|
||||
const pct = Math.round((Math.abs(value) / max) * 100)
|
||||
const isNegative = modifier && value < 0
|
||||
const isPositive = modifier && value > 0
|
||||
const HARD_KEYS = new Set(['area', 'location', 'budget', 'timing'])
|
||||
|
||||
function factorLabel(criterion: string): string {
|
||||
return CRITERION_LABEL[criterion] ?? criterion
|
||||
}
|
||||
|
||||
// Convert normalised weight to 1-5 importance level relative to other factors in the same set
|
||||
function importanceLabel(weight: number, maxWeight: number): { label: string; color: string } {
|
||||
const ratio = maxWeight > 0 ? weight / maxWeight : 0
|
||||
if (ratio >= 0.85) return { label: 'Entscheidend', color: '#1e3a5f' }
|
||||
if (ratio >= 0.65) return { label: 'Sehr wichtig', color: '#1d4ed8' }
|
||||
if (ratio >= 0.40) return { label: 'Wichtig', color: '#475569' }
|
||||
if (ratio >= 0.20) return { label: 'Wenig wichtig',color: '#94a3b8' }
|
||||
return { label: 'Unwichtig', color: '#cbd5e1' }
|
||||
}
|
||||
|
||||
// ── Single criterion row ───────────────────────────────────────────────────────
|
||||
|
||||
function CriterionRow({ factor, maxWeight }: { factor: ScoreFactor; maxWeight: number }) {
|
||||
const label = factorLabel(factor.criterion)
|
||||
const pct = Math.round(factor.weight * 100)
|
||||
const color = scoreColor(factor.score)
|
||||
const imp = importanceLabel(factor.weight, maxWeight)
|
||||
|
||||
return (
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 0.5 }}>
|
||||
<Typography variant="body2" sx={{ fontWeight: 500 }}>{label}</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{ fontWeight: 700, color: isNegative ? '#c0392b' : isPositive ? '#1a7a4a' : 'text.primary' }}
|
||||
>
|
||||
{modifier && value > 0 ? '+' : ''}{modifier ? value : `${value}/${max}`}
|
||||
<Box sx={{ mb: 1.5 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', mb: 0.4 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'baseline', gap: 0.75 }}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 600, color: '#1e293b', minWidth: 90 }}>{label}</Typography>
|
||||
<Typography variant="caption" sx={{ color: imp.color, fontSize: '0.68rem', fontWeight: 500 }}>
|
||||
{imp.label}
|
||||
</Typography>
|
||||
<Typography variant="caption" sx={{ color: '#cbd5e1', fontSize: '0.65rem' }}>
|
||||
{pct}%
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'baseline', gap: 1 }}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 700, color: scoreTextColor(factor.score) }}>
|
||||
{factor.score}/100
|
||||
</Typography>
|
||||
<Typography variant="caption" sx={{ color: '#64748b', fontSize: '0.68rem', minWidth: 44, textAlign: 'right' }}>
|
||||
{factor.contribution.toFixed(1)} Pkt
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
value={factor.score}
|
||||
color={color}
|
||||
sx={{ height: 5, borderRadius: 3, mb: 0.4 }}
|
||||
/>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', lineHeight: 1.3 }}>
|
||||
{factor.explanation}
|
||||
</Typography>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
// ── Standard breakdown (VERIFIED_PORTFOLIO / EXTERNAL / MAISON) ──────────────
|
||||
|
||||
interface StandardBreakdownProps {
|
||||
match: Match
|
||||
taxCalculatorUrl?: string
|
||||
}
|
||||
|
||||
function StandardBreakdown({ match, taxCalculatorUrl }: StandardBreakdownProps) {
|
||||
const sb = match.scoreBreakdown
|
||||
|
||||
// Use allFactors when available (typed English keys); fall back to pos + neg
|
||||
const hasAllFactors = !!match.allFactors
|
||||
const allFactors = match.allFactors ?? [...match.positiveFactors, ...(match.negativeFactors ?? [])]
|
||||
|
||||
const hardFactors = hasAllFactors
|
||||
? allFactors.filter(f => HARD_KEYS.has(f.criterion))
|
||||
: allFactors // static mock data: show flat, no grouping
|
||||
|
||||
const softFactors = hasAllFactors
|
||||
? allFactors.filter(f => !HARD_KEYS.has(f.criterion))
|
||||
: []
|
||||
|
||||
const hardContrib = Math.round(sb.hardMatchScore * 0.60 * 10) / 10
|
||||
const softContrib = Math.round(sb.softFactorScore * 0.40 * 10) / 10
|
||||
const maxHardWeight = Math.max(...hardFactors.map(f => f.weight), 0.001)
|
||||
const maxSoftWeight = Math.max(...softFactors.map(f => f.weight), 0.001)
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Hard criteria */}
|
||||
<Typography variant="caption" sx={{ fontWeight: 700, color: '#1e3a5f', textTransform: 'uppercase', letterSpacing: 0.6, display: 'block', mb: 1.25 }}>
|
||||
{hasAllFactors ? 'Hart-Kriterien' : 'Bewertungsfaktoren'}
|
||||
</Typography>
|
||||
|
||||
{hardFactors.map((f, i) => <CriterionRow key={i} factor={f} maxWeight={maxHardWeight} />)}
|
||||
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', bgcolor: '#eef2ff', px: 1.25, py: 0.75, borderRadius: 1, mt: 0.25, mb: hasAllFactors && softFactors.length > 0 ? 2 : 1.5 }}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 600, color: '#1e3a5f' }}>
|
||||
Hart-Kriterien {sb.hardMatchScore}/100 × 60%
|
||||
</Typography>
|
||||
<Typography variant="caption" sx={{ fontWeight: 800, color: '#1e3a5f' }}>
|
||||
{hardContrib} Pkt
|
||||
</Typography>
|
||||
</Box>
|
||||
{!modifier && (
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
value={Math.min(pct, 100)}
|
||||
sx={{ height: 8, borderRadius: 4, mb: 0.5 }}
|
||||
color={color}
|
||||
/>
|
||||
|
||||
{/* Soft factors: full list when allFactors present, otherwise summary row */}
|
||||
{!hasAllFactors && (
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', bgcolor: '#f8fafc', px: 1.25, py: 0.75, borderRadius: 1, mb: 1.5 }}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 600, color: '#475569' }}>
|
||||
Soft-Faktoren {sb.softFactorScore}/100 × 40%
|
||||
</Typography>
|
||||
<Typography variant="caption" sx={{ fontWeight: 800, color: '#475569' }}>
|
||||
{softContrib} Pkt
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
<Typography variant="caption" color="text.secondary">{description}</Typography>
|
||||
</Box>
|
||||
|
||||
{hasAllFactors && softFactors.length > 0 && (
|
||||
<>
|
||||
<Divider sx={{ mb: 2 }} />
|
||||
<Typography variant="caption" sx={{ fontWeight: 700, color: '#475569', textTransform: 'uppercase', letterSpacing: 0.6, display: 'block', mb: 1.25 }}>
|
||||
Soft-Faktoren
|
||||
</Typography>
|
||||
{softFactors.map((f, i) => <CriterionRow key={i} factor={f} maxWeight={maxSoftWeight} />)}
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', bgcolor: '#f8fafc', px: 1.25, py: 0.75, borderRadius: 1, mt: 0.25, mb: 1.5 }}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 600, color: '#475569' }}>
|
||||
Soft-Score {sb.softFactorScore}/100 × 40%
|
||||
</Typography>
|
||||
<Typography variant="caption" sx={{ fontWeight: 800, color: '#475569' }}>
|
||||
{softContrib} Pkt
|
||||
</Typography>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Tax link */}
|
||||
{taxCalculatorUrl && (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1.5, p: 1.25, bgcolor: '#f0f9ff', borderRadius: 1, border: '1px solid #bae6fd' }}>
|
||||
<ExternalLink size={13} color="#0369a1" style={{ flexShrink: 0 }} />
|
||||
<Box>
|
||||
<Typography variant="caption" sx={{ color: '#0369a1', fontWeight: 600, display: 'block', lineHeight: 1.2 }}>
|
||||
Steuerlast in Bewertung eingeflossen
|
||||
</Typography>
|
||||
<Link
|
||||
href={taxCalculatorUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
underline="hover"
|
||||
sx={{ fontSize: '0.72rem', color: '#0369a1' }}
|
||||
>
|
||||
Steuerrechner Gemeinde öffnen →
|
||||
</Link>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Divider sx={{ mb: 1.5 }} />
|
||||
|
||||
{/* Formula row — always visible */}
|
||||
<Box sx={{ bgcolor: '#f8fafc', px: 1.25, py: 0.75, borderRadius: 1, mb: 1.25 }}>
|
||||
<Typography variant="caption" sx={{ color: '#64748b', display: 'block', mb: 0.4, fontWeight: 500 }}>
|
||||
Berechnung
|
||||
</Typography>
|
||||
<Typography variant="caption" sx={{ fontFamily: 'monospace', color: '#1e293b', display: 'block', lineHeight: 1.6 }}>
|
||||
Hart {sb.hardMatchScore} × 60% + Soft {sb.softFactorScore} × 40%
|
||||
{' = '}{hardContrib} + {softContrib} = {sb.totalScore}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Total */}
|
||||
<Box sx={{
|
||||
display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
|
||||
bgcolor: sb.totalScore >= 78 ? '#f0fdf4' : sb.totalScore >= 52 ? '#fffbeb' : '#fef2f2',
|
||||
p: 1.5, borderRadius: 1,
|
||||
}}>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 700 }}>Gesamt-Score</Typography>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{ fontWeight: 800, color: scoreTextColor(sb.totalScore) }}
|
||||
>
|
||||
{sb.totalScore}/100
|
||||
</Typography>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -68,11 +225,15 @@ function SignalScoreDerivation({ match, signal }: SignalScoreDerivationProps) {
|
||||
const probPct = signal ? Math.round(signal.probability * 100) : null
|
||||
const credLabel = signal ? (CREDIBILITY_LABELS[signal.source.credibility] ?? signal.source.credibility) : null
|
||||
|
||||
// Approximate signal deduction for display purposes
|
||||
const signalDeduction = signal && !isVerifiedContract
|
||||
? Math.round((1 - signal.probability) * sb.hardMatchScore * 0.18)
|
||||
: 0
|
||||
|
||||
// Use allFactors or fall back to pos + neg
|
||||
const displayFactors = match.allFactors
|
||||
? match.allFactors
|
||||
: [...match.positiveFactors, ...(match.negativeFactors ?? [])]
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Schritt 1 — Kriterien-Match */}
|
||||
@@ -84,47 +245,32 @@ function SignalScoreDerivation({ match, signal }: SignalScoreDerivationProps) {
|
||||
Wie gut passt die Fläche, wenn das Signal eintrifft?
|
||||
</Typography>
|
||||
|
||||
{match.positiveFactors.map((f, i) => (
|
||||
<Box key={i} sx={{ mb: 1.25 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.6, mb: 0.4 }}>
|
||||
<CheckCircle2 size={12} color="#1a7a4a" style={{ flexShrink: 0 }} />
|
||||
<Box sx={{ flex: 1, display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 600, color: '#1e293b' }}>{f.criterion}</Typography>
|
||||
<Typography variant="caption" sx={{ fontWeight: 700, color: '#1a7a4a', ml: 1 }}>{f.score}/100</Typography>
|
||||
{displayFactors.map((f, i) => {
|
||||
const isPositive = f.score >= 70
|
||||
return (
|
||||
<Box key={i} sx={{ mb: 1.25 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.6, mb: 0.4 }}>
|
||||
{isPositive
|
||||
? <CheckCircle2 size={12} color="#1a7a4a" style={{ flexShrink: 0 }} />
|
||||
: <X size={12} color="#c0392b" style={{ flexShrink: 0 }} />
|
||||
}
|
||||
<Box sx={{ flex: 1, display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 600, color: '#1e293b' }}>{factorLabel(f.criterion)}</Typography>
|
||||
<Typography variant="caption" sx={{ fontWeight: 700, color: scoreTextColor(f.score), ml: 1 }}>{f.score}/100</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
value={f.score}
|
||||
color={scoreColor(f.score)}
|
||||
sx={{ height: 5, borderRadius: 3, mb: 0.35, ml: 2.25 }}
|
||||
/>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', ml: 2.25, lineHeight: 1.3 }}>
|
||||
{f.explanation}
|
||||
</Typography>
|
||||
</Box>
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
value={f.score}
|
||||
color={scoreColor(f.score)}
|
||||
sx={{ height: 5, borderRadius: 3, mb: 0.35, ml: 2.25 }}
|
||||
/>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', ml: 2.25, lineHeight: 1.3 }}>
|
||||
{f.explanation}
|
||||
</Typography>
|
||||
</Box>
|
||||
))}
|
||||
|
||||
{(match.negativeFactors ?? []).map((f, i) => (
|
||||
<Box key={i} sx={{ mb: 1.25 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.6, mb: 0.4 }}>
|
||||
<X size={12} color="#c0392b" style={{ flexShrink: 0 }} />
|
||||
<Box sx={{ flex: 1, display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 600, color: '#1e293b' }}>{f.criterion}</Typography>
|
||||
<Typography variant="caption" sx={{ fontWeight: 700, color: '#c0392b', ml: 1 }}>{f.score}/100</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
value={f.score}
|
||||
color="error"
|
||||
sx={{ height: 5, borderRadius: 3, mb: 0.35, ml: 2.25 }}
|
||||
/>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', ml: 2.25, lineHeight: 1.3 }}>
|
||||
{f.explanation}
|
||||
</Typography>
|
||||
</Box>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', bgcolor: '#f8fafc', px: 1.25, py: 0.75, borderRadius: 1, mt: 0.5 }}>
|
||||
<Typography variant="caption" sx={{ fontWeight: 600, color: '#475569' }}>Basis-Score</Typography>
|
||||
@@ -188,10 +334,7 @@ function SignalScoreDerivation({ match, signal }: SignalScoreDerivationProps) {
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 700 }}>Gesamt-Score</Typography>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
fontWeight: 800,
|
||||
color: sb.totalScore >= 78 ? '#1a7a4a' : sb.totalScore >= 52 ? '#d97706' : '#c0392b',
|
||||
}}
|
||||
sx={{ fontWeight: 800, color: scoreTextColor(sb.totalScore) }}
|
||||
>
|
||||
{sb.totalScore}/100
|
||||
</Typography>
|
||||
@@ -210,8 +353,6 @@ interface Props {
|
||||
}
|
||||
|
||||
export function ScoreBreakdownPanel({ match, taxCalculatorUrl, isFuture, signal }: Props) {
|
||||
const sb = match.scoreBreakdown
|
||||
|
||||
if (isFuture) {
|
||||
return (
|
||||
<Paper sx={{ p: 2.5, mb: 2 }}>
|
||||
@@ -221,79 +362,10 @@ export function ScoreBreakdownPanel({ match, taxCalculatorUrl, isFuture, signal
|
||||
)
|
||||
}
|
||||
|
||||
const hardColor = scoreColor(sb.hardMatchScore)
|
||||
const softColor = scoreColor(sb.softFactorScore)
|
||||
|
||||
return (
|
||||
<Paper sx={{ p: 2.5, mb: 2 }}>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700, mb: 2 }}>Score Breakdown</Typography>
|
||||
|
||||
<BreakdownRow
|
||||
label="Hard-Kriterien (60%)"
|
||||
value={sb.hardMatchScore}
|
||||
max={100}
|
||||
description="Fläche, Standort, Budget, Timing"
|
||||
color={hardColor}
|
||||
/>
|
||||
<BreakdownRow
|
||||
label="Soft Factors (40%)"
|
||||
value={sb.softFactorScore}
|
||||
max={100}
|
||||
description="Prestige, Erreichbarkeit, ESG, Steuerlast"
|
||||
color={softColor}
|
||||
/>
|
||||
|
||||
{taxCalculatorUrl && (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1.5, p: 1.25, bgcolor: '#f0f9ff', borderRadius: 1, border: '1px solid #bae6fd' }}>
|
||||
<ExternalLink size={13} color="#0369a1" style={{ flexShrink: 0 }} />
|
||||
<Box>
|
||||
<Typography variant="caption" sx={{ color: '#0369a1', fontWeight: 600, display: 'block', lineHeight: 1.2 }}>
|
||||
KI hat Steuerlast bewertet
|
||||
</Typography>
|
||||
<Link
|
||||
href={taxCalculatorUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
underline="hover"
|
||||
sx={{ fontSize: '0.72rem', color: '#0369a1' }}
|
||||
>
|
||||
Steuerrechner Gemeinde öffnen →
|
||||
</Link>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Divider sx={{ my: 1.5 }} />
|
||||
|
||||
<BreakdownRow
|
||||
label="Datenqualitäts-Modifier"
|
||||
value={sb.dataQualityModifier}
|
||||
max={15}
|
||||
description="Basierend auf Vollständigkeit und Aktualität der Objektdaten"
|
||||
modifier
|
||||
/>
|
||||
<BreakdownRow
|
||||
label="Konfidenz-Modifier"
|
||||
value={sb.confidenceModifier}
|
||||
max={15}
|
||||
description="Basierend auf Quelltyp (Verified/Extern/Signal)"
|
||||
modifier
|
||||
/>
|
||||
|
||||
<Divider sx={{ my: 1.5 }} />
|
||||
|
||||
<Box sx={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', bgcolor: '#f8fafc', p: 1.5, borderRadius: 1 }}>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 700 }}>Gesamt-Score</Typography>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
fontWeight: 800,
|
||||
color: sb.totalScore >= 78 ? '#1a7a4a' : sb.totalScore >= 52 ? '#d97706' : '#c0392b',
|
||||
}}
|
||||
>
|
||||
{sb.totalScore}/100
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography variant="h6" sx={{ fontWeight: 700, mb: 2 }}>Bewertungsherleitung</Typography>
|
||||
<StandardBreakdown match={match} taxCalculatorUrl={taxCalculatorUrl} />
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user