update ui admin

This commit is contained in:
2026-06-11 20:17:18 +05:30
parent 4ad40b2c6d
commit 0736712464
51 changed files with 5466 additions and 1445 deletions

View File

@@ -1,225 +1,237 @@
import { Grid, Stack, Typography, Box, Button, Divider, Table, TableBody, TableCell, TableHead, TableRow, MenuItem, TextField, Avatar, LinearProgress, Chip } from '@mui/material';
import { useNavigate } from 'react-router-dom';
import { Grid, Stack, Typography, Box, Button, Divider, LinearProgress, Chip, Avatar } from '@mui/material';
import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined';
import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined';
import TwoWheelerOutlinedIcon from '@mui/icons-material/TwoWheelerOutlined';
import HubOutlinedIcon from '@mui/icons-material/HubOutlined';
import CurrencyRupeeIcon from '@mui/icons-material/CurrencyRupee';
import TaskAltOutlinedIcon from '@mui/icons-material/TaskAltOutlined';
import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
import ArrowRightAltRoundedIcon from '@mui/icons-material/ArrowRightAltRounded';
import ScheduleOutlinedIcon from '@mui/icons-material/ScheduleOutlined';
import AutoAwesomeOutlinedIcon from '@mui/icons-material/AutoAwesomeOutlined';
import EnergySavingsLeafOutlinedIcon from '@mui/icons-material/EnergySavingsLeafOutlined';
import RouteOutlinedIcon from '@mui/icons-material/RouteOutlined';
import SpeedOutlinedIcon from '@mui/icons-material/SpeedOutlined';
import ArrowForwardRoundedIcon from '@mui/icons-material/ArrowForwardRounded';
import PageHeader from '@/components/PageHeader';
import StatCard from '@/components/StatCard';
import KpiStrip from '@/components/KpiStrip';
import MainCard from '@/components/MainCard';
import StatusChip from '@/components/StatusChip';
import AreaChart from '@/components/charts/AreaChart';
import DonutChart from '@/components/charts/DonutChart';
import UserAvatar from '@/components/UserAvatar';
import SystemPipeline from '@/components/SystemPipeline';
import ThreeMileStrip from '@/components/ThreeMileStrip';
import ProcessTracker from '@/components/ProcessTracker';
import AiImpactSummary from '@/components/AiImpactSummary';
import Toast, { useToast } from '@/components/Toast';
import { ordersTrend, statusBreakdown, orders, riders, aiMetrics, fleetSummary, verticals, verticalOf } from '@/data/mock';
import { dispatchQueue, activeDeliveries, aiInsights, executionFeed, fleetSummary, lanePerformance, hubCityStats, ordersTrend, analyticsKpis } from '@/data/mock';
import { inr } from '@/utils/format';
const VERTICAL_COLOR = Object.fromEntries(verticals.map((v) => [v.label, v.color]));
const SEV_DOT = { high: '#F04134', medium: '#FFBF00', low: '#00A2AE', info: '#8C8C8C' };
const hubUtil = Math.round(hubCityStats.reduce((s, h) => s + h.utilization, 0) / hubCityStats.length);
function SectionLabel({ children }) {
return <Typography variant="overline" color="text.secondary" sx={{ letterSpacing: '0.08em', display: 'block', mb: 1.25 }}>{children}</Typography>;
}
export default function Dashboard() {
const navigate = useNavigate();
const [toast, showToast] = useToast();
const priority = activeDeliveries.filter((d) => (d.priority === 'high' || d.priority === 'express') && d.status !== 'Delivered').slice(0, 4);
const delayed = activeDeliveries.filter((d) => d.etaStatus !== 'on-time' && d.status !== 'Delivered').slice(0, 4);
const recs = aiInsights.slice(0, 4);
const kpis = [
{ label: 'Total Orders', value: '1,402', icon: Inventory2OutlinedIcon },
{ label: 'Active Shipments', value: '96', color: '#1D4ED8', icon: LocalShippingOutlinedIcon },
{ label: 'Riders Online', value: '48', color: '#00773B', icon: TwoWheelerOutlinedIcon },
{ label: 'Hub Utilization', value: `${hubUtil}%`, color: hubUtil > 80 ? '#A82216' : '#8A6500', icon: HubOutlinedIcon },
{ label: 'Revenue Today', value: inr(384200), color: '#00727B', icon: CurrencyRupeeIcon },
{ label: 'SLA Performance', value: `${analyticsKpis.slaAchievement}%`, color: '#00773B', icon: TaskAltOutlinedIcon }
];
return (
<>
<PageHeader
title="System Overview"
breadcrumbs={[{ label: 'Dashboard' }]}
action={
<Stack direction="row" spacing={1.5}>
<TextField select size="small" defaultValue="all" sx={{ minWidth: 150 }}>
<MenuItem value="all">All Locations</MenuItem>
<MenuItem value="blr">Bengaluru</MenuItem>
<MenuItem value="mum">Mumbai</MenuItem>
</TextField>
<Button variant="outlined" startIcon={<FileDownloadOutlinedIcon />} onClick={() => showToast('System overview exported as CSV')}>Export</Button>
</Stack>
}
title="Operations Control Center"
breadcrumbs={[{ label: 'Control Center' }]}
action={<Button variant="outlined" startIcon={<FileDownloadOutlinedIcon />} onClick={() => showToast('Snapshot exported as CSV')}>Export</Button>}
/>
{/* End-to-end operating-system pipeline */}
<Box sx={{ mb: 1 }}>
<Typography variant="overline" color="text.secondary" sx={{ letterSpacing: '0.08em' }}>
End-to-End Intelligent Logistics Flow
</Typography>
</Box>
<Box sx={{ mb: 3 }}>
<SystemPipeline />
</Box>
{/* Top row — 6 live KPIs */}
<KpiStrip items={kpis} />
{/* Three-Mile model — First → Mid → Last */}
<Box sx={{ mb: 0.5 }}>
<Typography variant="overline" color="text.secondary" sx={{ letterSpacing: '0.08em' }}>
Three-Mile Network · One Connected System
</Typography>
</Box>
<Box sx={{ mb: 3 }}>
<ThreeMileStrip compact />
</Box>
<Grid container spacing={2.5}>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Total Shipments" value="1,402" icon={Inventory2OutlinedIcon} trend={8.4} caption="vs last month" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Delivered" value="1,330" icon={LocalShippingOutlinedIcon} color="success" trend={6.1} caption="98.6% on-time" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Active Riders" value="48" icon={TwoWheelerOutlinedIcon} color="info" trend={-2.3} caption="of 124 fleet" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Revenue" value={inr(384200)} icon={CurrencyRupeeIcon} color="warning" trend={11.7} caption="vs last month" /></Grid>
<Grid item xs={12} lg={8}>
<MainCard
title="Orders Overview"
action={<Stack direction="row" spacing={2}><Legend color="#C01227" label="Orders" /><Legend color="#00A854" label="Delivered" /></Stack>}
>
<AreaChart
labels={ordersTrend.map((d) => d.m)}
series={[
{ name: 'Orders', color: '#C01227', data: ordersTrend.map((d) => d.orders) },
{ name: 'Delivered', color: '#00A854', data: ordersTrend.map((d) => d.delivered) }
]}
/>
</MainCard>
</Grid>
<Grid item xs={12} lg={4}>
<MainCard title="Order Status">
<Box sx={{ py: 2 }}>
<DonutChart data={statusBreakdown} centerValue="1,402" centerLabel="Orders" />
</Box>
</MainCard>
</Grid>
{/* MileTruth AI + Sustainability */}
<Grid item xs={12} lg={5}>
<MainCard
title={
<Stack direction="row" spacing={1} alignItems="center">
<Avatar variant="rounded" sx={{ bgcolor: '#FFF1E6', color: '#EA580C', width: 32, height: 32 }}><AutoAwesomeOutlinedIcon fontSize="small" /></Avatar>
<Typography variant="h5">MileTruth AI Engine</Typography>
</Stack>
}
>
<Grid container spacing={2}>
<Grid item xs={6}><AiStat icon={RouteOutlinedIcon} color="#C01227" value={`${aiMetrics.routeSavings}%`} label="Route savings" /></Grid>
<Grid item xs={6}><AiStat icon={SpeedOutlinedIcon} color="#C01227" value={`${aiMetrics.avgEtaAccuracy}%`} label="ETA accuracy" /></Grid>
<Grid item xs={6}><AiStat icon={AutoAwesomeOutlinedIcon} color="#C01227" value={aiMetrics.reoptToday} label="Re-optimizations today" /></Grid>
<Grid item xs={6}><AiStat icon={LocalShippingOutlinedIcon} color="#C01227" value={`${aiMetrics.delaysAvoided}/${aiMetrics.delaysPredicted}`} label="Delays avoided" /></Grid>
</Grid>
</MainCard>
</Grid>
<Grid item xs={12} lg={3}>
<MainCard title="EV-First Operations">
<Stack alignItems="center" spacing={1} sx={{ py: 1 }}>
<Avatar variant="rounded" sx={{ bgcolor: 'success.lighter', color: 'success.main', width: 48, height: 48 }}><EnergySavingsLeafOutlinedIcon /></Avatar>
<Typography variant="h2" sx={{ fontWeight: 800, color: 'success.main' }}>{fleetSummary.evShare}%</Typography>
<Typography variant="caption" color="text.secondary">EV fleet share</Typography>
<Box sx={{ width: '100%', mt: 1 }}>
<LinearProgress variant="determinate" value={fleetSummary.evShare} color="success" sx={{ height: 8, borderRadius: 4 }} />
</Box>
<Typography variant="caption" color="text.secondary" sx={{ mt: 0.5 }}>{(fleetSummary.co2SavedKg / 1000).toFixed(1)}t CO₂ saved this month</Typography>
</Stack>
</MainCard>
</Grid>
<Grid item xs={12} lg={4}>
<MainCard title="Top Riders Today">
<Stack divider={<Divider />} spacing={0}>
{riders.slice(0, 4).map((r, i) => (
<Stack key={r.id} direction="row" spacing={2} alignItems="center" sx={{ py: 1.1 }}>
<Typography variant="subtitle2" color="text.secondary" sx={{ width: 18 }}>{i + 1}</Typography>
<UserAvatar name={r.name} size={36} />
<Box sx={{ flexGrow: 1 }}>
<Typography variant="subtitle2">{r.name}</Typography>
<Typography variant="caption" color="text.secondary">{r.vehicle} · {r.rating}</Typography>
</Box>
<Box sx={{ textAlign: 'right' }}>
<Typography variant="subtitle2">{r.deliveries}</Typography>
<Typography variant="caption" color="text.secondary">deliveries</Typography>
</Box>
</Stack>
))}
</Stack>
</MainCard>
</Grid>
<Grid item xs={12} lg={4}>
<MainCard title="By Industry Vertical">
<Stack spacing={1.5}>
{verticals.map((v) => (
<Box key={v.key} sx={{ border: '1px solid', borderColor: 'grey.200', borderRadius: 2, p: 1.5 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Stack direction="row" spacing={1} alignItems="center">
<Box sx={{ width: 10, height: 10, borderRadius: '3px', bgcolor: v.color }} />
<Box>
<Typography variant="subtitle2">{v.label}</Typography>
<Typography variant="caption" color="text.secondary">{v.desc}</Typography>
</Box>
{/* Second row — dispatch intelligence */}
<Box sx={{ mt: 1 }}>
<SectionLabel>Dispatch & Exceptions</SectionLabel>
<Grid container spacing={2.5}>
<Grid item xs={12} md={6} lg={3}>
<MainCard title="Live Dispatch Queue" action={<Button size="small" onClick={() => navigate('/orders/assign')} sx={{ fontWeight: 600 }}>Open</Button>} noPadding>
<Stack divider={<Divider />}>
{dispatchQueue.map((d) => (
<Box key={d.id} sx={{ px: 2, py: 1.25 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="subtitle2" sx={{ fontWeight: 700, color: 'primary.main' }}>{d.id}</Typography>
<Chip size="small" label={`${d.confidence}%`} sx={{ height: 18, bgcolor: 'grey.100', fontWeight: 700, '& .MuiChip-label': { px: 0.75, fontSize: 10 } }} />
</Stack>
<Box sx={{ textAlign: 'right' }}>
<Typography variant="h4" sx={{ fontWeight: 700, color: 'grey.900' }}>{v.shipments}</Typography>
<Typography variant="caption" color="text.secondary">{v.onTime}% on-time</Typography>
<Typography variant="caption" color="text.secondary" noWrap sx={{ display: 'block' }}>{d.pickup} {d.drop}</Typography>
<Typography variant="caption" sx={{ color: 'grey.500' }}>AI {d.suggestedRider} · SLA {d.sla}</Typography>
</Box>
))}
</Stack>
</MainCard>
</Grid>
<Grid item xs={12} md={6} lg={3}>
<MainCard title="Priority Deliveries" noPadding>
<Stack divider={<Divider />}>
{priority.map((d) => (
<Stack key={d.id} direction="row" justifyContent="space-between" alignItems="center" sx={{ px: 2, py: 1.25 }}>
<Box sx={{ minWidth: 0 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 700, color: 'primary.main' }}>{d.id}</Typography>
<Typography variant="caption" color="text.secondary" noWrap sx={{ display: 'block' }}>{d.destination}</Typography>
</Box>
<Box component="span" sx={{ px: 0.75, py: 0.25, borderRadius: 1, bgcolor: '#FEEAE9', color: '#A82216', fontSize: 10, fontWeight: 700, textTransform: 'uppercase', flexShrink: 0 }}>{d.priority}</Box>
</Stack>
))}
</Stack>
</MainCard>
</Grid>
<Grid item xs={12} md={6} lg={3}>
<MainCard title="Delayed Shipments" noPadding>
<Stack divider={<Divider />}>
{delayed.map((d) => (
<Stack key={d.id} direction="row" justifyContent="space-between" alignItems="center" sx={{ px: 2, py: 1.25 }}>
<Box sx={{ minWidth: 0 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 700, color: 'primary.main' }}>{d.id}</Typography>
<Typography variant="caption" color="text.secondary" noWrap sx={{ display: 'block' }}>{d.rider}</Typography>
</Box>
<Stack direction="row" spacing={0.4} alignItems="center" sx={{ color: 'error.main', flexShrink: 0 }}>
<ScheduleOutlinedIcon sx={{ fontSize: 14 }} />
<Typography variant="caption" sx={{ fontWeight: 700 }}>+{d.delayMin}m</Typography>
</Stack>
</Stack>
))}
{delayed.length === 0 && <Typography variant="caption" color="text.secondary" sx={{ p: 2, textAlign: 'center' }}>No delays.</Typography>}
</Stack>
</MainCard>
</Grid>
<Grid item xs={12} md={6} lg={3}>
<MainCard title="AI Recommendations" noPadding>
<Stack divider={<Divider />}>
{recs.map((r) => (
<Stack key={r.id} direction="row" spacing={1} alignItems="flex-start" sx={{ px: 2, py: 1.25 }}>
<AutoAwesomeOutlinedIcon sx={{ fontSize: 15, color: '#EA580C', mt: 0.2, flexShrink: 0 }} />
<Box sx={{ minWidth: 0 }}>
<Typography variant="caption" sx={{ fontWeight: 700, color: 'grey.800', display: 'block', lineHeight: 1.3 }}>{r.title}</Typography>
<Typography variant="caption" sx={{ color: 'primary.main', fontWeight: 600 }}>{r.action}</Typography>
</Box>
</Stack>
</Box>
))}
</Stack>
</MainCard>
))}
</Stack>
</MainCard>
</Grid>
</Grid>
</Box>
<Grid item xs={12} lg={8}>
<MainCard title="Recent Shipments" noPadding>
<Table>
<TableHead>
<TableRow>
<TableCell>Order ID</TableCell>
<TableCell>Customer</TableCell>
<TableCell>Vertical</TableCell>
<TableCell>Route</TableCell>
<TableCell>Status</TableCell>
<TableCell align="right">Amount</TableCell>
</TableRow>
</TableHead>
<TableBody>
{orders.slice(0, 6).map((o) => {
const v = verticalOf(o.tenant);
return (
<TableRow key={o.id} hover>
<TableCell sx={{ fontWeight: 600, color: 'primary.main' }}>{o.id}</TableCell>
<TableCell>{o.customer}</TableCell>
<TableCell>
<Chip size="small" label={v} sx={{ bgcolor: hexA(VERTICAL_COLOR[v], 0.12), color: VERTICAL_COLOR[v], fontWeight: 600 }} />
</TableCell>
<TableCell>
<Typography variant="caption" color="text.secondary">{o.pickup} {o.drop}</Typography>
</TableCell>
<TableCell><StatusChip status={o.status} /></TableCell>
<TableCell align="right" sx={{ fontWeight: 600 }}>{inr(o.charges)}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</MainCard>
{/* Third row — events / fleet / route efficiency */}
<Box sx={{ mt: 3.5 }}>
<SectionLabel>Operational Intelligence</SectionLabel>
<Grid container spacing={2.5}>
<Grid item xs={12} lg={4}>
<MainCard title="Recent Operational Events" noPadding>
<Stack divider={<Divider />}>
{executionFeed.map((e, i) => (
<Stack key={`${e.id}-${i}`} direction="row" spacing={1.25} alignItems="flex-start" sx={{ px: 2, py: 1.25 }}>
<Box sx={{ mt: 0.6, width: 8, height: 8, borderRadius: '50%', bgcolor: e.stage === 'Exception' ? 'error.main' : e.stage === 'Delivered' ? 'success.main' : 'info.main', flexShrink: 0 }} />
<Box sx={{ minWidth: 0, flexGrow: 1 }}>
<Stack direction="row" justifyContent="space-between" spacing={1}>
<Typography variant="subtitle2" sx={{ fontWeight: 700 }}>{e.stage}</Typography>
<Typography variant="caption" color="text.secondary" sx={{ flexShrink: 0 }}>{e.time}</Typography>
</Stack>
<Typography variant="caption" color="text.secondary" sx={{ display: 'block' }} noWrap>{e.id} · {e.loc}</Typography>
</Box>
</Stack>
))}
</Stack>
</MainCard>
</Grid>
<Grid item xs={12} md={6} lg={4}>
<MainCard title="Fleet Status Overview">
<Grid container spacing={1.5} sx={{ mb: 1.5 }}>
{[['On trip', fleetSummary.onTrip, '#1D4ED8'], ['Charging', fleetSummary.charging, '#00A2AE'], ['Idle', fleetSummary.idle, '#8C8C8C'], ['Maintenance', fleetSummary.maintenance, '#F04134']].map(([l, v, c]) => (
<Grid item xs={6} key={l}>
<Box sx={{ border: '1px solid', borderColor: 'grey.200', borderRadius: 2, p: 1.25 }}>
<Typography variant="h5" sx={{ fontWeight: 800, color: c }}>{v}</Typography>
<Typography variant="caption" color="text.secondary">{l}</Typography>
</Box>
</Grid>
))}
</Grid>
<Stack direction="row" justifyContent="space-between" sx={{ mb: 0.5 }}>
<Typography variant="caption" color="text.secondary">EV fleet share</Typography>
<Typography variant="caption" sx={{ fontWeight: 700 }}>{fleetSummary.evShare}%</Typography>
</Stack>
<LinearProgress variant="determinate" value={fleetSummary.evShare} color="success" sx={{ height: 7, borderRadius: 4 }} />
</MainCard>
</Grid>
<Grid item xs={12} md={6} lg={4}>
<MainCard title="Route Efficiency" noPadding>
<Stack divider={<Divider />}>
{lanePerformance.slice(0, 5).map((l) => (
<Box key={l.lane} sx={{ px: 2, py: 1.1 }}>
<Stack direction="row" justifyContent="space-between" sx={{ mb: 0.4 }}>
<Typography variant="caption" sx={{ fontWeight: 700, color: 'grey.800' }} noWrap>{l.lane}</Typography>
<Typography variant="caption" color="text.secondary">{l.onTime}% · {inr(l.costPer)}</Typography>
</Stack>
<LinearProgress variant="determinate" value={l.onTime} color={l.onTime >= 98 ? 'success' : l.onTime >= 96 ? 'warning' : 'error'} sx={{ height: 5, borderRadius: 3 }} />
</Box>
))}
</Stack>
</MainCard>
</Grid>
</Grid>
</Grid>
</Box>
{/* End-to-end flow + volume trend */}
<Box sx={{ mt: 3.5 }}>
<SectionLabel>Network Flow & Volume</SectionLabel>
<Grid container spacing={2.5}>
<Grid item xs={12} lg={5}>
<ProcessTracker />
</Grid>
<Grid item xs={12} lg={7}>
<MainCard
title="Order Volume Trends"
action={<Stack direction="row" spacing={2}><Legend color="#C01227" label="Orders" /><Legend color="#00A854" label="Delivered" /></Stack>}
sx={{ height: '100%' }}
>
<Box sx={{ py: 1 }}>
<AreaChart
height={320}
labels={ordersTrend.map((d) => d.m)}
series={[
{ name: 'Orders', color: '#C01227', data: ordersTrend.map((d) => d.orders) },
{ name: 'Delivered', color: '#00A854', data: ordersTrend.map((d) => d.delivered) }
]}
/>
</Box>
</MainCard>
</Grid>
</Grid>
</Box>
{/* AI impact */}
<Box sx={{ mt: 3.5 }}>
<AiImpactSummary />
</Box>
<Toast {...toast} />
</>
);
}
function AiStat({ icon: Icon, color, value, label }) {
return (
<Stack direction="row" spacing={1.25} alignItems="center">
<Avatar variant="rounded" sx={{ bgcolor: hexA(color, 0.12), color, width: 38, height: 38 }}><Icon fontSize="small" /></Avatar>
<Box>
<Typography variant="h4" sx={{ fontWeight: 700 }}>{value}</Typography>
<Typography variant="caption" color="text.secondary">{label}</Typography>
</Box>
</Stack>
);
}
function Legend({ color, label }) {
return (
<Stack direction="row" spacing={0.75} alignItems="center">
@@ -228,8 +240,3 @@ function Legend({ color, label }) {
</Stack>
);
}
const hexA = (hex, a) => {
const n = parseInt(hex.replace('#', ''), 16);
return `rgba(${n >> 16}, ${(n >> 8) & 255}, ${n & 255}, ${a})`;
};