36570c5bdc
- Fix MUI v9 API: PaperProps/InputLabelProps/inputProps → slotProps in 6 components - Add ExternalMarketResult alias, PropertyUnit import, OPERATIONS workspace config - Fix TradeOff.description → .concern, ScoreFactor.label → .criterion - Make schattenmarktRelease.leadTimeMonths optional, fix mock-data enum values - Fix useMatchDetailData query typing, weightingService missing WeightProfile keys - Split Pipeline/Compare/MatchDetail/IntelligenceMatchCard into sub-components - Fix all test fixtures (CreateNeedInput, CreatePropertyInput, TradeOffInput, etc.) - Add vercel.json for deployment, zero tsc errors Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
190 lines
5.7 KiB
TypeScript
190 lines
5.7 KiB
TypeScript
import type {
|
||
AssetType, ResultType, AvailabilityStatus, AvailabilityType,
|
||
FreshnessStatus, RiskLevel, SourceType, DataQualityLevel,
|
||
} from './enums'
|
||
import { AvailabilityStatus as AS } from './enums'
|
||
import type { PropertyUnit } from './unit'
|
||
|
||
// Re-export unit types so all existing imports from 'property' continue to work
|
||
export type { PropertyUnit, UnitBundle, UnitNeedMatch } from './unit'
|
||
|
||
// ── Location / Address ────────────────────────────────────────────────────────
|
||
|
||
export interface Location {
|
||
city: string
|
||
district?: string
|
||
canton?: string
|
||
region?: string
|
||
country: string
|
||
coordinates?: { lat: number; lng: number }
|
||
}
|
||
|
||
export interface Address {
|
||
street: string
|
||
houseNumber: string
|
||
postalCode: string
|
||
city: string
|
||
country: string
|
||
}
|
||
|
||
// ── Source Metadata ───────────────────────────────────────────────────────────
|
||
|
||
export interface SourceMeta {
|
||
sourceType: SourceType | string
|
||
sourceLabel?: string
|
||
sourceUrl?: string
|
||
sourceUpdatedAt?: string
|
||
externalId?: string
|
||
}
|
||
|
||
// ── Data Quality ──────────────────────────────────────────────────────────────
|
||
|
||
export interface DataQuality {
|
||
score: number
|
||
qualityLevel?: DataQualityLevel
|
||
missingCriticalFields: string[]
|
||
missingOptionalFields: string[]
|
||
lastVerifiedAt?: string
|
||
freshness: FreshnessStatus
|
||
warnings: string[]
|
||
}
|
||
|
||
// ── Hard Facts ────────────────────────────────────────────────────────────────
|
||
|
||
export interface PropertyHardFacts {
|
||
floor?: number
|
||
fitOut?: 'SHELL' | 'BASIC' | 'FULL' | 'PREMIUM'
|
||
parking?: number
|
||
publicTransportScore?: number
|
||
usageType?: string
|
||
ceilingHeightM?: number
|
||
loadingDocksCount?: number
|
||
powerSupplyKva?: number
|
||
hasServerRoom?: boolean
|
||
isBarrierFree?: boolean
|
||
hasAirConditioning?: boolean
|
||
hasStorefront?: boolean
|
||
}
|
||
|
||
// ── Soft Factors ──────────────────────────────────────────────────────────────
|
||
|
||
export interface SoftFactors {
|
||
prestigeScore?: number
|
||
visibilityScore?: number
|
||
footfallScore?: number
|
||
commuterAccessScore?: number
|
||
talentAccessScore?: number
|
||
esgScore?: number
|
||
flexibilityScore?: number
|
||
expansionPotentialScore?: number
|
||
taxEnvironmentScore?: number
|
||
// Legacy aliases kept for mock-data backward compatibility
|
||
prestige?: number
|
||
accessibility?: number
|
||
talentAccess?: number
|
||
esgRating?: string
|
||
passerbyFrequency?: 'LOW' | 'MEDIUM' | 'HIGH' | 'VERY_HIGH'
|
||
parkingSpots?: number
|
||
publicTransportMinutes?: number
|
||
infrastructureNotes?: string
|
||
}
|
||
|
||
// ── Property ──────────────────────────────────────────────────────────────────
|
||
|
||
export interface Property {
|
||
id: string
|
||
organizationId?: string
|
||
title: string
|
||
|
||
assetType: AssetType
|
||
resultType: ResultType
|
||
|
||
location: Location
|
||
address: Address
|
||
|
||
areaSqm: number
|
||
areaSqmMin?: number
|
||
areaSqmMax?: number
|
||
|
||
rentPricePerSqm: number
|
||
rentChfSqmYear?: number
|
||
totalRentMonthly?: number
|
||
ancillaryCosts?: number
|
||
|
||
availabilityDate: string
|
||
availabilityStatus: AvailabilityStatus
|
||
availabilityType?: AvailabilityType
|
||
|
||
sourceType: string
|
||
sourceLabel?: string
|
||
sourceUrl?: string
|
||
sourceUpdatedAt?: string
|
||
sourceMeta?: SourceMeta
|
||
|
||
confidenceScore: number
|
||
dataQuality: DataQuality
|
||
|
||
softFactors?: SoftFactors
|
||
hardFacts?: PropertyHardFacts
|
||
|
||
// Legacy fields — kept for backward compat
|
||
floorLevel?: number
|
||
expansionPotentialSqm?: number
|
||
|
||
contractDurationMonths?: number
|
||
riskLevel?: RiskLevel
|
||
description?: string
|
||
images?: string[]
|
||
floorPlanUrl?: string
|
||
leaseContractUrl?: string // link to the signed lease document
|
||
leaseContractName?: string // display label, e.g. "Mietvertrag 2021–2026"
|
||
|
||
propertyNumber?: string
|
||
units?: PropertyUnit[]
|
||
mapImageUrl?: string
|
||
|
||
leaseTerm?: string
|
||
leaseStartDate?: string
|
||
leaseEndDate?: string
|
||
breakoutOption?: boolean
|
||
breakoutOptionDate?: string
|
||
currentTenant?: string
|
||
importedFrom?: string
|
||
importedAt?: string
|
||
lastUpdatedAt?: string
|
||
|
||
schattenmarktRelease?: { enabled: boolean; leadTimeMonths?: number }
|
||
|
||
status?: 'ACTIVE' | 'INACTIVE' | 'DRAFT' | 'ARCHIVED'
|
||
lastReviewedAt?: string
|
||
createdAt: string
|
||
updatedAt: string
|
||
}
|
||
|
||
export type CreatePropertyInput = Omit<Property, 'id' | 'createdAt' | 'updatedAt'>
|
||
export type UpdatePropertyInput = Partial<CreatePropertyInput>
|
||
|
||
/**
|
||
* Returns the rentable units of a property.
|
||
* For properties without explicit units, synthesises one unit from property-level data
|
||
* so that all matching logic can operate uniformly at unit level.
|
||
*/
|
||
export function getEffectiveUnits(p: Property): PropertyUnit[] {
|
||
if (p.units && p.units.length > 0) return p.units
|
||
return [{
|
||
id: p.id,
|
||
propertyId: p.id,
|
||
floorLevel: p.hardFacts?.floor ?? p.floorLevel ?? 0,
|
||
unitLabel: undefined,
|
||
areaSqm: p.areaSqm,
|
||
available: p.availabilityStatus === AS.AVAILABLE_NOW || p.availabilityStatus === AS.AVAILABLE_SOON,
|
||
rentPricePerSqm: p.rentPricePerSqm,
|
||
currentTenant: p.currentTenant,
|
||
leaseTerm: p.leaseTerm,
|
||
leaseEndDate: p.leaseEndDate,
|
||
schattenmarktRelease: p.schattenmarktRelease?.enabled
|
||
? { enabled: true, availableFrom: p.leaseEndDate }
|
||
: undefined,
|
||
}]
|
||
}
|