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:
@@ -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}
|
||||
|
||||
@@ -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)} />
|
||||
|
||||
Reference in New Issue
Block a user