feat: remove EXTERNAL_MARKET result type entirely
All external market properties now surface as VERIFIED_PORTFOLIO. Removes the type from enums, domain types, mock data, matching engine, components, hooks, and tests — zero user-visible distinction remains. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,9 @@
|
|||||||
"Bash(start http://localhost:5173)",
|
"Bash(start http://localhost:5173)",
|
||||||
"Bash(npm install *)",
|
"Bash(npm install *)",
|
||||||
"Bash(git pull *)",
|
"Bash(git pull *)",
|
||||||
"Bash(node scripts/check-tokens.js)"
|
"Bash(node scripts/check-tokens.js)",
|
||||||
|
"Bash(echo \"EXIT:$?\")",
|
||||||
|
"Bash(echo \"EXIT_CODE:$?\")"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
"permissions": {
|
"permissions": {
|
||||||
"allow": [
|
"allow": [
|
||||||
"Bash(git commit -m ' *)",
|
"Bash(git commit -m ' *)",
|
||||||
"Bash(git commit *)"
|
"Bash(git commit *)",
|
||||||
|
"Bash(node -e ' *)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Chip } from '@mui/material'
|
import { Chip } from '@mui/material'
|
||||||
import { ShieldCheck, Globe, Building2, Sparkles } from 'lucide-react'
|
import { ShieldCheck, Building2, Sparkles } from 'lucide-react'
|
||||||
import type { ResultType } from '../../domain/enums'
|
import type { ResultType } from '../../domain/enums'
|
||||||
import { DS_COLORS } from '../../lib/ds'
|
import { DS_COLORS } from '../../lib/ds'
|
||||||
import { RESULT_TYPE_LABELS } from '../../lib/constants'
|
import { RESULT_TYPE_LABELS } from '../../lib/constants'
|
||||||
@@ -11,7 +11,6 @@ interface ResultTypeBadgeProps {
|
|||||||
|
|
||||||
const ICONS: Record<ResultType, React.ElementType> = {
|
const ICONS: Record<ResultType, React.ElementType> = {
|
||||||
VERIFIED_PORTFOLIO: ShieldCheck,
|
VERIFIED_PORTFOLIO: ShieldCheck,
|
||||||
EXTERNAL_MARKET: Globe,
|
|
||||||
MAISON_WORK: Building2,
|
MAISON_WORK: Building2,
|
||||||
FUTURE_AVAILABILITY: Sparkles,
|
FUTURE_AVAILABILITY: Sparkles,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ interface SourceTypeBadgeProps {
|
|||||||
|
|
||||||
const CONFIG: Record<string, { bg: string; color: string; Icon: React.ElementType }> = {
|
const CONFIG: Record<string, { bg: string; color: string; Icon: React.ElementType }> = {
|
||||||
VERIFIED_PORTFOLIO: { bg: 'rgba(30,58,95,0.1)', color: '#1e3a5f', Icon: ShieldCheck },
|
VERIFIED_PORTFOLIO: { bg: 'rgba(30,58,95,0.1)', color: '#1e3a5f', Icon: ShieldCheck },
|
||||||
EXTERNAL_MARKET: { bg: 'rgba(217,119,6,0.1)', color: '#b45309', Icon: Globe },
|
|
||||||
MAISON_WORK: { bg: 'rgba(3,105,161,0.1)', color: '#0369a1', Icon: Building2 },
|
MAISON_WORK: { bg: 'rgba(3,105,161,0.1)', color: '#0369a1', Icon: Building2 },
|
||||||
FUTURE_AVAILABILITY: { bg: 'rgba(124,58,237,0.1)', color: '#6d28d9', Icon: Sparkles },
|
FUTURE_AVAILABILITY: { bg: 'rgba(124,58,237,0.1)', color: '#6d28d9', Icon: Sparkles },
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { useCompareStore } from '../../stores/compareStore'
|
|||||||
|
|
||||||
const TYPE_DOT: Record<string, string> = {
|
const TYPE_DOT: Record<string, string> = {
|
||||||
VERIFIED_PORTFOLIO: '#1e3a5f',
|
VERIFIED_PORTFOLIO: '#1e3a5f',
|
||||||
EXTERNAL_MARKET: '#d97706',
|
|
||||||
MAISON_WORK: '#0369a1',
|
MAISON_WORK: '#0369a1',
|
||||||
FUTURE_AVAILABILITY: '#7c3aed',
|
FUTURE_AVAILABILITY: '#7c3aed',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import type { MatchCardViewModel } from './MatchCardViewModel'
|
|||||||
|
|
||||||
const RESULT_TYPE_META: Record<string, { label: string; color: string }> = {
|
const RESULT_TYPE_META: Record<string, { label: string; color: string }> = {
|
||||||
VERIFIED_PORTFOLIO: { label: 'Verified', color: '#1e3a5f' },
|
VERIFIED_PORTFOLIO: { label: 'Verified', color: '#1e3a5f' },
|
||||||
EXTERNAL_MARKET: { label: 'Direkt', color: '#d97706' },
|
|
||||||
MAISON_WORK: { label: 'Maison Work', color: '#0369a1' },
|
MAISON_WORK: { label: 'Maison Work', color: '#0369a1' },
|
||||||
FUTURE_AVAILABILITY: { label: 'Signal', color: '#7c3aed' },
|
FUTURE_AVAILABILITY: { label: 'Signal', color: '#7c3aed' },
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import type { FutureSignal } from '../../domain/futureSignal'
|
|||||||
|
|
||||||
const RESULT_TYPE_META: Record<string, { label: string; color: string }> = {
|
const RESULT_TYPE_META: Record<string, { label: string; color: string }> = {
|
||||||
VERIFIED_PORTFOLIO: { label: 'Verified Portfolio', color: '#1e3a5f' },
|
VERIFIED_PORTFOLIO: { label: 'Verified Portfolio', color: '#1e3a5f' },
|
||||||
EXTERNAL_MARKET: { label: 'Direktinserat', color: '#d97706' },
|
|
||||||
MAISON_WORK: { label: 'Maison Work', color: '#0369a1' },
|
MAISON_WORK: { label: 'Maison Work', color: '#0369a1' },
|
||||||
FUTURE_AVAILABILITY: { label: 'Zukunftssignal', color: '#7c3aed' },
|
FUTURE_AVAILABILITY: { label: 'Zukunftssignal', color: '#7c3aed' },
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export function ShortlistDetail({ shortlist }: Props) {
|
|||||||
matchId: item.resultId,
|
matchId: item.resultId,
|
||||||
needId: '',
|
needId: '',
|
||||||
matchScore: item.matchScore,
|
matchScore: item.matchScore,
|
||||||
resultType: item.resultType as 'VERIFIED_PORTFOLIO' | 'EXTERNAL_MARKET' | 'MAISON_WORK',
|
resultType: item.resultType as 'VERIFIED_PORTFOLIO' | 'MAISON_WORK',
|
||||||
property: { id: item.propertyId ?? item.resultId, title: item.title } as any,
|
property: { id: item.propertyId ?? item.resultId, title: item.title } as any,
|
||||||
match: { id: item.resultId, matchScore: item.matchScore, confidenceLevel: item.confidenceScore ?? 0.8 } as any,
|
match: { id: item.resultId, matchScore: item.matchScore, confidenceLevel: item.confidenceScore ?? 0.8 } as any,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import type { ShortlistItem } from '../../domain/shortlist'
|
|||||||
|
|
||||||
const RESULT_TYPE_LABEL: Record<string, string> = {
|
const RESULT_TYPE_LABEL: Record<string, string> = {
|
||||||
VERIFIED_PORTFOLIO: 'Portfolio',
|
VERIFIED_PORTFOLIO: 'Portfolio',
|
||||||
EXTERNAL_MARKET: 'Direkt',
|
|
||||||
MAISON_WORK: 'Maison Work',
|
MAISON_WORK: 'Maison Work',
|
||||||
FUTURE_AVAILABILITY: 'Signal',
|
FUTURE_AVAILABILITY: 'Signal',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ export type AssetType = typeof AssetType[keyof typeof AssetType]
|
|||||||
// ── Result Types ──────────────────────────────────────────────────────────────
|
// ── Result Types ──────────────────────────────────────────────────────────────
|
||||||
export const ResultType = {
|
export const ResultType = {
|
||||||
VERIFIED_PORTFOLIO: 'VERIFIED_PORTFOLIO',
|
VERIFIED_PORTFOLIO: 'VERIFIED_PORTFOLIO',
|
||||||
EXTERNAL_MARKET: 'EXTERNAL_MARKET',
|
|
||||||
MAISON_WORK: 'MAISON_WORK',
|
MAISON_WORK: 'MAISON_WORK',
|
||||||
FUTURE_AVAILABILITY: 'FUTURE_AVAILABILITY',
|
FUTURE_AVAILABILITY: 'FUTURE_AVAILABILITY',
|
||||||
} as const
|
} as const
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export interface PipelineItem {
|
|||||||
title: string
|
title: string
|
||||||
location: string
|
location: string
|
||||||
matchScore: number
|
matchScore: number
|
||||||
resultType: 'VERIFIED_PORTFOLIO' | 'EXTERNAL_MARKET' | 'MAISON_WORK' | 'FUTURE_AVAILABILITY'
|
resultType: 'VERIFIED_PORTFOLIO' | 'MAISON_WORK' | 'FUTURE_AVAILABILITY'
|
||||||
stage: PipelineStage
|
stage: PipelineStage
|
||||||
// Source match reference — used for navigating back to the detail page
|
// Source match reference — used for navigating back to the detail page
|
||||||
matchId?: string
|
matchId?: string
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ export const DATA_QUALITY_MODIFIER = {
|
|||||||
export const CONFIDENCE_MODIFIER = {
|
export const CONFIDENCE_MODIFIER = {
|
||||||
VERIFIED_HIGH: +3, // VERIFIED_PORTFOLIO + confidenceScore >= 0.80
|
VERIFIED_HIGH: +3, // VERIFIED_PORTFOLIO + confidenceScore >= 0.80
|
||||||
VERIFIED_MEDIUM: 0, // VERIFIED_PORTFOLIO + confidenceScore < 0.80
|
VERIFIED_MEDIUM: 0, // VERIFIED_PORTFOLIO + confidenceScore < 0.80
|
||||||
EXTERNAL_MARKET: -3, // EXTERNAL_MARKET result type
|
|
||||||
MAISON_WORK: -2, // MAISON_WORK — Maison Work API (slightly more trusted than scraped market)
|
MAISON_WORK: -2, // MAISON_WORK — Maison Work API (slightly more trusted than scraped market)
|
||||||
FUTURE_AVAILABILITY: -15, // FUTURE_AVAILABILITY — never treat as confirmed availability
|
FUTURE_AVAILABILITY: -15, // FUTURE_AVAILABILITY — never treat as confirmed availability
|
||||||
LOW_CONFIDENCE: -10, // confidenceScore < 0.50 (stacks with above)
|
LOW_CONFIDENCE: -10, // confidenceScore < 0.50 (stacks with above)
|
||||||
|
|||||||
@@ -15,14 +15,7 @@ interface UnifiedResultBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface VerifiedPortfolioResult extends UnifiedResultBase {
|
export interface VerifiedPortfolioResult extends UnifiedResultBase {
|
||||||
resultType: 'VERIFIED_PORTFOLIO'
|
resultType: 'VERIFIED_PORTFOLIO' | 'MAISON_WORK'
|
||||||
property: Property
|
|
||||||
match: Match
|
|
||||||
unit?: PropertyUnit // specific unit this match refers to
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ExternalMarketResult extends UnifiedResultBase {
|
|
||||||
resultType: 'EXTERNAL_MARKET' | 'MAISON_WORK'
|
|
||||||
property: Property
|
property: Property
|
||||||
match: Match
|
match: Match
|
||||||
unit?: PropertyUnit // specific unit this match refers to
|
unit?: PropertyUnit // specific unit this match refers to
|
||||||
@@ -38,5 +31,4 @@ export interface FutureAvailabilityResult extends UnifiedResultBase {
|
|||||||
|
|
||||||
export type UnifiedMatchResult =
|
export type UnifiedMatchResult =
|
||||||
| VerifiedPortfolioResult
|
| VerifiedPortfolioResult
|
||||||
| ExternalMarketResult
|
|
||||||
| FutureAvailabilityResult
|
| FutureAvailabilityResult
|
||||||
|
|||||||
@@ -38,20 +38,17 @@ describe('rankMatches', () => {
|
|||||||
expect(ranked[0].propertyId).toBe('high')
|
expect(ranked[0].propertyId).toBe('high')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('breaks ties by result type: VERIFIED_PORTFOLIO before EXTERNAL_MARKET', () => {
|
it('breaks ties by result type: VERIFIED_PORTFOLIO and MAISON_WORK ranked above FUTURE_AVAILABILITY', () => {
|
||||||
// Build one of each type but force identical score by using same property body
|
// Build one of each type but force identical score by using same property body
|
||||||
const prop = makeProperty()
|
const prop = makeProperty()
|
||||||
const verified = buildFullMatch(need, { ...prop, id: 'v', resultType: ResultType.VERIFIED_PORTFOLIO })
|
const verified = buildFullMatch(need, { ...prop, id: 'v', resultType: ResultType.VERIFIED_PORTFOLIO })
|
||||||
const external = buildFullMatch(need, { ...prop, id: 'e', resultType: ResultType.EXTERNAL_MARKET, confidenceScore: 0.85 })
|
const future = buildFullMatch(need, { ...prop, id: 'f', resultType: ResultType.FUTURE_AVAILABILITY, confidenceScore: 0.85 })
|
||||||
|
|
||||||
// If scores differ, force them equal for a clean tiebreak test
|
// Force same score for a clean tiebreak test
|
||||||
if (verified.matchScore !== external.matchScore) {
|
verified.matchScore = 75
|
||||||
const lower = Math.min(verified.matchScore, external.matchScore)
|
future.matchScore = 75
|
||||||
verified.matchScore = lower
|
|
||||||
external.matchScore = lower
|
|
||||||
}
|
|
||||||
|
|
||||||
const ranked = rankMatches([external, verified])
|
const ranked = rankMatches([future, verified])
|
||||||
expect(ranked[0].resultType).toBe(ResultType.VERIFIED_PORTFOLIO)
|
expect(ranked[0].resultType).toBe(ResultType.VERIFIED_PORTFOLIO)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -56,11 +56,6 @@ describe('calcConfidenceModifier', () => {
|
|||||||
expect(calcConfidenceModifier(p)).toBe(0)
|
expect(calcConfidenceModifier(p)).toBe(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns -3 for external market results', () => {
|
|
||||||
const p = makeProperty({ resultType: ResultType.EXTERNAL_MARKET, confidenceScore: 0.80 })
|
|
||||||
expect(calcConfidenceModifier(p)).toBe(-3)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('returns -2 for Maison Work results', () => {
|
it('returns -2 for Maison Work results', () => {
|
||||||
const p = makeProperty({ resultType: ResultType.MAISON_WORK, confidenceScore: 0.80 })
|
const p = makeProperty({ resultType: ResultType.MAISON_WORK, confidenceScore: 0.80 })
|
||||||
expect(calcConfidenceModifier(p)).toBe(-2)
|
expect(calcConfidenceModifier(p)).toBe(-2)
|
||||||
@@ -72,9 +67,9 @@ describe('calcConfidenceModifier', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('stacks an additional -10 penalty when confidenceScore < 0.50', () => {
|
it('stacks an additional -10 penalty when confidenceScore < 0.50', () => {
|
||||||
// EXTERNAL_MARKET (-3) + LOW_CONFIDENCE (-10) = -13
|
// MAISON_WORK (-2) + LOW_CONFIDENCE (-10) = -12
|
||||||
const p = makeProperty({ resultType: ResultType.EXTERNAL_MARKET, confidenceScore: 0.45 })
|
const p = makeProperty({ resultType: ResultType.MAISON_WORK, confidenceScore: 0.45 })
|
||||||
expect(calcConfidenceModifier(p)).toBe(-13)
|
expect(calcConfidenceModifier(p)).toBe(-12)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('stacks -10 on FUTURE_AVAILABILITY when confidence is also low', () => {
|
it('stacks -10 on FUTURE_AVAILABILITY when confidence is also low', () => {
|
||||||
@@ -157,12 +152,12 @@ describe('calculateScore', () => {
|
|||||||
expect(output.finalScore).toBeGreaterThanOrEqual(85)
|
expect(output.finalScore).toBeGreaterThanOrEqual(85)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('scores a weak match (wrong city, over budget, poor DQ, external market) below 50', () => {
|
it('scores a weak match (wrong city, over budget, poor DQ, low confidence) below 50', () => {
|
||||||
const need = makeNeed({ preferredLocations: ['Zürich'] })
|
const need = makeNeed({ preferredLocations: ['Zürich'] })
|
||||||
const prop = makeProperty({
|
const prop = makeProperty({
|
||||||
location: { city: 'Basel', country: 'CH' },
|
location: { city: 'Basel', country: 'CH' },
|
||||||
rentPricePerSqm: 70, // 140% of max budget 50 — not excluded but severe penalty
|
rentPricePerSqm: 70, // 140% of max budget 50 — not excluded but severe penalty
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
confidenceScore: 0.45, // triggers LOW_CONFIDENCE -10 stacking
|
confidenceScore: 0.45, // triggers LOW_CONFIDENCE -10 stacking
|
||||||
dataQuality: {
|
dataQuality: {
|
||||||
score: 0.30, // CRITICAL → -15
|
score: 0.30, // CRITICAL → -15
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ describe('softFactorEnrichmentService — score range invariant (all keys, 0–1
|
|||||||
describe('softFactorEnrichmentService — Pre-Market vs Market Signal', () => {
|
describe('softFactorEnrichmentService — Pre-Market vs Market Signal', () => {
|
||||||
it('returns identical estimate for same location regardless of resultType', () => {
|
it('returns identical estimate for same location regardless of resultType', () => {
|
||||||
// The enrichment service is location-only — resultType is irrelevant.
|
// The enrichment service is location-only — resultType is irrelevant.
|
||||||
// FUTURE_AVAILABILITY (pre-market) and EXTERNAL_MARKET should produce the same soft factor score.
|
// FUTURE_AVAILABILITY (pre-market) and VERIFIED_PORTFOLIO should produce the same soft factor score.
|
||||||
const sharedLocation = { city: 'Zürich', district: 'Oerlikon', country: 'CH' }
|
const sharedLocation = { city: 'Zürich', district: 'Oerlikon', country: 'CH' }
|
||||||
const sharedAddress = { street: 'Thurgauerstrasse', houseNumber: '1', postalCode: '8050', city: 'Zürich', country: 'CH' }
|
const sharedAddress = { street: 'Thurgauerstrasse', houseNumber: '1', postalCode: '8050', city: 'Zürich', country: 'CH' }
|
||||||
|
|
||||||
@@ -141,16 +141,16 @@ describe('softFactorEnrichmentService — Pre-Market vs Market Signal', () => {
|
|||||||
address: sharedAddress,
|
address: sharedAddress,
|
||||||
resultType: ResultType.FUTURE_AVAILABILITY,
|
resultType: ResultType.FUTURE_AVAILABILITY,
|
||||||
})
|
})
|
||||||
const marketResult = makeProperty({
|
const portfolioResult = makeProperty({
|
||||||
location: sharedLocation,
|
location: sharedLocation,
|
||||||
address: sharedAddress,
|
address: sharedAddress,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
})
|
})
|
||||||
|
|
||||||
const futureEst = softFactorEnrichmentService.estimate('accessibility', futureSignal)
|
const futureEst = softFactorEnrichmentService.estimate('accessibility', futureSignal)
|
||||||
const marketEst = softFactorEnrichmentService.estimate('accessibility', marketResult)
|
const portfolioEst = softFactorEnrichmentService.estimate('accessibility', portfolioResult)
|
||||||
expect(futureEst?.score).toBe(marketEst?.score)
|
expect(futureEst?.score).toBe(portfolioEst?.score)
|
||||||
expect(futureEst?.label).toBe(marketEst?.label)
|
expect(futureEst?.label).toBe(portfolioEst?.label)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('FUTURE_AVAILABILITY at Oerlikon gets meaningful accessibility score (> 0.70)', () => {
|
it('FUTURE_AVAILABILITY at Oerlikon gets meaningful accessibility score (> 0.70)', () => {
|
||||||
|
|||||||
@@ -191,10 +191,10 @@ export function rankMatches(matches: Match[]): Match[] {
|
|||||||
return [...matches].sort((a, b) => {
|
return [...matches].sort((a, b) => {
|
||||||
// Primary: matchScore descending
|
// Primary: matchScore descending
|
||||||
if (b.matchScore !== a.matchScore) return b.matchScore - a.matchScore
|
if (b.matchScore !== a.matchScore) return b.matchScore - a.matchScore
|
||||||
// Secondary: VERIFIED > EXTERNAL / MAISON_WORK > FUTURE
|
// Secondary: VERIFIED / MAISON_WORK > FUTURE
|
||||||
const typeOrder: Record<string, number> = { VERIFIED_PORTFOLIO: 0, EXTERNAL_MARKET: 1, MAISON_WORK: 1, FUTURE_AVAILABILITY: 2 }
|
const typeOrder: Record<string, number> = { VERIFIED_PORTFOLIO: 0, MAISON_WORK: 0, FUTURE_AVAILABILITY: 2 }
|
||||||
const aOrder = typeOrder[a.resultType ?? 'EXTERNAL_MARKET'] ?? 1
|
const aOrder = typeOrder[a.resultType ?? 'VERIFIED_PORTFOLIO'] ?? 0
|
||||||
const bOrder = typeOrder[b.resultType ?? 'EXTERNAL_MARKET'] ?? 1
|
const bOrder = typeOrder[b.resultType ?? 'VERIFIED_PORTFOLIO'] ?? 0
|
||||||
if (aOrder !== bOrder) return aOrder - bOrder
|
if (aOrder !== bOrder) return aOrder - bOrder
|
||||||
// Tertiary: higher confidence first
|
// Tertiary: higher confidence first
|
||||||
return (b.confidenceLevel ?? 0) - (a.confidenceLevel ?? 0)
|
return (b.confidenceLevel ?? 0) - (a.confidenceLevel ?? 0)
|
||||||
|
|||||||
@@ -499,8 +499,6 @@ export function calcConfidenceModifier(property: Property): number {
|
|||||||
let mod = 0
|
let mod = 0
|
||||||
if (property.resultType === ResultType.FUTURE_AVAILABILITY) {
|
if (property.resultType === ResultType.FUTURE_AVAILABILITY) {
|
||||||
mod += CONFIDENCE_MODIFIER.FUTURE_AVAILABILITY
|
mod += CONFIDENCE_MODIFIER.FUTURE_AVAILABILITY
|
||||||
} else if (property.resultType === ResultType.EXTERNAL_MARKET) {
|
|
||||||
mod += CONFIDENCE_MODIFIER.EXTERNAL_MARKET
|
|
||||||
} else if (property.resultType === ResultType.MAISON_WORK) {
|
} else if (property.resultType === ResultType.MAISON_WORK) {
|
||||||
mod += CONFIDENCE_MODIFIER.MAISON_WORK
|
mod += CONFIDENCE_MODIFIER.MAISON_WORK
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { useSchattenmarktSignals } from './useSchattenmarktSignals'
|
|||||||
import type {
|
import type {
|
||||||
UnifiedMatchResult,
|
UnifiedMatchResult,
|
||||||
VerifiedPortfolioResult,
|
VerifiedPortfolioResult,
|
||||||
ExternalMarketResult,
|
|
||||||
FutureAvailabilityResult,
|
FutureAvailabilityResult,
|
||||||
} from '../domain/unifiedResult'
|
} from '../domain/unifiedResult'
|
||||||
|
|
||||||
@@ -64,24 +63,11 @@ export function useUnifiedResults(needId?: string) {
|
|||||||
? (property.units?.find(u => u.id === match.unitId) ?? undefined)
|
? (property.units?.find(u => u.id === match.unitId) ?? undefined)
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
if (rt === 'EXTERNAL_MARKET' || rt === 'MAISON_WORK') {
|
|
||||||
const result: ExternalMarketResult = {
|
|
||||||
matchId: match.id,
|
|
||||||
needId: match.needId,
|
|
||||||
matchScore: match.matchScore,
|
|
||||||
resultType: rt as 'EXTERNAL_MARKET' | 'MAISON_WORK',
|
|
||||||
property,
|
|
||||||
match,
|
|
||||||
unit: matchUnit,
|
|
||||||
}
|
|
||||||
return [result]
|
|
||||||
}
|
|
||||||
|
|
||||||
const result: VerifiedPortfolioResult = {
|
const result: VerifiedPortfolioResult = {
|
||||||
matchId: match.id,
|
matchId: match.id,
|
||||||
needId: match.needId,
|
needId: match.needId,
|
||||||
matchScore: match.matchScore,
|
matchScore: match.matchScore,
|
||||||
resultType: 'VERIFIED_PORTFOLIO',
|
resultType: rt as 'VERIFIED_PORTFOLIO' | 'MAISON_WORK',
|
||||||
property,
|
property,
|
||||||
match,
|
match,
|
||||||
unit: matchUnit,
|
unit: matchUnit,
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ export const ASSET_TYPE_LABELS: Record<string, string> = {
|
|||||||
// Result type display labels
|
// Result type display labels
|
||||||
export const RESULT_TYPE_LABELS: Record<string, string> = {
|
export const RESULT_TYPE_LABELS: Record<string, string> = {
|
||||||
VERIFIED_PORTFOLIO: 'Plattform',
|
VERIFIED_PORTFOLIO: 'Plattform',
|
||||||
EXTERNAL_MARKET: 'Plattform',
|
|
||||||
MAISON_WORK: 'Maison Work',
|
MAISON_WORK: 'Maison Work',
|
||||||
FUTURE_AVAILABILITY: 'Zukunftssignal',
|
FUTURE_AVAILABILITY: 'Zukunftssignal',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { CONF_HIGH, CONF_MEDIUM, DQ_HIGH, DQ_MEDIUM } from './constants'
|
|||||||
export const DS_COLORS = {
|
export const DS_COLORS = {
|
||||||
resultType: {
|
resultType: {
|
||||||
VERIFIED_PORTFOLIO: { bg: 'rgba(30,58,95,0.10)', fg: '#1e3a5f' },
|
VERIFIED_PORTFOLIO: { bg: 'rgba(30,58,95,0.10)', fg: '#1e3a5f' },
|
||||||
EXTERNAL_MARKET: { bg: 'rgba(30,58,95,0.10)', fg: '#1e3a5f' },
|
|
||||||
MAISON_WORK: { bg: 'rgba(3,105,161,0.10)', fg: '#0369a1' },
|
MAISON_WORK: { bg: 'rgba(3,105,161,0.10)', fg: '#0369a1' },
|
||||||
FUTURE_AVAILABILITY: { bg: 'rgba(109,40,217,0.10)', fg: '#6d28d9' },
|
FUTURE_AVAILABILITY: { bg: 'rgba(109,40,217,0.10)', fg: '#6d28d9' },
|
||||||
},
|
},
|
||||||
@@ -199,7 +198,6 @@ export function scoreToDataQualityLevel(score: number): DataQualityLevel {
|
|||||||
|
|
||||||
export const RESULT_TYPE_META: Record<string, { label: string; color: string; bg: string }> = {
|
export const RESULT_TYPE_META: Record<string, { label: string; color: string; bg: string }> = {
|
||||||
VERIFIED_PORTFOLIO: { label: 'Verifiziertes Objekt', color: '#1e3a5f', bg: 'rgba(30,58,95,0.10)' },
|
VERIFIED_PORTFOLIO: { label: 'Verifiziertes Objekt', color: '#1e3a5f', bg: 'rgba(30,58,95,0.10)' },
|
||||||
EXTERNAL_MARKET: { label: 'Verifiziertes Objekt', color: '#1e3a5f', bg: 'rgba(30,58,95,0.10)' },
|
|
||||||
MAISON_WORK: { label: 'Maison Work', color: '#0369a1', bg: 'rgba(3,105,161,0.10)' },
|
MAISON_WORK: { label: 'Maison Work', color: '#0369a1', bg: 'rgba(3,105,161,0.10)' },
|
||||||
FUTURE_AVAILABILITY: { label: 'Zukunftssignal', color: '#7c3aed', bg: 'rgba(109,40,217,0.10)' },
|
FUTURE_AVAILABILITY: { label: 'Zukunftssignal', color: '#7c3aed', bg: 'rgba(109,40,217,0.10)' },
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ export function canViewProperty(
|
|||||||
if (property.resultType === ResultType.VERIFIED_PORTFOLIO) {
|
if (property.resultType === ResultType.VERIFIED_PORTFOLIO) {
|
||||||
return property.organizationId === user.organizationId
|
return property.organizationId === user.organizationId
|
||||||
}
|
}
|
||||||
if (property.resultType === ResultType.EXTERNAL_MARKET) return true
|
|
||||||
if (property.resultType === ResultType.MAISON_WORK) return true
|
if (property.resultType === ResultType.MAISON_WORK) return true
|
||||||
if (property.resultType === ResultType.FUTURE_AVAILABILITY) {
|
if (property.resultType === ResultType.FUTURE_AVAILABILITY) {
|
||||||
return hasPermission(user, Permission.FUTURE_SIGNAL_VIEW)
|
return hasPermission(user, Permission.FUTURE_SIGNAL_VIEW)
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export const mockPipelineItems: PipelineItem[] = [
|
|||||||
title: 'Gewerbe Güterstrasse Basel',
|
title: 'Gewerbe Güterstrasse Basel',
|
||||||
location: 'Basel',
|
location: 'Basel',
|
||||||
matchScore: 68,
|
matchScore: 68,
|
||||||
resultType: 'EXTERNAL_MARKET',
|
resultType: 'VERIFIED_PORTFOLIO',
|
||||||
stage: 'CLOSED_LOST',
|
stage: 'CLOSED_LOST',
|
||||||
areaLabel: '800 m²',
|
areaLabel: '800 m²',
|
||||||
rentLabel: 'CHF 28/m²',
|
rentLabel: 'CHF 28/m²',
|
||||||
|
|||||||
+23
-23
@@ -1071,14 +1071,14 @@ export const mockProperties: Property[] = [
|
|||||||
},
|
},
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
// EXTERNAL_MARKET (10)
|
// VERIFIED_PORTFOLIO — formerly external market (migrated)
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
{
|
{
|
||||||
id: 'prop-003',
|
id: 'prop-003',
|
||||||
title: 'Retail-Fläche Bahnhofstrasse 88',
|
title: 'Retail-Fläche Bahnhofstrasse 88',
|
||||||
assetType: AssetType.RETAIL,
|
assetType: AssetType.RETAIL,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Bern', district: 'Innenstadt', canton: 'BE', country: 'CH', coordinates: { lat: 46.9483, lng: 7.4474 } },
|
location: { city: 'Bern', district: 'Innenstadt', canton: 'BE', country: 'CH', coordinates: { lat: 46.9483, lng: 7.4474 } },
|
||||||
address: { street: 'Bahnhofstrasse', houseNumber: '88', postalCode: '3011', city: 'Bern', country: 'CH' },
|
address: { street: 'Bahnhofstrasse', houseNumber: '88', postalCode: '3011', city: 'Bern', country: 'CH' },
|
||||||
areaSqm: 320,
|
areaSqm: 320,
|
||||||
@@ -1122,7 +1122,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-004',
|
id: 'prop-004',
|
||||||
title: 'Gemischte Gewerbeeinheit Europaallee',
|
title: 'Gemischte Gewerbeeinheit Europaallee',
|
||||||
assetType: AssetType.MIXED,
|
assetType: AssetType.MIXED,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Zürich', district: 'Kreis 4', canton: 'ZH', country: 'CH', coordinates: { lat: 47.3775, lng: 8.5398 } },
|
location: { city: 'Zürich', district: 'Kreis 4', canton: 'ZH', country: 'CH', coordinates: { lat: 47.3775, lng: 8.5398 } },
|
||||||
address: { street: 'Europaallee', houseNumber: '21', postalCode: '8004', city: 'Zürich', country: 'CH' },
|
address: { street: 'Europaallee', houseNumber: '21', postalCode: '8004', city: 'Zürich', country: 'CH' },
|
||||||
areaSqm: 1150,
|
areaSqm: 1150,
|
||||||
@@ -1246,7 +1246,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-017',
|
id: 'prop-017',
|
||||||
title: 'Ladenfläche Löwenstrasse 28',
|
title: 'Ladenfläche Löwenstrasse 28',
|
||||||
assetType: AssetType.RETAIL,
|
assetType: AssetType.RETAIL,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Zürich', district: 'Innenstadt', canton: 'ZH', country: 'CH', coordinates: { lat: 47.3766, lng: 8.5385 } },
|
location: { city: 'Zürich', district: 'Innenstadt', canton: 'ZH', country: 'CH', coordinates: { lat: 47.3766, lng: 8.5385 } },
|
||||||
address: { street: 'Löwenstrasse', houseNumber: '28', postalCode: '8001', city: 'Zürich', country: 'CH' },
|
address: { street: 'Löwenstrasse', houseNumber: '28', postalCode: '8001', city: 'Zürich', country: 'CH' },
|
||||||
areaSqm: 350,
|
areaSqm: 350,
|
||||||
@@ -1290,7 +1290,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-018',
|
id: 'prop-018',
|
||||||
title: 'Bürofläche Breitenrain 14',
|
title: 'Bürofläche Breitenrain 14',
|
||||||
assetType: AssetType.OFFICE,
|
assetType: AssetType.OFFICE,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Bern', district: 'Breitenrain', canton: 'BE', country: 'CH', coordinates: { lat: 46.9598, lng: 7.4522 } },
|
location: { city: 'Bern', district: 'Breitenrain', canton: 'BE', country: 'CH', coordinates: { lat: 46.9598, lng: 7.4522 } },
|
||||||
address: { street: 'Breitenrainstrasse', houseNumber: '14', postalCode: '3014', city: 'Bern', country: 'CH' },
|
address: { street: 'Breitenrainstrasse', houseNumber: '14', postalCode: '3014', city: 'Bern', country: 'CH' },
|
||||||
areaSqm: 780,
|
areaSqm: 780,
|
||||||
@@ -1458,7 +1458,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-022',
|
id: 'prop-022',
|
||||||
title: 'Bürofläche St.Gallen Centrum 7',
|
title: 'Bürofläche St.Gallen Centrum 7',
|
||||||
assetType: AssetType.OFFICE,
|
assetType: AssetType.OFFICE,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'St. Gallen', district: 'Centrum', canton: 'SG', country: 'CH', coordinates: { lat: 47.4245, lng: 9.3767 } },
|
location: { city: 'St. Gallen', district: 'Centrum', canton: 'SG', country: 'CH', coordinates: { lat: 47.4245, lng: 9.3767 } },
|
||||||
address: { street: 'Marktgasse', houseNumber: '7', postalCode: '9000', city: 'St. Gallen', country: 'CH' },
|
address: { street: 'Marktgasse', houseNumber: '7', postalCode: '9000', city: 'St. Gallen', country: 'CH' },
|
||||||
areaSqm: 700,
|
areaSqm: 700,
|
||||||
@@ -1666,7 +1666,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-031',
|
id: 'prop-031',
|
||||||
title: 'Bürofläche Hardturm West 16',
|
title: 'Bürofläche Hardturm West 16',
|
||||||
assetType: AssetType.OFFICE,
|
assetType: AssetType.OFFICE,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Zürich', district: 'Zürich-West', canton: 'ZH', country: 'CH', coordinates: { lat: 47.3862, lng: 8.5045 } },
|
location: { city: 'Zürich', district: 'Zürich-West', canton: 'ZH', country: 'CH', coordinates: { lat: 47.3862, lng: 8.5045 } },
|
||||||
address: { street: 'Hardturmstrasse', houseNumber: '16', postalCode: '8005', city: 'Zürich', country: 'CH' },
|
address: { street: 'Hardturmstrasse', houseNumber: '16', postalCode: '8005', city: 'Zürich', country: 'CH' },
|
||||||
areaSqm: 780,
|
areaSqm: 780,
|
||||||
@@ -1780,7 +1780,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-034',
|
id: 'prop-034',
|
||||||
title: 'Ladenfläche Lorrainestrasse 8',
|
title: 'Ladenfläche Lorrainestrasse 8',
|
||||||
assetType: AssetType.RETAIL,
|
assetType: AssetType.RETAIL,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Bern', district: 'Lorraine', canton: 'BE', country: 'CH', coordinates: { lat: 46.9565, lng: 7.4388 } },
|
location: { city: 'Bern', district: 'Lorraine', canton: 'BE', country: 'CH', coordinates: { lat: 46.9565, lng: 7.4388 } },
|
||||||
address: { street: 'Lorrainestrasse', houseNumber: '8', postalCode: '3013', city: 'Bern', country: 'CH' },
|
address: { street: 'Lorrainestrasse', houseNumber: '8', postalCode: '3013', city: 'Bern', country: 'CH' },
|
||||||
areaSqm: 340,
|
areaSqm: 340,
|
||||||
@@ -1844,7 +1844,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-036',
|
id: 'prop-036',
|
||||||
title: 'Lagerhalle Klybeckstrasse 280',
|
title: 'Lagerhalle Klybeckstrasse 280',
|
||||||
assetType: AssetType.LOGISTICS,
|
assetType: AssetType.LOGISTICS,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Basel', district: 'Kleinhüningen', canton: 'BS', country: 'CH', coordinates: { lat: 47.5744, lng: 7.5862 } },
|
location: { city: 'Basel', district: 'Kleinhüningen', canton: 'BS', country: 'CH', coordinates: { lat: 47.5744, lng: 7.5862 } },
|
||||||
address: { street: 'Klybeckstrasse', houseNumber: '280', postalCode: '4057', city: 'Basel', country: 'CH' },
|
address: { street: 'Klybeckstrasse', houseNumber: '280', postalCode: '4057', city: 'Basel', country: 'CH' },
|
||||||
areaSqm: 2600,
|
areaSqm: 2600,
|
||||||
@@ -2206,7 +2206,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-055',
|
id: 'prop-055',
|
||||||
title: 'Ladenfläche Weststrasse 44',
|
title: 'Ladenfläche Weststrasse 44',
|
||||||
assetType: AssetType.RETAIL,
|
assetType: AssetType.RETAIL,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Zürich', district: 'Kreis 3', canton: 'ZH', country: 'CH', coordinates: { lat: 47.372, lng: 8.521 } },
|
location: { city: 'Zürich', district: 'Kreis 3', canton: 'ZH', country: 'CH', coordinates: { lat: 47.372, lng: 8.521 } },
|
||||||
address: { street: 'Weststrasse', houseNumber: '44', postalCode: '8003', city: 'Zürich', country: 'CH' },
|
address: { street: 'Weststrasse', houseNumber: '44', postalCode: '8003', city: 'Zürich', country: 'CH' },
|
||||||
areaSqm: 150,
|
areaSqm: 150,
|
||||||
@@ -2254,7 +2254,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-056',
|
id: 'prop-056',
|
||||||
title: 'Gewerbefläche Hohlstrasse 88',
|
title: 'Gewerbefläche Hohlstrasse 88',
|
||||||
assetType: AssetType.RETAIL,
|
assetType: AssetType.RETAIL,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Zürich', district: 'Kreis 4', canton: 'ZH', country: 'CH', coordinates: { lat: 47.375, lng: 8.527 } },
|
location: { city: 'Zürich', district: 'Kreis 4', canton: 'ZH', country: 'CH', coordinates: { lat: 47.375, lng: 8.527 } },
|
||||||
address: { street: 'Hohlstrasse', houseNumber: '88', postalCode: '8004', city: 'Zürich', country: 'CH' },
|
address: { street: 'Hohlstrasse', houseNumber: '88', postalCode: '8004', city: 'Zürich', country: 'CH' },
|
||||||
areaSqm: 155,
|
areaSqm: 155,
|
||||||
@@ -2667,7 +2667,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-064',
|
id: 'prop-064',
|
||||||
title: 'Bürofläche Förrlibuckstrasse 22',
|
title: 'Bürofläche Förrlibuckstrasse 22',
|
||||||
assetType: AssetType.OFFICE,
|
assetType: AssetType.OFFICE,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Zürich', district: 'Zürich-West', canton: 'ZH', country: 'CH', coordinates: { lat: 47.388, lng: 8.510 } },
|
location: { city: 'Zürich', district: 'Zürich-West', canton: 'ZH', country: 'CH', coordinates: { lat: 47.388, lng: 8.510 } },
|
||||||
address: { street: 'Förrlibuckstrasse', houseNumber: '22', postalCode: '8005', city: 'Zürich', country: 'CH' },
|
address: { street: 'Förrlibuckstrasse', houseNumber: '22', postalCode: '8005', city: 'Zürich', country: 'CH' },
|
||||||
areaSqm: 185,
|
areaSqm: 185,
|
||||||
@@ -2714,7 +2714,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-065',
|
id: 'prop-065',
|
||||||
title: 'Bürofläche Binzmühlestrasse 95, Oerlikon',
|
title: 'Bürofläche Binzmühlestrasse 95, Oerlikon',
|
||||||
assetType: AssetType.OFFICE,
|
assetType: AssetType.OFFICE,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Zürich', district: 'Oerlikon', canton: 'ZH', country: 'CH', coordinates: { lat: 47.411, lng: 8.544 } },
|
location: { city: 'Zürich', district: 'Oerlikon', canton: 'ZH', country: 'CH', coordinates: { lat: 47.411, lng: 8.544 } },
|
||||||
address: { street: 'Binzmühlestrasse', houseNumber: '95', postalCode: '8050', city: 'Zürich', country: 'CH' },
|
address: { street: 'Binzmühlestrasse', houseNumber: '95', postalCode: '8050', city: 'Zürich', country: 'CH' },
|
||||||
areaSqm: 175,
|
areaSqm: 175,
|
||||||
@@ -2813,7 +2813,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-067',
|
id: 'prop-067',
|
||||||
title: 'Bürofläche Altstetterstrasse 222',
|
title: 'Bürofläche Altstetterstrasse 222',
|
||||||
assetType: AssetType.OFFICE,
|
assetType: AssetType.OFFICE,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Zürich', district: 'Altstetten', canton: 'ZH', country: 'CH', coordinates: { lat: 47.385, lng: 8.488 } },
|
location: { city: 'Zürich', district: 'Altstetten', canton: 'ZH', country: 'CH', coordinates: { lat: 47.385, lng: 8.488 } },
|
||||||
address: { street: 'Altstetterstrasse', houseNumber: '222', postalCode: '8048', city: 'Zürich', country: 'CH' },
|
address: { street: 'Altstetterstrasse', houseNumber: '222', postalCode: '8048', city: 'Zürich', country: 'CH' },
|
||||||
areaSqm: 190,
|
areaSqm: 190,
|
||||||
@@ -3175,7 +3175,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-074',
|
id: 'prop-074',
|
||||||
title: 'Bürofläche Uraniastrasse 14',
|
title: 'Bürofläche Uraniastrasse 14',
|
||||||
assetType: AssetType.OFFICE,
|
assetType: AssetType.OFFICE,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Zürich', district: 'Kreis 1', canton: 'ZH', country: 'CH', coordinates: { lat: 47.376, lng: 8.541 } },
|
location: { city: 'Zürich', district: 'Kreis 1', canton: 'ZH', country: 'CH', coordinates: { lat: 47.376, lng: 8.541 } },
|
||||||
address: { street: 'Uraniastrasse', houseNumber: '14', postalCode: '8001', city: 'Zürich', country: 'CH' },
|
address: { street: 'Uraniastrasse', houseNumber: '14', postalCode: '8001', city: 'Zürich', country: 'CH' },
|
||||||
areaSqm: 510,
|
areaSqm: 510,
|
||||||
@@ -3223,7 +3223,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-075',
|
id: 'prop-075',
|
||||||
title: 'Bürofläche Seefeldstrasse 30',
|
title: 'Bürofläche Seefeldstrasse 30',
|
||||||
assetType: AssetType.OFFICE,
|
assetType: AssetType.OFFICE,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Zürich', district: 'Seefeld', canton: 'ZH', country: 'CH', coordinates: { lat: 47.356, lng: 8.550 } },
|
location: { city: 'Zürich', district: 'Seefeld', canton: 'ZH', country: 'CH', coordinates: { lat: 47.356, lng: 8.550 } },
|
||||||
address: { street: 'Seefeldstrasse', houseNumber: '30', postalCode: '8008', city: 'Zürich', country: 'CH' },
|
address: { street: 'Seefeldstrasse', houseNumber: '30', postalCode: '8008', city: 'Zürich', country: 'CH' },
|
||||||
areaSqm: 540,
|
areaSqm: 540,
|
||||||
@@ -3483,7 +3483,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-080',
|
id: 'prop-080',
|
||||||
title: 'Ladenfläche Spitalgasse 34',
|
title: 'Ladenfläche Spitalgasse 34',
|
||||||
assetType: AssetType.RETAIL,
|
assetType: AssetType.RETAIL,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Bern', district: 'Innenstadt', canton: 'BE', country: 'CH', coordinates: { lat: 46.948, lng: 7.447 } },
|
location: { city: 'Bern', district: 'Innenstadt', canton: 'BE', country: 'CH', coordinates: { lat: 46.948, lng: 7.447 } },
|
||||||
address: { street: 'Spitalgasse', houseNumber: '34', postalCode: '3011', city: 'Bern', country: 'CH' },
|
address: { street: 'Spitalgasse', houseNumber: '34', postalCode: '3011', city: 'Bern', country: 'CH' },
|
||||||
areaSqm: 280,
|
areaSqm: 280,
|
||||||
@@ -3531,7 +3531,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-081',
|
id: 'prop-081',
|
||||||
title: 'Ladenfläche Münstergasse 12',
|
title: 'Ladenfläche Münstergasse 12',
|
||||||
assetType: AssetType.RETAIL,
|
assetType: AssetType.RETAIL,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Bern', district: 'Innenstadt', canton: 'BE', country: 'CH', coordinates: { lat: 46.948, lng: 7.447 } },
|
location: { city: 'Bern', district: 'Innenstadt', canton: 'BE', country: 'CH', coordinates: { lat: 46.948, lng: 7.447 } },
|
||||||
address: { street: 'Münstergasse', houseNumber: '12', postalCode: '3011', city: 'Bern', country: 'CH' },
|
address: { street: 'Münstergasse', houseNumber: '12', postalCode: '3011', city: 'Bern', country: 'CH' },
|
||||||
areaSqm: 350,
|
areaSqm: 350,
|
||||||
@@ -3838,7 +3838,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-087',
|
id: 'prop-087',
|
||||||
title: 'Lagerhalle Hauptstrasse 180, Pratteln',
|
title: 'Lagerhalle Hauptstrasse 180, Pratteln',
|
||||||
assetType: AssetType.LOGISTICS,
|
assetType: AssetType.LOGISTICS,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Pratteln', district: 'Industriezone', canton: 'BL', country: 'CH', coordinates: { lat: 47.519, lng: 7.689 } },
|
location: { city: 'Pratteln', district: 'Industriezone', canton: 'BL', country: 'CH', coordinates: { lat: 47.519, lng: 7.689 } },
|
||||||
address: { street: 'Hauptstrasse', houseNumber: '180', postalCode: '4133', city: 'Pratteln', country: 'CH' },
|
address: { street: 'Hauptstrasse', houseNumber: '180', postalCode: '4133', city: 'Pratteln', country: 'CH' },
|
||||||
areaSqm: 3000,
|
areaSqm: 3000,
|
||||||
@@ -3885,7 +3885,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-088',
|
id: 'prop-088',
|
||||||
title: 'Lagerhalle Westquaistrasse 12',
|
title: 'Lagerhalle Westquaistrasse 12',
|
||||||
assetType: AssetType.LOGISTICS,
|
assetType: AssetType.LOGISTICS,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Basel', district: 'Kleinhüningen', canton: 'BS', country: 'CH', coordinates: { lat: 47.574, lng: 7.591 } },
|
location: { city: 'Basel', district: 'Kleinhüningen', canton: 'BS', country: 'CH', coordinates: { lat: 47.574, lng: 7.591 } },
|
||||||
address: { street: 'Westquaistrasse', houseNumber: '12', postalCode: '4057', city: 'Basel', country: 'CH' },
|
address: { street: 'Westquaistrasse', houseNumber: '12', postalCode: '4057', city: 'Basel', country: 'CH' },
|
||||||
areaSqm: 2600,
|
areaSqm: 2600,
|
||||||
@@ -4091,7 +4091,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-092',
|
id: 'prop-092',
|
||||||
title: 'Bürofläche Hohlstrasse 534, Altstetten',
|
title: 'Bürofläche Hohlstrasse 534, Altstetten',
|
||||||
assetType: AssetType.OFFICE,
|
assetType: AssetType.OFFICE,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Zürich', district: 'Altstetten', canton: 'ZH', country: 'CH', coordinates: { lat: 47.385, lng: 8.488 } },
|
location: { city: 'Zürich', district: 'Altstetten', canton: 'ZH', country: 'CH', coordinates: { lat: 47.385, lng: 8.488 } },
|
||||||
address: { street: 'Hohlstrasse', houseNumber: '534', postalCode: '8048', city: 'Zürich', country: 'CH' },
|
address: { street: 'Hohlstrasse', houseNumber: '534', postalCode: '8048', city: 'Zürich', country: 'CH' },
|
||||||
areaSqm: 900,
|
areaSqm: 900,
|
||||||
@@ -4138,7 +4138,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-093',
|
id: 'prop-093',
|
||||||
title: 'Bürofläche Technopark Zürich-West',
|
title: 'Bürofläche Technopark Zürich-West',
|
||||||
assetType: AssetType.OFFICE,
|
assetType: AssetType.OFFICE,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Zürich', district: 'Zürich-West', canton: 'ZH', country: 'CH', coordinates: { lat: 47.388, lng: 8.510 } },
|
location: { city: 'Zürich', district: 'Zürich-West', canton: 'ZH', country: 'CH', coordinates: { lat: 47.388, lng: 8.510 } },
|
||||||
address: { street: 'Technoparkstrasse', houseNumber: '2', postalCode: '8005', city: 'Zürich', country: 'CH' },
|
address: { street: 'Technoparkstrasse', houseNumber: '2', postalCode: '8005', city: 'Zürich', country: 'CH' },
|
||||||
areaSqm: 960,
|
areaSqm: 960,
|
||||||
@@ -4343,7 +4343,7 @@ export const mockProperties: Property[] = [
|
|||||||
id: 'prop-097',
|
id: 'prop-097',
|
||||||
title: 'Bürofläche Baarerstrasse 18, Zug',
|
title: 'Bürofläche Baarerstrasse 18, Zug',
|
||||||
assetType: AssetType.OFFICE,
|
assetType: AssetType.OFFICE,
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
location: { city: 'Zug', district: 'Innenstadt', canton: 'ZG', country: 'CH', coordinates: { lat: 47.174, lng: 8.514 } },
|
location: { city: 'Zug', district: 'Innenstadt', canton: 'ZG', country: 'CH', coordinates: { lat: 47.174, lng: 8.514 } },
|
||||||
address: { street: 'Baarerstrasse', houseNumber: '18', postalCode: '6300', city: 'Zug', country: 'CH' },
|
address: { street: 'Baarerstrasse', houseNumber: '18', postalCode: '6300', city: 'Zug', country: 'CH' },
|
||||||
areaSqm: 320,
|
areaSqm: 320,
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export default function MatchDetail() {
|
|||||||
const handleCompare = () => {
|
const handleCompare = () => {
|
||||||
if (match && !isFuture && property) {
|
if (match && !isFuture && property) {
|
||||||
addToCompare({
|
addToCompare({
|
||||||
resultType: property.resultType === 'EXTERNAL_MARKET' ? 'EXTERNAL_MARKET' : 'VERIFIED_PORTFOLIO',
|
resultType: property.resultType ?? 'VERIFIED_PORTFOLIO',
|
||||||
matchId: match.id, needId: match.needId, matchScore: match.matchScore, match, property,
|
matchId: match.id, needId: match.needId, matchScore: match.matchScore, match, property,
|
||||||
})
|
})
|
||||||
} else if (match && isFuture && signal) {
|
} else if (match && isFuture && signal) {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export default function Results() {
|
|||||||
const { platformCount, maisonWorkCount, futureCount, missingDataCount } = useMemo(() => {
|
const { platformCount, maisonWorkCount, futureCount, missingDataCount } = useMemo(() => {
|
||||||
let platform = 0, maison = 0, future = 0, missing = 0
|
let platform = 0, maison = 0, future = 0, missing = 0
|
||||||
for (const r of results) {
|
for (const r of results) {
|
||||||
if (r.resultType === 'VERIFIED_PORTFOLIO' || r.resultType === 'EXTERNAL_MARKET') platform++
|
if (r.resultType === 'VERIFIED_PORTFOLIO') platform++
|
||||||
else if (r.resultType === 'MAISON_WORK') maison++
|
else if (r.resultType === 'MAISON_WORK') maison++
|
||||||
else if (r.resultType === 'FUTURE_AVAILABILITY') future++
|
else if (r.resultType === 'FUTURE_AVAILABILITY') future++
|
||||||
if ('match' in r && Array.isArray((r as { match?: { missingData?: unknown[] } }).match?.missingData) &&
|
if ('match' in r && Array.isArray((r as { match?: { missingData?: unknown[] } }).match?.missingData) &&
|
||||||
@@ -91,7 +91,6 @@ export default function Results() {
|
|||||||
return filterSource === 'ALL' || filterSource === 'PLATFORM'
|
return filterSource === 'ALL' || filterSource === 'PLATFORM'
|
||||||
}
|
}
|
||||||
if (filterSource === 'ALL') return true
|
if (filterSource === 'ALL') return true
|
||||||
if (filterSource === 'PLATFORM') return r.resultType === 'EXTERNAL_MARKET'
|
|
||||||
return r.resultType === filterSource
|
return r.resultType === filterSource
|
||||||
})
|
})
|
||||||
return sortResults(filtered, sortBy)
|
return sortResults(filtered, sortBy)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export function buildCreatePropertyInput(fields: {
|
|||||||
return {
|
return {
|
||||||
title: `${ASSET_TYPE_LABELS[assetType] ?? assetType} · ${city}`,
|
title: `${ASSET_TYPE_LABELS[assetType] ?? assetType} · ${city}`,
|
||||||
assetType: assetType as typeof AssetType[keyof typeof AssetType],
|
assetType: assetType as typeof AssetType[keyof typeof AssetType],
|
||||||
resultType: ResultType.EXTERNAL_MARKET,
|
resultType: ResultType.VERIFIED_PORTFOLIO,
|
||||||
sourceType: 'DIRECT',
|
sourceType: 'DIRECT',
|
||||||
location: { city, country: 'CH' },
|
location: { city, country: 'CH' },
|
||||||
address: {
|
address: {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export interface DashboardStats {
|
export interface DashboardStats {
|
||||||
totalProperties: number
|
totalProperties: number
|
||||||
verifiedProperties: number
|
verifiedProperties: number
|
||||||
externalMarketProperties: number
|
verifiedPortfolioProperties: number
|
||||||
futureSignalProperties: number
|
futureSignalProperties: number
|
||||||
totalMatches: number
|
totalMatches: number
|
||||||
pendingReviews: number
|
pendingReviews: number
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const MockupDashboardProvider: IDashboardProvider = {
|
|||||||
const stats: DashboardStats = {
|
const stats: DashboardStats = {
|
||||||
totalProperties: props.length,
|
totalProperties: props.length,
|
||||||
verifiedProperties: props.filter(p => p.resultType === 'VERIFIED_PORTFOLIO').length,
|
verifiedProperties: props.filter(p => p.resultType === 'VERIFIED_PORTFOLIO').length,
|
||||||
externalMarketProperties: props.filter(p => p.resultType === 'EXTERNAL_MARKET').length,
|
verifiedPortfolioProperties: props.filter(p => p.resultType === 'VERIFIED_PORTFOLIO').length,
|
||||||
futureSignalProperties: props.filter(p => p.resultType === 'FUTURE_AVAILABILITY').length,
|
futureSignalProperties: props.filter(p => p.resultType === 'FUTURE_AVAILABILITY').length,
|
||||||
totalMatches: matches.length,
|
totalMatches: matches.length,
|
||||||
pendingReviews: queue.filter(r => r.status === 'PENDING').length,
|
pendingReviews: queue.filter(r => r.status === 'PENDING').length,
|
||||||
|
|||||||
Reference in New Issue
Block a user