Files
property-match/.claude/worktrees/agent-a82a3716/src/features/matching/matchCardAdapter.ts
T
Benjamin Sutter d15a13e485 feat: remove Administration workspace — keep only Verwaltung + Suche
- Delete all ops page components (ReviewQueue, AIMonitoring, Governance,
  SourceMonitoring, ActivityTimeline, SignalPipeline)
- Remove OPERATIONS workspace from AppShell config, nav order, path detection
- Remove all /ops/* routes from App.tsx
- Remove WorkspaceType.OPERATIONS from allowedWorkspaces in authService,
  sessionStore, permissions
- Keep MarketIntelligence page (already moved to /supply/market-intelligence)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 20:32:41 +02:00

111 lines
3.0 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 {
UnifiedMatchResult,
VerifiedPortfolioResult,
ExternalMarketResult,
FutureAvailabilityResult,
} from '../../domain/unifiedResult'
import type { ScoreFactor } from '../../domain/match'
import type {
MatchCardViewModel,
MatchCardAction,
MatchCardReason,
} from '../../components/match-card/MatchCardViewModel'
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
}
function capitalize(s: string): string {
return s.charAt(0).toUpperCase() + s.slice(1)
}
export function buildMatchCardViewModel(
result: UnifiedMatchResult,
actions: MatchCardAction[],
): MatchCardViewModel {
const { match, matchScore, resultType } = result
const isFuture = resultType === 'FUTURE_AVAILABILITY'
const property = !isFuture
? (result as VerifiedPortfolioResult | ExternalMarketResult).property
: undefined
const signal = isFuture
? (result as FutureAvailabilityResult).signal
: undefined
const city = property?.location?.city
const district = property?.location?.district
const locationLabel = city
? `${city}${district ? `, ${district}` : ''}`
: signal?.locationHint ?? ''
const availabilityLabel =
property?.availabilityDate ??
(signal?.timeHorizonMonths ? `~${signal.timeHorizonMonths} Monate` : undefined)
const sourceLabel =
property?.sourceLabel ??
property?.sourceMeta?.sourceLabel ??
property?.sourceMeta?.sourceType
const externalUrl = property?.sourceUrl ?? property?.sourceMeta?.sourceUrl
const reasons = buildReasons(match.positiveFactors)
const dataQualityScore =
property?.dataQuality?.score ?? signal?.confidenceScore ?? 0.5
return {
id: result.matchId,
title:
property?.title ??
signal?.companyName ??
signal?.locationHint ??
'',
resultType,
assetType: property?.assetType,
matchScore,
confidenceScore: match.confidenceLevel,
dataQualityScore,
locationLabel,
availabilityLabel,
sourceLabel,
externalUrl,
reasons,
tradeoffs: match.tradeoffs ?? [],
risks: match.risks ?? [],
missingData: match.missingData ?? [],
actions,
disclaimer: isFuture
? (signal?.disclaimer ?? 'Probabilistisches Signal kein bestätigtes Objekt')
: undefined,
explainabilitySummary: match.explainabilitySummary,
isReviewRequired: match.status === 'PENDING_REVIEW',
isStaleData: false,
}
}