7a0909e36a
- Add per-profile notification config (toggle, score threshold, email) with Accordion UI in SavedNeedDetail - NotificationButton: badge only lights up for unseen matches, dismisses on open (localStorage), uses per-profile minScore - SavedNeedDetail: replace deterministicMatchScore with real match data (useMatchesByNeed), sort by score desc, deduplicate by property - SavedProfilesTab & SavedNeedCard: top score bar now shows best real match score instead of criteria confidence - "Treffer" count uses per-profile notificationConfig.minScore (≥80 default) consistently across card, panel, and notification badge - Extract PropertyMatchRow to own file with Anfrage/Merken/Vergleichen/Details→/Zur Einheit→ action buttons - Details → navigates to /demand/results/:matchId using real match ID format (m__propId__prop__needId) - Add InquiryQuickDialog + AddToPipelineDialog to AISearch.tsx so dialogs work from SavedProfilesTab - Org-isolation: useNeeds filters by organizationId, org-mobimo gets 4 own search profiles - Mock data: all 18 org-wincasa need names changed to descriptive profile names; 4 new org-mobimo needs added - useUpdateNeed mutation hook for updating need fields (notificationConfig etc.) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
120 lines
2.8 KiB
TypeScript
120 lines
2.8 KiB
TypeScript
import type { AssetType, MatchStatus } from './enums'
|
||
|
||
export interface AreaRange {
|
||
min: number
|
||
max: number
|
||
}
|
||
|
||
export interface BudgetRange {
|
||
minPerSqm?: number
|
||
maxPerSqm: number
|
||
maxMonthlyTotal?: number
|
||
currency: string
|
||
}
|
||
|
||
export interface Timing {
|
||
earliestMoveIn: string
|
||
latestMoveIn: string
|
||
contractDurationMonths?: number
|
||
flexibleTiming: boolean
|
||
}
|
||
|
||
export interface WeightingProfile {
|
||
area: number
|
||
location: number
|
||
budget: number
|
||
timing: number
|
||
prestige: number
|
||
accessibility: number
|
||
expansionPotential: number
|
||
flexibility: number
|
||
visibility: number
|
||
footfall: number
|
||
talentAccess: number
|
||
esg: number
|
||
taxEnvironment: number
|
||
[key: string]: number
|
||
}
|
||
|
||
export interface SoftFactorPreferences {
|
||
minPrestige?: number
|
||
minAccessibility?: number
|
||
requireParking?: boolean
|
||
maxPublicTransportMinutes?: number
|
||
preferredEsgRating?: string
|
||
requireHighVisibility?: boolean
|
||
}
|
||
|
||
export interface SizeRange {
|
||
minSqm: number
|
||
maxSqm: number
|
||
}
|
||
|
||
export interface MustHaveCriterion {
|
||
criterion: string
|
||
description?: string
|
||
weight?: number // 0–1, how much a miss hurts the score
|
||
}
|
||
|
||
export interface WeightedPreference {
|
||
criterion: string
|
||
weight: number // 0–1
|
||
idealValue?: string | number
|
||
description?: string
|
||
}
|
||
|
||
export interface NotificationConfig {
|
||
enabled: boolean
|
||
minScore: number // 0–100, default 80
|
||
emailAddress?: string
|
||
}
|
||
|
||
export interface Need {
|
||
id: string
|
||
companyName: string
|
||
contactName?: string
|
||
assetType: AssetType
|
||
requiredArea: AreaRange
|
||
|
||
// F004 additions
|
||
desiredLocation?: string[] // preferred city/district list
|
||
sizeRange?: SizeRange // structured alias for requiredArea
|
||
mustHaveCriteria?: MustHaveCriterion[]
|
||
weightedPreferences?: WeightedPreference[]
|
||
status?: MatchStatus | 'ACTIVE' | 'INACTIVE' | 'DRAFT' | 'CLOSED'
|
||
|
||
preferredLocations: string[]
|
||
excludedLocations?: string[]
|
||
budgetRange: BudgetRange
|
||
timing: Timing
|
||
mustCriteriaText?: string[] // legacy — prefer mustHaveCriteria
|
||
|
||
// Structured requirement flags
|
||
requireGroundFloor?: boolean
|
||
requiredFitOut?: 'BASIC' | 'FULL' | 'PREMIUM'
|
||
requiredParkingMin?: number
|
||
requireAirConditioning?: boolean
|
||
requireLoadingDock?: boolean
|
||
requireBarrierFree?: boolean
|
||
minCeilingHeightM?: number
|
||
minContractDurationMonths?: number
|
||
searchRadius?: number
|
||
isAnonymous?: boolean
|
||
requiresDivisibility?: boolean
|
||
minDivisibleUnit?: number
|
||
fitOutBudgetMaxPerSqm?: number
|
||
|
||
softFactors?: SoftFactorPreferences
|
||
weightingProfile: WeightingProfile
|
||
confidenceInCriteria: number
|
||
extractedFromText?: string
|
||
notes?: string
|
||
notificationConfig?: NotificationConfig
|
||
organizationId?: string
|
||
createdAt: string
|
||
updatedAt: string
|
||
}
|
||
|
||
export type CreateNeedInput = Omit<Need, 'id' | 'createdAt' | 'updatedAt'>
|
||
export type UpdateNeedInput = Partial<CreateNeedInput>
|