beecf754d5
- PropertyIntelligenceCard: aspect-ratio image, hover animation, compact data-completeness row, tighter body padding - Results: replace verbose Card with compact active-search strip, remove DecisionContextPanel - Properties: add 3/5/10 column toggle in grid view, persist to localStorage - NeedInput, UnifiedResultCard/Feed, MatchCardViewModel: cleanup from earlier session Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
108 lines
3.2 KiB
TypeScript
108 lines
3.2 KiB
TypeScript
import type { ResultType } from '../../domain/enums'
|
||
import type { TradeOff, Risk, MissingDataItem, ScoreFactor } from '../../domain/match'
|
||
import type { PropertyUnit } from '../../domain/property'
|
||
import type { FitOutInvestment } from '../../lib/fitOutUtils'
|
||
|
||
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 // 0–1
|
||
dataQualityScore: number // 0–1
|
||
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
|
||
|
||
// Divisibility (Teilbarkeit)
|
||
isDivisible?: boolean
|
||
minDivisibleUnitSqm?: number
|
||
|
||
// Fit-out signal
|
||
fitOutLabel?: string // e.g. "FULL — bezugsfertig", "BASIC + CHF 200 MAB"
|
||
fitOutViable?: boolean // true when budget bridges the gap
|
||
fitOutInvestment?: FitOutInvestment
|
||
|
||
// 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
|
||
|
||
// Key metrics for compact display (Ginesta-style metric line)
|
||
areaSqm?: number
|
||
rentPerSqm?: number
|
||
|
||
// Score transparency
|
||
scoreBreakdown?: {
|
||
hardMatchScore: number
|
||
softFactorScore: number
|
||
totalScore: number
|
||
}
|
||
allFactors?: ScoreFactor[]
|
||
|
||
// States
|
||
isSelected?: boolean
|
||
isCompareSelected?: boolean
|
||
isRestricted?: boolean
|
||
isReviewRequired?: boolean
|
||
isStaleData?: boolean
|
||
}
|