Commit Graph

57 Commits

Author SHA1 Message Date
Benjamin Sutter 52dc7de25e feat(team): Datenschicht für Property On
Property On wird als Funktionsbereich "Teamübersicht" in Property Match
eingebettet — sieben digitale Mitarbeiter, die ausführen, während der Mensch
freigibt, anpasst oder zurückweist.

Durchgehend Provider → Service → Hook, kein übersprungener Layer:
- domain/: teamAgent, agentWorkItem, agentProtocol, agentConnection, agentFilters
- mock-data/: 7 Personalblätter aus dem Agenten-Katalog (Aufgabenzahlen exakt:
  Ferdi 9, Sina 7, Lea 11, Bruno 9, Reto 10, Livia 11, Nora 8), 14 Vorgänge,
  38 Protokolleinträge, 9 Verbindungen
- provider/: vier Mockup-Provider mit versionierter localStorage-Persistenz
- services/: Fachlogik inkl. Freigabe, Zurückweisung, Kennzahlen, Demo-Reset
- hooks/: React Query, Mutationen entwerten Vorgänge, Kennzahlen und Protokoll
  gemeinsam — sonst zeigt die Oberfläche widersprüchliche Zahlen

Namensgebung: Der Entitätstyp heisst TeamAgent, nicht Agent. Im Repo existiert
bereits PowerOn als KI-Backend-Proxy, und Claude Code legt Arbeitskopien unter
agent-* ab; ein nackter Typ Agent wäre in Suchen nicht auffindbar. Alle
Satellitentypen sind Agent*-präfixiert, weil domain/index.ts per export *
bündelt und generische Namen dort kollidieren.

Eigener Protokolltyp statt ActivityEvent: den Namen gibt es im Repo dreifach
und gegenseitig inkompatibel (domain/activityEvent.ts, governanceService.ts,
PropertyActivityLogPanel.tsx). Ein Anschluss hätte den Konflikt zementiert.

Demo-Uhr in lib/teamClock.ts: die Mockdaten sind auf den 20.05.2026 verankert.
Gegen die Systemzeit gerechnet wären die Filter "heute / diese Woche / dieser
Monat" an jedem anderen Kalendertag leer und die Auswertung sähe kaputt aus,
obwohl sie korrekt rechnet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 16:55:40 +02:00
Benjamin Sutter a8b54af0b8 feat(messages): unified conversation inbox — connect demand ↔ supply, offers reach the seeker
Phase 1 of the messaging rework: one conversation/thread model (Inquiry) is the single source of truth, read perspectivally by both sides.

- domain/inquiry: add kind (INQUIRY|OFFER), tenantOrgId (demand org), offeredPropertyIds
- provider/service: InquiryFilters gains tenantOrgId+kind; new createInquiry (demand→supply) & createOffer (supply→demand) materialize threads; addMessage bumps unread; reply carries senderType (tenant vs supply_user)
- hooks: useCreateInquiry, useCreateOffer; reply payload carries sender perspective
- demand: InquiryQuickDialog → createInquiry via provider (session-based tenant/org, no hardcode); Anfragen.tsx reads via React Query (tenantOrgId) with Gesendet/Erhalten tabs, replies via provider, shows offered properties for OFFER threads; inquiryStore reduced to dialog-only (removes Zustand server-data island)
- supply: OfferChatComposer send now materializes an OFFER conversation into the seeker's inbox (routed via latentNeed.tenantOrgId); Anfragencenter gains a "Gesendet" tab; ActiveInquiriesTab filters by perspective (org + kind)
- seed: merge demand inquiries with tenantOrgId=org-mobimo + 2 OFFER seeds; provider seeds from both sets

Closes the loop: a sent inquiry reaches the Verwaltung and a sent offer reaches the Nachfrager — both answerable in one thread.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 01:32:13 +02:00
Benjamin Sutter e169f8e310 feat(matching): annuity-based fit-out cost in score + fix unapplied DQ/confidence modifiers
Mieterausbau / fit-out economics — surface true total cost of occupancy:
- Annuity calc (annuityFactor + effectiveAnnualBurdenPerSqm) replaces straight-line ÷5; FITOUT_ANNUITY_RATE=5%
- New hardFacts.fitOutByLandlord: "Wer baut aus?" toggle in NewListing — landlord-borne fit-out is priced into rent (no surcharge), tenant-borne SHELL/BASIC adds annuitized cost minus MAB
- scoreBudget now compares effective annual burden (rent + fit-out annuity) vs budget instead of cold rent only; FULL/PREMIUM and landlord-borne unchanged
- FitOutCostPanel + CompareTableBody compute annuitized, tenant-aware burden
- Central FIT_OUT_LABELS with industry/international vocabulary (Rohbau·Core&Shell, Edelrohbau·CAT A, etc.)
- Activate existing generateFitOutAdvice via useFitOutAdvice hook + new FitOutAdvicePanel (MIETERAUSBAU/BKZ/MAB-Amortisation + negotiation tip), shown for tenant-borne SHELL/BASIC
- MAB field only asked when tenant builds out (optional) — one new toggle, no extra data burden for property managers

