Files
property-match/src/domain/marketSignal.ts
T
Benjamin Sutter cb18f3c381 feat: F021 market intelligence & signal discovery
Signal domain types, mock data (8 signals), provider interface + MockupMarketIntelligenceProvider, marketIntelligenceService, TanStack Query hooks, 10 ops components (SignalInbox, MarketSignalCard, MarketSignalFilterBar, MarketSignalSkeleton, MarketSignalEmptyState, SignalConfidenceBadge, SourceReliabilityBadge, SensitivityWarningPanel, SignalConversionPanel, SignalEvidenceList), MarketIntelligence page wired into AppShell nav and App router.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:07:58 +02:00

181 lines
6.6 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 { AssetType, SignalType, SensitivityLevel, FreshnessStatus } from './enums'
// ── Source Categories ─────────────────────────────────────────────────────────
export const MarketSignalSourceCategory = {
PUBLIC_LISTING_PLATFORM: 'PUBLIC_LISTING_PLATFORM',
BUILDING_PERMIT_REGISTER: 'BUILDING_PERMIT_REGISTER',
COMPANY_NEWS: 'COMPANY_NEWS',
JOB_GROWTH_SIGNAL: 'JOB_GROWTH_SIGNAL',
COMMERCIAL_REGISTER: 'COMMERCIAL_REGISTER',
INFRASTRUCTURE_PROJECT: 'INFRASTRUCTURE_PROJECT',
PORTFOLIO_IMPORT: 'PORTFOLIO_IMPORT',
LEASE_EXPIRY_DATA: 'LEASE_EXPIRY_DATA',
USER_DEMAND_SIGNAL: 'USER_DEMAND_SIGNAL',
MANUAL_ANALYST_SIGNAL: 'MANUAL_ANALYST_SIGNAL',
} as const
export type MarketSignalSourceCategory = typeof MarketSignalSourceCategory[keyof typeof MarketSignalSourceCategory]
export const MARKET_SIGNAL_SOURCE_LABELS: Record<MarketSignalSourceCategory, string> = {
PUBLIC_LISTING_PLATFORM: 'Listing-Plattform',
BUILDING_PERMIT_REGISTER: 'Baugesuch-Register',
COMPANY_NEWS: 'Unternehmensnachrichten',
JOB_GROWTH_SIGNAL: 'Stellenwachstum',
COMMERCIAL_REGISTER: 'Handelsregister',
INFRASTRUCTURE_PROJECT: 'Infrastrukturprojekt',
PORTFOLIO_IMPORT: 'Portfolio-Import',
LEASE_EXPIRY_DATA: 'Vertragslaufdaten',
USER_DEMAND_SIGNAL: 'Nutzernachfrage',
MANUAL_ANALYST_SIGNAL: 'Analyst-Signal',
}
// LEASE_EXPIRY_DATA and PORTFOLIO_IMPORT are sensitive — never expose raw to Demand Users
export const SENSITIVE_SOURCE_CATEGORIES: MarketSignalSourceCategory[] = [
MarketSignalSourceCategory.LEASE_EXPIRY_DATA,
MarketSignalSourceCategory.PORTFOLIO_IMPORT,
]
// ── Processing Status ─────────────────────────────────────────────────────────
export const SignalProcessingStatus = {
DETECTED: 'DETECTED',
NORMALIZED: 'NORMALIZED',
ENRICHED: 'ENRICHED',
NEEDS_REVIEW: 'NEEDS_REVIEW',
APPROVED_AS_SIGNAL: 'APPROVED_AS_SIGNAL',
REJECTED: 'REJECTED',
CONVERTED_TO_FUTURE_AVAILABILITY: 'CONVERTED_TO_FUTURE_AVAILABILITY',
ARCHIVED: 'ARCHIVED',
} as const
export type SignalProcessingStatus = typeof SignalProcessingStatus[keyof typeof SignalProcessingStatus]
export const SIGNAL_PROCESSING_STATUS_LABELS: Record<SignalProcessingStatus, string> = {
DETECTED: 'Erkannt',
NORMALIZED: 'Normalisiert',
ENRICHED: 'Angereichert',
NEEDS_REVIEW: 'Prüfung erforderlich',
APPROVED_AS_SIGNAL: 'Genehmigt',
REJECTED: 'Abgelehnt',
CONVERTED_TO_FUTURE_AVAILABILITY: 'Konvertiert',
ARCHIVED: 'Archiviert',
}
export const SIGNAL_PROCESSING_STATUS_COLORS: Record<SignalProcessingStatus, { bg: string; fg: string }> = {
DETECTED: { bg: 'rgba(148,163,184,0.15)', fg: '#64748b' },
NORMALIZED: { bg: 'rgba(59,130,246,0.12)', fg: '#2563eb' },
ENRICHED: { bg: 'rgba(99,102,241,0.12)', fg: '#4f46e5' },
NEEDS_REVIEW: { bg: 'rgba(245,158,11,0.12)', fg: '#d97706' },
APPROVED_AS_SIGNAL: { bg: 'rgba(34,197,94,0.12)', fg: '#16a34a' },
REJECTED: { bg: 'rgba(239,68,68,0.12)', fg: '#dc2626' },
CONVERTED_TO_FUTURE_AVAILABILITY: { bg: 'rgba(139,92,246,0.12)', fg: '#7c3aed' },
ARCHIVED: { bg: 'rgba(148,163,184,0.10)', fg: '#94a3b8' },
}
// ── Evidence & Entities ───────────────────────────────────────────────────────
export const ExtractedEntityType = {
COMPANY: 'COMPANY',
PERSON: 'PERSON',
LOCATION: 'LOCATION',
ASSET: 'ASSET',
DATE: 'DATE',
} as const
export type ExtractedEntityType = typeof ExtractedEntityType[keyof typeof ExtractedEntityType]
export interface ExtractedEntity {
type: ExtractedEntityType
value: string
confidence: number
}
export const EvidenceType = {
TEXT_EXCERPT: 'TEXT_EXCERPT',
URL_REFERENCE: 'URL_REFERENCE',
ANALYST_NOTE: 'ANALYST_NOTE',
DOCUMENT: 'DOCUMENT',
} as const
export type EvidenceType = typeof EvidenceType[keyof typeof EvidenceType]
export interface SignalEvidence {
id: string
signalId: string
evidenceType: EvidenceType
content: string
sourceUrl?: string
retrievedAt: string
confidence: number
}
// ── Core Signal ───────────────────────────────────────────────────────────────
export interface MarketSignal {
id: string
title: string
summary: string
sourceCategory: MarketSignalSourceCategory
sourceLabel: string
sourceUrl?: string
detectedAt: string
location: string
affectedAssetTypes: AssetType[]
signalType: SignalType
rawEvidenceSummary: string
extractedEntities: ExtractedEntity[]
evidence: SignalEvidence[]
sourceReliabilityScore: number // 01
confidenceScore: number // 01
sensitivityLevel: SensitivityLevel
freshnessStatus: FreshnessStatus
processingStatus: SignalProcessingStatus
linkedPropertyId?: string
linkedNeedId?: string
possibleFutureSignalId?: string
analystNotes: string
createdAt: string
updatedAt: string
}
// ── Intelligence Aggregates ───────────────────────────────────────────────────
export interface MarketInsight {
id: string
title: string
summary: string
signalIds: string[]
location: string
affectedAssetTypes: AssetType[]
confidenceScore: number
createdAt: string
analystId: string
}
export interface IntelligenceRun {
id: string
triggeredAt: string
completedAt?: string
signalsDetected: number
signalsProcessed: number
status: 'RUNNING' | 'COMPLETED' | 'FAILED'
sourceCategories: MarketSignalSourceCategory[]
}
export interface SignalConversionCandidate {
signalId: string
proposedTitle: string
proposedSummary: string
estimatedAvailabilityDate?: string
proposedConfidence: number
conversionRationale: string
requiresReview: boolean
}
// ── Filters ───────────────────────────────────────────────────────────────────
export interface MarketSignalFilters {
sourceCategory?: MarketSignalSourceCategory
processingStatus?: SignalProcessingStatus
sensitivityLevel?: SensitivityLevel
signalType?: SignalType
search?: string
}