feat: redesign Schattenmarkt card & detail view for explainability

- Remove abstract Konfidenz/DQ badges from match detail header
- Replace ScoreBreakdownPanel with 3-step Bewertungsherleitung for
  FUTURE_AVAILABILITY: Kriterien-Match per factor with bars, Signal-
  Abschlag (verified ERP = kein Abschlag / probabilistic = -N Punkte),
  Gesamt-Score
- Restructure FutureAvailabilityContextPanel: source section first,
  verified ERP box for LEASE_CONTRACT, remove Konfidenz row, disclaimer
  as grey footnote instead of yellow alert
- Add LEASE_EXPIRY to signal type/relevance bridge labels
- Show up to 3 match reasons with explanation on cards (was 1)
- matchCardAdapter: return top-3 positive factors as reasons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-20 12:39:12 +02:00
parent 9f391d17cb
commit d5a4862a28
5 changed files with 304 additions and 142 deletions
+6 -23
View File
@@ -15,29 +15,12 @@ import { getCityIntelligence } from '../../lib/locationIntelligence'
const HARD_CRITERIA = new Set(['area', 'location', 'budget', 'timing'])
function buildReasons(positiveFactors: ScoreFactor[]): MatchCardReason[] {
const reasons: MatchCardReason[] = []
const hardFact = positiveFactors.find(f => HARD_CRITERIA.has(f.criterion))
const softFact = positiveFactors.find(f => !HARD_CRITERIA.has(f.criterion))
if (hardFact) {
reasons.push({
type: 'HARD_FACT',
label: capitalize(hardFact.criterion),
explanation: hardFact.explanation,
score: hardFact.score,
})
}
if (softFact) {
reasons.push({
type: 'SOFT_FACTOR',
label: capitalize(softFact.criterion),
explanation: softFact.explanation,
score: softFact.score,
})
}
return reasons
return positiveFactors.slice(0, 3).map(f => ({
type: (HARD_CRITERIA.has(f.criterion) ? 'HARD_FACT' : 'SOFT_FACTOR') as MatchCardReason['type'],
label: capitalize(f.criterion),
explanation: f.explanation,
score: f.score,
}))
}
function capitalize(s: string): string {