Fix: DQ/confidence modifiers were computed but never applied to finalScore (hardcoded 0 in output) — now folded into rawFinal and exposed. Trust-first: weak data quality lowers the score. Resolves 3 pre-existing red tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 18:19:58 +02:00
Benjamin Sutter 7a0909e36a 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>
2026-06-19 20:53:49 +02:00
Benjamin Sutter 6aa4f96bd2 feat: responsive layout + Reminder Manager redesign
- Auto-collapse sidebar at <1536px (all laptops), expand at ≥1536px
- Responsive drawer/panel widths across Pipeline, Properties, MyListings, Anfragen, MatchDetail, AISearch
- Reminder Manager: dot+label priority badge, Fläche column removed, status only for non-active rows
- ReminderKpiBar: focal Überfällig card, three secondary KPIs
- ReminderDetailDrawer: Task Panel redesign — Finanzen removed, Notiz promoted, Pre-Market as inline badge, full create form
- useCreateReminder hook wired to reminderService.create with cache invalidation
- Mock data: contract-derived reminders (LEASE_EXPIRY, BREAK_OPTION, RENT_REVIEW, INSURANCE_RENEWAL, SCHATTENMARKT_RELEASE) now show auto-creation note in Verlauf

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 14:30:06 +02:00
Benjamin Sutter ee229f8f4f refactor: unit-centric lease view — Einheiten & Mietverträge
Replace building-level Miet- & Mieterinformationen section with
unit-level lease display. Each unit row now shows tenant name and
lease end date directly; expand reveals full contract details
(Mietbeginn, Mietende, Laufzeit, Breakout, source system, doc link).

Floor grouping groups consecutive same-floor units under one label.
Domain model extended with Lease and Tenant types; deprecated
property-level tenant fields preserved via @deprecated for compat.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 22:52:18 +02:00
Benjamin Sutter b2530f9e20 feat: Reale Jahresbelastung, must-have fixes, fitOut data coverage
Reale Jahresbelastung (Change 6):
- FitOutCostPanel zeigt Jahresmiete + amortisierte Ausbaukosten für alle fitOut-Werte
- FULL/PREMIUM: Ausbau CHF 0 (bezugsfertig), SHELL/BASIC: CRB/BKP-Richtwerte amortisiert über 5 J.
- Match-Card-Chip zeigt geschätzte Investition (orange wenn >100k)
- FitOutInvestment-Typ + calcFitOutInvestment() in fitOutUtils.ts
- BackendAIService + MockAIService mit generateFitOutAdvice (IAIService-Interface)

Must-have Kriterien (Nicht prüfbar Fix):
- ÖV-Anbindung: Minutengrenze aus Freitext extrahiert, gegen publicTransportMinutes geprüft
- Mindestfläche: m²-Wert aus Freitext extrahiert, gegen areaSqm geprüft
- Ausbaugrad: neues Keyword-Rule für FULL/PREMIUM

fitOut-Datenpflege:
- fitOut-Werte zu 32 fehlenden Properties ergänzt (Logistik=SHELL, Standard=BASIC, Modern=FULL)
- MAB-Werte (200/150/250 CHF/m²) zu 3 BASIC-Objekten hinzugefügt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 20:09:55 +02:00
Benjamin Sutter 36570c5bdc fix: resolve all TypeScript errors and refactor oversized components
- 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>
2026-05-26 00:08:35 +02:00
Benjamin Sutter 6fb2fb027c refactor: rename tab to KI-Signale, diversify DEMAND signal narratives
- "Digitale Signale" → "KI-Signale" tab label
- signal-001: Alpbach Advisors — new Zürich branch announced via press release
- signal-007: EBH Treuhand — Handelsregister merger of two Winterthur firms
- signal-010: Centra Schweiz — suburban→city relocation for talent attraction
- signal-013: Rheintaler Logistik Solutions — spin-off needs its own premises
- signal-015: BernTech — CEO publicly confirmed new office in VC press release
- signal-004 improved: lab expansion driven by published strategic plan, not headcount
- Each signal uses a different trigger: press/Handelsregister/annual report/VC round

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 23:53:30 +02:00
Benjamin Sutter 7f6b7766e6 feat: Marktchancen — Netzwerk-Hinweise tab (human-sourced market intel)
- MarktHinweis domain type: SUCHE/VERFUEGBARKEIT direction, INTERN/PLATTFORM
  visibility, anonymization flag, source tracking
