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:
@@ -1,4 +1,4 @@
|
||||
import { Box, Alert, Button, Typography } from '@mui/material'
|
||||
import { Box, Button, Typography } from '@mui/material'
|
||||
import { useNavigate } from 'react-router'
|
||||
import { useSupplyDashboard } from '../../hooks/useSupplyDashboard'
|
||||
import { useSessionStore } from '../../stores/sessionStore'
|
||||
@@ -6,21 +6,10 @@ import { UserRole } from '../../domain/enums'
|
||||
import {
|
||||
DashboardHeader,
|
||||
KpiGrid,
|
||||
StrongMatchOverview,
|
||||
DataQualityWidget,
|
||||
FutureSignalWidget,
|
||||
ReviewTaskWidget,
|
||||
QuickActionPanel,
|
||||
DashboardSkeleton,
|
||||
} from '../../components/supply'
|
||||
|
||||
function WidgetError({ label }: { label: string }) {
|
||||
return (
|
||||
<Alert severity="error" sx={{ height: '100%' }}>
|
||||
{label} konnte nicht geladen werden.
|
||||
</Alert>
|
||||
)
|
||||
}
|
||||
|
||||
export default function SupplyDashboard() {
|
||||
const { data, isLoading, isError, refetch } = useSupplyDashboard()
|
||||
@@ -30,11 +19,6 @@ export default function SupplyDashboard() {
|
||||
const role = currentUser?.role ?? UserRole.DEMAND_USER
|
||||
const isReviewer = role === UserRole.REVIEWER
|
||||
const isOwnerViewer = role === UserRole.OWNER_VIEWER
|
||||
const canSeeMatches = role !== UserRole.OWNER_VIEWER && role !== UserRole.DEMAND_USER
|
||||
const canSeeOperations =
|
||||
role === UserRole.SUPER_ADMIN ||
|
||||
role === UserRole.ORGANIZATION_ADMIN ||
|
||||
role === UserRole.REVIEWER
|
||||
|
||||
if (isLoading) return <DashboardSkeleton />
|
||||
|
||||
@@ -98,91 +82,12 @@ export default function SupplyDashboard() {
|
||||
<KpiGrid data={data} />
|
||||
)}
|
||||
|
||||
{/* REVIEWER: Review Queue first, then Matches */}
|
||||
{isReviewer && (
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 3 }}>
|
||||
{data.reviewTasks !== null ? (
|
||||
<ReviewTaskWidget
|
||||
tasks={data.reviewTasks}
|
||||
onNavigate={() => navigate('/ops/review-queue')}
|
||||
/>
|
||||
) : (
|
||||
<WidgetError label="Review Tasks" />
|
||||
)}
|
||||
{canSeeMatches &&
|
||||
(data.strongMatches !== null ? (
|
||||
<StrongMatchOverview
|
||||
matches={data.strongMatches}
|
||||
onNavigate={() => navigate('/supply/match-center')}
|
||||
/>
|
||||
) : (
|
||||
<WidgetError label="Starke Matches" />
|
||||
))}
|
||||
</Box>
|
||||
{isReviewer && data.reviewTasks !== null && (
|
||||
<ReviewTaskWidget
|
||||
tasks={data.reviewTasks}
|
||||
onNavigate={() => navigate('/ops/review-queue')}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Default: Matches + Data Quality */}
|
||||
{!isReviewer && canSeeMatches && (
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 3 }}>
|
||||
{data.strongMatches !== null ? (
|
||||
<StrongMatchOverview
|
||||
matches={data.strongMatches}
|
||||
onNavigate={() => navigate('/supply/match-center')}
|
||||
/>
|
||||
) : (
|
||||
<WidgetError label="Starke Matches" />
|
||||
)}
|
||||
{data.dataQuality !== null ? (
|
||||
<DataQualityWidget
|
||||
summary={data.dataQuality}
|
||||
onNavigate={() => navigate('/supply/data-quality')}
|
||||
/>
|
||||
) : (
|
||||
<WidgetError label="Datenqualität" />
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{canSeeMatches && data.strongMatches?.length === 0 && (
|
||||
<Alert
|
||||
severity="info"
|
||||
action={
|
||||
<Button size="small" onClick={() => navigate('/demand/ai-search')}>
|
||||
Bedarfsprofil erstellen
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
Noch keine Matches vorhanden. Erstellen Sie ein Bedarfsprofil, um passende Objekte zu
|
||||
finden.
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{!isOwnerViewer && (
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 3 }}>
|
||||
{data.futureSignals !== null ? (
|
||||
<FutureSignalWidget summary={data.futureSignals} />
|
||||
) : (
|
||||
<WidgetError label="Marktsignale" />
|
||||
)}
|
||||
{canSeeOperations && !isReviewer ? (
|
||||
data.reviewTasks !== null ? (
|
||||
<ReviewTaskWidget
|
||||
tasks={data.reviewTasks}
|
||||
onNavigate={() => navigate('/ops/review-queue')}
|
||||
/>
|
||||
) : (
|
||||
<WidgetError label="Review Tasks" />
|
||||
)
|
||||
) : !canSeeOperations && data.dataQuality !== null ? (
|
||||
<DataQualityWidget
|
||||
summary={data.dataQuality}
|
||||
onNavigate={() => navigate('/supply/data-quality')}
|
||||
/>
|
||||
) : null}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<QuickActionPanel />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user