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>
This commit is contained in:
+11
-2
@@ -1,6 +1,6 @@
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { useMutation, useQuery } from '@tanstack/react-query'
|
||||
import { aiService, parseListingText } from '../services/aiService'
|
||||
import type { OfferEmailPayload } from '../services/aiService'
|
||||
import type { OfferEmailPayload, FitOutAdviceInput } from '../services/aiService'
|
||||
|
||||
export function useParseNeed() {
|
||||
return useMutation({
|
||||
@@ -25,3 +25,12 @@ export function useParseListingText() {
|
||||
mutationFn: (text: string) => parseListingText(text),
|
||||
})
|
||||
}
|
||||
|
||||
export function useFitOutAdvice(input: FitOutAdviceInput | null) {
|
||||
return useQuery({
|
||||
queryKey: ['fitOutAdvice', input],
|
||||
queryFn: () => aiService.generateFitOutAdvice(input!),
|
||||
enabled: !!input,
|
||||
staleTime: Infinity,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ export interface NewListingFormState {
|
||||
// Technical
|
||||
floor: string
|
||||
fitOut: string
|
||||
fitOutByLandlord: boolean
|
||||
parking: string
|
||||
ceilingHeight: string
|
||||
mieterausbaubeitrag: string
|
||||
@@ -80,6 +81,7 @@ export interface NewListingFormHandlers {
|
||||
setSoftLevel: (key: string, value: string) => void
|
||||
setFloor: (v: string) => void
|
||||
setFitOut: (v: string) => void
|
||||
setFitOutByLandlord: (v: boolean) => void
|
||||
setParking: (v: string) => void
|
||||
setCeilingHeight: (v: string) => void
|
||||
setMieterausbaubeitrag: (v: string) => void
|
||||
@@ -117,6 +119,7 @@ export function useNewListingForm(pre: Prefill): NewListingFormState & NewListin
|
||||
const [softLevels, setSoftLevels] = useState<Record<string, string>>(() => ({ ...emptySoftLevels(), ...pre.softLevels }))
|
||||
const [floor, setFloor] = useState(pre.floor ?? '')
|
||||
const [fitOut, setFitOut] = useState(pre.fitOut ?? '')
|
||||
const [fitOutByLandlord, setFitOutByLandlord] = useState(pre.fitOutByLandlord ?? false)
|
||||
const [parking, setParking] = useState(pre.parking != null ? String(pre.parking) : '')
|
||||
const [ceilingHeight, setCeilingHeight]= useState(pre.ceilingHeight != null ? String(pre.ceilingHeight) : '')
|
||||
const [mieterausbaubeitrag, setMieterausbaubeitrag] = useState(pre.mieterausbaubeitragPerSqm != null ? String(pre.mieterausbaubeitragPerSqm) : '')
|
||||
@@ -169,7 +172,7 @@ export function useNewListingForm(pre: Prefill): NewListingFormState & NewListin
|
||||
assetType, street, houseNumber, postalCode, city,
|
||||
areaSqm: Number(areaSqm), rentPerSqm: Number(rentPerSqm),
|
||||
availableFrom, description, softLevels,
|
||||
floor, fitOut, parking, ceilingHeight, images, floorPlanUrl,
|
||||
floor, fitOut, fitOutByLandlord, parking, ceilingHeight, images, floorPlanUrl,
|
||||
mieterausbaubeitrag, isFlexible, minLettableSqm,
|
||||
}),
|
||||
{
|
||||
@@ -185,7 +188,7 @@ export function useNewListingForm(pre: Prefill): NewListingFormState & NewListin
|
||||
setAreaSqm(''); setRentPerSqm(''); setAvailableFrom(''); setDescription('')
|
||||
setContactName(''); setContactEmail(''); setContactPhone('')
|
||||
setSoftLevels(emptySoftLevels())
|
||||
setFloor(''); setFitOut(''); setParking(''); setCeilingHeight('')
|
||||
setFloor(''); setFitOut(''); setFitOutByLandlord(false); setParking(''); setCeilingHeight('')
|
||||
setMieterausbaubeitrag(''); setIsFlexible(false); setMinLettableSqm('')
|
||||
setImages([]); setImageInput(''); setFloorPlanUrl('')
|
||||
setAiText(''); setAiApplied(false)
|
||||
@@ -195,7 +198,7 @@ export function useNewListingForm(pre: Prefill): NewListingFormState & NewListin
|
||||
return {
|
||||
assetType, areaSqm, rentPerSqm, availableFrom, description,
|
||||
street, houseNumber, postalCode, city,
|
||||
softLevels, floor, fitOut, parking, ceilingHeight, mieterausbaubeitrag, isFlexible, minLettableSqm,
|
||||
softLevels, floor, fitOut, fitOutByLandlord, parking, ceilingHeight, mieterausbaubeitrag, isFlexible, minLettableSqm,
|
||||
contactName, contactEmail, contactPhone,
|
||||
images, imageInput, floorPlanUrl, aiText, aiApplied,
|
||||
error, created,
|
||||
@@ -205,7 +208,7 @@ export function useNewListingForm(pre: Prefill): NewListingFormState & NewListin
|
||||
setAssetType, setAreaSqm, setRentPerSqm, setAvailableFrom, setDescription,
|
||||
setStreet, setHouseNumber, setPostalCode, setCity,
|
||||
setSoftLevel,
|
||||
setFloor, setFitOut, setParking, setCeilingHeight, setMieterausbaubeitrag, setIsFlexible, setMinLettableSqm,
|
||||
setFloor, setFitOut, setFitOutByLandlord, setParking, setCeilingHeight, setMieterausbaubeitrag, setIsFlexible, setMinLettableSqm,
|
||||
setContactName, setContactEmail, setContactPhone,
|
||||
setImageInput, setFloorPlanUrl, setAiText,
|
||||
addImage, removeImage,
|
||||
|
||||
Reference in New Issue
Block a user