- Full data layer: IMarktHinweisProvider, MockupMarktHinweisProvider, service, hooks
- 7 seed entries: mix of intern/shared, demand/supply, own/partner Verwaltungen
- Marktchancen gets two-tab layout: Digitale Signale (crawler) + Netzwerk (human)
- HinweisListItem: direction icon, visibility badge (Intern/Geteilt/partner name)
- HinweisDetail: note card, portfolio match (SUCHE) or availability info (VERFUEGBARKEIT)
- HinweisErfassenDialog: direction toggle, asset type, location, area range,
  company + anonymize switch, source, note, card-based visibility picker
- HinweisPropertyCard: same Anschreiben composer pattern as crawler side

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 23:47:50 +02:00
Benjamin Sutter 63909c8caf feat: Marktchancen — source links, KI-Kontaktdaten, portfolio match, Anschreiben
- ExtractedContact domain type (ContactType, ContactConfidence) on FutureSignal
- Mock extractedContacts for all 6 DEMAND signals (website, email, phone/linkedin)
- Source URL row with external link button
- KI-Kontaktdaten section: clickable links, confidence dots, purple accent
- PropertyMatchCard: area fit % chip (green/yellow/red), inline Anschreiben toggle
- buildAnschreiben() pre-filled German outreach template, editable + copy button
- Portfolio match section groups properties by fit quality

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 23:32:42 +02:00
Benjamin Sutter cd220ed282 feat: Suchabo inline contact flow — chip opens need detail + OfferWizard
- Add >80% STRONG matches for all 6 direct listings (match-072–075)
- Add 4 new Needs (need-012–015) tailored to each previously unmatched listing
- Add 8 new LatentNeeds (lneed-007–014) for all needs in direct listing matches
- Add need→latentNeed mapping (src/lib/needToLatentNeedMap.ts)
- MyListings: clicking Suchabo chip opens dialog with company name, score,
  PublicNeedDetail view and OfferWizard — no navigation to Anfragencenter needed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 23:08:41 +02:00
