diff --git a/.claude/settings.json b/.claude/settings.json index 541118f..063e98e 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -11,7 +11,9 @@ "Bash(start http://localhost:5173)", "Bash(npm install *)", "Bash(git pull *)", - "Bash(node scripts/check-tokens.js)" + "Bash(node scripts/check-tokens.js)", + "Bash(echo \"EXIT:$?\")", + "Bash(echo \"EXIT_CODE:$?\")" ] } } diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 8081af2..2ae1f03 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -2,7 +2,8 @@ "permissions": { "allow": [ "Bash(git commit -m ' *)", - "Bash(git commit *)" + "Bash(git commit *)", + "Bash(node -e ' *)" ] } } diff --git a/src/components/badges/ResultTypeBadge.tsx b/src/components/badges/ResultTypeBadge.tsx index 8f602c4..631fd9d 100644 --- a/src/components/badges/ResultTypeBadge.tsx +++ b/src/components/badges/ResultTypeBadge.tsx @@ -1,5 +1,5 @@ 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 { DS_COLORS } from '../../lib/ds' import { RESULT_TYPE_LABELS } from '../../lib/constants' @@ -11,7 +11,6 @@ interface ResultTypeBadgeProps { const ICONS: Record = { VERIFIED_PORTFOLIO: ShieldCheck, - EXTERNAL_MARKET: Globe, MAISON_WORK: Building2, FUTURE_AVAILABILITY: Sparkles, } diff --git a/src/components/cards/SourceTypeBadge.tsx b/src/components/cards/SourceTypeBadge.tsx index 357036c..9104367 100644 --- a/src/components/cards/SourceTypeBadge.tsx +++ b/src/components/cards/SourceTypeBadge.tsx @@ -10,7 +10,6 @@ interface SourceTypeBadgeProps { const CONFIG: Record = { 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 }, FUTURE_AVAILABILITY: { bg: 'rgba(124,58,237,0.1)', color: '#6d28d9', Icon: Sparkles }, } diff --git a/src/components/layout/CompareTray.tsx b/src/components/layout/CompareTray.tsx index ac1f610..6906dad 100644 --- a/src/components/layout/CompareTray.tsx +++ b/src/components/layout/CompareTray.tsx @@ -5,7 +5,6 @@ import { useCompareStore } from '../../stores/compareStore' const TYPE_DOT: Record = { VERIFIED_PORTFOLIO: '#1e3a5f', - EXTERNAL_MARKET: '#d97706', MAISON_WORK: '#0369a1', FUTURE_AVAILABILITY: '#7c3aed', } diff --git a/src/components/match-card/MatchCardCompareMini.tsx b/src/components/match-card/MatchCardCompareMini.tsx index 07ca6b5..d67aa41 100644 --- a/src/components/match-card/MatchCardCompareMini.tsx +++ b/src/components/match-card/MatchCardCompareMini.tsx @@ -5,7 +5,6 @@ import type { MatchCardViewModel } from './MatchCardViewModel' const RESULT_TYPE_META: Record = { VERIFIED_PORTFOLIO: { label: 'Verified', color: '#1e3a5f' }, - EXTERNAL_MARKET: { label: 'Direkt', color: '#d97706' }, MAISON_WORK: { label: 'Maison Work', color: '#0369a1' }, FUTURE_AVAILABILITY: { label: 'Signal', color: '#7c3aed' }, } diff --git a/src/components/match-detail/MatchDetailHeader.tsx b/src/components/match-detail/MatchDetailHeader.tsx index 4a91701..a6b0beb 100644 --- a/src/components/match-detail/MatchDetailHeader.tsx +++ b/src/components/match-detail/MatchDetailHeader.tsx @@ -7,7 +7,6 @@ import type { FutureSignal } from '../../domain/futureSignal' const RESULT_TYPE_META: Record = { VERIFIED_PORTFOLIO: { label: 'Verified Portfolio', color: '#1e3a5f' }, - EXTERNAL_MARKET: { label: 'Direktinserat', color: '#d97706' }, MAISON_WORK: { label: 'Maison Work', color: '#0369a1' }, FUTURE_AVAILABILITY: { label: 'Zukunftssignal', color: '#7c3aed' }, } diff --git a/src/components/shortlist/ShortlistDetail.tsx b/src/components/shortlist/ShortlistDetail.tsx index 31d7f6d..2f8f4ad 100644 --- a/src/components/shortlist/ShortlistDetail.tsx +++ b/src/components/shortlist/ShortlistDetail.tsx @@ -72,7 +72,7 @@ export function ShortlistDetail({ shortlist }: Props) { matchId: item.resultId, needId: '', 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, match: { id: item.resultId, matchScore: item.matchScore, confidenceLevel: item.confidenceScore ?? 0.8 } as any, } diff --git a/src/components/shortlist/ShortlistItemCard.tsx b/src/components/shortlist/ShortlistItemCard.tsx index e9a49fb..fdc7cee 100644 --- a/src/components/shortlist/ShortlistItemCard.tsx +++ b/src/components/shortlist/ShortlistItemCard.tsx @@ -11,7 +11,6 @@ import type { ShortlistItem } from '../../domain/shortlist' const RESULT_TYPE_LABEL: Record = { VERIFIED_PORTFOLIO: 'Portfolio', - EXTERNAL_MARKET: 'Direkt', MAISON_WORK: 'Maison Work', FUTURE_AVAILABILITY: 'Signal', } diff --git a/src/domain/enums.ts b/src/domain/enums.ts index 5b1a664..a4185c7 100644 --- a/src/domain/enums.ts +++ b/src/domain/enums.ts @@ -14,7 +14,6 @@ export type AssetType = typeof AssetType[keyof typeof AssetType] // ── Result Types ────────────────────────────────────────────────────────────── export const ResultType = { VERIFIED_PORTFOLIO: 'VERIFIED_PORTFOLIO', - EXTERNAL_MARKET: 'EXTERNAL_MARKET', MAISON_WORK: 'MAISON_WORK', FUTURE_AVAILABILITY: 'FUTURE_AVAILABILITY', } as const diff --git a/src/domain/pipeline.ts b/src/domain/pipeline.ts index 74badcb..19c05f1 100644 --- a/src/domain/pipeline.ts +++ b/src/domain/pipeline.ts @@ -5,7 +5,7 @@ export interface PipelineItem { title: string location: string matchScore: number - resultType: 'VERIFIED_PORTFOLIO' | 'EXTERNAL_MARKET' | 'MAISON_WORK' | 'FUTURE_AVAILABILITY' + resultType: 'VERIFIED_PORTFOLIO' | 'MAISON_WORK' | 'FUTURE_AVAILABILITY' stage: PipelineStage // Source match reference — used for navigating back to the detail page matchId?: string diff --git a/src/domain/scoring.ts b/src/domain/scoring.ts index eda6360..7d15f60 100644 --- a/src/domain/scoring.ts +++ b/src/domain/scoring.ts @@ -42,7 +42,6 @@ export const DATA_QUALITY_MODIFIER = { export const CONFIDENCE_MODIFIER = { VERIFIED_HIGH: +3, // 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) FUTURE_AVAILABILITY: -15, // FUTURE_AVAILABILITY — never treat as confirmed availability LOW_CONFIDENCE: -10, // confidenceScore < 0.50 (stacks with above) diff --git a/src/domain/unifiedResult.ts b/src/domain/unifiedResult.ts index 5aabfee..6e546e8 100644 --- a/src/domain/unifiedResult.ts +++ b/src/domain/unifiedResult.ts @@ -15,14 +15,7 @@ interface UnifiedResultBase { } export interface VerifiedPortfolioResult extends UnifiedResultBase { - resultType: 'VERIFIED_PORTFOLIO' - property: Property - match: Match - unit?: PropertyUnit // specific unit this match refers to -} - -export interface ExternalMarketResult extends UnifiedResultBase { - resultType: 'EXTERNAL_MARKET' | 'MAISON_WORK' + resultType: 'VERIFIED_PORTFOLIO' | 'MAISON_WORK' property: Property match: Match unit?: PropertyUnit // specific unit this match refers to @@ -38,5 +31,4 @@ export interface FutureAvailabilityResult extends UnifiedResultBase { export type UnifiedMatchResult = | VerifiedPortfolioResult - | ExternalMarketResult | FutureAvailabilityResult diff --git a/src/features/matching/__tests__/rankingEngine.test.ts b/src/features/matching/__tests__/rankingEngine.test.ts index 2fe12b8..bbe667c 100644 --- a/src/features/matching/__tests__/rankingEngine.test.ts +++ b/src/features/matching/__tests__/rankingEngine.test.ts @@ -38,20 +38,17 @@ describe('rankMatches', () => { 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 const prop = makeProperty() 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 - if (verified.matchScore !== external.matchScore) { - const lower = Math.min(verified.matchScore, external.matchScore) - verified.matchScore = lower - external.matchScore = lower - } + // Force same score for a clean tiebreak test + verified.matchScore = 75 + future.matchScore = 75 - const ranked = rankMatches([external, verified]) + const ranked = rankMatches([future, verified]) expect(ranked[0].resultType).toBe(ResultType.VERIFIED_PORTFOLIO) }) diff --git a/src/features/matching/__tests__/scoreCalculator.test.ts b/src/features/matching/__tests__/scoreCalculator.test.ts index 48d2f0a..be32575 100644 --- a/src/features/matching/__tests__/scoreCalculator.test.ts +++ b/src/features/matching/__tests__/scoreCalculator.test.ts @@ -56,11 +56,6 @@ describe('calcConfidenceModifier', () => { 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', () => { const p = makeProperty({ resultType: ResultType.MAISON_WORK, confidenceScore: 0.80 }) expect(calcConfidenceModifier(p)).toBe(-2) @@ -72,9 +67,9 @@ describe('calcConfidenceModifier', () => { }) it('stacks an additional -10 penalty when confidenceScore < 0.50', () => { - // EXTERNAL_MARKET (-3) + LOW_CONFIDENCE (-10) = -13 - const p = makeProperty({ resultType: ResultType.EXTERNAL_MARKET, confidenceScore: 0.45 }) - expect(calcConfidenceModifier(p)).toBe(-13) + // MAISON_WORK (-2) + LOW_CONFIDENCE (-10) = -12 + const p = makeProperty({ resultType: ResultType.MAISON_WORK, confidenceScore: 0.45 }) + expect(calcConfidenceModifier(p)).toBe(-12) }) it('stacks -10 on FUTURE_AVAILABILITY when confidence is also low', () => { @@ -157,12 +152,12 @@ describe('calculateScore', () => { 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 prop = makeProperty({ location: { city: 'Basel', country: 'CH' }, 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 dataQuality: { score: 0.30, // CRITICAL → -15 diff --git a/src/features/matching/__tests__/softFactorEnrichment.test.ts b/src/features/matching/__tests__/softFactorEnrichment.test.ts index 346a587..f9d57c3 100644 --- a/src/features/matching/__tests__/softFactorEnrichment.test.ts +++ b/src/features/matching/__tests__/softFactorEnrichment.test.ts @@ -132,7 +132,7 @@ describe('softFactorEnrichmentService — score range invariant (all keys, 0–1 describe('softFactorEnrichmentService — Pre-Market vs Market Signal', () => { it('returns identical estimate for same location regardless of resultType', () => { // 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 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, resultType: ResultType.FUTURE_AVAILABILITY, }) - const marketResult = makeProperty({ + const portfolioResult = makeProperty({ location: sharedLocation, address: sharedAddress, - resultType: ResultType.EXTERNAL_MARKET, + resultType: ResultType.VERIFIED_PORTFOLIO, }) const futureEst = softFactorEnrichmentService.estimate('accessibility', futureSignal) - const marketEst = softFactorEnrichmentService.estimate('accessibility', marketResult) - expect(futureEst?.score).toBe(marketEst?.score) - expect(futureEst?.label).toBe(marketEst?.label) + const portfolioEst = softFactorEnrichmentService.estimate('accessibility', portfolioResult) + expect(futureEst?.score).toBe(portfolioEst?.score) + expect(futureEst?.label).toBe(portfolioEst?.label) }) it('FUTURE_AVAILABILITY at Oerlikon gets meaningful accessibility score (> 0.70)', () => { diff --git a/src/features/matching/rankingEngine.ts b/src/features/matching/rankingEngine.ts index 08a64fb..2407e8e 100644 --- a/src/features/matching/rankingEngine.ts +++ b/src/features/matching/rankingEngine.ts @@ -191,10 +191,10 @@ export function rankMatches(matches: Match[]): Match[] { return [...matches].sort((a, b) => { // Primary: matchScore descending if (b.matchScore !== a.matchScore) return b.matchScore - a.matchScore - // Secondary: VERIFIED > EXTERNAL / MAISON_WORK > FUTURE - const typeOrder: Record = { VERIFIED_PORTFOLIO: 0, EXTERNAL_MARKET: 1, MAISON_WORK: 1, FUTURE_AVAILABILITY: 2 } - const aOrder = typeOrder[a.resultType ?? 'EXTERNAL_MARKET'] ?? 1 - const bOrder = typeOrder[b.resultType ?? 'EXTERNAL_MARKET'] ?? 1 + // Secondary: VERIFIED / MAISON_WORK > FUTURE + const typeOrder: Record = { VERIFIED_PORTFOLIO: 0, MAISON_WORK: 0, FUTURE_AVAILABILITY: 2 } + const aOrder = typeOrder[a.resultType ?? 'VERIFIED_PORTFOLIO'] ?? 0 + const bOrder = typeOrder[b.resultType ?? 'VERIFIED_PORTFOLIO'] ?? 0 if (aOrder !== bOrder) return aOrder - bOrder // Tertiary: higher confidence first return (b.confidenceLevel ?? 0) - (a.confidenceLevel ?? 0) diff --git a/src/features/matching/scoreCalculator.ts b/src/features/matching/scoreCalculator.ts index d8b2dce..ee051f5 100644 --- a/src/features/matching/scoreCalculator.ts +++ b/src/features/matching/scoreCalculator.ts @@ -499,8 +499,6 @@ export function calcConfidenceModifier(property: Property): number { let mod = 0 if (property.resultType === ResultType.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) { mod += CONFIDENCE_MODIFIER.MAISON_WORK } else { diff --git a/src/hooks/useUnifiedResults.ts b/src/hooks/useUnifiedResults.ts index e4505fa..d04b3df 100644 --- a/src/hooks/useUnifiedResults.ts +++ b/src/hooks/useUnifiedResults.ts @@ -6,7 +6,6 @@ import { useSchattenmarktSignals } from './useSchattenmarktSignals' import type { UnifiedMatchResult, VerifiedPortfolioResult, - ExternalMarketResult, FutureAvailabilityResult, } from '../domain/unifiedResult' @@ -64,24 +63,11 @@ export function useUnifiedResults(needId?: string) { ? (property.units?.find(u => u.id === match.unitId) ?? 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 = { matchId: match.id, needId: match.needId, matchScore: match.matchScore, - resultType: 'VERIFIED_PORTFOLIO', + resultType: rt as 'VERIFIED_PORTFOLIO' | 'MAISON_WORK', property, match, unit: matchUnit, diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 55f19e7..704f1f2 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -69,7 +69,6 @@ export const ASSET_TYPE_LABELS: Record = { // Result type display labels export const RESULT_TYPE_LABELS: Record = { VERIFIED_PORTFOLIO: 'Plattform', - EXTERNAL_MARKET: 'Plattform', MAISON_WORK: 'Maison Work', FUTURE_AVAILABILITY: 'Zukunftssignal', } diff --git a/src/lib/ds.ts b/src/lib/ds.ts index 96cc500..6932b31 100644 --- a/src/lib/ds.ts +++ b/src/lib/ds.ts @@ -7,7 +7,6 @@ import { CONF_HIGH, CONF_MEDIUM, DQ_HIGH, DQ_MEDIUM } from './constants' export const DS_COLORS = { resultType: { 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' }, 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 = { 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)' }, FUTURE_AVAILABILITY: { label: 'Zukunftssignal', color: '#7c3aed', bg: 'rgba(109,40,217,0.10)' }, } diff --git a/src/lib/permissions.ts b/src/lib/permissions.ts index 7085b7e..f44a452 100644 --- a/src/lib/permissions.ts +++ b/src/lib/permissions.ts @@ -99,7 +99,6 @@ export function canViewProperty( if (property.resultType === ResultType.VERIFIED_PORTFOLIO) { 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.FUTURE_AVAILABILITY) { return hasPermission(user, Permission.FUTURE_SIGNAL_VIEW) diff --git a/src/mock-data/pipelineItems.ts b/src/mock-data/pipelineItems.ts index 5110c4b..2cc0915 100644 --- a/src/mock-data/pipelineItems.ts +++ b/src/mock-data/pipelineItems.ts @@ -121,7 +121,7 @@ export const mockPipelineItems: PipelineItem[] = [ title: 'Gewerbe Güterstrasse Basel', location: 'Basel', matchScore: 68, - resultType: 'EXTERNAL_MARKET', + resultType: 'VERIFIED_PORTFOLIO', stage: 'CLOSED_LOST', areaLabel: '800 m²', rentLabel: 'CHF 28/m²', diff --git a/src/mock-data/properties.ts b/src/mock-data/properties.ts index 27d9740..26fdac8 100644 --- a/src/mock-data/properties.ts +++ b/src/mock-data/properties.ts @@ -1071,14 +1071,14 @@ export const mockProperties: Property[] = [ }, // ───────────────────────────────────────────────────────────────────────────── - // EXTERNAL_MARKET (10) + // VERIFIED_PORTFOLIO — formerly external market (migrated) // ───────────────────────────────────────────────────────────────────────────── { id: 'prop-003', title: 'Retail-Fläche Bahnhofstrasse 88', 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 } }, address: { street: 'Bahnhofstrasse', houseNumber: '88', postalCode: '3011', city: 'Bern', country: 'CH' }, areaSqm: 320, @@ -1122,7 +1122,7 @@ export const mockProperties: Property[] = [ id: 'prop-004', title: 'Gemischte Gewerbeeinheit Europaallee', 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 } }, address: { street: 'Europaallee', houseNumber: '21', postalCode: '8004', city: 'Zürich', country: 'CH' }, areaSqm: 1150, @@ -1246,7 +1246,7 @@ export const mockProperties: Property[] = [ id: 'prop-017', title: 'Ladenfläche Löwenstrasse 28', 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 } }, address: { street: 'Löwenstrasse', houseNumber: '28', postalCode: '8001', city: 'Zürich', country: 'CH' }, areaSqm: 350, @@ -1290,7 +1290,7 @@ export const mockProperties: Property[] = [ id: 'prop-018', title: 'Bürofläche Breitenrain 14', 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 } }, address: { street: 'Breitenrainstrasse', houseNumber: '14', postalCode: '3014', city: 'Bern', country: 'CH' }, areaSqm: 780, @@ -1458,7 +1458,7 @@ export const mockProperties: Property[] = [ id: 'prop-022', title: 'Bürofläche St.Gallen Centrum 7', 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 } }, address: { street: 'Marktgasse', houseNumber: '7', postalCode: '9000', city: 'St. Gallen', country: 'CH' }, areaSqm: 700, @@ -1666,7 +1666,7 @@ export const mockProperties: Property[] = [ id: 'prop-031', title: 'Bürofläche Hardturm West 16', 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 } }, address: { street: 'Hardturmstrasse', houseNumber: '16', postalCode: '8005', city: 'Zürich', country: 'CH' }, areaSqm: 780, @@ -1780,7 +1780,7 @@ export const mockProperties: Property[] = [ id: 'prop-034', title: 'Ladenfläche Lorrainestrasse 8', 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 } }, address: { street: 'Lorrainestrasse', houseNumber: '8', postalCode: '3013', city: 'Bern', country: 'CH' }, areaSqm: 340, @@ -1844,7 +1844,7 @@ export const mockProperties: Property[] = [ id: 'prop-036', title: 'Lagerhalle Klybeckstrasse 280', 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 } }, address: { street: 'Klybeckstrasse', houseNumber: '280', postalCode: '4057', city: 'Basel', country: 'CH' }, areaSqm: 2600, @@ -2206,7 +2206,7 @@ export const mockProperties: Property[] = [ id: 'prop-055', title: 'Ladenfläche Weststrasse 44', 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 } }, address: { street: 'Weststrasse', houseNumber: '44', postalCode: '8003', city: 'Zürich', country: 'CH' }, areaSqm: 150, @@ -2254,7 +2254,7 @@ export const mockProperties: Property[] = [ id: 'prop-056', title: 'Gewerbefläche Hohlstrasse 88', 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 } }, address: { street: 'Hohlstrasse', houseNumber: '88', postalCode: '8004', city: 'Zürich', country: 'CH' }, areaSqm: 155, @@ -2667,7 +2667,7 @@ export const mockProperties: Property[] = [ id: 'prop-064', title: 'Bürofläche Förrlibuckstrasse 22', 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 } }, address: { street: 'Förrlibuckstrasse', houseNumber: '22', postalCode: '8005', city: 'Zürich', country: 'CH' }, areaSqm: 185, @@ -2714,7 +2714,7 @@ export const mockProperties: Property[] = [ id: 'prop-065', title: 'Bürofläche Binzmühlestrasse 95, Oerlikon', 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 } }, address: { street: 'Binzmühlestrasse', houseNumber: '95', postalCode: '8050', city: 'Zürich', country: 'CH' }, areaSqm: 175, @@ -2813,7 +2813,7 @@ export const mockProperties: Property[] = [ id: 'prop-067', title: 'Bürofläche Altstetterstrasse 222', 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 } }, address: { street: 'Altstetterstrasse', houseNumber: '222', postalCode: '8048', city: 'Zürich', country: 'CH' }, areaSqm: 190, @@ -3175,7 +3175,7 @@ export const mockProperties: Property[] = [ id: 'prop-074', title: 'Bürofläche Uraniastrasse 14', 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 } }, address: { street: 'Uraniastrasse', houseNumber: '14', postalCode: '8001', city: 'Zürich', country: 'CH' }, areaSqm: 510, @@ -3223,7 +3223,7 @@ export const mockProperties: Property[] = [ id: 'prop-075', title: 'Bürofläche Seefeldstrasse 30', 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 } }, address: { street: 'Seefeldstrasse', houseNumber: '30', postalCode: '8008', city: 'Zürich', country: 'CH' }, areaSqm: 540, @@ -3483,7 +3483,7 @@ export const mockProperties: Property[] = [ id: 'prop-080', title: 'Ladenfläche Spitalgasse 34', 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 } }, address: { street: 'Spitalgasse', houseNumber: '34', postalCode: '3011', city: 'Bern', country: 'CH' }, areaSqm: 280, @@ -3531,7 +3531,7 @@ export const mockProperties: Property[] = [ id: 'prop-081', title: 'Ladenfläche Münstergasse 12', 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 } }, address: { street: 'Münstergasse', houseNumber: '12', postalCode: '3011', city: 'Bern', country: 'CH' }, areaSqm: 350, @@ -3838,7 +3838,7 @@ export const mockProperties: Property[] = [ id: 'prop-087', title: 'Lagerhalle Hauptstrasse 180, Pratteln', 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 } }, address: { street: 'Hauptstrasse', houseNumber: '180', postalCode: '4133', city: 'Pratteln', country: 'CH' }, areaSqm: 3000, @@ -3885,7 +3885,7 @@ export const mockProperties: Property[] = [ id: 'prop-088', title: 'Lagerhalle Westquaistrasse 12', 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 } }, address: { street: 'Westquaistrasse', houseNumber: '12', postalCode: '4057', city: 'Basel', country: 'CH' }, areaSqm: 2600, @@ -4091,7 +4091,7 @@ export const mockProperties: Property[] = [ id: 'prop-092', title: 'Bürofläche Hohlstrasse 534, Altstetten', 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 } }, address: { street: 'Hohlstrasse', houseNumber: '534', postalCode: '8048', city: 'Zürich', country: 'CH' }, areaSqm: 900, @@ -4138,7 +4138,7 @@ export const mockProperties: Property[] = [ id: 'prop-093', title: 'Bürofläche Technopark Zürich-West', 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 } }, address: { street: 'Technoparkstrasse', houseNumber: '2', postalCode: '8005', city: 'Zürich', country: 'CH' }, areaSqm: 960, @@ -4343,7 +4343,7 @@ export const mockProperties: Property[] = [ id: 'prop-097', title: 'Bürofläche Baarerstrasse 18, Zug', 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 } }, address: { street: 'Baarerstrasse', houseNumber: '18', postalCode: '6300', city: 'Zug', country: 'CH' }, areaSqm: 320, diff --git a/src/pages/demand/MatchDetail.tsx b/src/pages/demand/MatchDetail.tsx index de4b94f..1507de5 100644 --- a/src/pages/demand/MatchDetail.tsx +++ b/src/pages/demand/MatchDetail.tsx @@ -88,7 +88,7 @@ export default function MatchDetail() { const handleCompare = () => { if (match && !isFuture && property) { 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, }) } else if (match && isFuture && signal) { diff --git a/src/pages/demand/Results.tsx b/src/pages/demand/Results.tsx index 3a776ff..acf9983 100644 --- a/src/pages/demand/Results.tsx +++ b/src/pages/demand/Results.tsx @@ -73,7 +73,7 @@ export default function Results() { const { platformCount, maisonWorkCount, futureCount, missingDataCount } = useMemo(() => { let platform = 0, maison = 0, future = 0, missing = 0 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 === 'FUTURE_AVAILABILITY') future++ 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' } if (filterSource === 'ALL') return true - if (filterSource === 'PLATFORM') return r.resultType === 'EXTERNAL_MARKET' return r.resultType === filterSource }) return sortResults(filtered, sortBy) diff --git a/src/pages/supply/newListingMapper.ts b/src/pages/supply/newListingMapper.ts index b9ae5bc..68597a9 100644 --- a/src/pages/supply/newListingMapper.ts +++ b/src/pages/supply/newListingMapper.ts @@ -47,7 +47,7 @@ export function buildCreatePropertyInput(fields: { return { title: `${ASSET_TYPE_LABELS[assetType] ?? assetType} · ${city}`, assetType: assetType as typeof AssetType[keyof typeof AssetType], - resultType: ResultType.EXTERNAL_MARKET, + resultType: ResultType.VERIFIED_PORTFOLIO, sourceType: 'DIRECT', location: { city, country: 'CH' }, address: { diff --git a/src/provider/IDashboardProvider.ts b/src/provider/IDashboardProvider.ts index c08ad70..a46d2fc 100644 --- a/src/provider/IDashboardProvider.ts +++ b/src/provider/IDashboardProvider.ts @@ -1,7 +1,7 @@ export interface DashboardStats { totalProperties: number verifiedProperties: number - externalMarketProperties: number + verifiedPortfolioProperties: number futureSignalProperties: number totalMatches: number pendingReviews: number diff --git a/src/provider/MockupDashboardProvider.ts b/src/provider/MockupDashboardProvider.ts index 2941522..95c90b9 100644 --- a/src/provider/MockupDashboardProvider.ts +++ b/src/provider/MockupDashboardProvider.ts @@ -32,7 +32,7 @@ export const MockupDashboardProvider: IDashboardProvider = { const stats: DashboardStats = { totalProperties: props.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, totalMatches: matches.length, pendingReviews: queue.filter(r => r.status === 'PENDING').length,