refactor: split PropertyDetailView (998→192 lines) and MatchDetail (464→236 lines)
PropertyDetailView.tsx extracted into 5 focused components: - PropertyDetailHelpers: Field, FieldGrid, SectionTitle, floorLabel - UnitStructurePanel: floor structure with unit matching - PreMarketPanel: schattenmarkt release controls - PropertyDetailOverview: overview tab content - MatchabilityTabPanel: need matches tab MatchDetail.tsx extracted into 2 focused components: - MatchDetailHero: image/map, header, key facts strip - MatchDetailPropertySections: Preis/Hauptangaben/Eigenschaften/etc. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Alert, Box, CircularProgress, Typography } from '@mui/material'
|
||||
import { useNavigate } from 'react-router'
|
||||
import type { PropertyNeedMatch } from '../../domain/match'
|
||||
import { matchService } from '../../services/matchService'
|
||||
import { useOfferWizardStore } from '../../stores/offerWizardStore'
|
||||
import { NeedMatchCard } from './NeedMatchCard'
|
||||
|
||||
export function MatchabilityTabPanel({ propertyId }: { propertyId: string }) {
|
||||
const [matches, setMatches] = useState<PropertyNeedMatch[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const navigate = useNavigate()
|
||||
const setSelectedProperties = useOfferWizardStore(s => s.setSelectedProperties)
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
setLoading(true)
|
||||
matchService.getNeedMatchesForProperty(propertyId, { minScore: 80 }).then(result => {
|
||||
if (!cancelled) { setMatches(result); setLoading(false) }
|
||||
})
|
||||
return () => { cancelled = true }
|
||||
}, [propertyId])
|
||||
|
||||
function handleNeedCardClick() {
|
||||
setSelectedProperties([propertyId])
|
||||
navigate('/supply/anfragen', { state: { tab: 1 } })
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Box sx={{ p: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<CircularProgress size={24} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ p: 2.5 }}>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 700, color: '#0f172a' }}>
|
||||
Matchability
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Bedarfsprofile mit ≥ 80% Match-Score — Karte klicken um Angebot zu erstellen
|
||||
</Typography>
|
||||
</Box>
|
||||
{matches.length === 0 ? (
|
||||
<Alert severity="info" sx={{ fontSize: '0.8125rem' }}>
|
||||
Keine Bedarfsprofile mit ≥ 80% Match-Score für dieses Objekt gefunden.
|
||||
</Alert>
|
||||
) : (
|
||||
matches.map(m => <NeedMatchCard key={m.matchId} match={m} onClick={handleNeedCardClick} />)
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user