import {
Alert,
Box,
Chip,
IconButton,
LinearProgress,
Skeleton,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Tooltip,
Typography,
} from '@mui/material'
import { Bookmark, Eye } from 'lucide-react'
import type { Property } from '../../domain/property'
import type { PropertyTableFilters } from './PropertyFilterBar'
import {
getAssetTypeColor,
getAssetTypeLabel,
qualityColor,
} from './propertyHelpers'
interface PropertyTableProps {
properties: Property[]
isLoading: boolean
isError: boolean
selectedId: string | null
onSelect: (id: string) => void
onViewDetail?: (id: string) => void
filters: PropertyTableFilters
onFiltersChange: (f: PropertyTableFilters) => void
}
const COL_HEADERS = [
'Objekt', 'Typ', 'Standort', 'Fläche', 'Miete CHF/m²/Jahr',
'Aktueller Mieter', 'Mietlaufzeit', 'Breakoutoption', 'Breakoutoption Zeitpunkt',
'Datenqualität', 'Aktionen',
]
function LoadingRows() {
return (
<>
{Array.from({ length: 5 }).map((_, i) => (
{COL_HEADERS.map((h) => (
))}
))}
>
)
}
export function PropertyTable({
properties,
isLoading,
isError,
selectedId,
onSelect,
onViewDetail,
filters,
onFiltersChange,
}: PropertyTableProps) {
if (isError) {
return Objekte konnten nicht geladen werden.
}
function handleSort(field: PropertyTableFilters['sortBy']) {
if (filters.sortBy === field) {
onFiltersChange({ ...filters, sortDir: filters.sortDir === 'asc' ? 'desc' : 'asc' })
} else {
onFiltersChange({ ...filters, sortBy: field, sortDir: 'desc' })
}
}
function SortableHeader({ field, label }: { field: PropertyTableFilters['sortBy']; label: string }) {
const isActive = filters.sortBy === field
return (
handleSort(field)}
>
{label}{isActive ? (filters.sortDir === 'asc' ? ' ↑' : ' ↓') : ''}
)
}
return (
Objekt
Typ
Standort
Aktueller Mieter
Mietlaufzeit
Breakoutoption
Breakoutoption Zeitpunkt
Aktionen
{isLoading ? (
) : properties.length === 0 ? (
Keine Objekte gefunden.
) : (
properties.map(p => {
const isSelected = p.id === selectedId
const qScore = p.dataQuality.score
const hasCritical = p.dataQuality.missingCriticalFields.length > 0
return (
onSelect(p.id)}
sx={{
cursor: 'pointer',
bgcolor: isSelected
? 'rgba(30,58,95,0.06)'
: hasCritical
? 'rgba(192,57,43,0.03)'
: 'inherit',
borderLeft: isSelected ? '3px solid #1e3a5f' : '3px solid transparent',
'&:hover': { bgcolor: isSelected ? 'rgba(30,58,95,0.08)' : 'rgba(0,0,0,0.02)' },
}}
>
{/* Objekt */}
{p.title}
{p.address.street} {p.address.houseNumber}, {p.address.city}
{/* Typ */}
{/* Standort */}
{p.location.city}
{p.location.canton && (
{p.location.canton}
)}
{/* Fläche */}
{p.areaSqm.toLocaleString('de-CH')}
{/* Miete */}
{p.rentPricePerSqm > 0 ? `CHF ${p.rentPricePerSqm.toLocaleString('de-CH')}` : k.A.}
{/* Aktueller Mieter */}
{p.currentTenant ?? –}
{/* Mietlaufzeit */}
{p.leaseTerm ?? –}
{/* Breakoutoption */}
{p.breakoutOption == null
? –
:
}
{/* Breakoutoption Zeitpunkt */}
{p.breakoutOptionDate
? new Date(p.breakoutOptionDate).toLocaleDateString('de-CH')
: –
}
{/* Datenqualität */}
{Math.round(qScore * 100)}%
{/* Aktionen */}
e.stopPropagation()}>
onViewDetail ? onViewDetail(p.id) : onSelect(p.id)}>
)
})
)}
)
}