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:
Benjamin Sutter
2026-05-21 20:41:46 +02:00
parent 34a4dcfb29
commit 27c53f3af1
13 changed files with 1126 additions and 299 deletions
@@ -6,6 +6,7 @@ import { TradeoffList } from './TradeoffList'
import { MatchDataQualitySummary } from './MatchDataQualitySummary'
import { MatchActionToolbar } from './MatchActionToolbar'
import { MatchCardRestrictedState } from './MatchCardRestrictedState'
import { ScoreInlineBreakdown } from './ScoreInlineBreakdown'
import type { MatchCardViewModel } from './MatchCardViewModel'
interface Props {
@@ -59,6 +60,18 @@ export function MatchCardExpanded({ vm }: Props) {
<Divider sx={{ mb: 2 }} />
{/* Score breakdown — full criteria with weights */}
{vm.scoreBreakdown && (
<Box sx={{ mb: 2 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 700, mb: 1.25, color: '#1e293b' }}>
Bewertungsherleitung
</Typography>
<ScoreInlineBreakdown scoreBreakdown={vm.scoreBreakdown} allFactors={vm.allFactors} />
</Box>
)}
<Divider sx={{ mb: 2 }} />
{/* Why it matches — all 3 reasons */}
{vm.reasons.length > 0 && (
<Box sx={{ mb: 2 }}>