refactor: split AppShell, match-detail panels, extract useCompareData

AppShell.tsx: 627→116 lines
- appShellConfig.ts: NavItem/WorkspaceConfig types, WORKSPACE_CONFIG, nav helpers
- AppShellSidebar.tsx: Sidebar component with visual constants
- AppShellTopBar.tsx: TopBar component

Match-detail panels:
- ScoreBreakdownPanel: 377→303 lines (scoreBreakdownConstants.ts + CriterionRow.tsx extracted)
- LocationIntelligencePanel: 383→333 lines (SoftFactorBar.tsx extracted)
- FutureAvailabilityContextPanel: 386→351 lines (futureAvailabilityConstants.tsx extracted)

Compare.tsx: 485→441 lines
- useCompareData hook: all queries and derived state extracted to hooks/useCompareData.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-24 01:00:38 +02:00
parent ae82d0e6a0
commit 0bacd188d6
13 changed files with 792 additions and 741 deletions
@@ -1,4 +1,4 @@
import { Box, Chip, Divider, LinearProgress, Paper, Tooltip, Typography } from '@mui/material'
import { Box, Chip, Divider, Paper, Typography } from '@mui/material'
import {
Activity, Building2, HardHat, MapPin, Percent,
TrendingDown, TrendingUp, Train, Users, Zap,
@@ -7,60 +7,10 @@ import { useNavigate } from 'react-router'
import { useProperties } from '../../hooks/useProperties'
import { getCityIntelligence } from '../../lib/locationIntelligence'
import type { Property } from '../../domain/property'
// ── Helpers ───────────────────────────────────────────────────────────────────
function scoreColor(v: number) {
if (v >= 0.72) return '#1a7a4a'
if (v >= 0.48) return '#d97706'
return '#c0392b'
}
function scoreLabel(v: number) {
if (v >= 0.82) return 'Sehr gut'
if (v >= 0.65) return 'Gut'
if (v >= 0.45) return 'Mittel'
return 'Schwach'
}
import { SoftFactorBar } from './SoftFactorBar'
// ── Sub-components ────────────────────────────────────────────────────────────
function SoftFactorBar({
label,
value,
icon,
tooltip,
}: {
label: string
value: number | undefined | null
icon: React.ReactNode
tooltip?: string
}) {
if (value === undefined || value === null) return null
const color = scoreColor(value)
const row = (
<Box sx={{ mb: 1.5 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 0.4 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.75 }}>
<Box sx={{ color: '#64748b', display: 'flex' }}>{icon}</Box>
<Typography variant="body2">{label}</Typography>
</Box>
<Chip
label={scoreLabel(value)}
size="small"
sx={{ bgcolor: color, color: 'white', fontSize: 10, height: 18, fontWeight: 600 }}
/>
</Box>
<LinearProgress
variant="determinate"
value={value * 100}
sx={{ height: 5, borderRadius: 3, bgcolor: '#f1f5f9', '& .MuiLinearProgress-bar': { bgcolor: color } }}
/>
</Box>
)
return tooltip ? <Tooltip title={tooltip} placement="left">{row}</Tooltip> : row
}
function KpiTile({
label,
value,