Files
property-match/src/components/match-card/MatchCardViewModel.ts
T
Benjamin Sutter 27c53f3af1 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>
2026-05-21 20:41:46 +02:00

94 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { ResultType } from '../../domain/enums'
import type { TradeOff, Risk, MissingDataItem, ScoreFactor } from '../../domain/match'
import type { PropertyUnit } from '../../domain/property'
export type MatchCardVariant = 'compact' | 'expanded' | 'review' | 'compare-mini'
export type MatchCardActionType =
| 'OPEN_DETAIL'
| 'ADD_COMPARE'
| 'SAVE_SHORTLIST'
| 'SEND_REVIEW'
| 'APPROVE'
| 'REJECT'
| 'REQUEST_DATA'
export interface MatchCardAction {
id: string
label: string
actionType: MatchCardActionType
variant?: 'primary' | 'secondary' | 'danger'
disabled?: boolean
onClick: () => void
}
export interface MatchCardReason {
type: 'HARD_FACT' | 'SOFT_FACTOR' | 'STRATEGIC'
label: string
explanation: string
score: number
}
export interface MatchCardViewModel {
id: string
title: string
resultType: ResultType
assetType?: string
matchScore: number
confidenceScore: number // 01
dataQualityScore: number // 01
locationLabel: string
availabilityLabel?: string
sourceLabel?: string
externalUrl?: string
reasons: MatchCardReason[] // max 3: HARD_FACT, SOFT_FACTOR, STRATEGIC
tradeoffs: TradeOff[]
risks: Risk[]
missingData: MissingDataItem[]
actions: MatchCardAction[]
disclaimer?: string // required for FUTURE_AVAILABILITY
explainabilitySummary?: string
taxCalculatorUrl?: string // deeplink to cantonal tax calculator
// Schattenmarkt / FUTURE_AVAILABILITY signal fields
signalSourceType?: 'PRESS' | 'CONSTRUCTION_PERMIT' | 'JOB_POSTING' | 'COMPANY_REPORT' | 'MARKET_DATA' | 'MANUAL' | 'LEASE_CONTRACT'
signalSourceUrl?: string
signalSourceCredibility?: 'LOW' | 'MEDIUM' | 'HIGH'
signalProbability?: number
signalMarketIndicator?: string
signalType?: string
signalIsVerified?: boolean
aiSignalSummary?: string
signalQuality?: 'HIGH' | 'MEDIUM' | 'LOW'
signalMarketIndicators?: string[]
signalStrategicInterpretation?: string
signalConfirmedFacts?: string[]
signalUnconfirmedFacts?: string[]
signalIsControlled?: boolean
signalAreaSqmEstimate?: number
// Unit-first display (primary)
propertySubtitle?: string // building name shown below the unit title
// Property / unit reference (for FUTURE_AVAILABILITY with a backing property)
propertyId?: string
unitId?: string
preMarketUnit?: PropertyUnit // specific unit being released pre-market
preMarketAllUnits?: PropertyUnit[] // all units of the backing property
// Score transparency
scoreBreakdown?: {
hardMatchScore: number
softFactorScore: number
totalScore: number
}
allFactors?: ScoreFactor[]
// States
isSelected?: boolean
isCompareSelected?: boolean
isRestricted?: boolean
isReviewRequired?: boolean
isStaleData?: boolean
}