c4c29ef7d3
Neuer Menüpunkt «Anfragencenter» (ehemals «Eingehende Bedarfe»): Aktive Anfragen: - Split-View: Anfrageliste (Liste/Grid-Toggle) + Chat-Detailansicht - Chat-Verlauf mit Sender-Styling (Mieter links, Supply rechts) - Antwortformular mit Betreff, Nachricht, Anhänge, Statuswechsel - Zugehörige Property Card neben dem Chat Latente Anfragen: - Drei-Spalten-Layout: Need-Liste | Need-Detail | Eigene Objekte - Need Cards mit AI-Zusammenfassung, Must-Haves, Präferenzen - Eigene Portfolio-Objekte sortiert nach deterministischem Match-Score - Checkbox-Selektion für Angebotsauswahl Angebot-Wizard (4 Schritte): - Schritt 1: Objekte auswählen (mit Score-Vorschau) - Schritt 2: PDF-Vorschau + editierbare Textfelder - Schritt 3: Angebot prüfen & bestätigen - Schritt 4: Nachricht an Suchenden mit KI-generierter Mail Architektur: - Domain: Inquiry, LatentNeed, OfferDraft Types - Provider: IInquiryProvider, ILatentNeedProvider, IOfferProvider + Mockups - Services: inquiryService, latentNeedService, offerService - Hooks: useInquiries, useLatentNeeds, useOffers (TanStack Query) - Store: offerWizardStore (Zustand, lokaler Wizard-State) - 27 neue Komponenten, alle Loading/Empty/Error States Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
112 lines
3.5 KiB
TypeScript
112 lines
3.5 KiB
TypeScript
import { Box, Button, CircularProgress, Typography } from '@mui/material'
|
|
import { ArrowRight, Building2, Calendar, MapPin, Ruler } from 'lucide-react'
|
|
import { useNavigate } from 'react-router'
|
|
import { usePropertyById } from '../../hooks/useProperties'
|
|
|
|
interface RelatedPropertyCardPanelProps {
|
|
propertyId: string
|
|
}
|
|
|
|
export function RelatedPropertyCardPanel({ propertyId }: RelatedPropertyCardPanelProps) {
|
|
const { data: property, isLoading } = usePropertyById(propertyId)
|
|
const navigate = useNavigate()
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', p: 4 }}>
|
|
<CircularProgress size={20} />
|
|
</Box>
|
|
)
|
|
}
|
|
|
|
if (!property) {
|
|
return (
|
|
<Box sx={{ p: 2 }}>
|
|
<Typography variant="body2" color="text.secondary">
|
|
Objekt nicht gefunden
|
|
</Typography>
|
|
</Box>
|
|
)
|
|
}
|
|
|
|
const image = property.images?.[0]
|
|
|
|
return (
|
|
<Box
|
|
sx={{
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: 1.5,
|
|
p: 2,
|
|
borderLeft: '1px solid #e2e8f0',
|
|
bgcolor: '#f8fafc',
|
|
height: '100%',
|
|
overflowY: 'auto',
|
|
}}
|
|
>
|
|
<Typography variant="caption" sx={{ color: '#64748b', fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5 }}>
|
|
Bezogenes Objekt
|
|
</Typography>
|
|
|
|
<Box
|
|
sx={{
|
|
height: 140,
|
|
borderRadius: 1.5,
|
|
overflow: 'hidden',
|
|
bgcolor: '#e2e8f0',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
backgroundImage: image ? `url(${image})` : 'none',
|
|
backgroundSize: 'cover',
|
|
backgroundPosition: 'center',
|
|
}}
|
|
>
|
|
{!image && <Building2 size={32} color="#94a3b8" />}
|
|
</Box>
|
|
|
|
<Typography variant="body1" sx={{ fontWeight: 600, color: '#0f172a', fontSize: '0.95rem' }}>
|
|
{property.title}
|
|
</Typography>
|
|
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.75 }}>
|
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, color: '#475569', fontSize: '0.8125rem' }}>
|
|
<MapPin size={14} />
|
|
<Typography variant="body2" sx={{ fontSize: '0.8125rem' }}>
|
|
{property.location.city}
|
|
{property.location.district ? `, ${property.location.district}` : ''}
|
|
</Typography>
|
|
</Box>
|
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, color: '#475569', fontSize: '0.8125rem' }}>
|
|
<Ruler size={14} />
|
|
<Typography variant="body2" sx={{ fontSize: '0.8125rem' }}>
|
|
{property.areaSqm.toLocaleString('de-CH')} m² · CHF {property.rentPricePerSqm}/m²
|
|
</Typography>
|
|
</Box>
|
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, color: '#475569', fontSize: '0.8125rem' }}>
|
|
<Calendar size={14} />
|
|
<Typography variant="body2" sx={{ fontSize: '0.8125rem' }}>
|
|
Verfügbar ab {new Date(property.availabilityDate).toLocaleDateString('de-CH')}
|
|
</Typography>
|
|
</Box>
|
|
</Box>
|
|
|
|
{property.description && (
|
|
<Typography variant="body2" sx={{ color: '#64748b', fontSize: '0.8125rem', lineHeight: 1.5 }}>
|
|
{property.description}
|
|
</Typography>
|
|
)}
|
|
|
|
<Button
|
|
variant="outlined"
|
|
size="small"
|
|
endIcon={<ArrowRight size={14} />}
|
|
onClick={() => navigate('/supply/properties')}
|
|
sx={{ textTransform: 'none', mt: 'auto' }}
|
|
>
|
|
Objekt ansehen
|
|
</Button>
|
|
</Box>
|
|
)
|
|
}
|