feat: saved profiles — per-profile notifications, real match scores, action buttons

- 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>
This commit is contained in:
Benjamin Sutter
2026-06-19 20:53:49 +02:00
parent 6aa4f96bd2
commit 7a0909e36a
10 changed files with 1131 additions and 110 deletions
+170 -36
View File
@@ -2,10 +2,10 @@ import { AssetType } from '../domain/enums'
import type { Need } from '../domain/need'
export const mockNeeds: Need[] = [
// --- need-001: Innovatech AG — OFFICE Zürich ---
// --- need-001: OFFICE Zürich-West ---
{
id: 'need-001',
companyName: 'Innovatech AG',
companyName: 'Bürofläche Zürich-West',
contactName: 'Sandra Meier',
assetType: AssetType.OFFICE,
requiredArea: { min: 600, max: 1000 },
@@ -38,10 +38,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2025-05-01T09:00:00Z',
},
// --- need-002: Schweizer Logistik GmbH — LOGISTICS Basel ---
// --- need-002: LOGISTICS Basel ---
{
id: 'need-002',
companyName: 'Schweizer Logistik GmbH',
companyName: 'Logistikfläche Basel',
contactName: 'Thomas Brun',
assetType: AssetType.LOGISTICS,
requiredArea: { min: 1500, max: 4000 },
@@ -72,10 +72,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2025-04-10T11:00:00Z',
},
// --- need-003: Pharma Holding AG — OFFICE Basel ---
// --- need-003: OFFICE Basel ---
{
id: 'need-003',
companyName: 'Pharma Holding AG',
companyName: 'Bürofläche Basel Repräsentanz',
contactName: 'Ursula Schmid',
assetType: AssetType.OFFICE,
requiredArea: { min: 500, max: 800 },
@@ -110,10 +110,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2025-05-05T14:00:00Z',
},
// --- need-004: Retailer Zürich AG — RETAIL Zürich ---
// --- need-004: RETAIL Zürich ---
{
id: 'need-004',
companyName: 'Retailer Zürich AG',
companyName: 'Ladenlokal Zürich Innenstadt',
contactName: 'Marco Colombo',
assetType: AssetType.RETAIL,
requiredArea: { min: 200, max: 500 },
@@ -144,10 +144,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2025-04-22T08:00:00Z',
},
// --- need-005: TechStart GmbH — OFFICE Zug/Zürich ---
// --- need-005: OFFICE Zug/Zürich ---
{
id: 'need-005',
companyName: 'TechStart GmbH',
companyName: 'Bürofläche Zug / Zürich',
contactName: 'Florian Keller',
assetType: AssetType.OFFICE,
requiredArea: { min: 300, max: 700 },
@@ -179,10 +179,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2025-05-08T10:00:00Z',
},
// --- need-006: Lager & Spedition AG — LOGISTICS Winterthur ---
// --- need-006: LOGISTICS Winterthur ---
{
id: 'need-006',
companyName: 'Lager & Spedition AG',
companyName: 'Lagerfläche Winterthur',
contactName: 'Beat Zimmermann',
assetType: AssetType.LOGISTICS,
requiredArea: { min: 1200, max: 3000 },
@@ -209,10 +209,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2025-05-09T16:00:00Z',
},
// --- need-007: Creative Studios AG — MIXED Zürich/Bern ---
// --- need-007: MIXED Zürich-West ---
{
id: 'need-007',
companyName: 'Creative Studios AG',
companyName: 'Gewerbefläche Zürich-West',
contactName: 'Nora Hauser',
assetType: AssetType.MIXED,
requiredArea: { min: 800, max: 1500 },
@@ -244,10 +244,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2025-05-03T11:00:00Z',
},
// --- need-008: Berner Produzenten GmbH — PRODUCTION Bern ---
// --- need-008: PRODUCTION Bern ---
{
id: 'need-008',
companyName: 'Berner Produzenten GmbH',
companyName: 'Produktionshalle Bern',
contactName: 'Hans Lüthi',
assetType: AssetType.PRODUCTION,
requiredArea: { min: 2000, max: 4000 },
@@ -274,10 +274,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2025-04-20T12:00:00Z',
},
// --- need-009: Geneva Commerce SA — RETAIL Genf ---
// --- need-009: RETAIL Genf ---
{
id: 'need-009',
companyName: 'Geneva Commerce SA',
companyName: 'Commerce Genève Centre',
contactName: 'Pierre Dupont',
assetType: AssetType.RETAIL,
requiredArea: { min: 150, max: 400 },
@@ -308,10 +308,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2025-04-18T09:00:00Z',
},
// --- need-011: Stadtladen Bern GmbH — RETAIL Bern Innenstadt ---
// --- need-011: RETAIL Bern Innenstadt ---
{
id: 'need-011',
companyName: 'Stadtladen Bern GmbH',
companyName: 'Ladenlokal Bern Altstadt',
contactName: 'Katrin Müller',
assetType: AssetType.RETAIL,
requiredArea: { min: 200, max: 400 },
@@ -342,10 +342,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2025-05-18T10:00:00Z',
},
// --- need-010: St.Galler Büros AG — OFFICE St.Gallen ---
// --- need-010: OFFICE St.Gallen ---
{
id: 'need-010',
companyName: 'St.Galler Büros AG',
companyName: 'Bürofläche St. Gallen',
contactName: 'Brigitte Fässler',
assetType: AssetType.OFFICE,
requiredArea: { min: 400, max: 800 },
@@ -377,10 +377,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2025-05-06T13:00:00Z',
},
// --- need-uc-a: Renato's Fahrradhändler — Use Case A ---
// --- need-uc-a: RETAIL Zürich EG ---
{
id: 'need-uc-a',
companyName: 'Velo City GmbH',
companyName: 'Ladenlokal Zürich EG Schaufenster',
contactName: 'Renato Marchetti',
assetType: AssetType.RETAIL,
requiredArea: { min: 120, max: 160 },
@@ -411,10 +411,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2025-05-22T10:00:00Z',
},
// --- need-uc-b: Renato's Umzugsfirma — Use Case B ---
// --- need-uc-b: OFFICE Zürich-West klein ---
{
id: 'need-uc-b',
companyName: 'Alp Transit Umzüge GmbH',
companyName: 'Bürofläche Zürich-West klein',
contactName: 'Renato Marchetti',
assetType: AssetType.OFFICE,
requiredArea: { min: 140, max: 200 },
@@ -445,10 +445,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2025-05-22T10:00:00Z',
},
// --- need-012: Gewerbe Solutions AG — LIGHT_INDUSTRIAL Pratteln ---
// --- need-012: LIGHT_INDUSTRIAL Pratteln ---
{
id: 'need-012',
companyName: 'Gewerbe Solutions AG',
companyName: 'Gewerbefläche Pratteln',
contactName: 'Andreas Weber',
assetType: AssetType.LIGHT_INDUSTRIAL,
requiredArea: { min: 400, max: 750 },
@@ -477,10 +477,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2026-03-01T09:00:00Z',
},
// --- need-013: Bern Consulting AG — OFFICE Bern ---
// --- need-013: OFFICE Bern ---
{
id: 'need-013',
companyName: 'Bern Consulting AG',
companyName: 'Bürofläche Bern Bahnhof',
contactName: 'Sabine Gerber',
assetType: AssetType.OFFICE,
requiredArea: { min: 180, max: 320 },
@@ -512,10 +512,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2026-04-05T10:00:00Z',
},
// --- need-014: Winterthur Handel GmbH — RETAIL Winterthur ---
// --- need-014: RETAIL Winterthur ---
{
id: 'need-014',
companyName: 'Winterthur Handel GmbH',
companyName: 'Retailfläche Winterthur',
contactName: 'Pascal Brunner',
assetType: AssetType.RETAIL,
requiredArea: { min: 150, max: 250 },
@@ -546,10 +546,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2026-03-20T11:00:00Z',
},
// --- need-015: Luzern Advisory GmbH — OFFICE Luzern ---
// --- need-015: OFFICE Luzern ---
{
id: 'need-015',
companyName: 'Luzern Advisory GmbH',
companyName: 'Bürofläche Luzern',
contactName: 'Markus Bucher',
assetType: AssetType.OFFICE,
requiredArea: { min: 350, max: 600 },
@@ -581,10 +581,10 @@ export const mockNeeds: Need[] = [
updatedAt: '2026-04-10T14:00:00Z',
},
// --- need-uc-c: Renato's Vermögensverwalter — Use Case C ---
// --- need-uc-c: OFFICE Zürich Premium ---
{
id: 'need-uc-c',
companyName: 'Wealth Advisory Partners AG',
companyName: 'Repräsentanzbüro Zürich Premium',
contactName: 'Renato Marchetti',
assetType: AssetType.OFFICE,
requiredArea: { min: 450, max: 560 },
@@ -616,4 +616,138 @@ export const mockNeeds: Need[] = [
createdAt: '2025-05-20T10:00:00Z',
updatedAt: '2025-05-22T10:00:00Z',
},
// --- org-mobimo: eigene Suchprofile der Mobimo Management AG ---
{
id: 'need-mob-001',
companyName: 'Bürofläche Zürich City',
contactName: 'Sandra Koch',
assetType: AssetType.OFFICE,
requiredArea: { min: 300, max: 600 },
preferredLocations: ['Zürich', 'Zürich City', 'Zürich Kreis 1', 'Zürich Kreis 4'],
excludedLocations: [],
budgetRange: { maxPerSqm: 480, maxMonthlyTotal: 20000, currency: 'CHF' },
timing: {
earliestMoveIn: '2025-10-01',
latestMoveIn: '2026-03-01',
contractDurationMonths: 60,
flexibleTiming: true,
},
mustCriteriaText: ['Gute ÖV-Anbindung', 'Repräsentative Lage', 'Klimaanlage'],
softFactors: {
minPrestige: 70,
minAccessibility: 80,
requireParking: false,
maxPublicTransportMinutes: 8,
},
weightingProfile: {
area: 0.18, location: 0.24, budget: 0.16, timing: 0.12,
prestige: 0.10, accessibility: 0.08, expansionPotential: 0.03, flexibility: 0.04,
visibility: 0.02, footfall: 0.01, talentAccess: 0.01, esg: 0.01, taxEnvironment: 0.00,
},
confidenceInCriteria: 0.91,
notificationConfig: { enabled: true, minScore: 80, emailAddress: 'sandra.koch@mobimo.ch' },
organizationId: 'org-mobimo',
createdAt: '2025-05-10T09:00:00Z',
updatedAt: '2025-06-01T10:00:00Z',
},
{
id: 'need-mob-002',
companyName: 'Showroom Bern Innenstadt',
contactName: 'Sandra Koch',
assetType: AssetType.RETAIL,
requiredArea: { min: 150, max: 300 },
preferredLocations: ['Bern', 'Bern Innenstadt', 'Bern Gurtengasse', 'Bern Marktgasse'],
excludedLocations: [],
budgetRange: { maxPerSqm: 380, maxMonthlyTotal: 8000, currency: 'CHF' },
timing: {
earliestMoveIn: '2025-11-01',
latestMoveIn: '2026-06-01',
contractDurationMonths: 36,
flexibleTiming: false,
},
mustCriteriaText: ['Schaufenster Erdgeschoss', 'Hoher Publikumsverkehr', 'Liefermöglichkeit'],
softFactors: {
minPrestige: 65,
minAccessibility: 75,
requireParking: false,
maxPublicTransportMinutes: 5,
},
weightingProfile: {
area: 0.14, location: 0.28, budget: 0.15, timing: 0.10,
prestige: 0.08, accessibility: 0.07, expansionPotential: 0.02, flexibility: 0.03,
visibility: 0.08, footfall: 0.04, talentAccess: 0.00, esg: 0.01, taxEnvironment: 0.00,
},
confidenceInCriteria: 0.87,
notificationConfig: { enabled: false, minScore: 75 },
organizationId: 'org-mobimo',
createdAt: '2025-04-20T11:00:00Z',
updatedAt: '2025-05-15T09:00:00Z',
},
{
id: 'need-mob-003',
companyName: 'Lager & Service Schaffhausen',
contactName: 'Sandra Koch',
assetType: AssetType.LOGISTICS,
requiredArea: { min: 800, max: 1500 },
preferredLocations: ['Schaffhausen', 'Neuhausen am Rheinfall', 'Thayngen'],
excludedLocations: [],
budgetRange: { maxPerSqm: 160, currency: 'CHF' },
timing: {
earliestMoveIn: '2026-01-01',
latestMoveIn: '2026-09-01',
contractDurationMonths: 48,
flexibleTiming: true,
},
mustCriteriaText: ['Autobahnanschluss < 10 Min', 'Rampe / Ladetor', 'Büroanteil mind. 80 m²'],
softFactors: {
requireParking: true,
},
weightingProfile: {
area: 0.22, location: 0.20, budget: 0.20, timing: 0.12,
prestige: 0.01, accessibility: 0.12, expansionPotential: 0.05, flexibility: 0.03,
visibility: 0.01, footfall: 0.00, talentAccess: 0.01, esg: 0.02, taxEnvironment: 0.01,
},
confidenceInCriteria: 0.83,
notificationConfig: { enabled: true, minScore: 85 },
organizationId: 'org-mobimo',
createdAt: '2025-06-01T08:00:00Z',
updatedAt: '2025-06-10T14:00:00Z',
},
{
id: 'need-mob-004',
companyName: 'Repräsentanzfläche Zürich-West',
contactName: 'Sandra Koch',
assetType: AssetType.OFFICE,
requiredArea: { min: 500, max: 900 },
preferredLocations: ['Zürich-West', 'Zürich Kreis 5', 'Zürich Kreis 4', 'Zürich Hardbrücke'],
excludedLocations: [],
budgetRange: { maxPerSqm: 520, maxMonthlyTotal: 35000, currency: 'CHF' },
timing: {
earliestMoveIn: '2026-03-01',
latestMoveIn: '2026-12-01',
contractDurationMonths: 84,
flexibleTiming: false,
},
mustCriteriaText: ['Moderner Neubau oder Kernsanierung', 'Kollaborative Fläche', 'Fahrradabstellplätze', 'Minergie oder LEED-zertifiziert'],
softFactors: {
minPrestige: 75,
minAccessibility: 85,
requireParking: false,
maxPublicTransportMinutes: 6,
},
weightingProfile: {
area: 0.16, location: 0.22, budget: 0.14, timing: 0.10,
prestige: 0.12, accessibility: 0.09, expansionPotential: 0.05, flexibility: 0.04,
visibility: 0.02, footfall: 0.01, talentAccess: 0.03, esg: 0.02, taxEnvironment: 0.00,
},
confidenceInCriteria: 0.93,
organizationId: 'org-mobimo',
createdAt: '2025-03-15T13:00:00Z',
updatedAt: '2025-06-05T11:00:00Z',
},
]