feat: Verwaltungszugang refactor — lease data, 3-tab detail, market signals

- Dashboard: remove QuickActionPanel, StrongMatchOverview, FutureSignalWidget,
  DataQualityWidget, header buttons, Marktchancen nav; KpiGrid → 4 cards
- Property domain: 9 new fields (leaseTerm, leaseStartDate/End, breakoutOption,
  currentTenant, importedFrom, importedAt, lastUpdatedAt)
- Mock data: annual CHF/m²/Jahr prices (×12), Swiss images, tenant/lease data
  for all 10 VERIFIED_PORTFOLIO properties; need budgets updated to annual
- PropertyTable: swap columns — add Aktueller Mieter, Mietlaufzeit,
  Breakoutoption, Breakoutoption Zeitpunkt; remove Verfügbarkeit, Konfidenz, Quelle
- PropertyDetailView: 3 tabs (Übersicht, Matchability, Marktsignale) with
  inline edit mode, NeedMatchCard list, PropertyMarketSignalsTab
- New: marketReport domain + mock data + service, NeedMatchCard,
  PropertyMarketSignalsTab with 4 sections + PDF button
- matchService: getNeedMatchesForProperty(propertyId, {minScore})

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-19 15:43:52 +02:00
parent 52a2693cad
commit 920ce8eb09
17 changed files with 1036 additions and 476 deletions
+12 -25
View File
@@ -1,5 +1,4 @@
import { Box, Button, Chip, Typography } from '@mui/material'
import { useNavigate } from 'react-router'
import { Box, Chip, Typography } from '@mui/material'
interface DashboardHeaderProps {
orgName?: string
@@ -18,31 +17,19 @@ function formatLastUpdated(iso: string): string {
}
export function DashboardHeader({ orgName = 'Demo Organisation', lastUpdated }: DashboardHeaderProps) {
const navigate = useNavigate()
return (
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
<Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 0.5 }}>
<Typography variant="h6" sx={{ fontWeight: 600 }}>
{orgName}
</Typography>
<Chip label="Demo" size="small" color="info" variant="outlined" />
</Box>
{lastUpdated && (
<Typography variant="caption" sx={{ color: 'text.secondary' }}>
Zuletzt aktualisiert: {formatLastUpdated(lastUpdated)}
</Typography>
)}
</Box>
<Box sx={{ display: 'flex', gap: 1 }}>
<Button variant="contained" size="small" onClick={() => navigate('/supply/match-center')}>
Match Center
</Button>
<Button variant="outlined" size="small" onClick={() => navigate('/supply/data-quality')}>
Datenqualität
</Button>
<Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 0.5 }}>
<Typography variant="h6" sx={{ fontWeight: 600 }}>
{orgName}
</Typography>
<Chip label="Demo" size="small" color="info" variant="outlined" />
</Box>
{lastUpdated && (
<Typography variant="caption" sx={{ color: 'text.secondary' }}>
Zuletzt aktualisiert: {formatLastUpdated(lastUpdated)}
</Typography>
)}
</Box>
)
}