feat: expose all 13 scoring criteria as weighting sliders

Previously only 8 of the 13 engine criteria had sliders — flexibility,
visibility, footfall, talentAccess, esg were scored using fixed defaults
with no user control. Now all 13 criteria appear in the WeightingEditor.

- needBuilder.ts: WEIGHTING_KEYS and WEIGHTING_LABELS extended to all 13
- scoreCalculator.ts: CORE array in resolveProfile now includes all 13
- need.ts: WeightingProfile interface explicit for all 13 fields
- needs.ts: all 11 mock need profiles updated with asset-type-appropriate
  weights (RETAIL needs high footfall/visibility; LOGISTICS low; OFFICE
  high talentAccess; TechStart high taxEnvironment for Zug, etc.)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-21 20:58:01 +02:00
parent 113274cfc7
commit ba910806d6
4 changed files with 53 additions and 90 deletions
+5
View File
@@ -27,6 +27,11 @@ export interface WeightingProfile {
prestige: number
accessibility: number
expansionPotential: number
flexibility: number
visibility: number
footfall: number
talentAccess: number
esg: number
taxEnvironment: number
[key: string]: number
}
+10 -1
View File
@@ -54,7 +54,11 @@ export const NeedBuilderStep = {
} as const
export type NeedBuilderStep = typeof NeedBuilderStep[keyof typeof NeedBuilderStep]
export const WEIGHTING_KEYS = ['area', 'location', 'budget', 'timing', 'prestige', 'accessibility', 'expansionPotential', 'taxEnvironment'] as const
export const WEIGHTING_KEYS = [
'area', 'location', 'budget', 'timing',
'prestige', 'accessibility', 'expansionPotential', 'flexibility',
'visibility', 'footfall', 'talentAccess', 'esg', 'taxEnvironment',
] as const
export type WeightingKey = typeof WEIGHTING_KEYS[number]
export const WEIGHTING_LABELS: Record<WeightingKey, string> = {
@@ -65,5 +69,10 @@ export const WEIGHTING_LABELS: Record<WeightingKey, string> = {
prestige: 'Prestige',
accessibility: 'Erreichbarkeit',
expansionPotential: 'Expansionspotenzial',
flexibility: 'Flexibilität',
visibility: 'Sichtbarkeit',
footfall: 'Passantenfrequenz',
talentAccess: 'Talent-Zugang',
esg: 'ESG',
taxEnvironment: 'Steuerlast',
}
+5 -1
View File
@@ -22,7 +22,11 @@ function resolveProfile(need: Need, property: Property): ScoringWeightProfile {
if (!np) return base
// Apply need's custom core weights, then renormalize the full profile to 1.00
const CORE = ['area', 'location', 'budget', 'timing', 'prestige', 'accessibility', 'expansionPotential', 'taxEnvironment']
const CORE = [
'area', 'location', 'budget', 'timing',
'prestige', 'accessibility', 'expansionPotential', 'flexibility',
'visibility', 'footfall', 'talentAccess', 'esg', 'taxEnvironment',
]
for (const key of CORE) {
if (typeof np[key] === 'number') base[key] = np[key]
}
+33 -88
View File
@@ -26,14 +26,9 @@ export const mockNeeds: Need[] = [
maxPublicTransportMinutes: 6,
},
weightingProfile: {
area: 0.20,
location: 0.20,
budget: 0.15,
timing: 0.15,
prestige: 0.10,
accessibility: 0.10,
expansionPotential: 0.05,
flexibility: 0.05,
area: 0.18, location: 0.18, budget: 0.14, timing: 0.13,
prestige: 0.08, accessibility: 0.10, expansionPotential: 0.04, flexibility: 0.05,
visibility: 0.02, footfall: 0.01, talentAccess: 0.06, esg: 0.02, taxEnvironment: 0.00,
},
confidenceInCriteria: 0.88,
extractedFromText: 'Wir suchen moderne Büroflächen in Zürich-West, ca. 700900m², Budget max CHF 42/m², Bezug Herbst 2025.',
@@ -62,14 +57,9 @@ export const mockNeeds: Need[] = [
requireParking: true,
},
weightingProfile: {
area: 0.25,
location: 0.20,
budget: 0.20,
timing: 0.15,
prestige: 0.02,
accessibility: 0.10,
expansionPotential: 0.05,
flexibility: 0.03,
area: 0.23, location: 0.18, budget: 0.18, timing: 0.13,
prestige: 0.02, accessibility: 0.10, expansionPotential: 0.05, flexibility: 0.03,
visibility: 0.01, footfall: 0.00, talentAccess: 0.02, esg: 0.02, taxEnvironment: 0.03,
},
confidenceInCriteria: 0.94,
organizationId: 'org-wincasa',
@@ -101,14 +91,9 @@ export const mockNeeds: Need[] = [
maxPublicTransportMinutes: 10,
},
weightingProfile: {
area: 0.20,
location: 0.25,
budget: 0.20,
timing: 0.10,
prestige: 0.12,
accessibility: 0.08,
expansionPotential: 0.03,
flexibility: 0.02,
area: 0.18, location: 0.22, budget: 0.17, timing: 0.09,
prestige: 0.10, accessibility: 0.08, expansionPotential: 0.03, flexibility: 0.02,
visibility: 0.02, footfall: 0.01, talentAccess: 0.08, esg: 0.05, taxEnvironment: 0.00,
},
confidenceInCriteria: 0.91,
extractedFromText: 'Suche repräsentative Büroflächen im Raum Basel/Allschwil, 500800m², max. CHF 38/m², Bezug Q4 2025.',
@@ -140,14 +125,9 @@ export const mockNeeds: Need[] = [
maxPublicTransportMinutes: 5,
},
weightingProfile: {
area: 0.15,
location: 0.35,
budget: 0.15,
timing: 0.10,
prestige: 0.15,
accessibility: 0.05,
expansionPotential: 0.02,
flexibility: 0.03,
area: 0.10, location: 0.20, budget: 0.12, timing: 0.08,
prestige: 0.06, accessibility: 0.04, expansionPotential: 0.02, flexibility: 0.04,
visibility: 0.12, footfall: 0.15, talentAccess: 0.01, esg: 0.01, taxEnvironment: 0.00,
},
confidenceInCriteria: 0.96,
extractedFromText: 'Exklusive Retailfläche in Zürich Innenstadt gesucht, 250450m², Schaufenster, max. CHF 95/m².',
@@ -180,14 +160,9 @@ export const mockNeeds: Need[] = [
maxPublicTransportMinutes: 10,
},
weightingProfile: {
area: 0.20,
location: 0.25,
budget: 0.20,
timing: 0.15,
prestige: 0.05,
accessibility: 0.10,
expansionPotential: 0.03,
flexibility: 0.02,
area: 0.17, location: 0.22, budget: 0.17, timing: 0.12,
prestige: 0.05, accessibility: 0.10, expansionPotential: 0.03, flexibility: 0.02,
visibility: 0.02, footfall: 0.01, talentAccess: 0.08, esg: 0.02, taxEnvironment: 0.04,
},
confidenceInCriteria: 0.85,
extractedFromText: 'Junges Tech-Unternehmen sucht Büro in Zug oder Zürich, 350600m², moderner Ausbau, max. CHF 45/m².',
@@ -216,14 +191,9 @@ export const mockNeeds: Need[] = [
requireParking: true,
},
weightingProfile: {
area: 0.30,
location: 0.25,
budget: 0.20,
timing: 0.10,
prestige: 0.01,
accessibility: 0.10,
expansionPotential: 0.02,
flexibility: 0.02,
area: 0.27, location: 0.22, budget: 0.18, timing: 0.10,
prestige: 0.01, accessibility: 0.10, expansionPotential: 0.02, flexibility: 0.02,
visibility: 0.01, footfall: 0.00, talentAccess: 0.02, esg: 0.02, taxEnvironment: 0.02,
},
confidenceInCriteria: 0.92,
organizationId: 'org-wincasa',
@@ -255,14 +225,9 @@ export const mockNeeds: Need[] = [
maxPublicTransportMinutes: 12,
},
weightingProfile: {
area: 0.20,
location: 0.20,
budget: 0.15,
timing: 0.15,
prestige: 0.10,
accessibility: 0.10,
expansionPotential: 0.05,
flexibility: 0.05,
area: 0.17, location: 0.15, budget: 0.13, timing: 0.12,
prestige: 0.07, accessibility: 0.09, expansionPotential: 0.04, flexibility: 0.06,
visibility: 0.07, footfall: 0.05, talentAccess: 0.06, esg: 0.03, taxEnvironment: 0.00,
},
confidenceInCriteria: 0.82,
extractedFromText: 'Kreativagentur sucht Gewerbe-/Bürofläche in Zürich oder Bern, 9001400m², gemischte Nutzung, Budget max. CHF 50/m².',
@@ -291,14 +256,9 @@ export const mockNeeds: Need[] = [
requireParking: true,
},
weightingProfile: {
area: 0.30,
location: 0.20,
budget: 0.20,
timing: 0.10,
prestige: 0.01,
accessibility: 0.10,
expansionPotential: 0.07,
flexibility: 0.02,
area: 0.26, location: 0.18, budget: 0.18, timing: 0.10,
prestige: 0.01, accessibility: 0.12, expansionPotential: 0.07, flexibility: 0.02,
visibility: 0.01, footfall: 0.00, talentAccess: 0.03, esg: 0.03, taxEnvironment: 0.02,
},
confidenceInCriteria: 0.95,
organizationId: 'org-wincasa',
@@ -329,14 +289,9 @@ export const mockNeeds: Need[] = [
maxPublicTransportMinutes: 5,
},
weightingProfile: {
area: 0.15,
location: 0.35,
budget: 0.15,
timing: 0.10,
prestige: 0.15,
accessibility: 0.05,
expansionPotential: 0.02,
flexibility: 0.03,
area: 0.10, location: 0.22, budget: 0.12, timing: 0.08,
prestige: 0.07, accessibility: 0.04, expansionPotential: 0.01, flexibility: 0.04,
visibility: 0.12, footfall: 0.14, talentAccess: 0.01, esg: 0.01, taxEnvironment: 0.00,
},
confidenceInCriteria: 0.93,
extractedFromText: 'Recherche surface commerciale en centre-ville de Genève, 200350m², vitrine obligatoire, budget max CHF 115/m².',
@@ -368,14 +323,9 @@ export const mockNeeds: Need[] = [
maxPublicTransportMinutes: 5,
},
weightingProfile: {
area: 0.15,
location: 0.35,
budget: 0.15,
timing: 0.10,
prestige: 0.15,
accessibility: 0.05,
expansionPotential: 0.02,
flexibility: 0.03,
area: 0.10, location: 0.22, budget: 0.12, timing: 0.08,
prestige: 0.06, accessibility: 0.04, expansionPotential: 0.01, flexibility: 0.04,
visibility: 0.12, footfall: 0.15, talentAccess: 0.01, esg: 0.01, taxEnvironment: 0.00,
},
confidenceInCriteria: 0.92,
extractedFromText: 'Suche Retailfläche in Bern Innenstadt, 200400m², Schaufensterfront, Fussgängerzone, max. CHF 150/m².',
@@ -408,14 +358,9 @@ export const mockNeeds: Need[] = [
maxPublicTransportMinutes: 8,
},
weightingProfile: {
area: 0.22,
location: 0.28,
budget: 0.20,
timing: 0.12,
prestige: 0.08,
accessibility: 0.06,
expansionPotential: 0.02,
flexibility: 0.02,
area: 0.20, location: 0.25, budget: 0.18, timing: 0.11,
prestige: 0.07, accessibility: 0.06, expansionPotential: 0.02, flexibility: 0.02,
visibility: 0.02, footfall: 0.01, talentAccess: 0.05, esg: 0.02, taxEnvironment: 0.00,
},
confidenceInCriteria: 0.87,
extractedFromText: 'Büroflächen in St. Gallen oder Umgebung gesucht, 450750m², max. CHF 32/m², Bezug Anfang 2026.',