feat(demand): market rent estimate (asking vs. fair) + pre-market expected price
- lib/rentEstimate: deterministic market estimate from Standort-Intelligence (median rent benchmark + vacancy/trend) → fair rent, verdict BELOW/AT/ABOVE, deltaPct, rationale; suggestFutureRent indexes today's price by city rent trend - MarketPricePanel in MatchDetail: asking vs. fair market rent + verdict badge + rationale; shows pre-market expected price (heute → erwartet) - unit.expectedRentPerSqm: future pre-market price; scorer prefers it over current rent for FUTURE_AVAILABILITY - UnitStructurePanel editor: expected-price field (with indexed suggestion) shown for pre-market-released units - tests: rentEstimate verdict thresholds + future-rent indexing Note: rent estimate is a deterministic market-data calc (not an LLM) — honest & testable; can be routed through IAIService later if a real model is wanted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ import { useInquiryStore } from '../../stores/inquiryStore'
|
||||
import { AddToPipelineDialog } from '../../components/shortlist'
|
||||
import { MatchReasonList } from '../../components/match-card/MatchReasonList'
|
||||
import { getCityIntelligence, lookupCityCoords } from '../../lib/locationIntelligence'
|
||||
import { NextActionsPanel, FitOutCostPanel, FitOutAdvicePanel } from '../../components/match-detail'
|
||||
import { NextActionsPanel, FitOutCostPanel, FitOutAdvicePanel, MarketPricePanel } from '../../components/match-detail'
|
||||
import type { MatchCardReason } from '../../components/match-card/MatchCardViewModel'
|
||||
import { useMatchDetailData } from '../../hooks/useMatchDetailData'
|
||||
import { useMatchDetail } from '../../hooks/useMatches'
|
||||
@@ -39,6 +39,7 @@ export default function MatchDetail() {
|
||||
const { openInquiryDialog } = useInquiryStore()
|
||||
|
||||
const { match, property, need, signal, isLoading, isFuture } = useMatchDetailData(matchId ?? '')
|
||||
const detailUnit = property?.units?.find(u => u.id === match?.unitId)
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
@@ -238,6 +239,16 @@ export default function MatchDetail() {
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* ── Marktpreis-Einschätzung (Angebot vs. Markt; bei Pre-Market inkl. Zukunftspreis) ── */}
|
||||
{property?.location?.city && property.rentPricePerSqm > 0 && (
|
||||
<MarketPricePanel
|
||||
city={property.location.city}
|
||||
assetType={property.assetType}
|
||||
askingRentPerSqm={detailUnit?.rentPricePerSqm ?? property.rentPricePerSqm}
|
||||
futureRentPerSqm={isFuture ? detailUnit?.expectedRentPerSqm : undefined}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* ── Full analysis toggle + expanded panels ── */}
|
||||
<MatchDetailScoreBreakdown
|
||||
match={match}
|
||||
|
||||
Reference in New Issue
Block a user