Benjamin Sutter a9ee53f185 feat: Inserate — edit drawer + Suchabo match count per listing
Each listing row now shows a color-coded Suchabo chip (green ≥80 / amber
≥60 / red) with a tooltip listing individual scores. Clicking a row opens
a right-side PropertyDetailView drawer with full edit + Matchability tab
(which shows the matching Suchabos). Added 7 mock matches across the 6
direct listings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 22:54:06 +02:00
Benjamin Sutter fe8444c1d3 feat: add 6 mock direct listings (sourceType DIRECT) for Inserate page
4 active + 2 inactive — Office/Retail/Light-Industrial/Logistics across
Zürich, Bern, Winterthur, Pratteln, Muttenz, Luzern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 22:47:12 +02:00
Benjamin Sutter a9e038a64f feat: Marktsignale KPI panel with adjustable radius (5/10/15/20 km)
Adds MarketKpis domain type with formula-based radius scaling, a MarketKpiPanel
component (vacancy rate, platform searches, avg vacancy duration, demand/supply
ratio — color-coded with interpretation badges), and integrates it at the top of
PropertyMarketSignalsTab. Demand/supply section labels now update with the
selected radius. Mock data covers all 5 existing market reports.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 22:28:04 +02:00
Benjamin Sutter 61a26d00a7 feat: Mietvertrag-Mock für 50 % der Objekte (URL + Bezeichnung)
Jedes zweite Objekt bekommt per Post-Processing-Loop eine
leaseContractUrl (docs.wincasa.ch/…) + leaseContractName
(„Mietvertrag YYYY–YYYY") — ohne echten Inhalt, nur für die UI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 22:19:37 +02:00
Benjamin Sutter e95490eb72 feat: Grundriss-Feature, Listenansicht mit Bildern, Gewerbe-Label, Image-Pool
- Grundriss (FloorPlanSection): PropertyUnit.floorPlanUrl?, Property.floorPlanUrl?;
  FloorPlanSection in MatchDetail + PropertyDetail; FloorPlanUrlSection in NewListing-Formular
- Listenansicht (MatchCardCompact): horizontales Layout mit 120px Bildstreifen,
  Score-Badge, Asset-Label-Overlay, alle 3 grünen Punkte, Anfrage-Button
- Light Industrial → "Gewerbe" überall (NeedInput, NeedCardPreview, CriteriaReviewPanel,
  newListingConstants, MyListings, propertyHelpers)
- "Zum Originalinserat"-Button nur bei Maison-Work-Objekten
- Image-Pool: propertyImageResolver mit sequentiellem Pool-Index (keine doppelten Bilder),
  nur Innenaufnahmen, rotate()-Trick für Sub-Pools
- Overlay-Labels (Objekttyp + Stadtteil) in LocationPreview + IntelligenceMatchCard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 22:12:43 +02:00
Benjamin Sutter 3ab6eeccf2 fix: demand Anfragen page shows own sent inquiries (demand perspective)
Replace supply-side mock data with demand-side conversations where the
logged-in user is the sender (right/dark) and verwalter replies are left.
List shows property name instead of tenant name.
New domain fields: propertyManagerName, propertyManagerCompany, propertyAddress.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 18:07:59 +02:00
Benjamin Sutter 609a3634bd feat: remove EXTERNAL_MARKET result type entirely
All external market properties now surface as VERIFIED_PORTFOLIO.
Removes the type from enums, domain types, mock data, matching engine,
components, hooks, and tests — zero user-visible distinction remains.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 17:28:42 +02:00
Benjamin Sutter 8f1db31683 refactor: GenericBadge + Provider-Isolation
Teil A — GenericBadge:
- Neue src/components/shared/GenericBadge.tsx mit zwei Varianten:
  transparent (${color}18 Hintergrund, farbiger Text, opt. Border)
  solid (gefüllte Farbe, weisser Text)
- Props: label, color, variant, showBorder, bold, icon, size, tooltip, ariaLabel
- 7 Badges auf GenericBadge refactored (Config-Objekt + 1-Zeiler):
  ReviewStatusBadge, ReviewPriorityBadge, ReviewEntityTypeBadge,
  AIOutputStatusBadge, AIErrorBadge, MatchStatusBadge, ShortlistStatusBadge
- Barrel-Export in src/components/shared/index.ts

Teil B — Provider-Isolation:
- src/mock-data/propertyStore.ts als neutrales Daten-Modul erstellt
- MockupPropertyProvider: importiert aus mock-data statt selbst zu definieren
- MockupUnitProvider: importiert aus mock-data statt aus MockupPropertyProvider
- matchSyncService: importiert aus mock-data statt aus MockupPropertyProvider
- Kein Provider importiert mehr einen anderen Provider

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 13:13:34 +02:00
Benjamin Sutter 22c195b4a5 refactor: split large page components + taxonomy/HeatBadge/FutureAvailability improvements
- Taxonomy: merge VERIFIED_PORTFOLIO + EXTERNAL_MARKET display → 'Plattform' (dark blue) across all surfaces
- HeatBadge: new flame indicator for hot properties (grid, list, pipeline views)
- FutureAvailabilityContextPanel: richer detail page with AI summary, strategic assessment, sources
- Refactor Pipeline.tsx (630→152 lines) → pipeline/PipelineCard, PipelineColumn, PipelineDetailPanel, pipelineConstants, pipelineUtils
- Refactor IntelligenceMatchCard.tsx (483→179 lines) → FutureAvailabilityCard extracted
- Refactor MatchDetail.tsx (559→464 lines) → useMatchDetailData hook, MatchDetailPropertyDetails
- Refactor Compare.tsx (638→485 lines) → compareUtils, CompareCriteriaCard extracted

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 23:36:03 +02:00
Benjamin Sutter 72e4f08900 fix: Compare→Pipeline flow, pipeline card navigation, remove floating AI button
- Remove CompareTray fixed bottom bar; replace with compare count badge on Vergleich nav item
- Pipeline cards redesigned to match search result card visual style (MatchScoreDisplay, type chip, stage chip, MapPin layout)
- CompareColumnHeader: redesigned with proper Details/Merken action buttons, clickable title
- AddToPipelineDialog now mounted on Compare page (was missing — bookmark had no effect)
- Pipeline card ExternalLink icon navigates to property detail page; detailPath prefers stable propertyId over volatile session matchId
- Add matchId field to PipelineItem domain; pipelineStore stores it on save
- All 8 mock pipeline items now have propertyId for reliable cross-session navigation
- Remove GlobalAIAssistantButton floating overlay (was blocking form submissions and clicks)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 22:11:29 +02:00
Benjamin Sutter 6a6ff7f2e2 feat: Pipeline↔Anfragen integration + Compare→Pipeline + KI stage detection
Navigation: Deal Pipeline moved after Vergleich (before Anfragen)

Compare → Pipeline:
- Bookmark icon per column header; BookmarkCheck when already in pipeline
- Passes propertyId, propertyAddress, area/rent labels on save

Pipeline cards now unit-level:
- propertyAddress shown with MapPin on every card
- Chat icon (MessageSquare) on cards with linked inquiry → navigates to /demand/anfragen?inquiry=xxx
- Detail panel: Chat chip links to specific inquiry thread, propertyAddress displayed

Anfragen → Pipeline KI detection:
- Keyword scan on every sent message (besichtigung → VISITED, mietvertrag → NEGOTIATION, unterschrieben → CLOSED_WON)
- Only advances stage, never goes back
- Purple KI alert banner with direct Pipeline link, auto-dismisses after 6s
- Pipeline badge in inquiry list + stage chip in chat header with nav link
- URL param ?inquiry=xxx pre-selects inquiry (used from Pipeline chat button)

Domain: PipelineItem gains propertyId, unitId, propertyAddress, inquiryId
Mock data: pl-001/pl-002/pl-004 linked to inq-001/inq-005/inq-004

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 21:42:21 +02:00
Benjamin Sutter 9f7062d137 feat: UC-A/B/C demo — 50 mock properties, district scoring, gold/silver cards visible
- Add 50 mock properties (prop-050–099) tuned for UC-A (Fahrradhändler RETAIL),
  UC-B (Umzugsfirma OFFICE Zürich-West) and UC-C (Vermögensverwalter PREMIUM)
- District-aware scoreLocation: district match→100, city-only→70 when need
  specifies districts, canton→60, no match→35; fixes UC-C prop-001 over-scoring
- mustHaveScorer: add klimatisierung keyword; parking minimum count logic
- Soft factor scale fix: integer 0–100 values no longer multiplied ×100
- footfall: map passerbyFrequency string (HIGH→85, MEDIUM_HIGH→68…) before
  enrichment fallback so RETAIL properties score correctly
- Parser: Kreis list extraction ("Kreis 3, 4, 5, und 8" → 4 district entries),
  neighbourhood→district map (Seefeld, Bahnhofstrasse), prestige signals
- Results: remove VERIFIED_PORTFOLIO role gate — all users see portfolio cards,
  enabling gold (85+) and silver (70–84) cards for every demo use case
- Fix flash of wrong cards on NeedBuilder nav (effectiveNeedId not activeNeed?.id)
- needs.ts: UC-A preferredLocations now includes Kreis 3/4/5/8 entries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 20:31:30 +02:00
Benjamin Sutter ba910806d6 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>
2026-05-21 20:58:01 +02:00
Benjamin Sutter 27c53f3af1 feat: score transparency on all cards + budget parser fix
- Single scoring system: MockupNeedProvider rewrites matches via calculateScore
  exclusively — allFactors always populated, no more dual-system (computeScore
  removed), weights from weightingProfile reflected in every breakdown
- ScoreInlineBreakdown: new component shows hard/soft criteria with importance
  labels (Entscheidend/Sehr wichtig/…) and formula on compact + expanded cards
- MatchCardAdapter: passes scoreBreakdown + allFactors to ViewModel
- MatchDetail: 'Zukunftssignal' label replaced with 'Future Availability'
- aiService budget parser: values < 100 treated as monthly and multiplied by 12
  to produce correct annual CHF/m²/year value — fixes 0-result searches

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 20:41:46 +02:00
Benjamin Sutter 34a4dcfb29 feat: unit-first architecture — floor + street as primary card title
Every VERIFIED_PORTFOLIO property now has explicit units in mock data
(added to prop-002, prop-008, prop-009, prop-010, prop-011, prop-014).

PropertyUnit extracted to domain/unit.ts with formatFloorLabel,
formatUnitTitle, formatMultiUnitFloors helpers; re-exported from
property.ts so all existing imports are unaffected.

Cards now show floor + street as the primary title:
- Specific unit match: "1.OG Nord · Zollstrasse 12"
- Multi-unit (whole building): "1.OG–3.OG · Zollstrasse 12"
- EG retail: "EG Verkauf · Löwenplatz 3"
Building name (e.g. "Bürofläche Zollstrasse 12") appears as subtitle.
Both IntelligenceMatchCard (grid) and MatchCardCompact (list) updated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 11:19:13 +02:00
Benjamin Sutter 69b96f293a refactor: remove Schattenmarkt from all user-visible UI strings
- Pipeline.tsx: FUTURE_AVAILABILITY label → 'Future Availability'
- ReminderTypeBadge: SCHATTENMARKT_RELEASE label → 'Pre-Market'
- ReminderFilterBar: SCHATTENMARKT_RELEASE label → 'Pre-Market'
- ReminderKpiBar: 'Schattenmarkt-Risiko' → 'Pre-Market Risiko'
- ReminderDetailDrawer: section title + switch label → 'Pre-Market'
- mock-data/matches: explainabilitySummary → 'Pre-Market freigegeben'
- mock-data/reminders: notes → 'Pre-Market Freigabe'

Internal identifiers (schattenmarktRelease field, useSchattenmarktSignals hook,
signal ID prefix schattenmarkt-*) are unchanged — renaming them touches too many
call sites for no user-visible gain.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 00:11:35 +02:00
Benjamin Sutter d902feb6c0 feat: Objekt→Einheit architecture — unit-level pre-market, property detail page, click-through from cards
- Domain: PropertyUnit extended with propertyId + schattenmarktRelease per unit
- Domain: FutureAvailabilityResult carries resolved property + unit
- useSchattenmarktSignals: generates unit-level signals (schattenmarkt-{propId}-{unitId})
- useUnifiedResults: resolves backing property + unit on FUTURE_AVAILABILITY fast path
- IUnitProvider + MockupUnitProvider: first-class unit access and mutation
- matchCardAdapter: maps preMarketUnit, preMarketAllUnits, propertyId, unitId to ViewModel
- IntelligenceMatchCard: PRE-MARKET VERIFIED shows unit info strip + "Zur Einheit →" button
- PropertyDetailView: unit-level toggles + date pickers inside PreMarketPanel
- New page: /demand/property/:propertyId with unit table, status chips, inquiry form
- App.tsx: demand route /demand/property/:propertyId registered
- Mock data: prop-001/007/037 units updated with correct lease dates + unit-level schattenmarktRelease

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 19:47:59 +02:00
Benjamin Sutter dd2eb9be3f feat: add prop-037 + match-063/064 — PRE-MARKET VERIFIED for Bern Retail search
Adds VERIFIED_PORTFOLIO retail property (Spitalgasse 18, Bern Innenstadt)
with schattenmarktRelease enabled, completing card-type coverage for
need-011 (Stadtladen Bern GmbH): now has VERIFIED_PORTFOLIO, PRE-MARKET
VERIFIED, EXTERNAL_MARKET, MAISON_WORK, and MARKET SIGNAL cards across
gold/silver/bronze score tiers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 18:18:12 +02:00
Benjamin Sutter d2d2f5f1bb feat: enable Pre-Market Matching on 7 more portfolio properties — 10 active signals total
Activated schattenmarktRelease on prop-008 through prop-014 with realistic
future lease end dates (2026–2027) and appropriate lead times so all
trigger dates fall before MOCK_TODAY (2026-05-20).

Active PRE-MARKET VERIFIED signals: prop-001, 002, 007, 008, 009, 010, 011, 012, 013, 014

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 17:59:07 +02:00
Benjamin Sutter 67c0701bfe fix: remove EXPANSION fake properties and their matches from mock data
Deleted prop-005, prop-026, prop-028, prop-030 (resultType FUTURE_AVAILABILITY
but representing demand-side Expansion signals) and the 6 associated matches
(match-007, match-018, match-029, match-047, match-054, match-055).

These fake properties bypassed the signalDirection filter in useUnifiedResults,
causing EXPANSION signals to appear in the demand search feed. Future
Availability in the demand feed now contains only genuine supply-side signals.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 16:10:23 +02:00
Benjamin Sutter b0ad6643ff feat: Future Availability — Logiktrennung Supply/Demand + Markt-Leads
EXPANSION-Signale (Unternehmen sucht Fläche) sind Nachfragesignale und
gehören nicht in den Demand-Feed. Neue Architektur:

- signalDirection: 'SUPPLY' | 'DEMAND' auf FutureSignal-Domain
- SUPPLY-Signale: POSSIBLE_MOVE_OUT / CONSTRUCTION_PROJECT (Crawler)
  und LEASE_EXPIRY+isVerified (Verwaltung ERP) → Demand-Feed wie bisher
- DEMAND-Signale: EXPANSION → aus Demand-Feed gefiltert
- Neues useMarketLeads-Hook + /supply/market-leads Seite für Verwaltung:
  zeigt EXPANSION-Signale als potenzielle Mieter mit Portfolio-Matching
- Alle 15 Signale in futureSignals.ts inhaltlich bereinigt und
  mit korrekter signalDirection versehen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 15:56:29 +02:00
Benjamin Sutter 5fe15bac62 feat: Future Availability Intelligence — rebrand & card redesign
Replaces "Schattenmarkt" branding with professional Future Availability
Intelligence throughout the UI. Two distinct card types: Pre-Market
(LEASE_EXPIRY + verified, green accent) and Market Signal (probabilistic,
blue accent). Professioneller Data-Header replaces dark purple gradient.
New detail panel sections: Erkannte Marktindikatoren, Strategische
Interpretation, Bestätigt/Nicht bestätigt. All 15 mock signals enriched
with concrete Swiss CRE market indicators and strategic interpretations.
Signal quality shown as 4-dot indicator (Hoch/Mittel/Niedrig).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 13:06:16 +02:00
Benjamin Sutter 9f391d17cb feat: Reminder Manager + Schattenmarkt-Freigabe + mock data overhaul
- Add Reminder Manager page (/supply/reminder-manager) with KPI bar,
  filter bar, list/card feed, and detail drawer (7 sections incl.
  activity log, snooze, complete, dismiss actions)
- Add Schattenmarkt-Freigabe toggle on PropertyDetailView: Verwaltung
  can opt-in properties for early market exposure before contract expiry
- Auto-generate FutureSignal cards via useSchattenmarktSignals hook
  when leaseEndDate - leadTimeMonths <= MOCK_TODAY
- Fix useUnifiedResults: use property.resultType as fallback (was
  defaulting everything to VERIFIED_PORTFOLIO)
- Fix demand Results: hide VERIFIED_PORTFOLIO from non-manager users
  even when showOwnProperties toggle was previously enabled
- Fix AppShell: redirect to allowed workspace on user role switch
- Fix 3 wrong match scores (match-002: 93→62, match-009: 86→55,
  match-017: 87→52)
- Add 7 new match records (match-050–056) for need-001, need-002,
  need-011
- Add need-011 (Retail Bern Innenstadt)
- Add prop-031–036 (EXTERNAL_MARKET / MAISON_WORK / FUTURE_AVAILABILITY)
- Fix duplicate image URLs across all properties
- Add signal-011 (Bern Altstadt, Mode Boutique) + propertyId to
  signal-001

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 11:52:24 +02:00
Benjamin Sutter 0663757cde fix: Schattenmarkt bypass source filter + bump external/maison match scores for all tiers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 23:10:41 +02:00
Benjamin Sutter 3756675b6f feat: add Maison Work source type, rename ExternalMarket to Direktinserat, hide own portfolio from demand search by default
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 23:02:05 +02:00
Benjamin Sutter f0e58f5f7a feat: unit-level matching — each floor lettable independently or as bundle
- PropertyUnit: add isFlexible, minLettableSqm, offeredSqm fields
- New UnitBundle + UnitNeedMatch domain types
- unitMatchService: match individual units or bundles against LatentNeeds
  (exact, partial/Teilfläche, bundle match types with area scoring)
- Mock units updated: prop-001/007/012/013 free units flagged as flexible
  with minimum lettable areas
- UnitStructurePanel replaces old Stockwerkstruktur table:
  - Checkboxes on free units (when ≥2 exist) for bundle selection
  - Inline match pills per free unit (top need + score)
  - Expand row to see all matching needs with area details
  - 'Teilfläche möglich' chip for flexible units with min sqm
  - Bundle panel appears when 2+ units selected: shows combined sqm,
    bundle matches, and note about remaining area after contract

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 20:24:17 +02:00
Benjamin Sutter 7cda66f98a fix: convert strings with Swiss apostrophes (3'000, 2'500, 1'500) to template literals 2026-05-19 20:09:54 +02:00
Benjamin Sutter 18a7f146b0 fix: replace smart quotes with ASCII quotes in mock data files 2026-05-19 20:01:29 +02:00
Benjamin Sutter b4d398270f feat: Anfragencenter Parts A–E — read/unread, prep wizard, offer flow, report preview
- Part A: Replace InquiryStatus badges with WhatsApp-style unread indicators
  (isRead, unreadCount, lastReadAt on Inquiry; markThreadAsRead in service + hook)
- Part B: RelatedPropertyCardPanel — reposition "Objekt ansehen", add "Weitere
  Matches" section via matchService.getAdditionalMatchesForInquiry
- Part C: PreparationWizard 4-step dialog — property selection, report generation
  progress, field editing + ReportObjectFieldSelector, PDF preview + finalize
- Part D: OfferCreationWizard 4-step dialog triggered from first tenant message —
  data capture, field editing, viewing appointments, PDF generation + attach to reply
- Part E: LatentInquiryReportPreview (2-page A4 doc), ReportObjectFieldSelector
  (5 accordion groups, 35+ optional fields), mapImageUrl on Property domain

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 19:20:13 +02:00
Benjamin Sutter 7800d127fe fix: BerichtDialog, images, and add property number + floor/unit structure
- BerichtDialog: conditionally mount instead of always-mounted with
  open prop; starts in 'generating' state immediately on open
- Images: fix all 6 wrong VERIFIED_PORTFOLIO and EXTERNAL_MARKET photos
  to match spec per assetType (OFFICE/RETAIL/LOGISTICS/PRODUCTION/MIXED)
- Domain: add PropertyUnit interface + propertyNumber field to Property
- Mock data: propertyNumber + units[] for prop-001/007/012/013
- UI: Stockwerkstruktur section in Übersicht tab (floor/unit table with
  availability, tenant, area); Objekt-Nr. shown below key metrics

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 16:14:22 +02:00
Benjamin Sutter 920ce8eb09 feat: Verwaltungszugang refactor — lease data, 3-tab detail, market signals
- Dashboard: remove QuickActionPanel, StrongMatchOverview, FutureSignalWidget,
  DataQualityWidget, header buttons, Marktchancen nav; KpiGrid → 4 cards
- Property domain: 9 new fields (leaseTerm, leaseStartDate/End, breakoutOption,
  currentTenant, importedFrom, importedAt, lastUpdatedAt)
- Mock data: annual CHF/m²/Jahr prices (×12), Swiss images, tenant/lease data
  for all 10 VERIFIED_PORTFOLIO properties; need budgets updated to annual
- PropertyTable: swap columns — add Aktueller Mieter, Mietlaufzeit,
  Breakoutoption, Breakoutoption Zeitpunkt; remove Verfügbarkeit, Konfidenz, Quelle
- PropertyDetailView: 3 tabs (Übersicht, Matchability, Marktsignale) with
  inline edit mode, NeedMatchCard list, PropertyMarketSignalsTab
- New: marketReport domain + mock data + service, NeedMatchCard,
  PropertyMarketSignalsTab with 4 sections + PDF button
- matchService: getNeedMatchesForProperty(propertyId, {minScore})

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 15:43:52 +02:00
Benjamin Sutter 14badd1ca8 feat: all 4 UX priorities — wizard, examples, onboarding, pipeline
- Offer wizard: 4→3 steps by merging 'Prüfen' into PDF review step
- Search mask: quick-select chips for area ranges, budget, and city presets
- Onboarding: WelcomeDialog (3 slides, localStorage) on first visit
- Deal Pipeline: Kanban board (6 stages, 8 mock items, advance-to-next-stage)
  added to demand workspace navigation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 22:09:06 +02:00
Benjamin Sutter b5cdb0353b feat: Schattenmarkt cards — source pills, probability bar, AI summary
Complete IntelligenceMatchCard redesign for FUTURE_AVAILABILITY signals:
purple gradient hero, source type pills, credibility indicators,
probability LinearProgress bar, market indicator badge, AI-generated
signal summary block. Added aiSummary field to FutureSignal domain
and rich German AI summaries to mock signals 001–005.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 21:49:27 +02:00
Benjamin Sutter c4c29ef7d3 feat: F030 Anfragencenter — Aktive & Latente Anfragen + Angebot-Wizard
Neuer Menüpunkt «Anfragencenter» (ehemals «Eingehende Bedarfe»):

Aktive Anfragen:
- Split-View: Anfrageliste (Liste/Grid-Toggle) + Chat-Detailansicht
- Chat-Verlauf mit Sender-Styling (Mieter links, Supply rechts)
- Antwortformular mit Betreff, Nachricht, Anhänge, Statuswechsel
- Zugehörige Property Card neben dem Chat

Latente Anfragen:
- Drei-Spalten-Layout: Need-Liste | Need-Detail | Eigene Objekte
- Need Cards mit AI-Zusammenfassung, Must-Haves, Präferenzen
- Eigene Portfolio-Objekte sortiert nach deterministischem Match-Score
- Checkbox-Selektion für Angebotsauswahl

Angebot-Wizard (4 Schritte):
- Schritt 1: Objekte auswählen (mit Score-Vorschau)
- Schritt 2: PDF-Vorschau + editierbare Textfelder
- Schritt 3: Angebot prüfen & bestätigen
- Schritt 4: Nachricht an Suchenden mit KI-generierter Mail

Architektur:
- Domain: Inquiry, LatentNeed, OfferDraft Types
- Provider: IInquiryProvider, ILatentNeedProvider, IOfferProvider + Mockups
- Services: inquiryService, latentNeedService, offerService
- Hooks: useInquiries, useLatentNeeds, useOffers (TanStack Query)
- Store: offerWizardStore (Zustand, lokaler Wizard-State)
- 27 neue Komponenten, alle Loading/Empty/Error States

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 18:03:05 +02:00
Benjamin Sutter 05ac0083b2 feat: Steuerlast-Kriterium, ImmoScout-Layout, Gold/Silver/Bronze-Trennung
- Flächensuche: Flexibilität durch Steuerlast (taxEnvironment) ersetzt
- MatchDetail: Bild als Hero-Banner oben, Karte eingebettet im Inhalt darunter
- Ergebnisliste: Gold/Silber/Bronze-Abschnitte mit farbigen Trennlinien
- ScoreBreakdown: KI-Steuerlast-Link zur kantonalen Steuerrechner-Seite
- Beispieldaten: alle Objekte mit passenden Bildern versehen
- locationIntelligence: taxCalculatorUrl pro Kanton ergänzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 14:14:10 +02:00
Benjamin Sutter c75d3c8121 feat: F029 intelligence card system — premium AI-native CRE cards
Gold/Silver/Bronze FIFA-style score badges overlaid on location imagery.
List/grid view toggle on Results and Properties pages (persisted in
localStorage). IntelligenceMatchCard for match results grid view;
PropertyIntelligenceCard for property grid view with warm brown theme.
LocationPreview component with Google Maps link. 10 mock properties
enriched with Unsplash building images.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 13:25:27 +02:00
Benjamin Sutter 71f4b1eeb6 feat: F019 AI monitoring layer — output transparency & governance workspace
Full 2-panel workspace at /ops/ai-monitoring: metrics strip (total, failed,
pending, approval rate, top prompt version, active model), filterable output
table by type/status/error, and detail panel with output preview, prompt
versioning, error details, and role-aware review actions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 13:11:35 +02:00
Benjamin Sutter 5d3323617c feat: F018 human review queue — 2-panel governance workspace
Replaces the old ad-hoc ReviewQueue page with a full governance-compliant
workflow: filterable task list, detail panel with confidence/risk context,
role-aware approve/reject/escalate/more-data actions, and persistent notes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 13:03:34 +02:00