When clicking + Inserat in UnitStructurePanel, the new listing form
now pre-populates: floor label, fit-out standard, parking, ceiling
height, Mieterausbaubeitrag, Teilbar toggle, min lettable sqm, and
availability date from the unit and its parent property.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AISearch: clear parsed criteria when user manually edits text input so stale locations (e.g. Zürich) no longer persist after retyping
- needParser: fix ZURICH_SIGNALS substring bug — "kreis N" now uses word-boundary regex so "umkreis 22" no longer matches "kreis 2"
- CompareTableBody: row 9 shows full cost breakdown (Miete + NK + amortised fit-out) using FITOUT_AMORTIZATION_YEARS from constants
- FitOutCostPanel: replace local AMORTIZATION_YEARS with FITOUT_AMORTIZATION_YEARS from constants.ts (single source of truth)
- constants.ts: add FITOUT_AMORTIZATION_YEARS = 5 — change here to affect all cost calculations
- PropertyIntelligenceCard: translate raw fitOut enum to German labels (Rohbau/Grundausbau/Vollausbau/Premium-Ausbau) with explanatory tooltips; add tooltip on contract duration chip
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Toggle 'Anonymes Suchprofil' im AISearchSavePreview (nicht in Suchmaske)
- Box wechselt zu lila wenn aktiv, Text erklärt was anonymisiert wird
- isAnonymous wird via buildNeedInput in gespeichertes Need geschrieben
- MatchListCard + NeedSelectionPanel: zeigen 'Anonyme Anfrage' (lila) statt Firmenname
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Properties page: collapsible decision/filter strip (localStorage state), slim
single-row Datenpflege status bar (chips + button), 4-column card grid
- PropertyIntelligenceCard: reduced image height (160→120px), tighter body padding
- OfferWizard (latente Anfragen): new "Felder wählen" step between property
selection and PDF preview; uses ReportObjectFieldSelector per property
- MockPdfPreview: adds second PDF page showing selected properties side by side
with photo, title, location and all chosen hard/soft fact fields
- offerWizardStore: adds select_fields step type and fieldSelections state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two-pane UX (from Market Intelligence) with supply-relevant data
(FutureSignal + portfolio property matches from Markt-Leads):
- Left pane: filterable signal list with probability badge + portfolio match count
- Right pane: company overview, KI-Analyse, market indicators, Faktencheck,
portfolio matches (click → Meine Objekte), Reminder erstellen action
- No OPS-level actions (approve/reject/review stripped out)
- Nav: remove Markt-Leads entry, rename to Marktchancen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PropertyDetailView gets hideTabs prop — MyListings passes ['Matchability']
since the Suchabo chip already covers the match/contact use case inline.
Tab content now keys by name not index, so removal doesn't shift panels.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- reminderService: dueThisWeek/dueThisMonth use exclusive day ranges (1-7,
8-30) matching getHorizon() buckets; schattenmarktReadyCount counts by
type=SCHATTENMARKT_RELEASE (same criteria as the feed filter)
- ReminderKpiBar: clicking a horizon card resets filterType to ALL;
clicking Pre-Market card resets filterHorizon to ALL — ensures the number
shown on the card always equals the number of rows displayed below
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New listings are created via the button on the Inserate page, not as a
first-class nav destination.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace 3-row filter panel with a grouped feed (Überfällig / Diese Woche /
Dieser Monat / Später). KPI cards are now clickable shortcuts that filter to
their time horizon or type (Pre-Market). Filter bar condenses to one row:
search + type dropdown + archive toggle. Rows get a left priority-color
border (red/orange/yellow). Default status filter is ACTIVE+SNOOZED.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
New inquiryStore (Zustand) holds sent inquiries for the session.
InquiryQuickDialog reads from the store (no props), renders wherever needed.
Sent inquiries appear immediately at the top of the Anfragen page.
Entry points:
- Match cards: "Anfrage" button on every card in the results feed
- MatchDetail: primary action in NextActionsPanel
- Pipeline: chat icon on every DraggableCard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Phase 1 — MatchDetail: collapse from 11 panels to 3 visible sections
(Warum dieser Match? / Nächste Aktion / toggle). All analysis panels
(ScoreBreakdown, Risk, MissingData, NeedAlignment, Location, Tradeoffs,
FutureAvailabilityContext) hidden behind "Vollständige Analyse anzeigen".
Removes 2-column sidebar layout for cleaner single-column reading flow.
Phase 2 — Supply Dashboard: replace data quality KPIs with demand
intelligence. Hero now shows "Starke Match-Anfragen", active properties,
and Zukunftssignale count. Top 4 matches listed with score + reason +
next action. Data quality demoted to secondary collapsed notice.
Phase 3 — Future Availability: add informational banner above the results
feed when Zukunftssignale are present. Framed as professional market
intelligence (contract expiries, construction signals) not as risk.
Removed Zukunftssignal from DecisionContextPanel risks array.
Phase 4 — Naming: standardise RESULT_TYPE_META to "Verifiziertes Objekt" /
"Externes Angebot" / "Maison Work" / "Zukunftssignal" across all screens.
EXTERNAL_MARKET gets distinct amber color (#d97706). Remove Gold/Silver/
Bronze tier label from match score badge — only the % number is shown.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- NeedAlignmentPanel: display budget row in monthly (CHF/m²/Mt.) to match
the Preis section, use exact division (no Math.round) to avoid 13×12≠152
- MatchDetailPropertySections + PropertyDetailPublicSections: replace
Math.round(rentPricePerSqm/12) with exact division; show 2 decimal places
when monthly is not a whole number (e.g. CHF 12.67 instead of CHF 13)
- Add /Jahr suffix to all 15 displays showing rentPricePerSqm or maxPerSqm
without a time unit across results, compare, match-detail, supply, and
anfragencenter components
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
PipelineItems are domain data and must not live in Zustand. Moves the
full stack to the correct layer: MockupPipelineProvider (localStorage
persistence + seed fallback) → pipelineService → usePipeline hooks
(useQuery for reads, useMutation for writes with cache invalidation).
pipelineStore is now UI-only: dialogOpen, pendingItem, openSavedDialog,
closeSavedDialog. All consumers updated to use the new hooks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Results.tsx:
- useMemo: filter + sort in one pass (was 7 separate array iterations per render)
- useMemo: platform/maison/future/missingData counts in single for-loop
- Fix: move queryClient.invalidateQueries from render body into useEffect
Properties.tsx:
- useMemo: wrap applyFilters() call (was full copy+sort on every render)
- useMemo: compute matchReady/criticalGaps/lowConfidence/staleOrOutdated/
allMissingFields in a single for-loop (was 5 separate filter passes)
ReminderFeed.tsx:
- useMemo: wrap applyFilters() call
- useCallback: resetFilters (passed to ReminderEmptyState)
PropertyIntelligenceCard, ReminderListRow:
- React.memo: grid/list items no longer re-render when unrelated parent
state changes (e.g. selectedId, filter UI state)
tsc --noEmit passes with zero errors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add matchScoreHex(), criterionScoreColor(), criterionScoreTextColor() to lib/utils.ts
- Add RESULT_TYPE_META (labels + colors) to lib/ds.ts as single source of truth
- Remove 5 local scoreColor() functions: StrongMatchMiniCard, MatchListCard,
pipelineUtils (re-exported as matchScoreHex), CriterionRow, ScoreBreakdownPanel,
ScoreInlineBreakdown
- Remove local RESULT_TYPE_META/TYPE_META from MatchCardHeader, ResultTypeBadge;
remove RESULT_TYPE_LABEL/COLOR from pipelineConstants — all now use lib/ds.ts
- Replace local confidenceColor() in MatchCardHeader, ConfidenceFieldBadge,
ReliabilityScorePanel with confidenceHex() from lib/utils.ts
- Replace local qualityColor() in propertyHelpers, DataQualityWidget with
dataQualityColor() from lib/utils.ts
- Fix FUTURE_AVAILABILITY label inconsistency ('Future'/'Zukunft' → 'Zukunftssignal')
- Fix EXTERNAL_MARKET label inconsistency ('Direktinserat' → 'Plattform')
- tsc --noEmit passes with zero errors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- layoutStore: remove 4 dead field groups (pinnedPanels, selectedResultId,
compareTrayVisible, notificationsOpen) — none were read outside the store
- CompareTray: drop dead useLayoutStore side-effect (state was write-only)
- 8 components: replace bare useStore() with explicit selectors / useShallow
to prevent unnecessary re-renders on unrelated state mutations
- lib/constants: add STALE_MARKET_SIGNALS + STALE_REVIEW_QUEUE (30s each)
- useMarketSignals, useReviewQueue: use global constants instead of
hook-local magic numbers
- Add STATE_MANAGEMENT.md: decision tree + rules for RQ/Zustand/local/derived
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
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>
- Score: remove hidden dataQuality/confidence modifiers from finalScore;
formula now correctly shows hard×60% + soft×40% = displayed total
- "Ihr Objekt" badge: only shown for PROPERTY_MANAGER/ORGANIZATION_ADMIN
in both IntelligenceMatchCard and MatchCardHeader (DEMAND_USER sees
Verified Portfolio as a normal listing without ownership indicator)
- Compare: fix factor lookup to use allFactors (includes mid-range 45–70
scores) so Prestige, Erreichbarkeit etc. no longer show "Nicht verfügbar"
- Compare: richer AI summary with overallAssessment, perPropertyAssessment
(strengths/weaknesses/bestFor/keyRisk per property), and recommendation
- Compare/Results: pb:10 so CompareTray never overlaps last row of content
- AppShell: dynamic route names for /demand/results/:id → "Match Detail"
and /demand/property/:id → "Objekt Detail" (no more UUID in header)
- MatchDetail: remove "Match EF9" debug chip from sticky nav
- LocationIntelligencePanel: comparable listings are now clickable links
navigating to /demand/property/:id
- Comparable links: blue text + hover highlight
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
Hard criteria (Fläche, Standort, Budget, Verfügbarkeit) in dark blue,
soft criteria (Prestige, Erreichbarkeit, …) in purple — separated by
a divider with group labels.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New /supply/new-listing page (Path A: pre-filled from portfolio unit,
Path B: standalone blank form); creates EXTERNAL_MARKET + sourceType DIRECT
- "+ Inserat" button on available units in PropertyDetailView navigates
with prefilled address/area/rent
- Nav entry "Neues Inserat" added to supply sidebar
- SourceProvenancePanel hidden for DIRECT source listings (no external source)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>