feat: Future Availability — Logiktrennung Supply/Demand + Markt-Leads
EXPANSION-Signale (Unternehmen sucht Fläche) sind Nachfragesignale und gehören nicht in den Demand-Feed. Neue Architektur: - signalDirection: 'SUPPLY' | 'DEMAND' auf FutureSignal-Domain - SUPPLY-Signale: POSSIBLE_MOVE_OUT / CONSTRUCTION_PROJECT (Crawler) und LEASE_EXPIRY+isVerified (Verwaltung ERP) → Demand-Feed wie bisher - DEMAND-Signale: EXPANSION → aus Demand-Feed gefiltert - Neues useMarketLeads-Hook + /supply/market-leads Seite für Verwaltung: zeigt EXPANSION-Signale als potenzielle Mieter mit Portfolio-Matching - Alle 15 Signale in futureSignals.ts inhaltlich bereinigt und mit korrekter signalDirection versehen Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useFutureSignals } from './useFutureSignals'
|
||||
import { useProperties } from './useProperties'
|
||||
import type { FutureSignal } from '../domain/futureSignal'
|
||||
import type { Property } from '../domain/property'
|
||||
|
||||
export interface MarketLead {
|
||||
signal: FutureSignal
|
||||
matchingProperties: Property[]
|
||||
}
|
||||
|
||||
export function useMarketLeads(): { data: MarketLead[]; isLoading: boolean } {
|
||||
const { data: signals = [], isLoading: signalsLoading } = useFutureSignals()
|
||||
const { data: properties = [], isLoading: propertiesLoading } = useProperties()
|
||||
|
||||
const data = useMemo((): MarketLead[] => {
|
||||
const demandSignals = signals.filter(s => s.signalDirection === 'DEMAND')
|
||||
|
||||
return demandSignals.map(signal => {
|
||||
const signalCity = signal.locationHint.split(',')[0].trim().toLowerCase()
|
||||
|
||||
const matchingProperties = properties.filter(p => {
|
||||
if (p.resultType !== 'VERIFIED_PORTFOLIO') return false
|
||||
const propCity = (p.location?.city ?? '').toLowerCase()
|
||||
const cityMatch = propCity.includes(signalCity) || signalCity.includes(propCity)
|
||||
const areaOk = signal.areaSqmEstimate
|
||||
? p.areaSqm >= signal.areaSqmEstimate * 0.5 && p.areaSqm <= signal.areaSqmEstimate * 2.2
|
||||
: true
|
||||
return cityMatch && areaOk
|
||||
})
|
||||
|
||||
return { signal, matchingProperties }
|
||||
})
|
||||
}, [signals, properties])
|
||||
|
||||
return { data, isLoading: signalsLoading || propertiesLoading }
|
||||
}
|
||||
@@ -37,7 +37,7 @@ export function useUnifiedResults(needId?: string) {
|
||||
// Fast path: explicit FUTURE_AVAILABILITY on the match (no property lookup needed)
|
||||
if (match.resultType === 'FUTURE_AVAILABILITY') {
|
||||
const signal = allSignals.find(s => s.id === refId || s.propertyId === refId)
|
||||
if (!signal) return []
|
||||
if (!signal || signal.signalDirection === 'DEMAND') return []
|
||||
return [{ matchId: match.id, needId: match.needId, matchScore: match.matchScore,
|
||||
resultType: 'FUTURE_AVAILABILITY', signal, match }]
|
||||
}
|
||||
@@ -51,7 +51,7 @@ export function useUnifiedResults(needId?: string) {
|
||||
|
||||
if (rt === 'FUTURE_AVAILABILITY') {
|
||||
const signal = allSignals.find(s => s.id === refId || s.propertyId === refId)
|
||||
if (!signal) return []
|
||||
if (!signal || signal.signalDirection === 'DEMAND') return []
|
||||
return [{ matchId: match.id, needId: match.needId, matchScore: match.matchScore,
|
||||
resultType: 'FUTURE_AVAILABILITY', signal, match }]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user