feat: full responsive layout — mobile sidebar, stacked panels, fullscreen drawers

- AppShell: permanent sidebar on desktop, temporary drawer + hamburger on mobile (<md)
- TopBar: hamburger icon on mobile, AI-Assistent als Icon-Button auf kleinen Screens
- MatchDetail: Sidebar stacks below content on mobile, key facts wrap, px responsive
- Properties: Drawer fullscreen (100vw) on mobile, 650px on desktop
- ActiveInquiriesTab: mobile shows list → tap opens detail with back button
- LatentInquiriesTab: mobile sequential navigation (list → detail → own properties), tablet 2-col, desktop 3-col
- PublicNeedList: fullWidth prop for mobile layout
- InquiryDetailPanel: property card stacks below chat on mobile

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-18 21:24:00 +02:00
parent 259b4e3c68
commit a75818b983
7 changed files with 220 additions and 105 deletions
+9 -8
View File
@@ -207,8 +207,8 @@ export default function MatchDetail() {
{/* Property header — white section below hero */}
<Box sx={{ bgcolor: 'white', borderBottom: '1px solid #e2e8f0' }}>
<Box sx={{ px: 3, pt: 2.5, pb: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 2 }}>
<Box sx={{ px: { xs: 2, sm: 3 }, pt: 2.5, pb: 2 }}>
<Box sx={{ display: 'flex', flexDirection: { xs: 'column', sm: 'row' }, alignItems: { xs: 'flex-start', sm: 'flex-start' }, justifyContent: 'space-between', gap: 2 }}>
{/* Left: title + address + chips */}
<Box sx={{ flex: 1, minWidth: 0 }}>
<Typography variant="h5" sx={{ fontWeight: 700, mb: 0.25, lineHeight: 1.3 }}>
@@ -264,12 +264,13 @@ export default function MatchDetail() {
{/* Key facts strip */}
<Divider />
<Box sx={{ display: 'flex', px: 3, py: 1.5, gap: 0 }}>
<Box sx={{ display: 'flex', flexWrap: 'wrap', px: { xs: 2, sm: 3 }, py: 1.5, gap: 0 }}>
{keyFacts.map((fact, i) => (
<Box key={fact.label} sx={{
flex: 1,
pl: i === 0 ? 0 : 2,
pr: 2,
flex: '1 1 120px',
pl: i === 0 ? 0 : { xs: 1.5, sm: 2 },
pr: { xs: 1.5, sm: 2 },
py: { xs: 0.5, sm: 0 },
borderLeft: i === 0 ? 'none' : '1px solid #e2e8f0',
}}>
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', lineHeight: 1.2 }}>
@@ -284,7 +285,7 @@ export default function MatchDetail() {
</Box>
{/* Main content */}
<Box sx={{ px: 3, py: 3, display: 'flex', gap: 3, alignItems: 'flex-start' }}>
<Box sx={{ px: { xs: 1.5, sm: 3 }, py: { xs: 2, sm: 3 }, display: 'flex', flexDirection: { xs: 'column', lg: 'row' }, gap: 3, alignItems: 'flex-start' }}>
{/* Main column */}
<Box sx={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 2 }}>
@@ -340,7 +341,7 @@ export default function MatchDetail() {
</Box>
{/* Sidebar */}
<Box sx={{ width: 320, flexShrink: 0, position: 'sticky', top: 64, display: 'flex', flexDirection: 'column', gap: 2 }}>
<Box sx={{ width: { xs: '100%', lg: 320 }, flexShrink: 0, position: { xs: 'static', lg: 'sticky' }, top: 64, display: 'flex', flexDirection: 'column', gap: 2 }}>
<ScoreBreakdownPanel match={match} taxCalculatorUrl={taxCalculatorUrl} />
<NextActionsPanel
match={match}
+4 -2
View File
@@ -1,5 +1,5 @@
import { useState } from 'react'
import { Box, Drawer } from '@mui/material'
import { Box, Drawer, useMediaQuery, useTheme } from '@mui/material'
import { useNavigate } from 'react-router'
import { PageHeader } from '../../components/layout'
import { DecisionContextPanel } from '../../components/ui'
@@ -49,6 +49,8 @@ function applyFilters(properties: Property[], filters: PropertyTableFilters): Pr
export default function Properties() {
const navigate = useNavigate()
const theme = useTheme()
const isMobile = useMediaQuery(theme.breakpoints.down('md'))
const [selectedId, setSelectedId] = useState<string | null>(null)
const [filters, setFilters] = useState<PropertyTableFilters>({})
const [view, setView] = useState<'list' | 'grid'>(() =>
@@ -155,7 +157,7 @@ export default function Properties() {
anchor="right"
open={!!selectedId}
onClose={() => setSelectedId(null)}
slotProps={{ paper: { sx: { width: 650, boxShadow: '-4px 0 24px rgba(0,0,0,0.10)' } } }}
slotProps={{ paper: { sx: { width: isMobile ? '100vw' : 650, boxShadow: '-4px 0 24px rgba(0,0,0,0.10)' } } }}
>
{selectedId && (
<PropertyDetailView propertyId={selectedId} onClose={() => setSelectedId(null)} />