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>
This commit is contained in:
Benjamin Sutter
2026-05-24 22:12:43 +02:00
parent 3ab6eeccf2
commit e95490eb72
39 changed files with 685 additions and 147 deletions
+2 -2
View File
@@ -27,7 +27,7 @@ export const mockPipelineItems: PipelineItem[] = [
location: 'Zürich-Oerlikon',
matchScore: 87,
resultType: 'VERIFIED_PORTFOLIO',
stage: 'QUALIFIED',
stage: 'CONTACTED',
areaLabel: '1200 m²',
rentLabel: 'CHF 38/m²',
availabilityLabel: '2026-10-01',
@@ -74,7 +74,7 @@ export const mockPipelineItems: PipelineItem[] = [
location: 'Zürich-West / Technopark',
matchScore: 76,
resultType: 'FUTURE_AVAILABILITY',
stage: 'QUALIFIED',
stage: 'CONTACTED',
areaLabel: '~600 m²',
availabilityLabel: '~10 Monate',
addedAt: '2026-05-06T10:00:00Z',
+21
View File
@@ -1,5 +1,6 @@
import { AssetType, ResultType, AvailabilityStatus, DataFreshness, RiskLevel } from '../domain/enums'
import type { Property } from '../domain/property'
import { resolvePropertyImage, getPoolKey } from '../lib/propertyImageResolver'
export const mockProperties: Property[] = [
@@ -4487,3 +4488,23 @@ export const mockProperties: Property[] = [
},
]
// Assign images sequentially per pool so no two properties ever share the same image
// (until pool wraps, which happens after ~12 properties per pool type)
const _poolCounters: Record<string, number> = {}
mockProperties.forEach(p => {
const district = (p.location.district ?? '').toLowerCase()
const prestige = p.softFactors?.prestige ?? 60
const key = getPoolKey(p.assetType, district, prestige)
const poolIndex = _poolCounters[key] ?? 0
_poolCounters[key] = poolIndex + 1
p.images = [resolvePropertyImage({
assetType: p.assetType,
city: p.location.city,
district: p.location.district,
prestige: p.softFactors?.prestige,
floorLevel: p.floorLevel,
propertyId: p.id,
poolIndex,
})]
})