diff --git a/src/components/PageHeader.jsx b/src/components/PageHeader.jsx
index 2cc3a61..0a616e9 100644
--- a/src/components/PageHeader.jsx
+++ b/src/components/PageHeader.jsx
@@ -36,7 +36,7 @@ export default function PageHeader({ title, breadcrumbs = [], action }) {
)}
- {action && {action}}
+ {action && {action}}
);
}
diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx
index 3c95b0c..3446a83 100644
--- a/src/pages/Dashboard.jsx
+++ b/src/pages/Dashboard.jsx
@@ -1,8 +1,9 @@
import { useState, useEffect, useMemo } from 'react';
import {
Grid, Card, Box, Stack, Typography, Button, Divider, LinearProgress, CircularProgress, Alert,
- Table, TableBody, TableCell, TableHead, TableRow, TableContainer
+ Table, TableBody, TableCell, TableHead, TableRow, TableContainer, useMediaQuery
} from '@mui/material';
+import { useTheme } from '@mui/material/styles';
import RefreshIcon from '@mui/icons-material/Refresh';
import ApartmentOutlinedIcon from '@mui/icons-material/ApartmentOutlined';
import FiberNewOutlinedIcon from '@mui/icons-material/FiberNewOutlined';
@@ -68,7 +69,7 @@ function Panel({ icon: Icon, title, action, color = 'primary', noPadding = false
`linear-gradient(90deg, ${theme.palette[color].lighter}66 0%, transparent 100%)`
}}
>
@@ -78,12 +79,14 @@ function Panel({ icon: Icon, title, action, color = 'primary', noPadding = false
{title}
{action}
- {children}
+ {children}
);
}
export default function Dashboard() {
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
const [clients, setClients] = useState([]);
const [team, setTeam] = useState([]);
const [loading, setLoading] = useState(true);
@@ -161,6 +164,21 @@ export default function Dashboard() {
{recent.length === 0 ? (
+ ) : isMobile ? (
+ }>
+ {recent.map((c) => (
+
+
+
+ {c.name}
+
+ {[titleCase(c.businessType), c.city].filter(Boolean).join(' · ') || '—'} · {c.parcelVolume.toLocaleString('en-IN')} parcels
+
+
+
+
+ ))}
+
) : (
diff --git a/src/pages/Settings.jsx b/src/pages/Settings.jsx
index c5253f5..c325fe0 100644
--- a/src/pages/Settings.jsx
+++ b/src/pages/Settings.jsx
@@ -71,7 +71,7 @@ function Section({ icon: Icon, title, subtitle, color = 'primary', danger = fals
`linear-gradient(90deg, ${theme.palette[color].lighter}66 0%, ${theme.palette.background.paper} 72%)`
}}
>
@@ -83,7 +83,7 @@ function Section({ icon: Icon, title, subtitle, color = 'primary', danger = fals
{subtitle && {subtitle}}
- {children}
+ {children}
);
}
@@ -155,10 +155,13 @@ export default function Settings() {
title="Settings"
breadcrumbs={[{ label: 'Settings' }]}
action={
-
+
{dirty && }
-
- } onClick={save}>Save Changes
+
+ } onClick={save} sx={{ flex: { xs: 1, sm: 'none' } }}>Save Changes
}
/>
diff --git a/src/pages/team/TeamUsers.jsx b/src/pages/team/TeamUsers.jsx
index 1f23716..dc3e36d 100644
--- a/src/pages/team/TeamUsers.jsx
+++ b/src/pages/team/TeamUsers.jsx
@@ -2,9 +2,10 @@ import { useState, useMemo, useEffect } from 'react';
import {
Card, Stack, Button, TextField, InputAdornment, Box, Tabs, Tab, Chip, Link,
Table, TableBody, TableCell, TableContainer, TableHead, TableRow, IconButton,
- TablePagination, Typography, CircularProgress, Alert, Tooltip,
+ TablePagination, Typography, CircularProgress, Alert, Tooltip, Divider, useMediaQuery,
Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions
} from '@mui/material';
+import { useTheme } from '@mui/material/styles';
import SearchIcon from '@mui/icons-material/Search';
import AddIcon from '@mui/icons-material/Add';
import RefreshIcon from '@mui/icons-material/Refresh';
@@ -94,7 +95,51 @@ function RoleCell({ role }) {
);
}
+// Mobile presentation of a user row — a self-contained card instead of a wide table row.
+function UserCard({ row, index, onEdit, onDelete }) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {row.email ? (
+
+ {row.email}
+
+ ) : No email}
+
+
+
+ {row.phone ? (
+
+ {row.phone}
+
+ ) : No phone}
+
+
+
+
+
+ #{index}
+
+ } onClick={() => onEdit(row)}>Edit
+ } onClick={() => onDelete(row)}>Delete
+
+
+
+ );
+}
+
export default function TeamUsers() {
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
@@ -167,9 +212,9 @@ export default function TeamUsers() {
title="App Users"
breadcrumbs={[{ label: 'App Users' }]}
action={
-
- } onClick={load} disabled={loading}>Refresh
- } onClick={() => setDialog({ open: true, mode: 'add', initial: null })}>Add User
+
+ } onClick={load} disabled={loading} sx={{ flex: { xs: 1, sm: 'none' } }}>Refresh
+ } onClick={() => setDialog({ open: true, mode: 'add', initial: null })} sx={{ flex: { xs: 1, sm: 'none' } }}>Add User
}
/>
@@ -177,7 +222,7 @@ export default function TeamUsers() {
`linear-gradient(90deg, ${theme.palette.primary.lighter}66 0%, ${theme.palette.background.paper} 70%)`
}}
@@ -191,10 +236,10 @@ export default function TeamUsers() {
-
+
{ setSearch(e.target.value); setPage(0); }}
- sx={{ minWidth: 300 }}
+ sx={{ width: { xs: '100%', md: 300 } }}
InputProps={{ startAdornment: }}
/>
@@ -218,33 +263,37 @@ export default function TeamUsers() {
{error && Retry}>{error}}
-
-
-
-
- S.No
- User
- Email
- Phone
- Role
- Actions
-
-
-
- {loading ? (
-
-
-
-
+ {loading ? (
+
+ ) : paged.length === 0 ? (
+
+ ) : isMobile ? (
+
+ {paged.map((row, i) => (
+ setDialog({ open: true, mode: 'edit', initial: r })}
+ onDelete={(r) => setToDelete(r)}
+ />
+ ))}
+
+ ) : (
+
+
+
+
+ S.No
+ User
+ Email
+ Phone
+ Role
+ Actions
- ) : paged.length === 0 ? (
-
-
-
-
-
- ) : (
- paged.map((row, i) => (
+
+
+ {paged.map((row, i) => (
{page * rpp + i + 1}
@@ -268,11 +317,11 @@ export default function TeamUsers() {
setToDelete(row)}>
- ))
- )}
-
-
-
+ ))}
+
+
+
+ )}
setPage(p)}
rowsPerPage={rpp} onRowsPerPageChange={(e) => { setRpp(+e.target.value); setPage(0); }} rowsPerPageOptions={[5, 10, 25]}
diff --git a/src/pages/team/UserFormDialog.jsx b/src/pages/team/UserFormDialog.jsx
index 67bc237..bad3693 100644
--- a/src/pages/team/UserFormDialog.jsx
+++ b/src/pages/team/UserFormDialog.jsx
@@ -1,8 +1,9 @@
import { useState, useEffect } from 'react';
import {
Dialog, DialogTitle, DialogContent, DialogActions, Button, Grid, TextField,
- MenuItem, Alert, CircularProgress, IconButton
+ MenuItem, Alert, CircularProgress, IconButton, useMediaQuery
} from '@mui/material';
+import { useTheme } from '@mui/material/styles';
import CloseIcon from '@mui/icons-material/Close';
import { createPoint, setPayload, COLLECTIONS } from '@/utils/qdrant';
@@ -14,6 +15,8 @@ const withValue = (opts, v) => (v && !opts.includes(v) ? [v, ...opts] : opts);
export default function UserFormDialog({ open, mode, initial, onClose, onSaved }) {
const isEdit = mode === 'edit';
+ const theme = useTheme();
+ const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
const [form, setForm] = useState(EMPTY);
const [saving, setSaving] = useState(false);
const [error, setError] = useState(null);
@@ -56,7 +59,7 @@ export default function UserFormDialog({ open, mode, initial, onClose, onSaved }
};
return (
-
+
+
+
+
+
+ } onClick={() => onEdit(row)}>Edit
+ } onClick={() => onDelete(row)}>Delete
+
+
+
+
+
+
+
+
+
+ );
+}
+
function ClientRow({ row, index, onEdit, onDelete }) {
const [open, setOpen] = useState(false);
@@ -254,145 +451,7 @@ function ClientRow({ row, index, onEdit, onDelete }) {
-
- `linear-gradient(90deg, ${theme.palette.primary.lighter}88 0%, ${theme.palette.background.paper} 75%)`
- }}
- >
-
-
-
- {row.name}
-
-
- {row.clientId}
-
-
-
-
- } onClick={() => onEdit(row)}>Edit Client
-
-
-
-
-
-
-
-
-
-
-
- {row.clientId}
-
-
-
-
-
-
-
-
-
- {row.logisticsSegment ? (
-
- {String(row.logisticsSegment).split(/[,/]/).map((seg) => seg.trim()).filter(Boolean).map((seg) => (
-
- ))}
-
- ) : }
-
-
-
-
- {(() => {
- const stops = [row.transitFrom, ...String(row.transitTo || '').split(',')]
- .map((s) => s.trim()).filter(Boolean);
- if (!stops.length) return ;
- return (
-
- {stops.map((stop, idx) => (
-
- {idx > 0 && }
-
-
- ))}
-
- );
- })()}
-
-
-
-
-
-
-
- {[row.city, row.businessState].filter(Boolean).join(', ') || '—'}
-
-
-
-
-
- {row.surveyAddress || '—'}
-
-
-
- {row.surveyLat && row.surveyLng ? (
-
-
-
-
-
- {[row.neighbourhood, row.city].filter(Boolean).join(', ') || 'Pinned location'}
-
-
-
- }
- sx={{
- py: 0.25, px: 1.25, minWidth: 0, fontSize: '0.75rem', fontWeight: 600, borderRadius: 5,
- color: 'primary.main', borderColor: 'primary.100', bgcolor: 'primary.lighter',
- '&:hover': { borderColor: 'primary.main', bgcolor: 'primary.lighter' }
- }}
- >
- View on map
-
-
- ) : —}
-
-
-
-
-
-
-
-
-
-
- {row.notes && (
-
-
-
-
- Notes
-
- {row.notes}
-
-
- )}
-
-
-
-
-
-
+
@@ -401,6 +460,8 @@ function ClientRow({ row, index, onEdit, onDelete }) {
}
export default function Tenants() {
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const [clients, setClients] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
@@ -489,9 +550,9 @@ export default function Tenants() {
title="Clients"
breadcrumbs={[{ label: 'Clients' }]}
action={
-
- } onClick={load} disabled={loading}>Refresh
- } onClick={() => setDialog({ open: true, mode: 'add', initial: null })}>Add Client
+
+ } onClick={load} disabled={loading} sx={{ flex: { xs: 1, sm: 'none' } }}>Refresh
+ } onClick={() => setDialog({ open: true, mode: 'add', initial: null })} sx={{ flex: { xs: 1, sm: 'none' } }}>Add Client
}
/>
@@ -523,7 +584,7 @@ export default function Tenants() {
{ setSearch(e.target.value); setPage(0); }}
- sx={{ minWidth: 300 }}
+ sx={{ width: { xs: '100%', md: 300 } }}
InputProps={{ startAdornment: }}
/>
@@ -547,35 +608,38 @@ export default function Tenants() {
{error && Retry}>{error}}
-
-
-
-
-
- ID
- Client
- Contact
- Location
- Volume
- Status
- Actions
-
-
-
- {loading ? (
-
-
-
-
+ {loading ? (
+
+ ) : paged.length === 0 ? (
+
+ ) : isMobile ? (
+
+ {paged.map((row) => (
+ setDialog({ open: true, mode: 'edit', initial: r })}
+ onDelete={(r) => setToDelete(r)}
+ />
+ ))}
+
+ ) : (
+
+
+
+
+
+ ID
+ Client
+ Contact
+ Location
+ Volume
+ Status
+ Actions
- ) : paged.length === 0 ? (
-
-
-
-
-
- ) : (
- paged.map((row, i) => (
+
+
+ {paged.map((row, i) => (
setDialog({ open: true, mode: 'edit', initial: r })}
onDelete={(r) => setToDelete(r)}
/>
- ))
- )}
-
-
-
+ ))}
+
+
+
+ )}
setPage(p)}
rowsPerPage={rpp} onRowsPerPageChange={(e) => { setRpp(+e.target.value); setPage(0); }} rowsPerPageOptions={[5, 10, 25]}