import { Box, Card, Chip, IconButton, LinearProgress, MenuItem, Select, Table, TableBody, TableCell, TableHead, TableRow, TextField, Tooltip, Typography, } from '@mui/material' import { useQuery } from '@tanstack/react-query' import { Eye, MoreHorizontal, Plus } from 'lucide-react' import { useState } from 'react' import { EmptyState, ErrorState, LoadingPage } from '../../components/ui' import { propertyService } from '../../services/propertyService' import { AssetType, AvailabilityStatus, ResultType } from '../../domain/enums' function getAssetTypeLabel(type: AssetType): string { switch (type) { case AssetType.OFFICE: return 'Büro' case AssetType.LOGISTICS: return 'Logistik' case AssetType.RETAIL: return 'Retail' case AssetType.GASTRO: return 'Gastro' case AssetType.PRODUCTION: return 'Produktion' case AssetType.MIXED: return 'Gemischt' case AssetType.LIGHT_INDUSTRIAL: return 'Leichtindustrie' case AssetType.UNKNOWN: return 'Unbekannt' } } function getAssetTypeColor(type: AssetType): string { switch (type) { case AssetType.OFFICE: return '#1e3a5f' case AssetType.LOGISTICS: return '#d97706' case AssetType.RETAIL: return '#7c3aed' case AssetType.GASTRO: return '#0d9488' case AssetType.PRODUCTION: return '#92400e' case AssetType.MIXED: return '#6b7280' case AssetType.LIGHT_INDUSTRIAL: return '#b45309' case AssetType.UNKNOWN: return '#9ca3af' } } function getAvailabilityLabel(status: AvailabilityStatus): string { switch (status) { case AvailabilityStatus.AVAILABLE_NOW: return 'Verfügbar' case AvailabilityStatus.AVAILABLE_SOON: return 'Bald verfügbar' case AvailabilityStatus.FUTURE_SIGNAL: return 'Zukunftssignal' case AvailabilityStatus.OCCUPIED: return 'Belegt' case AvailabilityStatus.UNKNOWN: return 'Unbekannt' } } function getAvailabilityColor(status: AvailabilityStatus): 'success' | 'warning' | 'secondary' | 'error' | 'default' { switch (status) { case AvailabilityStatus.AVAILABLE_NOW: return 'success' case AvailabilityStatus.AVAILABLE_SOON: return 'warning' case AvailabilityStatus.FUTURE_SIGNAL: return 'secondary' case AvailabilityStatus.OCCUPIED: return 'error' case AvailabilityStatus.UNKNOWN: return 'default' } } function getResultTypeLabel(type: ResultType): string { switch (type) { case ResultType.VERIFIED_PORTFOLIO: return 'Verified Portfolio' case ResultType.EXTERNAL_MARKET: return 'Marktinserat' case ResultType.FUTURE_AVAILABILITY: return 'Zukunftssignal' } } function getResultTypeColor(type: ResultType): string { switch (type) { case ResultType.VERIFIED_PORTFOLIO: return '#1e3a5f' case ResultType.EXTERNAL_MARKET: return '#d97706' case ResultType.FUTURE_AVAILABILITY: return '#7c3aed' } } function getConfidenceColor(score: number): string { if (score >= 0.85) return '#1a7a4a' if (score >= 0.65) return '#1e3a5f' return '#d97706' } function getQualityColor(score: number): 'success' | 'warning' | 'error' { if (score >= 0.8) return 'success' if (score >= 0.6) return 'warning' return 'error' } export default function Properties() { const [selectedResultType, setSelectedResultType] = useState('ALL') const [selectedAssetType, setSelectedAssetType] = useState('ALL') const [searchQuery, setSearchQuery] = useState('') const { data: resp, isLoading, error } = useQuery({ queryKey: ['properties'], queryFn: () => propertyService.getAll(), }) if (isLoading) return if (error) return const properties = resp?.data ?? [] const filtered = properties.filter(p => { if (selectedResultType !== 'ALL' && p.resultType !== selectedResultType) return false if (selectedAssetType !== 'ALL' && p.assetType !== selectedAssetType) return false if (searchQuery) { const q = searchQuery.toLowerCase() const matchesTitle = p.title.toLowerCase().includes(q) const matchesCity = p.location.city.toLowerCase().includes(q) const matchesStreet = p.address.street.toLowerCase().includes(q) if (!matchesTitle && !matchesCity && !matchesStreet) return false } return true }) const sourceTypeFilters: { value: ResultType | 'ALL'; label: string; color: string }[] = [ { value: 'ALL', label: 'Alle', color: '#6b7280' }, { value: ResultType.VERIFIED_PORTFOLIO, label: 'Verified Portfolio', color: '#1e3a5f' }, { value: ResultType.EXTERNAL_MARKET, label: 'Marktinserate', color: '#d97706' }, { value: ResultType.FUTURE_AVAILABILITY, label: 'Zukunftssignale', color: '#7c3aed' }, ] return ( {/* Page Header */} Objekte {/* Content */} {/* Filter Bar */} {/* Row 1: Source type chips */} {sourceTypeFilters.map(f => ( setSelectedResultType(f.value)} sx={ selectedResultType === f.value ? { bgcolor: f.color, color: 'white', borderColor: f.color, fontWeight: 600, cursor: 'pointer' } : { borderColor: f.color, color: f.color, cursor: 'pointer' } } /> ))} {/* Row 2: Asset type select + search */} setSearchQuery(e.target.value)} placeholder="Suche nach Titel, Stadt, Strasse…" size="small" sx={{ ml: 'auto', minWidth: 260 }} /> {/* Properties Table */} {filtered.length === 0 ? ( ) : ( Objekt Typ Standort Fläche Miete/m² Quelle Konfidenz Datenqualität Verfügbarkeit Aktionen {filtered.map(property => { const hasCritical = property.dataQuality.missingCriticalFields.length > 0 return ( {/* Objekt */} {property.title} {property.address.street} {property.address.houseNumber}, {property.address.city} {/* Typ */} {/* Standort */} {property.location.city} {property.location.canton && ( {property.location.canton} )} {/* Fläche */} {property.areaSqm.toLocaleString('de-CH')} m² {/* Miete/m² */} CHF {property.rentPricePerSqm} {/* Quelle */} {/* Konfidenz */} {Math.round(property.confidenceScore * 100)}% {/* Datenqualität */} {property.dataQuality.missingCriticalFields.length > 0 && ( Kritische Felder fehlen: {property.dataQuality.missingCriticalFields.map(f => ( • {f} ))} )} {property.dataQuality.warnings.length > 0 && ( Warnungen: {property.dataQuality.warnings.map((w, i) => ( • {w} ))} )} {property.dataQuality.missingCriticalFields.length === 0 && property.dataQuality.warnings.length === 0 && ( Keine Probleme )} } > {Math.round(property.dataQuality.score * 100)}% {/* Verfügbarkeit */} {/* Aktionen */} ) })}
)}
) }