updated the ui for the doormile

This commit is contained in:
2026-06-08 18:02:27 +05:30
parent a162fa89e5
commit 4ad40b2c6d
24 changed files with 2434 additions and 238 deletions

View File

@@ -35,6 +35,19 @@ export default function App() {
<Route path="/deliveries" element={load(() => import('@/pages/Deliveries'))} />
{/* Logistics Operating System — layer surfaces */}
<Route path="/three-mile" element={load(() => import('@/pages/network/ThreeMile'))} />
<Route path="/cold-chain" element={load(() => import('@/pages/coldchain/ColdChain'))} />
<Route path="/trust" element={load(() => import('@/pages/trust/TrustCompliance'))} />
<Route path="/hubs" element={load(() => import('@/pages/hubs/HubNetwork'))} />
<Route path="/fleet" element={load(() => import('@/pages/fleet/Fleet'))} />
<Route path="/dispatch" element={load(() => import('@/pages/dispatch/AiDispatch'))} />
<Route path="/tracking" element={load(() => import('@/pages/tracking/LiveTracking'))} />
<Route path="/tracking/journey" element={load(() => import('@/pages/tracking/ShipmentJourney'))} />
<Route path="/tracking/journey/:id" element={load(() => import('@/pages/tracking/ShipmentJourney'))} />
<Route path="/analytics" element={load(() => import('@/pages/analytics/Analytics'))} />
<Route path="/integrations" element={load(() => import('@/pages/integrations/Integrations'))} />
<Route path="/tenants" element={load(() => import('@/pages/tenants/Tenants'))} />
<Route path="/tenants/create" element={load(() => import('@/pages/tenants/CreateClient'))} />
@@ -55,7 +68,6 @@ export default function App() {
<Route path="/invoice" element={load(() => import('@/pages/invoice/Invoices'))} />
<Route path="/invoice/:id" element={load(() => import('@/pages/invoice/InvoicePreview'))} />
<Route path="/requests" element={load(() => import('@/pages/Requests'))} />
<Route path="/profile" element={load(() => import('@/pages/Profile'))} />
<Route path="/settings" element={load(() => import('@/pages/Settings'))} />
</Route>

View File

@@ -0,0 +1,24 @@
import { Dialog, DialogTitle, DialogContent, DialogActions, Button, IconButton } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
// ==============================|| GENERIC FORM / DETAIL DIALOG ||============================== //
export default function FormDialog({ open, onClose, title, children, onSubmit, submitLabel = 'Save', maxWidth = 'sm', hideActions = false }) {
return (
<Dialog open={open} onClose={onClose} maxWidth={maxWidth} fullWidth>
<DialogTitle sx={{ fontWeight: 700, pr: 6 }}>
{title}
<IconButton onClick={onClose} size="small" sx={{ position: 'absolute', right: 12, top: 12, color: 'grey.500' }}>
<CloseIcon fontSize="small" />
</IconButton>
</DialogTitle>
<DialogContent dividers>{children}</DialogContent>
{!hideActions && (
<DialogActions sx={{ px: 3, py: 2 }}>
<Button color="inherit" onClick={onClose}>Cancel</Button>
{onSubmit && <Button variant="contained" onClick={onSubmit}>{submitLabel}</Button>}
</DialogActions>
)}
</Dialog>
);
}

View File

@@ -0,0 +1,83 @@
import { Box, Stack, Typography } from '@mui/material';
import ChevronRightRoundedIcon from '@mui/icons-material/ChevronRightRounded';
// ==============================|| LAYER BANNER ||============================== //
// Clean section header for each operating-system page. Flat neutral surface with a
// single subtle accent on the icon tile (no gradients / hero bands).
export default function LayerBanner({ no, title, subtitle, color = '#C01227', steps = [], icon: Icon }) {
return (
<Box
sx={{
borderRadius: 2,
p: { xs: 2, md: 2.5 },
mb: 3,
bgcolor: 'background.paper',
border: '1px solid',
borderColor: 'grey.200'
}}
>
<Stack direction="row" spacing={2} alignItems="center">
<Box
sx={{
width: 48,
height: 48,
flexShrink: 0,
borderRadius: 1.5,
bgcolor: hexA(color, 0.1),
color,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
{Icon ? <Icon sx={{ fontSize: 24 }} /> : <Typography variant="h5" sx={{ fontWeight: 800 }}>{no}</Typography>}
</Box>
<Box sx={{ flexGrow: 1, minWidth: 0 }}>
{no ? (
<Typography variant="overline" color="text.secondary" sx={{ letterSpacing: '0.1em' }}>
Layer {no}
</Typography>
) : null}
<Typography variant="h4" sx={{ fontWeight: 700, color: 'grey.900', lineHeight: 1.2 }}>
{title}
</Typography>
<Typography variant="body2" color="text.secondary">
{subtitle}
</Typography>
</Box>
</Stack>
{steps.length > 0 && (
<Stack direction="row" alignItems="center" sx={{ mt: 2, flexWrap: { xs: 'wrap', lg: 'nowrap' }, gap: 1 }}>
{steps.map((s, i) => (
<Stack key={s} direction="row" alignItems="center" sx={{ flexShrink: 0 }}>
<Box
sx={{
px: 1.5,
py: 0.6,
borderRadius: 1,
bgcolor: 'grey.50',
border: '1px solid',
borderColor: 'grey.200',
fontSize: '0.75rem',
fontWeight: 600,
color: 'text.secondary',
whiteSpace: 'nowrap'
}}
>
{s}
</Box>
{i < steps.length - 1 && <ChevronRightRoundedIcon sx={{ color: 'grey.400', mx: 0.25, fontSize: 18 }} />}
</Stack>
))}
</Stack>
)}
</Box>
);
}
const hexA = (hex, a) => {
const n = parseInt(hex.replace('#', ''), 16);
return `rgba(${n >> 16}, ${(n >> 8) & 255}, ${n & 255}, ${a})`;
};

View File

@@ -28,7 +28,37 @@ const MAP = {
open: { color: 'info', label: 'Open' },
overdue: { color: 'error', label: 'Overdue' },
prepaid: { color: 'success', label: 'Prepaid' },
cod: { color: 'warning', label: 'COD' }
cod: { color: 'warning', label: 'COD' },
// trust & risk (Layer 2)
cleared: { color: 'success', label: 'Cleared' },
review: { color: 'warning', label: 'Review' },
hold: { color: 'error', label: 'On Hold' },
low: { color: 'success', label: 'Low' },
medium: { color: 'warning', label: 'Medium' },
high: { color: 'error', label: 'High' },
// hub network (Layer 3)
busy: { color: 'warning', label: 'Busy' },
scheduled: { color: 'info', label: 'Scheduled' },
loading: { color: 'warning', label: 'Loading' },
// fleet (Layer 4)
'on-trip': { color: 'info', label: 'On Trip' },
charging: { color: 'info', label: 'Charging' },
idle: { color: 'default', label: 'Idle' },
maintenance: { color: 'warning', label: 'Maintenance' },
// AI dispatch (Layer 5)
matched: { color: 'info', label: 'Matched' },
optimizing: { color: 'warning', label: 'Optimizing' },
dispatched: { color: 'primary', label: 'Dispatched' },
// execution (Layer 6)
'picked-up': { color: 'primary', label: 'Picked Up' },
exception: { color: 'error', label: 'Exception' },
// integrations (Layer 8)
connected: { color: 'success', label: 'Connected' },
degraded: { color: 'warning', label: 'Degraded' },
// cold chain (pharma)
'in-range': { color: 'success', label: 'In Range' },
'at-risk': { color: 'warning', label: 'At Risk' },
breach: { color: 'error', label: 'Breach' }
};
const TONE = {

View File

@@ -0,0 +1,77 @@
import { Box, Stack, Typography } from '@mui/material';
import { useNavigate } from 'react-router-dom';
import { systemLayers } from '@/data/mock';
// ==============================|| SYSTEM PIPELINE (8-layer operating-system strip) ||============================== //
// Restrained, monochrome cards — neutral surface, dark metrics, brand-red only on hover,
// a single semantic health dot. No rainbow borders / colored badges.
const ROUTES = {
book: '/orders',
trust: '/trust',
hubs: '/hubs',
fleet: '/fleet',
dispatch: '/dispatch',
execution: '/tracking',
analytics: '/analytics',
integrations: '/integrations'
};
export default function SystemPipeline() {
const navigate = useNavigate();
return (
<Box
sx={{
display: 'grid',
gridTemplateColumns: { xs: 'repeat(2, 1fr)', sm: 'repeat(4, 1fr)', xl: 'repeat(8, 1fr)' },
gap: 1.25
}}
>
{systemLayers.map((l) => (
<Box
key={l.key}
onClick={() => navigate(ROUTES[l.key])}
sx={{
cursor: 'pointer',
height: '100%',
borderRadius: 1.5,
p: 1.75,
bgcolor: 'background.paper',
border: '1px solid',
borderColor: 'grey.200',
transition: 'border-color .15s ease, box-shadow .15s ease',
'&:hover': { borderColor: 'primary.main', boxShadow: '0 1px 2px rgba(16,24,40,0.08)' }
}}
>
<Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ mb: 1.25 }}>
<Box
sx={{
width: 24,
height: 24,
borderRadius: 1,
bgcolor: 'grey.100',
color: 'grey.700',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 12,
fontWeight: 700
}}
>
{l.no}
</Box>
<Box
sx={{ width: 7, height: 7, borderRadius: '50%', bgcolor: l.health === 'optimizing' ? 'warning.main' : 'success.main' }}
/>
</Stack>
<Typography variant="subtitle2" sx={{ fontWeight: 700, color: 'grey.900', lineHeight: 1.2 }}>{l.title}</Typography>
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mb: 1 }}>{l.subtitle}</Typography>
<Typography variant="h4" sx={{ fontWeight: 700, color: 'grey.900' }}>{l.metric}</Typography>
<Typography variant="caption" color="text.secondary">{l.metricLabel}</Typography>
</Box>
))}
</Box>
);
}

View File

@@ -0,0 +1,68 @@
import { Box, Stack, Typography, LinearProgress } from '@mui/material';
import ChevronRightRoundedIcon from '@mui/icons-material/ChevronRightRounded';
import WarehouseOutlinedIcon from '@mui/icons-material/WarehouseOutlined';
import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined';
import HomeOutlinedIcon from '@mui/icons-material/HomeOutlined';
import { threeMile } from '@/data/mock';
const ICONS = { first: WarehouseOutlinedIcon, mid: LocalShippingOutlinedIcon, last: HomeOutlinedIcon };
// ==============================|| THREE-MILE STRIP (First → Mid → Last Mile) ||============================== //
// Neutral cards, dark metrics, single grey progress — no per-stage rainbow colors.
export default function ThreeMileStrip({ compact = false }) {
return (
<Stack direction={{ xs: 'column', md: 'row' }} spacing={1.5} alignItems="stretch">
{threeMile.map((s, i) => {
const Icon = ICONS[s.key];
return (
<Stack key={s.key} direction={{ xs: 'column', md: 'row' }} spacing={1.5} alignItems="center" sx={{ flex: 1 }}>
<Box
sx={{
flex: 1,
width: '100%',
borderRadius: 1.5,
p: { xs: 1.75, md: 2 },
bgcolor: 'background.paper',
border: '1px solid',
borderColor: 'grey.200'
}}
>
<Stack direction="row" spacing={1.25} alignItems="center">
<Box sx={{ width: 38, height: 38, borderRadius: 1.5, bgcolor: 'grey.100', color: 'grey.700', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Icon fontSize="small" />
</Box>
<Box sx={{ flexGrow: 1 }}>
<Typography variant="subtitle1" sx={{ fontWeight: 700, color: 'grey.900', lineHeight: 1.1 }}>{s.title}</Typography>
<Typography variant="caption" color="text.secondary">{s.subtitle}</Typography>
</Box>
</Stack>
<Stack direction="row" alignItems="baseline" spacing={0.75} sx={{ mt: 1.5 }}>
<Typography variant="h4" sx={{ fontWeight: 700, color: 'grey.900' }}>{s.metric}</Typography>
<Typography variant="caption" color="text.secondary">{s.metricLabel}</Typography>
</Stack>
{!compact && (
<>
<Stack direction="row" justifyContent="space-between" sx={{ mt: 1.5, mb: 0.5 }}>
<Typography variant="caption" color="text.secondary">On-time</Typography>
<Typography variant="caption" sx={{ fontWeight: 700, color: 'grey.800' }}>{s.onTime}%</Typography>
</Stack>
<LinearProgress variant="determinate" value={s.onTime} sx={{ height: 5, borderRadius: 3, bgcolor: 'grey.100', '& .MuiLinearProgress-bar': { bgcolor: 'grey.800' } }} />
<Stack spacing={0.25} sx={{ mt: 1.25 }}>
{s.features.map((f) => (
<Typography key={f} variant="caption" color="text.secondary"> {f}</Typography>
))}
</Stack>
</>
)}
</Box>
{i < threeMile.length - 1 && (
<ChevronRightRoundedIcon sx={{ color: 'grey.300', transform: { xs: 'rotate(90deg)', md: 'none' }, flexShrink: 0 }} />
)}
</Stack>
);
})}
</Stack>
);
}

23
src/components/Toast.jsx Normal file
View File

@@ -0,0 +1,23 @@
import { Snackbar, Alert } from '@mui/material';
// ==============================|| TOAST / SNACKBAR ||============================== //
// Usage: const [toast, showToast] = useToast(); ... showToast('Saved'); <Toast {...toast} />
import { useState, useCallback } from 'react';
export function useToast() {
const [state, setState] = useState({ open: false, message: '', severity: 'success' });
const show = useCallback((message, severity = 'success') => setState({ open: true, message, severity }), []);
const onClose = useCallback(() => setState((s) => ({ ...s, open: false })), []);
return [{ ...state, onClose }, show];
}
export default function Toast({ open, message, severity = 'success', onClose }) {
return (
<Snackbar open={open} autoHideDuration={3000} onClose={onClose} anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}>
<Alert onClose={onClose} severity={severity} variant="filled" sx={{ width: '100%' }}>
{message}
</Alert>
</Snackbar>
);
}

View File

@@ -97,12 +97,6 @@ export const invoiceLineItems = [
{ particulars: 'COD handling fee', unit: 'order', qty: 260, rate: 6, other: 0, amount: 1560 }
];
export const requests = [
{ id: 1, requestor: 'Freshly Foods', bank: 'HDFC Bank', ifsc: 'HDFC0001234', refNo: 'RQ-88121', amount: 24500, reason: 'Weekly settlement payout', contact: 'Anil Gupta', address: 'Koramangala 4th Block', city: 'Bengaluru', zip: '560034', accountNo: '5012 3344 7788', pricing: [{ category: 'Standard', skill: 'Bike', cost: 9 }, { category: 'Express', skill: 'Bike', cost: 12 }] },
{ id: 2, requestor: 'UrbanCart', bank: 'ICICI Bank', ifsc: 'ICIC0004567', refNo: 'RQ-88122', amount: 18900, reason: 'Fuel reimbursement', contact: 'Meera Nair', address: 'Indiranagar', city: 'Bengaluru', zip: '560038', accountNo: '6022 1199 4455', pricing: [{ category: 'Standard', skill: 'Bike', cost: 8 }] },
{ id: 3, requestor: 'MediQuick Pharma', bank: 'Axis Bank', ifsc: 'UTIB0007788', refNo: 'RQ-88123', amount: 9700, reason: 'Adjustment - April', contact: 'Rohit Sen', address: 'Andheri West', city: 'Mumbai', zip: '400058', accountNo: '7033 5566 1122', pricing: [{ category: 'Standard', skill: 'Bike', cost: 10 }] }
];
// reports
export const ordersSummary = tenants.map((t, i) => ({
id: t.id,
@@ -179,3 +173,247 @@ export const statusBreakdown = [
{ label: 'Pending', value: 48, color: '#FFBF00' },
{ label: 'Cancelled', value: 18, color: '#F04134' }
];
// ==============================|| THREE-MILE MODEL (First / Mid / Last Mile) ||============================== //
// Mirrors doormile.com's primary narrative: Origin → Hub → Hub → Doorstep.
export const threeMile = [
{
key: 'first', title: 'First Mile', subtitle: 'Origin to Hub', color: '#EA580C',
metric: 184, metricLabel: 'pickups today', onTime: 98.1,
features: ['AI-scheduled pickups', 'Dynamic load consolidation', 'Yard & dock management', 'Pickup quality checks']
},
{
key: 'mid', title: 'Mid Mile', subtitle: 'Hub to Hub Transit', color: '#0E7C7B',
metric: 4, metricLabel: 'line-hauls live', onTime: 97.4,
features: ['Optimised line-haul routing', 'Cross-docking & sortation', 'Live SLA monitoring', 'EV-first corridors']
},
{
key: 'last', title: 'Last Mile', subtitle: 'Hub to Doorstep', color: '#1D4ED8',
metric: 96, metricLabel: 'out for delivery', onTime: 98.9,
features: ['Multi-stop route optimization', 'Precise delivery windows', 'Digital proof of delivery', 'Real-time customer updates']
}
];
// ==============================|| END-TO-END SHIPMENT JOURNEY (hop-by-hop) ||============================== //
// Follows ONE parcel Chennai → Bengaluru through every node: agent → nearest hub → origin main hub
// → line-haul → destination main hub → sub hub → delivery agent → customer. With live monitoring & reroute.
export const shipmentJourney = {
id: 'DM-CHN-BLR-7741',
product: 'Documents & electronics · 3.2 kg',
from: { city: 'Chennai', area: 'T. Nagar', name: 'Suresh Kumar' },
to: { city: 'Bengaluru', area: 'Koramangala', name: 'Riya Sharma' },
client: 'TechNova Retail',
distance: 346,
mode: 'Standard · EV-first',
placed: '08 Jun, 09:12 AM',
eta: '09 Jun, 02:30 PM',
progress: 52,
currentStage: 'Line-Haul · Chennai → Bengaluru',
hops: [
{ key: 'booked', mile: 'First Mile', title: 'Shipment Booked', node: 'Chennai · T. Nagar', handler: 'Customer · Suresh Kumar', icon: 'order', time: '08 Jun, 09:12 AM', status: 'done', detail: 'Shipment ID generated · digital docs & e-waybill created' },
{ key: 'pickup', mile: 'First Mile', title: 'Agent Pickup', node: 'Chennai · T. Nagar', handler: 'Rider · Faisal Khan (EV 2W)', icon: 'agent', time: '08 Jun, 10:05 AM', status: 'done', detail: 'OTP verified · photo proof captured · tamper seal applied' },
{ key: 'nearhub', mile: 'First Mile', title: 'Nearest Hub — Check-in', node: 'Nungambakkam Micro Hub', handler: 'Hub operations', icon: 'hub', time: '08 Jun, 10:48 AM', status: 'done', detail: 'Scanned in · sorted for origin main hub' },
{ key: 'mainhub-out', mile: 'Mid Mile', title: 'Origin Main Hub — Dispatched', node: 'Guindy Regional Hub (Chennai)', handler: 'Hub operations', icon: 'hub', time: '08 Jun, 01:20 PM', status: 'done', detail: 'Consolidated & loaded onto line-haul EV truck' },
{ key: 'linehaul', mile: 'Mid Mile', title: 'Line-Haul In Transit', node: 'NH48 · Chennai → Bengaluru', handler: 'Driver · Imran Sheikh (EV Truck 4W)', icon: 'truck', time: '08 Jun, 01:35 PM', status: 'active', detail: 'En route · live GPS · 178 km to destination hub' },
{ key: 'desthub', mile: 'Mid Mile', title: 'Destination Main Hub — Arrival', node: 'Hoskote Regional Hub (Bengaluru)', handler: 'Hub operations', icon: 'hub', time: 'Est. 09 Jun, 06:30 AM', status: 'pending', detail: 'Inbound scan & sortation' },
{ key: 'subhub', mile: 'Last Mile', title: 'Sub Hub — Last-Mile Sort', node: 'Koramangala Micro Hub', handler: 'Hub operations', icon: 'hub', time: 'Est. 09 Jun, 10:15 AM', status: 'pending', detail: 'Routed to delivery agent zone' },
{ key: 'assigned', mile: 'Last Mile', title: 'Delivery Agent Assigned', node: 'Bengaluru · Koramangala', handler: 'MileTruth AI · auto-assign', icon: 'agent', time: 'Est. 09 Jun, 11:00 AM', status: 'pending', detail: 'Multi-stop route optimized for the agent' },
{ key: 'ofd', mile: 'Last Mile', title: 'Out for Delivery', node: 'Bengaluru · Koramangala', handler: 'Rider · auto-assigned (EV 2W)', icon: 'agent', time: 'Est. 09 Jun, 01:40 PM', status: 'pending', detail: 'Live tracking link shared with customer' },
{ key: 'delivered', mile: 'Last Mile', title: 'Delivered', node: 'Bengaluru · Koramangala', handler: 'Customer · Riya Sharma', icon: 'done', time: 'Est. 09 Jun, 02:30 PM', status: 'pending', detail: 'OTP + photo proof of delivery' }
],
events: [
{ time: '08 Jun, 03:10 PM', type: 'reroute', title: 'Traffic on NH48 near Krishnagiri', detail: 'MileTruth AI rerouted via Hosur bypass — ETA protected (+0 min)' },
{ time: '08 Jun, 05:40 PM', type: 'monitor', title: 'Weather check · clear', detail: 'No disruption forecast on the corridor' },
{ time: '08 Jun, 08:15 PM', type: 'monitor', title: 'On schedule', detail: 'Vehicle at 62% battery · charging stop planned at Hosur' }
]
};
// ==============================|| INDUSTRY VERTICALS ||============================== //
export const verticals = [
{ key: 'fmcg', label: 'FMCG', desc: 'High-volume, expiry-sensitive', shipments: 612, onTime: 98.9, color: '#EA580C' },
{ key: 'pharma', label: 'Pharma', desc: 'Cold-chain & compliance', shipments: 248, onTime: 99.4, color: '#0E7C7B' },
{ key: 'enterprise', label: 'Enterprise & B2B', desc: 'Appointment & white-glove', shipments: 542, onTime: 97.6, color: '#1D4ED8' }
];
// maps each business client to its industry vertical
export const tenantVertical = {
'Freshly Foods': 'FMCG',
'GreenLeaf Organics': 'FMCG',
'BloomBox Florists': 'FMCG',
'MediQuick Pharma': 'Pharma',
UrbanCart: 'Enterprise & B2B',
'TechNova Retail': 'Enterprise & B2B'
};
export const verticalOf = (tenant) => tenantVertical[tenant] || 'Enterprise & B2B';
// ==============================|| PHARMA COLD-CHAIN ||============================== //
export const coldChainSummary = { monitored: 248, inRange: 241, atRisk: 5, breaches: 2, compliance: 99.2, avgTemp: 4.2 };
export const coldChainShipments = [
{ id: 'DM-CC-2041', product: 'Insulin vials', tenant: 'MediQuick Pharma', range: '2 8 °C', temp: 4.6, route: 'Andheri → Bandra', status: 'in-range', excursionMin: 0 },
{ id: 'DM-CC-2042', product: 'mRNA vaccine', tenant: 'MediQuick Pharma', range: '-20 -15 °C', temp: -17.2, route: 'Hub HYD → BLR', status: 'in-range', excursionMin: 0 },
{ id: 'DM-CC-2043', product: 'Frozen seafood', tenant: 'Freshly Foods', range: '≤ -18 °C', temp: -12.4, route: 'Koramangala → HSR', status: 'breach', excursionMin: 14 },
{ id: 'DM-CC-2044', product: 'Dairy & yogurt', tenant: 'GreenLeaf Organics', range: '2 6 °C', temp: 7.1, route: 'Koregaon → Viman Nagar', status: 'at-risk', excursionMin: 3 },
{ id: 'DM-CC-2045', product: 'Blood samples', tenant: 'MediQuick Pharma', range: '2 8 °C', temp: 5.3, route: 'Connaught Pl → Saket', status: 'in-range', excursionMin: 0 },
{ id: 'DM-CC-2046', product: 'Antibiotics', tenant: 'MediQuick Pharma', range: '15 25 °C', temp: 21.4, route: 'Hitech City → Gachibowli', status: 'in-range', excursionMin: 0 }
];
// ==============================|| LOGISTICS OPERATING SYSTEM — LAYER DATA ||============================== //
// Mirrors the 8-layer end-to-end flow: Book → Trust → Hub → Fleet → MileTruth AI → Execution → Analytics → Integrations.
// The 8 layers of the operating system — drives the System Overview pipeline strip.
export const systemLayers = [
{ no: 1, key: 'book', title: 'Book & Create', subtitle: 'Shipment intake', color: '#1E3A8A', metric: '1,402', metricLabel: 'shipments', health: 'healthy' },
{ no: 2, key: 'trust', title: 'Trust & Identity', subtitle: 'KYC · fraud · custody', color: '#5B5BD6', metric: '99.2%', metricLabel: 'cleared', health: 'healthy' },
{ no: 3, key: 'hubs', title: 'Hub Network', subtitle: 'Sort · line-haul', color: '#0E7C7B', metric: '12', metricLabel: 'hubs live', health: 'healthy' },
{ no: 4, key: 'fleet', title: 'Fleet & Riders', subtitle: 'EV-first capacity', color: '#15803D', metric: '48', metricLabel: 'riders online', health: 'healthy' },
{ no: 5, key: 'dispatch', title: 'MileTruth AI', subtitle: 'Dispatch · optimize', color: '#EA580C', metric: '34%', metricLabel: 'route savings', health: 'optimizing' },
{ no: 6, key: 'execution', title: 'Execution', subtitle: 'Track · proof', color: '#1D4ED8', metric: '96', metricLabel: 'in transit', health: 'healthy' },
{ no: 7, key: 'analytics', title: 'Analytics', subtitle: 'Insight · ML loop', color: '#7C3AED', metric: '98.6%', metricLabel: 'on-time', health: 'healthy' },
{ no: 8, key: 'integrations', title: 'Integrations', subtitle: 'APIs · partners', color: '#0F766E', metric: '24', metricLabel: 'connectors', health: 'healthy' }
];
// ---- Layer 2: Trust, Security & Compliance ----
export const trustChecks = [
{ key: 'kyc', title: 'KYC Verification', desc: 'Verify senders & businesses', icon: 'kyc', passRate: 99.2, pending: 6, color: '#5B5BD6' },
{ key: 'id', title: 'ID Verification', desc: 'Aadhaar / GST / PAN validation', icon: 'id', passRate: 98.7, pending: 3, color: '#1D4ED8' },
{ key: 'fraud', title: 'Fraud Detection', desc: 'ML model flags risky shipments', icon: 'fraud', passRate: 97.4, pending: 9, color: '#C01227' },
{ key: 'tamper', title: 'Tamper Protection', desc: 'Secure packaging & seal tracking', icon: 'tamper', passRate: 99.8, pending: 1, color: '#EA580C' },
{ key: 'custody', title: 'Chain of Custody', desc: 'Immutable logs at every touchpoint', icon: 'custody', passRate: 100, pending: 0, color: '#0E7C7B' },
{ key: 'compliance', title: 'Compliance Engine', desc: 'Legal, tax & regulatory checks', icon: 'compliance', passRate: 99.5, pending: 2, color: '#15803D' }
];
export const trustQueue = [
{ id: 'DM-10244', entity: 'Arjun Mehta', type: 'Sender KYC', check: 'Aadhaar', risk: 'low', score: 12, status: 'cleared', flagged: 'ID match · address verified' },
{ id: 'DM-10246', entity: 'TechNova Retail', type: 'Business GST', check: 'GST', risk: 'medium', score: 48, status: 'review', flagged: 'High-value COD ₹4,999 · ID required' },
{ id: 'DM-10248', entity: 'Suresh Kumar', type: 'Sender KYC', check: 'PAN', risk: 'low', score: 18, status: 'cleared', flagged: 'Repeat customer' },
{ id: 'DM-10251', entity: 'QuickMart Express', type: 'Fraud screen', check: 'ML ID3', risk: 'high', score: 82, status: 'hold', flagged: 'Velocity anomaly · 14 orders / 5 min' },
{ id: 'DM-10242', entity: 'Karthik Rao', type: 'Tamper seal', check: 'Seal scan', risk: 'low', score: 5, status: 'cleared', flagged: 'Seal intact · photo logged' }
];
// ---- Layer 3: Intelligent Hub Network ----
export const hubs = [
{ id: 'HUB-BLR-01', name: 'Koramangala Micro Hub', type: 'Micro Hub', city: 'Bengaluru', lat: 12.9352, lng: 77.6245, capacity: 1200, load: 940, inbound: 180, outbound: 210, dock: 4, status: 'online' },
{ id: 'HUB-BLR-02', name: 'Whitefield City Hub', type: 'City Hub', city: 'Bengaluru', lat: 12.9698, lng: 77.7499, capacity: 4000, load: 2860, inbound: 520, outbound: 610, dock: 10, status: 'online' },
{ id: 'HUB-BLR-RG', name: 'Hoskote Regional Hub', type: 'Regional Hub', city: 'Bengaluru', lat: 13.0707, lng: 77.7980, capacity: 12000, load: 7400, inbound: 1900, outbound: 2100, dock: 24, status: 'online' },
{ id: 'HUB-MUM-01', name: 'Andheri City Hub', type: 'City Hub', city: 'Mumbai', lat: 19.1197, lng: 72.8468, capacity: 4500, load: 3950, inbound: 700, outbound: 760, dock: 12, status: 'busy' },
{ id: 'HUB-HYD-01', name: 'Hitech Cross Dock', type: 'Cross Dock', city: 'Hyderabad', lat: 17.4435, lng: 78.3772, capacity: 3000, load: 1100, inbound: 340, outbound: 360, dock: 8, status: 'online' },
{ id: 'HUB-DEL-RG', name: 'Bilaspur Regional Hub', type: 'Regional Hub', city: 'Delhi NCR', lat: 28.4231, lng: 77.0490, capacity: 14000, load: 11200, inbound: 2400, outbound: 2600, dock: 28, status: 'busy' }
];
export const hubNetworkTypes = [
{ type: 'Micro Hubs (Urban)', count: 5, desc: 'Last-mile EV staging' },
{ type: 'City Hubs', count: 4, desc: 'Sort & local distribution' },
{ type: 'Regional Hubs', count: 2, desc: 'Aggregation & line-haul origin' },
{ type: 'Cross Dock Points', count: 1, desc: 'No-store transfer' }
];
export const lineHauls = [
{ id: 'LH-001', from: 'Hoskote Regional Hub', to: 'Bilaspur Regional Hub', corridor: 'BLR → DEL', distance: 2150, vehicle: 'EV Truck 4W', load: 86, eta: '34h', status: 'in-transit' },
{ id: 'LH-002', from: 'Andheri City Hub', to: 'Hoskote Regional Hub', corridor: 'MUM → BLR', distance: 980, vehicle: 'ICE Truck 6W', load: 72, eta: '18h', status: 'in-transit' },
{ id: 'LH-003', from: 'Hitech Cross Dock', to: 'Hoskote Regional Hub', corridor: 'HYD → BLR', distance: 570, vehicle: 'EV Truck 4W', load: 64, eta: '11h', status: 'scheduled' },
{ id: 'LH-004', from: 'Bilaspur Regional Hub', to: 'Andheri City Hub', corridor: 'DEL → MUM', distance: 1420, vehicle: 'ICE Truck 6W', load: 91, eta: '24h', status: 'loading' }
];
// ---- Layer 4: Fleet & Rider Operating System ----
export const fleet = [
{ id: 'VH-EV-018', model: 'Tata Ace EV', type: 'EV 4W', powertrain: 'EV', battery: 78, range: 96, health: 94, capacityKg: 600, status: 'on-trip', rider: 'Mohan Das', hub: 'Koramangala Micro Hub', uptime: 98.2 },
{ id: 'VH-EV-022', model: 'Euler HiLoad EV', type: 'EV 3W', powertrain: 'EV', battery: 41, range: 38, health: 89, capacityKg: 688, status: 'charging', rider: '—', hub: 'Whitefield City Hub', uptime: 96.5 },
{ id: 'VH-2W-104', model: 'Ola S1 Pro', type: 'EV 2W', powertrain: 'EV', battery: 63, range: 71, health: 91, capacityKg: 20, status: 'on-trip', rider: 'Ravi Teja', hub: 'Hitech Cross Dock', uptime: 97.1 },
{ id: 'VH-IC-211', model: 'Mahindra Bolero', type: 'ICE 4W', powertrain: 'ICE', battery: null, range: 320, health: 82, capacityKg: 1500, status: 'idle', rider: '—', hub: 'Hoskote Regional Hub', uptime: 93.4 },
{ id: 'VH-2W-118', model: 'Honda Activa', type: 'ICE 2W', powertrain: 'ICE', battery: null, range: 180, health: 76, capacityKg: 25, status: 'maintenance', rider: '—', hub: 'Andheri City Hub', uptime: 88.9 },
{ id: 'VH-EV-031', model: 'Tata Ace EV', type: 'EV 4W', powertrain: 'EV', battery: 88, range: 108, health: 96, capacityKg: 600, status: 'idle', rider: '—', hub: 'Bilaspur Regional Hub', uptime: 99.0 }
];
export const fleetSummary = {
total: 124, ev: 86, ice: 38, onTrip: 52, charging: 14, idle: 41, maintenance: 17,
evShare: 69, co2SavedKg: 12840, avgBattery: 64, avgHealth: 90
};
// ---- Layer 5: MileTruth AI Engine — dispatch & optimization ----
export const dispatchQueue = [
{ id: 'DM-10246', pickup: 'Hitech City', drop: 'Gachibowli', priority: 'high', sla: '45 min', suggestedRider: 'Ravi Teja', confidence: 96, status: 'matched', etaMin: 22 },
{ id: 'DM-10244', pickup: 'Connaught Place', drop: 'Saket, Block C', priority: 'standard', sla: '90 min', suggestedRider: 'Sandeep Roy', confidence: 88, status: 'optimizing', etaMin: 41 },
{ id: 'DM-10248', pickup: 'T. Nagar', drop: 'Adyar', priority: 'standard', sla: '120 min', suggestedRider: 'Faisal Khan', confidence: 91, status: 'matched', etaMin: 33 },
{ id: 'DM-10242', pickup: 'Indiranagar Store', drop: 'Whitefield, Phase 1', priority: 'express', sla: '60 min', suggestedRider: 'Mohan Das', confidence: 94, status: 'dispatched', etaMin: 28 }
];
export const aiTriggers = [
{ key: 'new-order', label: 'New Order', count: 18, icon: 'order' },
{ key: 'rider-delay', label: 'Rider Delay / Exception', count: 3, icon: 'delay' },
{ key: 'traffic', label: 'Traffic Change', count: 7, icon: 'traffic' },
{ key: 'weather', label: 'Weather Change', count: 1, icon: 'weather' },
{ key: 'cancellation', label: 'Cancellation / Return', count: 2, icon: 'cancel' },
{ key: 'high-priority', label: 'High Priority Shipment', count: 4, icon: 'priority' }
];
export const aiPipeline = [
{ stage: 'Data Ingestion', items: ['Live orders', 'Rider GPS', 'Traffic / weather', 'Hub status'] },
{ stage: 'Data Processing', items: ['Cleaning', 'GPS smoothing', 'Geohash encoding', 'Feature eng.'] },
{ stage: 'Intelligence', items: ['Demand prediction', 'Delay prediction', 'Risk scoring (ID3)', 'Zone learning'] },
{ stage: 'Optimization', items: ['OR-Tools VRP', 'Multi-objective', 'Distance+Time+Cost', 'Re-optimization'] },
{ stage: 'Assignment', items: ['Rider matching', 'Batch creation', 'Route sequencing', 'Trip planning'] },
{ stage: 'Output', items: ['Optimized routes', 'ETA', 'Rider plans', 'Delivery windows'] }
];
export const aiMetrics = { routeSavings: 34, avgEtaAccuracy: 92, batchRate: 2.6, reoptToday: 41, delaysPredicted: 28, delaysAvoided: 23 };
// ---- Layer 6: Execution & Visibility ----
export const executionFeed = [
{ id: 'DM-10242', stage: 'In-Transit', rider: 'Mohan Das', loc: 'HSR Layout', detail: 'On route · 4.2 km to drop', time: '10:42 AM', proof: null },
{ id: 'DM-10243', stage: 'Picked Up', rider: 'Imran Sheikh', loc: 'Andheri West', detail: 'OTP verified · photo proof logged', time: '10:38 AM', proof: 'pickup' },
{ id: 'DM-10247', stage: 'Delivered', rider: 'Ravi Teja', loc: 'Viman Nagar', detail: 'eSign captured · POD uploaded', time: '10:30 AM', proof: 'delivery' },
{ id: 'DM-10251', stage: 'Exception', rider: 'Faisal Khan', loc: 'Adyar', detail: 'Customer unavailable · reattempt scheduled', time: '10:21 AM', proof: null },
{ id: 'DM-10246', stage: 'Dispatched', rider: 'Sandeep Roy', loc: 'Hitech City', detail: 'Rider en route to pickup', time: '10:18 AM', proof: null }
];
export const executionStages = [
{ key: 'pickup', title: 'Pickup Execution', items: ['Rider reaches pickup', 'OTP / eSign', 'Photo proof', 'Proof of pickup'], count: 12 },
{ key: 'transit', title: 'In-Transit Tracking', items: ['Live GPS', 'Route monitoring', 'ETA updates', 'Geofencing'], count: 96 },
{ key: 'rerouting', title: 'Dynamic Re-routing', items: ['Avoid delays', 'Optimize live', 'Re-assign if needed', 'SLA protection'], count: 7 },
{ key: 'delivery', title: 'Delivery Execution', items: ['OTP / eSign', 'Photo proof', 'Proof of delivery', 'Feedback'], count: 1330 },
{ key: 'exception', title: 'Exception Handling', items: ['Delay mgmt', 'Reattempt', 'Damage / loss', 'Escalation'], count: 4 }
];
// ---- Layer 7: Analytics & Intelligence ----
export const analyticsKpis = {
onTime: 98.6, successRate: 99.1, costPerDelivery: 74, fuelEnergyCost: 21, loadFactor: 82,
customerInsightsNps: 71, slaAchievement: 97.4
};
export const lanePerformance = [
{ lane: 'BLR → BLR (intra)', shipments: 820, onTime: 99.2, costPer: 68, ev: 92 },
{ lane: 'BLR → DEL', shipments: 142, onTime: 96.8, costPer: 188, ev: 64 },
{ lane: 'MUM → BLR', shipments: 98, onTime: 97.5, costPer: 162, ev: 58 },
{ lane: 'HYD → BLR', shipments: 76, onTime: 98.1, costPer: 121, ev: 71 },
{ lane: 'DEL → MUM', shipments: 64, onTime: 95.4, costPer: 174, ev: 49 }
];
export const mlLoop = [
{ step: 'Data Logging', detail: 'Every shipment event captured' },
{ step: 'Model Retraining', detail: 'Nightly on fresh outcomes' },
{ step: 'A/B Testing', detail: 'Routing & pricing variants' },
{ step: 'Hyperparameter Tuning', detail: 'Auto-tuned weekly' }
];
export const outcomes = [
{ label: 'Faster Deliveries', value: '35%+', caption: 'improvement', color: '#00A854' },
{ label: 'Lower Operational Cost', value: '3040%', caption: 'savings', color: '#0E7C7B' },
{ label: 'Higher Reliability', value: '99%+', caption: 'success rate', color: '#1D4ED8' },
{ label: 'Full Visibility', value: 'E2E', caption: 'end-to-end', color: '#7C3AED' },
{ label: 'Sustainable & Green', value: 'EV-First', caption: 'operations', color: '#15803D' }
];
// ---- Layer 8: Integrations & Ecosystem ----
export const integrations = [
{ name: 'Order API', group: 'APIs & Integrations', desc: 'Create & manage shipments', status: 'connected', calls: '1.2M / day', icon: 'api' },
{ name: 'Tracking API', group: 'APIs & Integrations', desc: 'Real-time status & ETA', status: 'connected', calls: '4.8M / day', icon: 'api' },
{ name: 'Webhooks', group: 'APIs & Integrations', desc: 'Event push to partners', status: 'connected', calls: '900K / day', icon: 'api' },
{ name: 'SAP ERP / WMS', group: 'Enterprise Systems', desc: 'Warehouse & inventory sync', status: 'connected', calls: 'realtime', icon: 'erp' },
{ name: 'TMS', group: 'Enterprise Systems', desc: 'Transport management', status: 'connected', calls: 'realtime', icon: 'erp' },
{ name: 'CRM', group: 'Enterprise Systems', desc: 'Customer records', status: 'degraded', calls: 'realtime', icon: 'erp' },
{ name: 'Razorpay', group: 'Payment & Billing', desc: 'Prepaid / COD settlement', status: 'connected', calls: '320K / day', icon: 'pay' },
{ name: 'Corporate Billing', group: 'Payment & Billing', desc: 'Invoice & reconciliation', status: 'connected', calls: 'daily', icon: 'pay' },
{ name: '3PL Partners', group: 'Partners & Ecosystem', desc: 'Capacity overflow routing', status: 'connected', calls: '12 partners', icon: 'partner' },
{ name: 'Transport Partners', group: 'Partners & Ecosystem', desc: 'Line-haul carriers', status: 'connected', calls: '8 carriers', icon: 'partner' },
{ name: 'Warehouse Partners', group: 'Partners & Ecosystem', desc: 'Fulfilment nodes', status: 'pending', calls: '—', icon: 'partner' }
];

View File

@@ -34,13 +34,14 @@ import DoneAllIcon from '@mui/icons-material/DoneAll';
import Logo from '@/components/Logo';
const RED = '#C01227';
const RED = '#C01227'; // brand accent (avatars, dots)
const BAR = '#8E1F2A'; // muted deep-brick top bar (toned down from vivid #C01227)
const INITIAL_NOTIFICATIONS = [
{ id: 1, icon: Inventory2OutlinedIcon, title: 'New order #ORD-10482 placed', time: '2 min ago', to: '/orders', read: false },
{ id: 2, icon: TwoWheelerOutlinedIcon, title: 'Rider Imran went online', time: '18 min ago', to: '/riders', read: false },
{ id: 3, icon: PaymentsOutlinedIcon, title: 'Invoice INV-2041 marked paid', time: '1 hr ago', to: '/invoice', read: false },
{ id: 4, icon: AssignmentOutlinedIcon, title: '3 new onboarding requests', time: '3 hrs ago', to: '/requests', read: true }
{ id: 4, icon: AssignmentOutlinedIcon, title: 'MileTruth AI re-optimized 41 routes', time: '3 hrs ago', to: '/dispatch', read: true }
];
const MESSAGES = [
@@ -78,7 +79,7 @@ export default function Header({ onToggle }) {
<AppBar
position="fixed"
elevation={0}
sx={{ bgcolor: RED, color: '#fff', zIndex: (t) => t.zIndex.drawer + 1, boxShadow: '0 1px 0 rgba(0,0,0,0.06)' }}
sx={{ bgcolor: BAR, color: '#fff', zIndex: (t) => t.zIndex.drawer + 1, boxShadow: '0 1px 0 rgba(0,0,0,0.06)' }}
>
<Toolbar sx={{ minHeight: 64, px: { xs: 1.5, sm: 2.5 }, gap: 1 }}>
<IconButton color="inherit" edge="start" onClick={onToggle} sx={{ mr: 0.5 }}>
@@ -194,7 +195,7 @@ export default function Header({ onToggle }) {
);
})}
<Divider />
<MenuItem onClick={() => { closeNotif(); navigate('/requests'); }} sx={{ justifyContent: 'center', color: 'primary.main', fontWeight: 600 }}>
<MenuItem onClick={() => { closeNotif(); navigate('/dashboard'); }} sx={{ justifyContent: 'center', color: 'primary.main', fontWeight: 600 }}>
View all activity
</MenuItem>
</Menu>

View File

@@ -19,10 +19,10 @@ import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord';
import navItems from '@/menu/navItems';
import Logo from '@/components/Logo';
export const DRAWER_WIDTH = 264;
export const MINI_WIDTH = 78;
export const DRAWER_WIDTH = 232;
export const MINI_WIDTH = 76;
const RED = '#C01227';
const NAV_BG = '#8E1F2A'; // muted deep-brick brand red (toned down from vivid #C01227)
function NavLeaf({ item, open, active, depth = 0, onClick }) {
const Icon = item.icon;
@@ -80,11 +80,31 @@ export default function Sidebar({ open, mobileOpen, onMobileClose, isMobile }) {
};
const content = (
<Box sx={{ bgcolor: RED, height: '100%', color: '#fff', display: 'flex', flexDirection: 'column' }}>
<Box sx={{ bgcolor: NAV_BG, height: '100%', color: '#fff', display: 'flex', flexDirection: 'column' }}>
<Toolbar sx={{ px: expanded ? 2.5 : 0, justifyContent: expanded ? 'flex-start' : 'center', minHeight: 64 }}>
<Logo onDark compact={!expanded} />
</Toolbar>
<Box sx={{ overflowY: 'auto', overflowX: 'hidden', flexGrow: 1, pb: 2 }}>
<Box
sx={{
overflowY: 'auto',
overflowX: 'hidden',
flexGrow: 1,
pb: 2,
// slim, subtle scrollbar tuned for the dark-red sidebar — only shows on hover
scrollbarWidth: 'thin',
scrollbarColor: 'transparent transparent',
'&:hover': { scrollbarColor: 'rgba(255,255,255,0.3) transparent' },
'&::-webkit-scrollbar': { width: 6 },
'&::-webkit-scrollbar-track': { background: 'transparent' },
'&::-webkit-scrollbar-thumb': {
backgroundColor: 'transparent',
borderRadius: 8,
transition: 'background-color 0.2s ease'
},
'&:hover::-webkit-scrollbar-thumb': { backgroundColor: 'rgba(255,255,255,0.28)' },
'&::-webkit-scrollbar-thumb:hover': { backgroundColor: 'rgba(255,255,255,0.45)' }
}}
>
{navItems.map((grp) => (
<Box key={grp.group} sx={{ mt: 1 }}>
{expanded && (
@@ -156,8 +176,11 @@ export default function Sidebar({ open, mobileOpen, onMobileClose, isMobile }) {
</Box>
{expanded && (
<Box sx={{ p: 2, borderTop: '1px solid rgba(255,255,255,0.12)' }}>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.55)' }}>
Doormile Console v1.0
<Typography variant="caption" sx={{ color: '#fff', fontWeight: 600, display: 'block', lineHeight: 1.3 }}>
Delivering Trust.
</Typography>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.6)' }}>
Beyond Boundaries · v1.0
</Typography>
</Box>
)}

View File

@@ -7,34 +7,70 @@ import GroupsOutlinedIcon from '@mui/icons-material/GroupsOutlined';
import TwoWheelerOutlinedIcon from '@mui/icons-material/TwoWheelerOutlined';
import BarChartOutlinedIcon from '@mui/icons-material/BarChartOutlined';
import ReceiptLongOutlinedIcon from '@mui/icons-material/ReceiptLongOutlined';
import AssignmentOutlinedIcon from '@mui/icons-material/AssignmentOutlined';
import SummarizeOutlinedIcon from '@mui/icons-material/SummarizeOutlined';
import FactCheckOutlinedIcon from '@mui/icons-material/FactCheckOutlined';
import MapOutlinedIcon from '@mui/icons-material/MapOutlined';
import SecurityOutlinedIcon from '@mui/icons-material/SecurityOutlined';
import HubOutlinedIcon from '@mui/icons-material/HubOutlined';
import ElectricRickshawOutlinedIcon from '@mui/icons-material/ElectricRickshawOutlined';
import AutoAwesomeOutlinedIcon from '@mui/icons-material/AutoAwesomeOutlined';
import MyLocationOutlinedIcon from '@mui/icons-material/MyLocationOutlined';
import TrendingUpOutlinedIcon from '@mui/icons-material/TrendingUpOutlined';
import ApiOutlinedIcon from '@mui/icons-material/ApiOutlined';
import RouteOutlinedIcon from '@mui/icons-material/RouteOutlined';
import AcUnitOutlinedIcon from '@mui/icons-material/AcUnitOutlined';
import AltRouteOutlinedIcon from '@mui/icons-material/AltRouteOutlined';
// ==============================|| DOORMILE - SIDEBAR NAV CONFIG ||============================== //
// ==============================|| DOORMILE - LOGISTICS OPERATING SYSTEM NAV ||============================== //
// Groups mirror the 8-layer end-to-end flow of the Doormile operating system.
const navItems = [
{
group: 'Operations',
group: 'Overview',
items: [
{ id: 'dashboard', title: 'Dashboard', url: '/dashboard', icon: DashboardOutlinedIcon },
{ id: 'orders', title: 'Orders', url: '/orders', icon: Inventory2OutlinedIcon },
{ id: 'deliveries', title: 'Deliveries', url: '/deliveries', icon: MopedOutlinedIcon }
{ id: 'dashboard', title: 'System Overview', url: '/dashboard', icon: DashboardOutlinedIcon },
{ id: 'three-mile', title: 'Three-Mile Network', url: '/three-mile', icon: RouteOutlinedIcon }
]
},
{
group: 'Network',
group: '1 · Book & Create',
items: [
{ id: 'tenants', title: 'Tenants', url: '/tenants', icon: ApartmentOutlinedIcon },
{ id: 'pricing', title: 'Pricing', url: '/pricing', icon: PaymentsOutlinedIcon },
{ id: 'customers', title: 'Customers', url: '/customers', icon: GroupsOutlinedIcon },
{ id: 'orders', title: 'Shipments', url: '/orders', icon: Inventory2OutlinedIcon },
{ id: 'customers', title: 'Customers', url: '/customers', icon: GroupsOutlinedIcon }
]
},
{
group: '2 · Trust & Identity',
items: [{ id: 'trust', title: 'Trust & Compliance', url: '/trust', icon: SecurityOutlinedIcon }]
},
{
group: '3 · Hub Network',
items: [{ id: 'hubs', title: 'Hub Network', url: '/hubs', icon: HubOutlinedIcon }]
},
{
group: '4 · Fleet & Riders',
items: [
{ id: 'fleet', title: 'Fleet', url: '/fleet', icon: ElectricRickshawOutlinedIcon },
{ id: 'riders', title: 'Riders', url: '/riders', icon: TwoWheelerOutlinedIcon }
]
},
{
group: 'Finance & Insights',
group: '5 · MileTruth AI',
items: [{ id: 'dispatch', title: 'AI Dispatch', url: '/dispatch', icon: AutoAwesomeOutlinedIcon }]
},
{
group: '6 · Execution',
items: [
{ id: 'tracking', title: 'Live Tracking', url: '/tracking', icon: MyLocationOutlinedIcon },
{ id: 'journey', title: 'Shipment Journey', url: '/tracking/journey', icon: AltRouteOutlinedIcon },
{ id: 'cold-chain', title: 'Cold Chain', url: '/cold-chain', icon: AcUnitOutlinedIcon },
{ id: 'deliveries', title: 'Deliveries', url: '/deliveries', icon: MopedOutlinedIcon }
]
},
{
group: '7 · Analytics',
items: [
{ id: 'analytics', title: 'Analytics', url: '/analytics', icon: TrendingUpOutlinedIcon },
{
id: 'reports',
title: 'Reports',
@@ -45,9 +81,21 @@ const navItems = [
{ id: 'riders-summary', title: 'Riders Summary', url: '/reports/riders-summary', icon: TwoWheelerOutlinedIcon },
{ id: 'riders-logs', title: 'Riders Logs', url: '/reports/riders-logs', icon: MapOutlinedIcon }
]
}
]
},
{ id: 'invoice', title: 'Invoice', url: '/invoice', icon: ReceiptLongOutlinedIcon },
{ id: 'requests', title: 'Requests', url: '/requests', icon: AssignmentOutlinedIcon }
{
group: '8 · Integrations',
items: [
{ id: 'integrations', title: 'Integrations', url: '/integrations', icon: ApiOutlinedIcon },
{ id: 'tenants', title: 'Clients', url: '/tenants', icon: ApartmentOutlinedIcon }
]
},
{
group: 'Finance',
items: [
{ id: 'pricing', title: 'Pricing', url: '/pricing', icon: PaymentsOutlinedIcon },
{ id: 'invoice', title: 'Invoice', url: '/invoice', icon: ReceiptLongOutlinedIcon }
]
}
];

View File

@@ -1,9 +1,13 @@
import { Grid, Card, CardContent, Stack, Typography, Box, Button, Divider, Table, TableBody, TableCell, TableHead, TableRow, Avatar, MenuItem, TextField } from '@mui/material';
import { Grid, Stack, Typography, Box, Button, Divider, Table, TableBody, TableCell, TableHead, TableRow, MenuItem, TextField, Avatar, LinearProgress, Chip } 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 CurrencyRupeeIcon from '@mui/icons-material/CurrencyRupee';
import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
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 PageHeader from '@/components/PageHeader';
import StatCard from '@/components/StatCard';
@@ -12,14 +16,20 @@ import StatusChip from '@/components/StatusChip';
import AreaChart from '@/components/charts/AreaChart';
import DonutChart from '@/components/charts/DonutChart';
import UserAvatar from '@/components/UserAvatar';
import { ordersTrend, statusBreakdown, orders, riders } from '@/data/mock';
import SystemPipeline from '@/components/SystemPipeline';
import ThreeMileStrip from '@/components/ThreeMileStrip';
import Toast, { useToast } from '@/components/Toast';
import { ordersTrend, statusBreakdown, orders, riders, aiMetrics, fleetSummary, verticals, verticalOf } from '@/data/mock';
import { inr } from '@/utils/format';
const VERTICAL_COLOR = Object.fromEntries(verticals.map((v) => [v.label, v.color]));
export default function Dashboard() {
const [toast, showToast] = useToast();
return (
<>
<PageHeader
title="Dashboard"
title="System Overview"
breadcrumbs={[{ label: 'Dashboard' }]}
action={
<Stack direction="row" spacing={1.5}>
@@ -28,15 +38,35 @@ export default function Dashboard() {
<MenuItem value="blr">Bengaluru</MenuItem>
<MenuItem value="mum">Mumbai</MenuItem>
</TextField>
<Button variant="outlined" startIcon={<FileDownloadOutlinedIcon />}>Export</Button>
<Button variant="outlined" startIcon={<FileDownloadOutlinedIcon />} onClick={() => showToast('System overview exported as CSV')}>Export</Button>
</Stack>
}
/>
{/* 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>
{/* 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 Orders" 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="vs last month" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Active Riders" value="48" icon={TwoWheelerOutlinedIcon} color="info" trend={-2.3} caption="vs last month" /></Grid>
<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}>
@@ -61,39 +91,42 @@ export default function Dashboard() {
</MainCard>
</Grid>
<Grid item xs={12} lg={7}>
<MainCard title="Recent Orders" noPadding>
<Table>
<TableHead>
<TableRow>
<TableCell>Order ID</TableCell>
<TableCell>Customer</TableCell>
<TableCell>Route</TableCell>
<TableCell>Status</TableCell>
<TableCell align="right">Amount</TableCell>
</TableRow>
</TableHead>
<TableBody>
{orders.slice(0, 6).map((o) => (
<TableRow key={o.id} hover>
<TableCell sx={{ fontWeight: 600, color: 'primary.main' }}>{o.id}</TableCell>
<TableCell>{o.customer}</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>
{/* 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={5}>
<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, 5).map((r, i) => (
<Stack key={r.id} direction="row" spacing={2} alignItems="center" sx={{ py: 1.25 }}>
{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 }}>
@@ -109,11 +142,84 @@ export default function Dashboard() {
</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>
</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>
</Box>
</Stack>
</Box>
))}
</Stack>
</MainCard>
</Grid>
<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>
</Grid>
</Grid>
<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">
@@ -122,3 +228,8 @@ 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})`;
};

View File

@@ -1,162 +0,0 @@
import { useState } from 'react';
import {
Card, Stack, Button, Box, Collapse, Tabs, Tab, Typography, Grid,
Table, TableBody, TableCell, TableContainer, TableHead, TableRow, IconButton,
TablePagination, Dialog, DialogTitle, DialogContent, DialogActions, TextField
} from '@mui/material';
import AddIcon from '@mui/icons-material/Add';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
import PageHeader from '@/components/PageHeader';
import { requests } from '@/data/mock';
import { inr } from '@/utils/format';
function RequestRow({ row, index }) {
const [open, setOpen] = useState(false);
const [tab, setTab] = useState(0);
return (
<>
<TableRow hover sx={{ '& > *': { borderBottom: open ? 'unset' : undefined } }}>
<TableCell>{index + 1}</TableCell>
<TableCell sx={{ fontWeight: 600 }}>{row.requestor}</TableCell>
<TableCell>{row.bank}</TableCell>
<TableCell>{row.ifsc}</TableCell>
<TableCell>{row.refNo}</TableCell>
<TableCell align="right" sx={{ fontWeight: 600 }}>{inr(row.amount)}</TableCell>
<TableCell>{row.reason}</TableCell>
<TableCell align="center">
<IconButton size="small" onClick={() => setOpen((o) => !o)}>
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
</TableCell>
</TableRow>
<TableRow>
<TableCell sx={{ py: 0, borderBottom: open ? 1 : 0, borderColor: 'divider' }} colSpan={8}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ m: 2 }}>
<Tabs value={tab} onChange={(_, v) => setTab(v)} sx={{ mb: 2 }}>
<Tab label="Client Details" />
<Tab label="Client Pricing" />
</Tabs>
{tab === 0 && (
<Grid container spacing={2}>
<Grid item xs={12} sm={6} md={3}>
<Typography variant="caption" color="text.secondary">Contact Name</Typography>
<Typography variant="body2" sx={{ fontWeight: 600 }}>{row.contact}</Typography>
</Grid>
<Grid item xs={12} sm={6} md={3}>
<Typography variant="caption" color="text.secondary">Address</Typography>
<Typography variant="body2" sx={{ fontWeight: 600 }}>{row.address}</Typography>
</Grid>
<Grid item xs={12} sm={6} md={3}>
<Typography variant="caption" color="text.secondary">City</Typography>
<Typography variant="body2" sx={{ fontWeight: 600 }}>{row.city}</Typography>
</Grid>
<Grid item xs={12} sm={6} md={3}>
<Typography variant="caption" color="text.secondary">Zip Code</Typography>
<Typography variant="body2" sx={{ fontWeight: 600 }}>{row.zip}</Typography>
</Grid>
</Grid>
)}
{tab === 1 && (
<Table size="small">
<TableHead>
<TableRow sx={{ '& th': { bgcolor: 'grey.50', fontWeight: 700 } }}>
<TableCell>#</TableCell>
<TableCell>Category</TableCell>
<TableCell>Skill</TableCell>
<TableCell align="right">Cost/Hr</TableCell>
</TableRow>
</TableHead>
<TableBody>
{row.pricing.map((p, i) => (
<TableRow key={i}>
<TableCell>{i + 1}</TableCell>
<TableCell>{p.category}</TableCell>
<TableCell>{p.skill}</TableCell>
<TableCell align="right">{inr(p.cost)}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)}
</Box>
</Collapse>
</TableCell>
</TableRow>
</>
);
}
export default function Requests() {
const [page, setPage] = useState(0);
const [rpp, setRpp] = useState(10);
const [open, setOpen] = useState(false);
const paged = requests.slice(page * rpp, page * rpp + rpp);
return (
<>
<PageHeader
title="Requests"
breadcrumbs={[{ label: 'Requests' }]}
action={
<Button variant="contained" startIcon={<AddIcon />} onClick={() => setOpen(true)}>
Create Request
</Button>
}
/>
<Card>
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell>#</TableCell>
<TableCell>Requestor</TableCell>
<TableCell>Bank</TableCell>
<TableCell>IFSC</TableCell>
<TableCell>Ref No</TableCell>
<TableCell align="right">Amount</TableCell>
<TableCell>Reason</TableCell>
<TableCell align="center" />
</TableRow>
</TableHead>
<TableBody>
{paged.map((row, idx) => (
<RequestRow key={row.id} row={row} index={page * rpp + idx} />
))}
</TableBody>
</Table>
</TableContainer>
<TablePagination
component="div" count={requests.length} page={page} onPageChange={(_, p) => setPage(p)}
rowsPerPage={rpp} onRowsPerPageChange={(e) => { setRpp(+e.target.value); setPage(0); }} rowsPerPageOptions={[5, 10, 25, 100]}
/>
</Card>
<Dialog open={open} onClose={() => setOpen(false)} maxWidth="sm" fullWidth>
<DialogTitle>Create Request</DialogTitle>
<DialogContent>
<Grid container spacing={2.5} sx={{ mt: 0 }}>
<Grid item xs={12} sm={6}><TextField label="Reference No" type="number" fullWidth /></Grid>
<Grid item xs={12} sm={6}><TextField label="Requestor" fullWidth /></Grid>
<Grid item xs={12} sm={6}><TextField label="Bank Name" fullWidth /></Grid>
<Grid item xs={12} sm={6}><TextField label="Amount" type="number" fullWidth /></Grid>
<Grid item xs={12} sm={6}><TextField label="Account No" type="number" fullWidth /></Grid>
<Grid item xs={12} sm={6}><TextField label="IFSC Code" fullWidth /></Grid>
<Grid item xs={12}><TextField label="Reason" fullWidth multiline minRows={3} /></Grid>
</Grid>
</DialogContent>
<DialogActions sx={{ px: 3, pb: 2 }}>
<Button onClick={() => setOpen(false)} color="inherit">Close</Button>
<Button variant="contained" onClick={() => setOpen(false)}>Update</Button>
</DialogActions>
</Dialog>
</>
);
}

View File

@@ -0,0 +1,141 @@
import { Grid, Stack, Typography, Box, Avatar, LinearProgress, Table, TableBody, TableCell, TableHead, TableRow, Button, Divider } from '@mui/material';
import TrendingUpOutlinedIcon from '@mui/icons-material/TrendingUpOutlined';
import CurrencyRupeeIcon from '@mui/icons-material/CurrencyRupee';
import SsidChartOutlinedIcon from '@mui/icons-material/SsidChartOutlined';
import AutorenewOutlinedIcon from '@mui/icons-material/AutorenewOutlined';
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
import PageHeader from '@/components/PageHeader';
import StatCard from '@/components/StatCard';
import MainCard from '@/components/MainCard';
import LayerBanner from '@/components/LayerBanner';
import AreaChart from '@/components/charts/AreaChart';
import Toast, { useToast } from '@/components/Toast';
import { analyticsKpis, lanePerformance, mlLoop, outcomes, ordersTrend } from '@/data/mock';
import { inr } from '@/utils/format';
export default function Analytics() {
const [toast, showToast] = useToast();
return (
<>
<PageHeader
title="Analytics & Intelligence Layer"
breadcrumbs={[{ label: 'Analytics' }]}
action={<Button variant="outlined" startIcon={<FileDownloadOutlinedIcon />} onClick={() => showToast('Analytics report exported')}>Export Report</Button>}
/>
<LayerBanner
no={7}
icon={TrendingUpOutlinedIcon}
color="#7C3AED"
title="Analytics & Intelligence"
subtitle="Operational, cost, business & predictive insight feeding a continuous ML improvement loop."
steps={['Operational Analytics', 'Cost Intelligence', 'Business Intelligence', 'Predictive Insights', 'Continuous Improvement']}
/>
<Grid container spacing={2.5}>
<Grid item xs={12} sm={6} lg={3}><StatCard title="On-Time Delivery" value={`${analyticsKpis.onTime}%`} icon={CheckCircleOutlineIcon} color="success" trend={1.2} caption="SLA achievement 97.4%" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Success Rate" value={`${analyticsKpis.successRate}%`} icon={TrendingUpOutlinedIcon} color="info" trend={0.6} caption="first-attempt" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Cost / Delivery" value={inr(analyticsKpis.costPerDelivery)} icon={CurrencyRupeeIcon} color="warning" trend={-4.1} caption="energy cost ₹21" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Load Factor" value={`${analyticsKpis.loadFactor}%`} icon={SsidChartOutlinedIcon} color="primary" trend={2.3} caption="capacity utilised" /></Grid>
<Grid item xs={12} lg={8}>
<MainCard
title="Cost vs Volume Trend"
action={<Stack direction="row" spacing={2}><Legend color="#7C3AED" label="Orders" /><Legend color="#00A854" label="Delivered" /></Stack>}
>
<AreaChart
labels={ordersTrend.map((d) => d.m)}
series={[
{ name: 'Orders', color: '#7C3AED', 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="Continuous Improvement (ML Loop)">
<Stack divider={<Divider />} spacing={0}>
{mlLoop.map((m) => (
<Stack key={m.step} direction="row" spacing={1.5} alignItems="center" sx={{ py: 1.35 }}>
<Avatar variant="rounded" sx={{ bgcolor: '#F3E8FF', color: '#7C3AED', width: 38, height: 38 }}>
<AutorenewOutlinedIcon fontSize="small" />
</Avatar>
<Box>
<Typography variant="subtitle2">{m.step}</Typography>
<Typography variant="caption" color="text.secondary">{m.detail}</Typography>
</Box>
</Stack>
))}
</Stack>
</MainCard>
</Grid>
<Grid item xs={12} lg={7}>
<MainCard title="Lane Performance" noPadding>
<Table>
<TableHead>
<TableRow>
<TableCell>Lane</TableCell>
<TableCell align="right">Shipments</TableCell>
<TableCell>On-Time</TableCell>
<TableCell align="right">Cost / Del</TableCell>
<TableCell>EV %</TableCell>
</TableRow>
</TableHead>
<TableBody>
{lanePerformance.map((l) => (
<TableRow key={l.lane} hover>
<TableCell><Typography variant="subtitle2">{l.lane}</Typography></TableCell>
<TableCell align="right">{l.shipments.toLocaleString('en-IN')}</TableCell>
<TableCell sx={{ minWidth: 110 }}>
<Stack direction="row" spacing={1} alignItems="center">
<Box sx={{ width: 54 }}>
<LinearProgress variant="determinate" value={l.onTime} color={l.onTime > 97 ? 'success' : 'warning'} sx={{ height: 6, borderRadius: 3 }} />
</Box>
<Typography variant="caption" sx={{ fontWeight: 600 }}>{l.onTime}%</Typography>
</Stack>
</TableCell>
<TableCell align="right">{inr(l.costPer)}</TableCell>
<TableCell><Typography variant="caption" sx={{ fontWeight: 600, color: l.ev > 70 ? 'success.main' : 'text.secondary' }}>{l.ev}%</Typography></TableCell>
</TableRow>
))}
</TableBody>
</Table>
</MainCard>
</Grid>
<Grid item xs={12} lg={5}>
<MainCard title="System Outcomes">
<Grid container spacing={1.5}>
{outcomes.map((o) => (
<Grid item xs={12} sm={6} key={o.label}>
<Box sx={{ border: '1px solid', borderColor: 'grey.200', borderRadius: 2, p: 1.75, height: '100%' }}>
<Stack direction="row" spacing={1} alignItems="center">
<CheckCircleOutlineIcon sx={{ color: 'success.main', fontSize: 20 }} />
<Typography variant="h4" sx={{ fontWeight: 800, color: 'grey.900' }}>{o.value}</Typography>
</Stack>
<Typography variant="subtitle2" sx={{ mt: 0.5 }}>{o.label}</Typography>
<Typography variant="caption" color="text.secondary">{o.caption}</Typography>
</Box>
</Grid>
))}
</Grid>
</MainCard>
</Grid>
</Grid>
<Toast {...toast} />
</>
);
}
function Legend({ color, label }) {
return (
<Stack direction="row" spacing={0.75} alignItems="center">
<Box sx={{ width: 10, height: 10, borderRadius: '3px', bgcolor: color }} />
<Typography variant="caption" color="text.secondary">{label}</Typography>
</Stack>
);
}

View File

@@ -49,18 +49,21 @@ export default function Login() {
<Box sx={{ position: 'absolute', width: 280, height: 280, borderRadius: '50%', bgcolor: 'rgba(255,255,255,0.06)', bottom: -80, left: -60 }} />
<Logo onDark />
<Box sx={{ position: 'relative' }}>
<Typography variant="h2" sx={{ color: '#fff', fontWeight: 800, lineHeight: 1.2 }}>
Move every parcel,
<br /> on time, every time.
<Typography variant="overline" sx={{ color: 'rgba(255,255,255,0.7)', letterSpacing: '0.14em' }}>
One Connected System · One Promise Kept
</Typography>
<Typography variant="h2" sx={{ color: '#fff', fontWeight: 800, lineHeight: 1.2, mt: 0.5 }}>
Delivering Trust.
<br /> Beyond Boundaries.
</Typography>
<Typography sx={{ color: 'rgba(255,255,255,0.85)', mt: 2, maxWidth: 420 }}>
The command center for your last-mile operation orders, riders, pricing and settlements in one corporate console.
The MileTruth AI command center for Connected Miles first-mile, mid-mile and last-mile delivery, unified in one intelligent console.
</Typography>
<Stack spacing={1.5} sx={{ mt: 4 }}>
{[
{ icon: BoltIcon, t: 'AI-assisted route optimisation' },
{ icon: LocalShippingOutlinedIcon, t: 'Real-time rider & delivery tracking' },
{ icon: VerifiedOutlinedIcon, t: 'Automated client invoicing & payouts' }
{ icon: BoltIcon, t: 'MileTruth™ AI route optimisation' },
{ icon: LocalShippingOutlinedIcon, t: 'EV-first real-time tracking & proof of delivery' },
{ icon: VerifiedOutlinedIcon, t: 'Cold-chain & chain-of-custody compliance' }
].map((f) => (
<Stack key={f.t} direction="row" spacing={1.5} alignItems="center">
<Box sx={{ width: 34, height: 34, borderRadius: 2, bgcolor: 'rgba(255,255,255,0.16)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>

View File

@@ -0,0 +1,146 @@
import { Grid, Stack, Typography, Box, Avatar, LinearProgress, Table, TableBody, TableCell, TableHead, TableRow, Button } from '@mui/material';
import AcUnitOutlinedIcon from '@mui/icons-material/AcUnitOutlined';
import DeviceThermostatOutlinedIcon from '@mui/icons-material/DeviceThermostatOutlined';
import WarningAmberOutlinedIcon from '@mui/icons-material/WarningAmberOutlined';
import VerifiedOutlinedIcon from '@mui/icons-material/VerifiedOutlined';
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
import PageHeader from '@/components/PageHeader';
import StatCard from '@/components/StatCard';
import MainCard from '@/components/MainCard';
import StatusChip from '@/components/StatusChip';
import LayerBanner from '@/components/LayerBanner';
import Toast, { useToast } from '@/components/Toast';
import { coldChainSummary, coldChainShipments } from '@/data/mock';
const TEMP_COLOR = { 'in-range': '#00A854', 'at-risk': '#FFBF00', breach: '#F04134' };
export default function ColdChain() {
const [toast, showToast] = useToast();
return (
<>
<PageHeader
title="Pharma Cold-Chain"
breadcrumbs={[{ label: 'Cold Chain' }]}
action={<Button variant="outlined" startIcon={<FileDownloadOutlinedIcon />} onClick={() => showToast('Cold-chain compliance report generated')}>Compliance Report</Button>}
/>
<LayerBanner
no={0}
icon={AcUnitOutlinedIcon}
color="#0E7C7B"
title="Temperature-Monitored Logistics"
subtitle="Battery-aware EV cold-chain with live temperature monitoring, excursion alerts and regulatory compliance."
steps={['Sensor Telemetry', 'Live Monitoring', 'Excursion Alerts', 'Compliance Logs']}
/>
<Grid container spacing={2.5}>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Monitored Shipments" value={coldChainSummary.monitored} icon={AcUnitOutlinedIcon} color="info" caption="cold-chain active" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="In Range" value={coldChainSummary.inRange} icon={CheckCircleOutlineIcon} color="success" caption={`avg ${coldChainSummary.avgTemp}°C`} /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="At Risk / Breach" value={`${coldChainSummary.atRisk} / ${coldChainSummary.breaches}`} icon={WarningAmberOutlinedIcon} color="warning" caption="needs attention" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Compliance" value={`${coldChainSummary.compliance}%`} icon={VerifiedOutlinedIcon} color="success" trend={0.3} caption="regulatory pass" /></Grid>
<Grid item xs={12}>
<MainCard title="Live Temperature Monitoring" noPadding>
<Table>
<TableHead>
<TableRow>
<TableCell>Shipment</TableCell>
<TableCell>Product</TableCell>
<TableCell>Client</TableCell>
<TableCell>Required Range</TableCell>
<TableCell>Current Temp</TableCell>
<TableCell>Excursion</TableCell>
<TableCell>Status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{coldChainShipments.map((s) => {
const color = TEMP_COLOR[s.status];
return (
<TableRow key={s.id} hover>
<TableCell sx={{ fontWeight: 600, color: 'primary.main' }}>{s.id}</TableCell>
<TableCell>
<Typography variant="subtitle2">{s.product}</Typography>
<Typography variant="caption" color="text.secondary">{s.route}</Typography>
</TableCell>
<TableCell><Typography variant="caption">{s.tenant}</Typography></TableCell>
<TableCell>{s.range}</TableCell>
<TableCell>
<Stack direction="row" spacing={1} alignItems="center">
<Avatar variant="rounded" sx={{ bgcolor: hexA(color, 0.12), color, width: 30, height: 30 }}>
<DeviceThermostatOutlinedIcon sx={{ fontSize: 18 }} />
</Avatar>
<Typography variant="subtitle2" sx={{ fontWeight: 700, color }}>{s.temp}°C</Typography>
</Stack>
</TableCell>
<TableCell>
{s.excursionMin > 0 ? (
<Typography variant="caption" sx={{ fontWeight: 600, color: 'error.main' }}>{s.excursionMin} min</Typography>
) : (
<Typography variant="caption" color="text.secondary"></Typography>
)}
</TableCell>
<TableCell><StatusChip status={s.status} /></TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</MainCard>
</Grid>
<Grid item xs={12} md={6}>
<MainCard title="Compliance Health">
<Stack spacing={2} sx={{ py: 0.5 }}>
<Bar label="In-range shipments" value={Math.round((coldChainSummary.inRange / coldChainSummary.monitored) * 100)} color="#00A854" />
<Bar label="Regulatory compliance" value={coldChainSummary.compliance} color="#0E7C7B" />
<Bar label="Excursion-free deliveries" value={97} color="#1D4ED8" />
</Stack>
</MainCard>
</Grid>
<Grid item xs={12} md={6}>
<MainCard title="Cold-Chain Capabilities">
<Grid container spacing={1.5}>
{[
{ t: 'Live sensor telemetry', d: 'IoT probes per shipment' },
{ t: 'Excursion alerts', d: 'Instant breach notification' },
{ t: 'Battery-aware EV routing', d: 'Reefer charge planning' },
{ t: 'Chain-of-custody logs', d: 'Immutable audit trail' }
].map((c) => (
<Grid item xs={12} sm={6} key={c.t}>
<Box sx={{ border: '1px solid', borderColor: 'grey.200', borderRadius: 2, p: 1.75, height: '100%' }}>
<Stack direction="row" spacing={1} alignItems="center">
<AcUnitOutlinedIcon sx={{ color: '#0E7C7B', fontSize: 20 }} />
<Typography variant="subtitle2">{c.t}</Typography>
</Stack>
<Typography variant="caption" color="text.secondary" sx={{ mt: 0.5, display: 'block' }}>{c.d}</Typography>
</Box>
</Grid>
))}
</Grid>
</MainCard>
</Grid>
</Grid>
<Toast {...toast} />
</>
);
}
function Bar({ label, value, color }) {
return (
<Box>
<Stack direction="row" justifyContent="space-between" sx={{ mb: 0.5 }}>
<Typography variant="caption" color="text.secondary">{label}</Typography>
<Typography variant="caption" sx={{ fontWeight: 700, color }}>{value}%</Typography>
</Stack>
<LinearProgress variant="determinate" value={value} sx={{ height: 6, borderRadius: 3, bgcolor: hexA(color, 0.12), '& .MuiLinearProgress-bar': { bgcolor: color } }} />
</Box>
);
}
const hexA = (hex, a) => {
const n = parseInt(hex.replace('#', ''), 16);
return `rgba(${n >> 16}, ${(n >> 8) & 255}, ${n & 255}, ${a})`;
};

View File

@@ -0,0 +1,161 @@
import { useState } from 'react';
import { Grid, Card, CardContent, Stack, Typography, Box, Avatar, LinearProgress, Table, TableBody, TableCell, TableHead, TableRow, Button, Chip, Divider } from '@mui/material';
import AutoAwesomeOutlinedIcon from '@mui/icons-material/AutoAwesomeOutlined';
import RouteOutlinedIcon from '@mui/icons-material/RouteOutlined';
import SpeedOutlinedIcon from '@mui/icons-material/SpeedOutlined';
import InsightsOutlinedIcon from '@mui/icons-material/InsightsOutlined';
import PsychologyOutlinedIcon from '@mui/icons-material/PsychologyOutlined';
import FiberNewOutlinedIcon from '@mui/icons-material/FiberNewOutlined';
import TimerOutlinedIcon from '@mui/icons-material/TimerOutlined';
import TrafficOutlinedIcon from '@mui/icons-material/TrafficOutlined';
import ThunderstormOutlinedIcon from '@mui/icons-material/ThunderstormOutlined';
import ReplayOutlinedIcon from '@mui/icons-material/ReplayOutlined';
import PriorityHighOutlinedIcon from '@mui/icons-material/PriorityHighOutlined';
import PlayArrowOutlinedIcon from '@mui/icons-material/PlayArrowOutlined';
import PageHeader from '@/components/PageHeader';
import StatCard from '@/components/StatCard';
import MainCard from '@/components/MainCard';
import StatusChip from '@/components/StatusChip';
import LayerBanner from '@/components/LayerBanner';
import Toast, { useToast } from '@/components/Toast';
import { dispatchQueue, aiTriggers, aiPipeline, aiMetrics } from '@/data/mock';
const TRIGGER_ICONS = {
order: FiberNewOutlinedIcon,
delay: TimerOutlinedIcon,
traffic: TrafficOutlinedIcon,
weather: ThunderstormOutlinedIcon,
cancel: ReplayOutlinedIcon,
priority: PriorityHighOutlinedIcon
};
const NEXT_STATUS = { optimizing: 'matched', matched: 'dispatched', dispatched: 'dispatched' };
export default function AiDispatch() {
const [rows, setRows] = useState(dispatchQueue);
const [running, setRunning] = useState(false);
const [toast, showToast] = useToast();
const runOptimization = () => {
setRunning(true);
setRows((q) =>
q.map((d) => ({
...d,
confidence: Math.min(99, d.confidence + 2 + ((d.id.charCodeAt(d.id.length - 1) % 4))),
etaMin: Math.max(12, d.etaMin - 3),
status: NEXT_STATUS[d.status] || d.status
}))
);
showToast('MileTruth AI re-optimized the queue · ETAs improved');
setRunning(false);
};
return (
<>
<PageHeader
title="MileTruth AI Engine"
breadcrumbs={[{ label: 'AI Dispatch' }]}
action={<Button variant="contained" startIcon={<PlayArrowOutlinedIcon />} onClick={runOptimization} disabled={running}>Run Optimization</Button>}
/>
<LayerBanner
no={5}
icon={AutoAwesomeOutlinedIcon}
color="#EA580C"
title="AI Dispatch & Optimization"
subtitle="Smart pricing, route optimization, delay prediction and dynamic re-routing in real time."
steps={['Data Ingestion', 'Processing', 'Intelligence', 'Optimization', 'Assignment', 'Output']}
/>
<Grid container spacing={2.5}>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Route Savings" value={`${aiMetrics.routeSavings}%`} icon={RouteOutlinedIcon} color="success" trend={2.8} caption="vs naïve routing" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="ETA Accuracy" value={`${aiMetrics.avgEtaAccuracy}%`} icon={SpeedOutlinedIcon} color="info" trend={1.4} caption="predicted vs actual" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Re-optimizations" value={aiMetrics.reoptToday} icon={PsychologyOutlinedIcon} color="warning" caption="triggered today" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Delays Avoided" value={`${aiMetrics.delaysAvoided}/${aiMetrics.delaysPredicted}`} icon={InsightsOutlinedIcon} color="primary" caption="predicted & prevented" /></Grid>
<Grid item xs={12} lg={8}>
<MainCard title="Live Dispatch Queue" noPadding>
<Table>
<TableHead>
<TableRow>
<TableCell>Shipment</TableCell>
<TableCell>Route</TableCell>
<TableCell>Priority</TableCell>
<TableCell>SLA</TableCell>
<TableCell>AI-matched Rider</TableCell>
<TableCell>Confidence</TableCell>
<TableCell>Status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((d) => (
<TableRow key={d.id} hover>
<TableCell sx={{ fontWeight: 600, color: 'primary.main' }}>{d.id}</TableCell>
<TableCell><Typography variant="caption" color="text.secondary">{d.pickup} {d.drop}</Typography></TableCell>
<TableCell>
<Chip size="small" label={d.priority} sx={{ textTransform: 'capitalize', bgcolor: d.priority === 'high' ? '#FEEAE9' : d.priority === 'express' ? '#FFF7E0' : '#F0F0F0', color: d.priority === 'high' ? '#A82216' : d.priority === 'express' ? '#8A6500' : '#595959' }} />
</TableCell>
<TableCell><Typography variant="caption">{d.sla}</Typography></TableCell>
<TableCell>{d.suggestedRider}</TableCell>
<TableCell sx={{ minWidth: 110 }}>
<Stack direction="row" spacing={1} alignItems="center">
<Box sx={{ width: 56 }}>
<LinearProgress variant="determinate" value={d.confidence} color={d.confidence > 90 ? 'success' : 'warning'} sx={{ height: 6, borderRadius: 3 }} />
</Box>
<Typography variant="caption" sx={{ fontWeight: 600 }}>{d.confidence}%</Typography>
</Stack>
</TableCell>
<TableCell><StatusChip status={d.status} /></TableCell>
</TableRow>
))}
</TableBody>
</Table>
</MainCard>
</Grid>
<Grid item xs={12} lg={4}>
<MainCard title="Re-optimization Triggers">
<Stack divider={<Divider />} spacing={0}>
{aiTriggers.map((t) => {
const Icon = TRIGGER_ICONS[t.icon] || FiberNewOutlinedIcon;
return (
<Stack key={t.key} direction="row" spacing={1.5} alignItems="center" sx={{ py: 1.15 }}>
<Avatar variant="rounded" sx={{ bgcolor: 'warning.lighter', color: 'warning.dark', width: 36, height: 36 }}>
<Icon fontSize="small" />
</Avatar>
<Typography variant="body2" sx={{ flexGrow: 1 }}>{t.label}</Typography>
<Chip size="small" label={t.count} color={t.count > 5 ? 'warning' : 'default'} />
</Stack>
);
})}
</Stack>
</MainCard>
</Grid>
<Grid item xs={12}>
<MainCard title="MileTruth AI Pipeline">
<Stack direction={{ xs: 'column', md: 'row' }} spacing={2} alignItems="stretch">
{aiPipeline.map((stage, i) => (
<Box key={stage.stage} sx={{ flex: 1, position: 'relative' }}>
<Box sx={{ border: '1px solid', borderColor: 'grey.200', borderRadius: 2, p: 1.75, height: '100%', bgcolor: 'grey.50' }}>
<Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 1 }}>
<Box sx={{ width: 22, height: 22, borderRadius: '50%', bgcolor: 'warning.main', color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 12, fontWeight: 700 }}>{i + 1}</Box>
<Typography variant="subtitle2" sx={{ fontWeight: 700 }}>{stage.stage}</Typography>
</Stack>
<Stack spacing={0.5}>
{stage.items.map((it) => (
<Typography key={it} variant="caption" color="text.secondary"> {it}</Typography>
))}
</Stack>
</Box>
</Box>
))}
</Stack>
</MainCard>
</Grid>
</Grid>
<Toast {...toast} />
</>
);
}

200
src/pages/fleet/Fleet.jsx Normal file
View File

@@ -0,0 +1,200 @@
import { useState } from 'react';
import { Grid, Card, CardContent, Stack, Typography, Box, Avatar, LinearProgress, Table, TableBody, TableCell, TableHead, TableRow, Button, Tooltip, TextField, MenuItem } from '@mui/material';
import ElectricRickshawOutlinedIcon from '@mui/icons-material/ElectricRickshawOutlined';
import BatteryChargingFullOutlinedIcon from '@mui/icons-material/BatteryChargingFullOutlined';
import EnergySavingsLeafOutlinedIcon from '@mui/icons-material/EnergySavingsLeafOutlined';
import HealthAndSafetyOutlinedIcon from '@mui/icons-material/HealthAndSafetyOutlined';
import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined';
import AddOutlinedIcon from '@mui/icons-material/AddOutlined';
import BoltOutlinedIcon from '@mui/icons-material/BoltOutlined';
import PageHeader from '@/components/PageHeader';
import StatCard from '@/components/StatCard';
import MainCard from '@/components/MainCard';
import StatusChip from '@/components/StatusChip';
import LayerBanner from '@/components/LayerBanner';
import DonutChart from '@/components/charts/DonutChart';
import FormDialog from '@/components/FormDialog';
import Toast, { useToast } from '@/components/Toast';
import { fleet, fleetSummary } from '@/data/mock';
const BLANK = { id: '', model: '', type: 'EV 4W', powertrain: 'EV', capacityKg: '', hub: 'Koramangala Micro Hub' };
export default function Fleet() {
const [rows, setRows] = useState(fleet);
const [open, setOpen] = useState(false);
const [form, setForm] = useState(BLANK);
const [toast, showToast] = useToast();
const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value }));
const addVehicle = () => {
if (!form.model.trim()) return showToast('Enter a vehicle model', 'warning');
const isEv = form.powertrain === 'EV';
const id = form.id.trim() || `VH-${isEv ? 'EV' : 'IC'}-${String(100 + rows.length).slice(-3)}`;
setRows((r) => [
{ id, model: form.model, type: form.type, powertrain: form.powertrain, battery: isEv ? 100 : null, range: isEv ? 110 : 300, health: 100, capacityKg: Number(form.capacityKg) || 500, status: 'idle', rider: '—', hub: form.hub, uptime: 100 },
...r
]);
setForm(BLANK);
setOpen(false);
showToast(`${form.model} added to fleet`);
};
const mix = [
{ label: 'EV', value: fleetSummary.ev, color: '#00A854' },
{ label: 'ICE', value: fleetSummary.ice, color: '#8C8C8C' }
];
const states = [
{ label: 'On Trip', value: fleetSummary.onTrip, color: '#00A2AE' },
{ label: 'Charging', value: fleetSummary.charging, color: '#1D4ED8' },
{ label: 'Idle', value: fleetSummary.idle, color: '#FFBF00' },
{ label: 'Maintenance', value: fleetSummary.maintenance, color: '#F04134' }
];
return (
<>
<PageHeader
title="Fleet & Rider Operating System"
breadcrumbs={[{ label: 'Fleet' }]}
action={<Button variant="contained" startIcon={<AddOutlinedIcon />} onClick={() => setOpen(true)}>Add Vehicle</Button>}
/>
<LayerBanner
no={4}
icon={ElectricRickshawOutlinedIcon}
color="#15803D"
title="Fleet Management · EV-First"
subtitle="Vehicle health, battery monitoring, capacity & load planning across an EV-native fleet."
steps={['Vehicle Health', 'Battery Monitoring', 'Capacity Check', 'Load Optimization', 'Multi-Trip Planning']}
/>
<Grid container spacing={2.5}>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Fleet Size" value={fleetSummary.total} icon={LocalShippingOutlinedIcon} color="info" caption={`${fleetSummary.onTrip} on trip now`} /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="EV Share" value={`${fleetSummary.evShare}%`} icon={EnergySavingsLeafOutlinedIcon} color="success" trend={4.2} caption="EV-first target 85%" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Avg Battery" value={`${fleetSummary.avgBattery}%`} icon={BatteryChargingFullOutlinedIcon} color="warning" caption={`${fleetSummary.charging} charging`} /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="CO₂ Saved" value={`${(fleetSummary.co2SavedKg / 1000).toFixed(1)}t`} icon={BoltOutlinedIcon} color="success" trend={9.6} caption="this month" /></Grid>
<Grid item xs={12} lg={4}>
<MainCard title="Powertrain Mix">
<Box sx={{ py: 1 }}>
<DonutChart data={mix} centerValue={`${fleetSummary.evShare}%`} centerLabel="EV" />
</Box>
</MainCard>
</Grid>
<Grid item xs={12} lg={4}>
<MainCard title="Fleet Status">
<Box sx={{ py: 1 }}>
<DonutChart data={states} centerValue={fleetSummary.total} centerLabel="Vehicles" />
</Box>
</MainCard>
</Grid>
<Grid item xs={12} lg={4}>
<MainCard title="Operating Health">
<Stack spacing={2} sx={{ py: 0.5 }}>
<Metric icon={HealthAndSafetyOutlinedIcon} color="#00A854" label="Avg Vehicle Health" value={`${fleetSummary.avgHealth}%`} bar={fleetSummary.avgHealth} />
<Metric icon={BatteryChargingFullOutlinedIcon} color="#1D4ED8" label="Avg Battery (EV)" value={`${fleetSummary.avgBattery}%`} bar={fleetSummary.avgBattery} />
<Metric icon={EnergySavingsLeafOutlinedIcon} color="#15803D" label="EV Fleet Share" value={`${fleetSummary.evShare}%`} bar={fleetSummary.evShare} />
</Stack>
</MainCard>
</Grid>
<Grid item xs={12}>
<MainCard title="Vehicles" noPadding>
<Table>
<TableHead>
<TableRow>
<TableCell>Vehicle</TableCell>
<TableCell>Type</TableCell>
<TableCell>Battery / Range</TableCell>
<TableCell>Health</TableCell>
<TableCell align="right">Capacity</TableCell>
<TableCell>Assigned</TableCell>
<TableCell>Hub</TableCell>
<TableCell>Status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((v) => (
<TableRow key={v.id} hover>
<TableCell>
<Stack direction="row" spacing={1.25} alignItems="center">
<Avatar variant="rounded" sx={{ bgcolor: v.powertrain === 'EV' ? 'success.lighter' : 'grey.100', color: v.powertrain === 'EV' ? 'success.main' : 'grey.600', width: 36, height: 36 }}>
{v.powertrain === 'EV' ? <BoltOutlinedIcon fontSize="small" /> : <LocalShippingOutlinedIcon fontSize="small" />}
</Avatar>
<Box>
<Typography variant="subtitle2">{v.model}</Typography>
<Typography variant="caption" color="text.secondary">{v.id}</Typography>
</Box>
</Stack>
</TableCell>
<TableCell><Typography variant="caption">{v.type}</Typography></TableCell>
<TableCell sx={{ minWidth: 130 }}>
{v.powertrain === 'EV' ? (
<>
<Stack direction="row" justifyContent="space-between">
<Typography variant="caption" color="text.secondary">{v.battery}%</Typography>
<Typography variant="caption" color="text.secondary">{v.range} km</Typography>
</Stack>
<LinearProgress variant="determinate" value={v.battery} color={v.battery < 30 ? 'error' : v.battery < 50 ? 'warning' : 'success'} sx={{ height: 6, borderRadius: 3, mt: 0.5 }} />
</>
) : (
<Typography variant="caption" color="text.secondary">Fuel · {v.range} km range</Typography>
)}
</TableCell>
<TableCell>
<Tooltip title={`Uptime ${v.uptime}%`}>
<Typography variant="subtitle2" sx={{ color: v.health < 80 ? 'warning.main' : 'success.main' }}>{v.health}%</Typography>
</Tooltip>
</TableCell>
<TableCell align="right">{v.capacityKg} kg</TableCell>
<TableCell>{v.rider}</TableCell>
<TableCell><Typography variant="caption" color="text.secondary">{v.hub}</Typography></TableCell>
<TableCell><StatusChip status={v.status} /></TableCell>
</TableRow>
))}
</TableBody>
</Table>
</MainCard>
</Grid>
</Grid>
<FormDialog open={open} onClose={() => setOpen(false)} title="Add Vehicle" onSubmit={addVehicle} submitLabel="Add Vehicle">
<Grid container spacing={2} sx={{ mt: 0 }}>
<Grid item xs={12} sm={6}><TextField fullWidth size="small" label="Vehicle ID (optional)" value={form.id} onChange={set('id')} placeholder="auto-generated" /></Grid>
<Grid item xs={12} sm={6}><TextField fullWidth size="small" label="Model" value={form.model} onChange={set('model')} placeholder="e.g. Tata Ace EV" /></Grid>
<Grid item xs={12} sm={6}>
<TextField select fullWidth size="small" label="Powertrain" value={form.powertrain} onChange={set('powertrain')}>
<MenuItem value="EV">EV</MenuItem>
<MenuItem value="ICE">ICE</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6}>
<TextField select fullWidth size="small" label="Type" value={form.type} onChange={set('type')}>
{['EV 2W', 'EV 3W', 'EV 4W', 'ICE 2W', 'ICE 4W'].map((t) => <MenuItem key={t} value={t}>{t}</MenuItem>)}
</TextField>
</Grid>
<Grid item xs={12} sm={6}><TextField fullWidth size="small" type="number" label="Capacity (kg)" value={form.capacityKg} onChange={set('capacityKg')} /></Grid>
<Grid item xs={12} sm={6}>
<TextField select fullWidth size="small" label="Home Hub" value={form.hub} onChange={set('hub')}>
{['Koramangala Micro Hub', 'Whitefield City Hub', 'Hoskote Regional Hub', 'Andheri City Hub', 'Hitech Cross Dock', 'Bilaspur Regional Hub'].map((h) => <MenuItem key={h} value={h}>{h}</MenuItem>)}
</TextField>
</Grid>
</Grid>
</FormDialog>
<Toast {...toast} />
</>
);
}
function Metric({ icon: Icon, color, label, value, bar }) {
return (
<Box>
<Stack direction="row" spacing={1} alignItems="center" sx={{ mb: 0.5 }}>
<Icon sx={{ fontSize: 18, color }} />
<Typography variant="caption" color="text.secondary" sx={{ flexGrow: 1 }}>{label}</Typography>
<Typography variant="subtitle2" sx={{ fontWeight: 700 }}>{value}</Typography>
</Stack>
<LinearProgress variant="determinate" value={bar} sx={{ height: 6, borderRadius: 3, '& .MuiLinearProgress-bar': { bgcolor: color } }} />
</Box>
);
}

View File

@@ -0,0 +1,199 @@
import { useState } from 'react';
import { Grid, Card, CardContent, Stack, Typography, Box, Avatar, LinearProgress, Table, TableBody, TableCell, TableHead, TableRow, Button, Divider, TextField, MenuItem } from '@mui/material';
import WarehouseOutlinedIcon from '@mui/icons-material/WarehouseOutlined';
import HubOutlinedIcon from '@mui/icons-material/HubOutlined';
import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined';
import RouteOutlinedIcon from '@mui/icons-material/RouteOutlined';
import AddLocationAltOutlinedIcon from '@mui/icons-material/AddLocationAltOutlined';
import PageHeader from '@/components/PageHeader';
import StatCard from '@/components/StatCard';
import MainCard from '@/components/MainCard';
import StatusChip from '@/components/StatusChip';
import LayerBanner from '@/components/LayerBanner';
import MapPlaceholder from '@/components/MapPlaceholder';
import FormDialog from '@/components/FormDialog';
import Toast, { useToast } from '@/components/Toast';
import { hubs, hubNetworkTypes, lineHauls } from '@/data/mock';
const BLANK = { name: '', type: 'Micro Hub', city: 'Bengaluru', capacity: '', dock: '' };
export default function HubNetwork() {
const [rows, setRows] = useState(hubs);
const [open, setOpen] = useState(false);
const [form, setForm] = useState(BLANK);
const [toast, showToast] = useToast();
const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value }));
const addHub = () => {
if (!form.name.trim()) return showToast('Enter a hub name', 'warning');
const cityCode = (form.city.slice(0, 3) || 'HUB').toUpperCase();
setRows((r) => [
...r,
{ id: `HUB-${cityCode}-${String(10 + r.length).slice(-2)}`, name: form.name, type: form.type, city: form.city, lat: 0, lng: 0, capacity: Number(form.capacity) || 1000, load: 0, inbound: 0, outbound: 0, dock: Number(form.dock) || 4, status: 'online' }
]);
setForm(BLANK);
setOpen(false);
showToast(`${form.name} added to network`);
};
const totalCap = rows.reduce((s, h) => s + h.capacity, 0);
const totalLoad = rows.reduce((s, h) => s + h.load, 0);
const util = Math.round((totalLoad / totalCap) * 100);
return (
<>
<PageHeader
title="Hub & Network Orchestration"
breadcrumbs={[{ label: 'Hub Network' }]}
action={<Button variant="contained" startIcon={<AddLocationAltOutlinedIcon />} onClick={() => setOpen(true)}>Add Hub</Button>}
/>
<LayerBanner
no={3}
icon={HubOutlinedIcon}
color="#0E7C7B"
title="Intelligent Hub Network"
subtitle="MileTruth AI selects the optimal origin hub, then sorts, line-hauls and routes to destination."
steps={['Nearest Hub', 'Pickup Assignment', 'Hub Operations', 'Line-Haul Transfer', 'Destination Hub']}
/>
<Grid container spacing={2.5}>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Active Hubs" value={hubs.length} icon={WarehouseOutlinedIcon} color="info" caption="across 4 cities" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Network Utilisation" value={`${util}%`} icon={HubOutlinedIcon} color="warning" trend={3.1} caption="vs yesterday" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Line-Hauls Running" value={lineHauls.filter((l) => l.status === 'in-transit').length} icon={LocalShippingOutlinedIcon} color="primary" caption="inter-city" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Throughput Today" value={totalLoad.toLocaleString('en-IN')} icon={RouteOutlinedIcon} color="success" trend={6.4} caption="parcels sorted" /></Grid>
<Grid item xs={12} lg={8}>
<MainCard title="Hub Load & Capacity" noPadding>
<Table>
<TableHead>
<TableRow>
<TableCell>Hub</TableCell>
<TableCell>Type</TableCell>
<TableCell>City</TableCell>
<TableCell>Load / Capacity</TableCell>
<TableCell align="center">Docks</TableCell>
<TableCell>Status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((h) => {
const pct = Math.round((h.load / h.capacity) * 100);
return (
<TableRow key={h.id} hover>
<TableCell>
<Typography variant="subtitle2">{h.name}</Typography>
<Typography variant="caption" color="text.secondary">{h.id}</Typography>
</TableCell>
<TableCell><Typography variant="caption">{h.type}</Typography></TableCell>
<TableCell>{h.city}</TableCell>
<TableCell sx={{ minWidth: 160 }}>
<Stack direction="row" justifyContent="space-between">
<Typography variant="caption" color="text.secondary">{h.load.toLocaleString('en-IN')} / {h.capacity.toLocaleString('en-IN')}</Typography>
<Typography variant="caption" sx={{ fontWeight: 600 }}>{pct}%</Typography>
</Stack>
<LinearProgress variant="determinate" value={pct} color={pct > 90 ? 'error' : pct > 75 ? 'warning' : 'success'} sx={{ height: 6, borderRadius: 3, mt: 0.5 }} />
</TableCell>
<TableCell align="center">{h.dock}</TableCell>
<TableCell><StatusChip status={h.status} /></TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</MainCard>
</Grid>
<Grid item xs={12} lg={4}>
<MainCard title="Network Types">
<Stack divider={<Divider />} spacing={0}>
{hubNetworkTypes.map((t) => (
<Stack key={t.type} direction="row" spacing={2} alignItems="center" sx={{ py: 1.4 }}>
<Avatar variant="rounded" sx={{ bgcolor: 'info.lighter', color: 'info.main', width: 40, height: 40 }}>
<WarehouseOutlinedIcon fontSize="small" />
</Avatar>
<Box sx={{ flexGrow: 1 }}>
<Typography variant="subtitle2">{t.type}</Typography>
<Typography variant="caption" color="text.secondary">{t.desc}</Typography>
</Box>
<Typography variant="h4" sx={{ fontWeight: 700, color: 'grey.800' }}>{t.count}</Typography>
</Stack>
))}
</Stack>
</MainCard>
</Grid>
<Grid item xs={12} lg={7}>
<MainCard title="Line-Haul Corridors" noPadding>
<Table>
<TableHead>
<TableRow>
<TableCell>Corridor</TableCell>
<TableCell>Vehicle</TableCell>
<TableCell align="right">Distance</TableCell>
<TableCell>Load</TableCell>
<TableCell>ETA</TableCell>
<TableCell>Status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{lineHauls.map((l) => (
<TableRow key={l.id} hover>
<TableCell>
<Typography variant="subtitle2">{l.corridor}</Typography>
<Typography variant="caption" color="text.secondary">{l.from} {l.to}</Typography>
</TableCell>
<TableCell><Typography variant="caption">{l.vehicle}</Typography></TableCell>
<TableCell align="right">{l.distance.toLocaleString('en-IN')} km</TableCell>
<TableCell sx={{ minWidth: 90 }}>
<LinearProgress variant="determinate" value={l.load} sx={{ height: 6, borderRadius: 3 }} />
<Typography variant="caption" color="text.secondary">{l.load}%</Typography>
</TableCell>
<TableCell>{l.eta}</TableCell>
<TableCell><StatusChip status={l.status} /></TableCell>
</TableRow>
))}
</TableBody>
</Table>
</MainCard>
</Grid>
<Grid item xs={12} lg={5}>
<MainCard title="Network Map">
<MapPlaceholder
height={320}
label="Hub Network"
showRoute={false}
pins={[
{ x: '24%', y: '60%', label: 'BLR', color: '#0E7C7B' },
{ x: '14%', y: '38%', label: 'MUM', color: '#1D4ED8' },
{ x: '40%', y: '34%', label: 'HYD', color: '#EA580C' },
{ x: '46%', y: '14%', label: 'DEL', color: '#C01227' }
]}
/>
</MainCard>
</Grid>
</Grid>
<FormDialog open={open} onClose={() => setOpen(false)} title="Add Hub" onSubmit={addHub} submitLabel="Add Hub">
<Grid container spacing={2} sx={{ mt: 0 }}>
<Grid item xs={12}><TextField fullWidth size="small" label="Hub Name" value={form.name} onChange={set('name')} placeholder="e.g. Electronic City Micro Hub" /></Grid>
<Grid item xs={12} sm={6}>
<TextField select fullWidth size="small" label="Type" value={form.type} onChange={set('type')}>
{['Micro Hub', 'City Hub', 'Regional Hub', 'Cross Dock'].map((t) => <MenuItem key={t} value={t}>{t}</MenuItem>)}
</TextField>
</Grid>
<Grid item xs={12} sm={6}>
<TextField select fullWidth size="small" label="City" value={form.city} onChange={set('city')}>
{['Bengaluru', 'Mumbai', 'Delhi NCR', 'Hyderabad', 'Chennai', 'Pune'].map((c) => <MenuItem key={c} value={c}>{c}</MenuItem>)}
</TextField>
</Grid>
<Grid item xs={12} sm={6}><TextField fullWidth size="small" type="number" label="Capacity (parcels)" value={form.capacity} onChange={set('capacity')} /></Grid>
<Grid item xs={12} sm={6}><TextField fullWidth size="small" type="number" label="Docks" value={form.dock} onChange={set('dock')} /></Grid>
</Grid>
</FormDialog>
<Toast {...toast} />
</>
);
}

View File

@@ -0,0 +1,133 @@
import { useState } from 'react';
import { Grid, Card, CardContent, Stack, Typography, Box, Avatar, Button, Chip, TextField, MenuItem } from '@mui/material';
import HubOutlinedIcon from '@mui/icons-material/HubOutlined';
import ApiOutlinedIcon from '@mui/icons-material/ApiOutlined';
import BusinessOutlinedIcon from '@mui/icons-material/BusinessOutlined';
import PaymentsOutlinedIcon from '@mui/icons-material/PaymentsOutlined';
import GroupsOutlinedIcon from '@mui/icons-material/GroupsOutlined';
import AddLinkOutlinedIcon from '@mui/icons-material/AddLinkOutlined';
import PageHeader from '@/components/PageHeader';
import StatCard from '@/components/StatCard';
import MainCard from '@/components/MainCard';
import StatusChip from '@/components/StatusChip';
import LayerBanner from '@/components/LayerBanner';
import FormDialog from '@/components/FormDialog';
import Toast, { useToast } from '@/components/Toast';
import { integrations } from '@/data/mock';
const GROUP_META = {
'APIs & Integrations': { icon: ApiOutlinedIcon, color: '#0F766E' },
'Enterprise Systems': { icon: BusinessOutlinedIcon, color: '#1D4ED8' },
'Payment & Billing': { icon: PaymentsOutlinedIcon, color: '#15803D' },
'Partners & Ecosystem': { icon: GroupsOutlinedIcon, color: '#EA580C' }
};
const BLANK = { name: '', group: 'APIs & Integrations', desc: '' };
export default function Integrations() {
const [rows, setRows] = useState(integrations);
const [open, setOpen] = useState(false);
const [form, setForm] = useState(BLANK);
const [toast, showToast] = useToast();
const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value }));
const addIntegration = () => {
if (!form.name.trim()) return showToast('Enter an integration name', 'warning');
const meta = GROUP_META[form.group] || {};
setRows((r) => [...r, { name: form.name, group: form.group, desc: form.desc || 'Custom connector', status: 'pending', calls: '—', icon: form.group === 'Payment & Billing' ? 'pay' : form.group === 'Enterprise Systems' ? 'erp' : form.group === 'Partners & Ecosystem' ? 'partner' : 'api' }]);
setForm(BLANK);
setOpen(false);
showToast(`${form.name} added — pending connection`);
};
const groups = [...new Set(rows.map((i) => i.group))];
const connected = rows.filter((i) => i.status === 'connected').length;
const issues = rows.filter((i) => i.status !== 'connected').length;
return (
<>
<PageHeader
title="Integrations & Ecosystem"
breadcrumbs={[{ label: 'Integrations' }]}
action={<Button variant="contained" startIcon={<AddLinkOutlinedIcon />} onClick={() => setOpen(true)}>Add Integration</Button>}
/>
<LayerBanner
no={8}
icon={HubOutlinedIcon}
color="#0F766E"
title="Integrations & Ecosystem"
subtitle="One system that plugs into partner APIs, ERP/WMS, payments and the broader logistics ecosystem."
steps={['APIs & Integrations', 'Enterprise Systems', 'Payment & Billing', 'Partners & Ecosystem']}
/>
<Grid container spacing={2.5} sx={{ mb: 0.5 }}>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Connectors" value={rows.length} icon={HubOutlinedIcon} color="info" caption="across 4 categories" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Connected" value={connected} icon={ApiOutlinedIcon} color="success" caption="healthy" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Attention" value={issues} icon={BusinessOutlinedIcon} color="warning" caption="degraded / pending" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="API Calls" value="7.2M" icon={PaymentsOutlinedIcon} color="primary" trend={5.8} caption="per day" /></Grid>
</Grid>
{groups.map((g) => {
const meta = GROUP_META[g] || { icon: HubOutlinedIcon, color: '#C01227' };
const GIcon = meta.icon;
return (
<Box key={g} sx={{ mt: 2.5 }}>
<MainCard
title={
<Stack direction="row" spacing={1.25} alignItems="center">
<Avatar variant="rounded" sx={{ bgcolor: hexA(meta.color, 0.12), color: meta.color, width: 34, height: 34 }}>
<GIcon fontSize="small" />
</Avatar>
<Typography variant="h5">{g}</Typography>
</Stack>
}
>
<Grid container spacing={2}>
{rows.filter((i) => i.group === g).map((i) => (
<Grid item xs={12} sm={6} lg={4} key={i.name}>
<Card variant="outlined" sx={{ height: '100%' }}>
<CardContent>
<Stack direction="row" justifyContent="space-between" alignItems="flex-start">
<Box>
<Typography variant="subtitle1" sx={{ fontWeight: 700 }}>{i.name}</Typography>
<Typography variant="caption" color="text.secondary">{i.desc}</Typography>
</Box>
<StatusChip status={i.status} />
</Stack>
<Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ mt: 1.5 }}>
<Chip size="small" label={i.calls} sx={{ bgcolor: 'grey.100' }} />
<Button size="small" onClick={() => showToast(i.status === 'pending' ? `Connecting ${i.name}` : `${i.name} settings opened`)}>
{i.status === 'pending' ? 'Connect' : 'Configure'}
</Button>
</Stack>
</CardContent>
</Card>
</Grid>
))}
</Grid>
</MainCard>
</Box>
);
})}
<FormDialog open={open} onClose={() => setOpen(false)} title="Add Integration" onSubmit={addIntegration} submitLabel="Add">
<Grid container spacing={2} sx={{ mt: 0 }}>
<Grid item xs={12}><TextField fullWidth size="small" label="Integration Name" value={form.name} onChange={set('name')} placeholder="e.g. Shopify, Freshdesk" /></Grid>
<Grid item xs={12}>
<TextField select fullWidth size="small" label="Category" value={form.group} onChange={set('group')}>
{Object.keys(GROUP_META).map((g) => <MenuItem key={g} value={g}>{g}</MenuItem>)}
</TextField>
</Grid>
<Grid item xs={12}><TextField fullWidth size="small" label="Description" value={form.desc} onChange={set('desc')} placeholder="What does this connector do?" /></Grid>
</Grid>
</FormDialog>
<Toast {...toast} />
</>
);
}
const hexA = (hex, a) => {
const n = parseInt(hex.replace('#', ''), 16);
return `rgba(${n >> 16}, ${(n >> 8) & 255}, ${n & 255}, ${a})`;
};

View File

@@ -0,0 +1,60 @@
import { Grid, Stack, Typography, Box, Button } from '@mui/material';
import RouteOutlinedIcon from '@mui/icons-material/RouteOutlined';
import WarehouseOutlinedIcon from '@mui/icons-material/WarehouseOutlined';
import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined';
import HomeOutlinedIcon from '@mui/icons-material/HomeOutlined';
import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
import PageHeader from '@/components/PageHeader';
import StatCard from '@/components/StatCard';
import MainCard from '@/components/MainCard';
import LayerBanner from '@/components/LayerBanner';
import ThreeMileStrip from '@/components/ThreeMileStrip';
import MapPlaceholder from '@/components/MapPlaceholder';
import Toast, { useToast } from '@/components/Toast';
import { threeMile } from '@/data/mock';
export default function ThreeMile() {
const [toast, showToast] = useToast();
return (
<>
<PageHeader
title="Three-Mile Network"
breadcrumbs={[{ label: 'Three-Mile Network' }]}
action={<Button variant="outlined" startIcon={<FileDownloadOutlinedIcon />} onClick={() => showToast('Three-mile report exported')}>Export</Button>}
/>
<LayerBanner
no={0}
icon={RouteOutlinedIcon}
color="#C01227"
title="One Connected System · First → Mid → Last Mile"
subtitle="Doormile unifies first-mile pickup, mid-mile line-haul and last-mile delivery into one connected flow."
steps={['Origin to Hub', 'Hub to Hub Transit', 'Hub to Doorstep']}
/>
<Grid container spacing={2.5} sx={{ mb: 2.5 }}>
<Grid item xs={12} sm={4}><StatCard title="First Mile · Pickups" value={threeMile[0].metric} icon={WarehouseOutlinedIcon} color="warning" caption={`${threeMile[0].onTime}% on-time`} /></Grid>
<Grid item xs={12} sm={4}><StatCard title="Mid Mile · Line-Hauls" value={threeMile[1].metric} icon={LocalShippingOutlinedIcon} color="info" caption={`${threeMile[1].onTime}% on-time`} /></Grid>
<Grid item xs={12} sm={4}><StatCard title="Last Mile · Out for Delivery" value={threeMile[2].metric} icon={HomeOutlinedIcon} color="primary" caption={`${threeMile[2].onTime}% on-time`} /></Grid>
</Grid>
<Box sx={{ mb: 2.5 }}>
<ThreeMileStrip />
</Box>
<MainCard title="Connected Miles — Live Flow">
<MapPlaceholder
height={340}
label="First → Mid → Last Mile"
pins={[
{ x: '14%', y: '74%', label: 'Origin', color: '#EA580C' },
{ x: '44%', y: '46%', label: 'Hub', color: '#0E7C7B' },
{ x: '82%', y: '20%', label: 'Doorstep', color: '#1D4ED8' }
]}
/>
</MainCard>
<Toast {...toast} />
</>
);
}

View File

@@ -0,0 +1,234 @@
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Grid, Stack, Typography, Box, Avatar, Table, TableBody, TableCell, TableHead, TableRow, Button, Chip, TextField, InputAdornment, Divider } from '@mui/material';
import MyLocationOutlinedIcon from '@mui/icons-material/MyLocationOutlined';
import PhotoCameraOutlinedIcon from '@mui/icons-material/PhotoCameraOutlined';
import AltRouteOutlinedIcon from '@mui/icons-material/AltRouteOutlined';
import WarningAmberOutlinedIcon from '@mui/icons-material/WarningAmberOutlined';
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
import ShareOutlinedIcon from '@mui/icons-material/ShareOutlined';
import ContentCopyOutlinedIcon from '@mui/icons-material/ContentCopyOutlined';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked';
import PageHeader from '@/components/PageHeader';
import StatCard from '@/components/StatCard';
import MainCard from '@/components/MainCard';
import StatusChip from '@/components/StatusChip';
import LayerBanner from '@/components/LayerBanner';
import MapPlaceholder from '@/components/MapPlaceholder';
import FormDialog from '@/components/FormDialog';
import Toast, { useToast } from '@/components/Toast';
import { executionStages, executionFeed, ridersLive, orderTimeline } from '@/data/mock';
export default function LiveTracking() {
const navigate = useNavigate();
const [track, setTrack] = useState(null);
const [share, setShare] = useState(false);
const [toast, showToast] = useToast();
const trackLink = track ? `https://track.doormile.com/${track.id}` : 'https://track.doormile.com';
const copyLink = () => {
if (navigator.clipboard) navigator.clipboard.writeText(trackLink).catch(() => {});
showToast('Tracking link copied');
};
return (
<>
<PageHeader
title="Execution & Visibility Layer"
breadcrumbs={[{ label: 'Live Tracking' }]}
action={<Button variant="outlined" startIcon={<ShareOutlinedIcon />} onClick={() => setShare(true)}>Share Tracking</Button>}
/>
<LayerBanner
no={6}
icon={MyLocationOutlinedIcon}
color="#1D4ED8"
title="Execution & Visibility"
subtitle="Live GPS, photo proof of pickup & delivery, dynamic re-routing and exception handling."
steps={['Pickup Execution', 'In-Transit Tracking', 'Dynamic Re-routing', 'Delivery Execution', 'Exception Handling']}
/>
<Grid container spacing={2.5}>
{executionStages.map((s) => (
<Grid item xs={6} md={2.4} key={s.key}>
<Box sx={{ bgcolor: 'background.paper', border: '1px solid', borderColor: 'grey.200', borderRadius: 2, p: 2, height: '100%' }}>
<Typography variant="h3" sx={{ fontWeight: 700, color: 'grey.800' }}>{s.count.toLocaleString('en-IN')}</Typography>
<Typography variant="subtitle2" sx={{ mt: 0.25 }}>{s.title}</Typography>
<Stack spacing={0.25} sx={{ mt: 1 }}>
{s.items.slice(0, 3).map((it) => (
<Typography key={it} variant="caption" color="text.secondary"> {it}</Typography>
))}
</Stack>
</Box>
</Grid>
))}
<Grid item xs={12} lg={7}>
<MainCard title="Live Fleet Map">
<MapPlaceholder
height={380}
label="Live Tracking"
riders={ridersLive.slice(0, 4).map((r, i) => ({
x: ['28%', '52%', '70%', '40%'][i],
y: ['44%', '30%', '58%', '66%'][i],
active: r.active
}))}
/>
</MainCard>
</Grid>
<Grid item xs={12} lg={5}>
<MainCard title="Live Execution Feed" contentSx={{ p: 0 }}>
<Stack>
{executionFeed.map((e, i) => (
<Box key={e.id} sx={{ p: 2, borderTop: i === 0 ? 'none' : '1px solid', borderColor: 'grey.100' }}>
<Stack direction="row" spacing={1.5} alignItems="flex-start">
<Avatar variant="rounded" sx={{ width: 38, height: 38, ...stageStyle(e.stage) }}>
{stageIcon(e.stage)}
</Avatar>
<Box sx={{ flexGrow: 1, minWidth: 0 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="subtitle2" sx={{ color: 'primary.main' }}>{e.id}</Typography>
<Typography variant="caption" color="text.secondary">{e.time}</Typography>
</Stack>
<Stack direction="row" spacing={0.75} alignItems="center" sx={{ my: 0.25 }}>
<StatusChip status={mapStage(e.stage)} label={e.stage} />
{e.proof && <Chip size="small" icon={<PhotoCameraOutlinedIcon sx={{ fontSize: 14 }} />} label="Proof" sx={{ bgcolor: 'grey.100' }} />}
</Stack>
<Typography variant="caption" color="text.secondary">{e.rider} · {e.loc}</Typography>
<Typography variant="caption" sx={{ display: 'block' }}>{e.detail}</Typography>
</Box>
</Stack>
</Box>
))}
</Stack>
</MainCard>
</Grid>
<Grid item xs={12}>
<MainCard title="Customer & Business Visibility" noPadding>
<Table>
<TableHead>
<TableRow>
<TableCell>Shipment</TableCell>
<TableCell>Rider</TableCell>
<TableCell>Current Stage</TableCell>
<TableCell>Location</TableCell>
<TableCell>Update</TableCell>
<TableCell align="right">Tracking</TableCell>
</TableRow>
</TableHead>
<TableBody>
{executionFeed.map((e) => (
<TableRow key={e.id} hover>
<TableCell sx={{ fontWeight: 600, color: 'primary.main' }}>{e.id}</TableCell>
<TableCell>{e.rider}</TableCell>
<TableCell><StatusChip status={mapStage(e.stage)} label={e.stage} /></TableCell>
<TableCell>{e.loc}</TableCell>
<TableCell><Typography variant="caption" color="text.secondary">{e.detail}</Typography></TableCell>
<TableCell align="right">
<Button size="small" startIcon={<VisibilityOutlinedIcon />} onClick={() => setTrack(e)}>Track</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</MainCard>
</Grid>
</Grid>
{/* Tracking detail */}
<FormDialog
open={Boolean(track)}
onClose={() => setTrack(null)}
title={track ? `Tracking · ${track.id}` : 'Tracking'}
hideActions
>
{track && (
<Stack spacing={2}>
<Stack direction="row" spacing={1} alignItems="center">
<StatusChip status={mapStage(track.stage)} label={track.stage} />
<Typography variant="caption" color="text.secondary">Updated {track.time}</Typography>
</Stack>
<Stack direction="row" spacing={3}>
<Box><Typography variant="caption" color="text.secondary">Rider</Typography><Typography variant="subtitle2">{track.rider}</Typography></Box>
<Box><Typography variant="caption" color="text.secondary">Location</Typography><Typography variant="subtitle2">{track.loc}</Typography></Box>
</Stack>
<Typography variant="body2" color="text.secondary">{track.detail}</Typography>
<MapPlaceholder height={180} label={track.id} />
<Box>
<Typography variant="subtitle2" sx={{ mb: 1 }}>Journey</Typography>
<Stack spacing={0}>
{orderTimeline.map((t, i) => (
<Stack key={t.label} direction="row" spacing={1.5} alignItems="center" sx={{ py: 0.75 }}>
{t.done ? <CheckCircleIcon sx={{ color: 'success.main', fontSize: 20 }} /> : <RadioButtonUncheckedIcon sx={{ color: 'grey.400', fontSize: 20 }} />}
<Typography variant="body2" sx={{ flexGrow: 1, fontWeight: t.done ? 600 : 400, color: t.done ? 'text.primary' : 'text.secondary' }}>{t.label}</Typography>
<Typography variant="caption" color="text.secondary">{t.time}</Typography>
</Stack>
))}
</Stack>
</Box>
<Divider />
<Stack direction="row" spacing={1.5} justifyContent="flex-end">
<Button startIcon={<ShareOutlinedIcon />} onClick={() => { setShare(true); }}>Share</Button>
<Button variant="contained" startIcon={<AltRouteOutlinedIcon />} onClick={() => navigate('/tracking/journey')}>View full journey</Button>
</Stack>
</Stack>
)}
</FormDialog>
{/* Share tracking */}
<FormDialog open={share} onClose={() => setShare(false)} title="Share Live Tracking" hideActions>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1.5 }}>
Anyone with this link can follow the shipment in real time.
</Typography>
<TextField
fullWidth
size="small"
value={trackLink}
InputProps={{
readOnly: true,
endAdornment: (
<InputAdornment position="end">
<Button size="small" startIcon={<ContentCopyOutlinedIcon fontSize="small" />} onClick={copyLink}>Copy</Button>
</InputAdornment>
)
}}
/>
<Stack direction="row" spacing={1.5} sx={{ mt: 2 }}>
<Button variant="contained" onClick={() => { showToast('Tracking sent via SMS'); setShare(false); }}>Send SMS</Button>
<Button variant="outlined" onClick={() => { showToast('Tracking sent on WhatsApp'); setShare(false); }}>WhatsApp</Button>
</Stack>
</FormDialog>
<Toast {...toast} />
</>
);
}
const mapStage = (stage) => {
const k = { 'In-Transit': 'in-transit', 'Picked Up': 'picked-up', Delivered: 'delivered', Exception: 'exception', Dispatched: 'dispatched' };
return k[stage] || 'active';
};
function stageStyle(stage) {
const m = {
'In-Transit': { bgcolor: 'info.lighter', color: 'info.main' },
'Picked Up': { bgcolor: 'primary.lighter', color: 'primary.main' },
Delivered: { bgcolor: 'success.lighter', color: 'success.main' },
Exception: { bgcolor: 'error.lighter', color: 'error.main' },
Dispatched: { bgcolor: 'grey.100', color: 'grey.700' }
};
return m[stage] || m.Dispatched;
}
function stageIcon(stage) {
if (stage === 'Exception') return <WarningAmberOutlinedIcon fontSize="small" />;
if (stage === 'In-Transit' || stage === 'Dispatched') return <AltRouteOutlinedIcon fontSize="small" />;
return <PhotoCameraOutlinedIcon fontSize="small" />;
}

View File

@@ -0,0 +1,174 @@
import { Grid, Stack, Typography, Box, Avatar, Button, Chip, LinearProgress, Divider } from '@mui/material';
import ReceiptLongOutlinedIcon from '@mui/icons-material/ReceiptLongOutlined';
import TwoWheelerOutlinedIcon from '@mui/icons-material/TwoWheelerOutlined';
import WarehouseOutlinedIcon from '@mui/icons-material/WarehouseOutlined';
import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined';
import HomeOutlinedIcon from '@mui/icons-material/HomeOutlined';
import CheckIcon from '@mui/icons-material/Check';
import AltRouteOutlinedIcon from '@mui/icons-material/AltRouteOutlined';
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';
import ShareOutlinedIcon from '@mui/icons-material/ShareOutlined';
import AutoAwesomeOutlinedIcon from '@mui/icons-material/AutoAwesomeOutlined';
import PageHeader from '@/components/PageHeader';
import MainCard from '@/components/MainCard';
import MapPlaceholder from '@/components/MapPlaceholder';
import Toast, { useToast } from '@/components/Toast';
import { shipmentJourney as j } from '@/data/mock';
const HOP_ICONS = { order: ReceiptLongOutlinedIcon, agent: TwoWheelerOutlinedIcon, hub: WarehouseOutlinedIcon, truck: LocalShippingOutlinedIcon, done: HomeOutlinedIcon };
export default function ShipmentJourney() {
const [toast, showToast] = useToast();
const doneCount = j.hops.filter((h) => h.status === 'done').length;
return (
<>
<PageHeader
title="Shipment Journey"
breadcrumbs={[{ label: 'Live Tracking', to: '/tracking' }, { label: j.id }]}
action={
<Stack direction="row" spacing={1.5}>
<Button variant="outlined" startIcon={<AltRouteOutlinedIcon />} onClick={() => showToast('MileTruth AI re-evaluated the route — ETA protected')}>Re-optimize</Button>
<Button variant="contained" startIcon={<ShareOutlinedIcon />} onClick={() => showToast('Tracking link shared with customer')}>Share</Button>
</Stack>
}
/>
{/* Summary */}
<Box sx={{ borderRadius: 2, border: '1px solid', borderColor: 'grey.200', bgcolor: 'background.paper', p: { xs: 2, md: 2.5 }, mb: 2.5 }}>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} md={5}>
<Typography variant="overline" color="text.secondary">{j.id} · {j.client}</Typography>
<Stack direction="row" spacing={1.5} alignItems="center" sx={{ mt: 0.5 }}>
<Box>
<Typography variant="h5" sx={{ fontWeight: 700, color: 'grey.900' }}>{j.from.city}</Typography>
<Typography variant="caption" color="text.secondary">{j.from.area}</Typography>
</Box>
<Box sx={{ flexGrow: 1, height: 2, bgcolor: 'grey.200', position: 'relative', mx: 1, maxWidth: 90 }}>
<LocalShippingOutlinedIcon sx={{ fontSize: 18, color: 'primary.main', position: 'absolute', top: -9, left: `${j.progress}%`, transform: 'translateX(-50%)' }} />
</Box>
<Box>
<Typography variant="h5" sx={{ fontWeight: 700, color: 'grey.900' }}>{j.to.city}</Typography>
<Typography variant="caption" color="text.secondary">{j.to.area}</Typography>
</Box>
</Stack>
<Typography variant="caption" color="text.secondary" sx={{ mt: 0.5, display: 'block' }}>{j.product} · {j.mode}</Typography>
</Grid>
<Grid item xs={6} md={2}><Summary label="Current stage" value={j.currentStage} small /></Grid>
<Grid item xs={6} md={2}><Summary label="ETA" value={j.eta} /></Grid>
<Grid item xs={6} md={1.5}><Summary label="Distance" value={`${j.distance} km`} /></Grid>
<Grid item xs={6} md={1.5}>
<Typography variant="caption" color="text.secondary">Progress</Typography>
<Typography variant="h5" sx={{ fontWeight: 700, color: 'grey.900' }}>{j.progress}%</Typography>
<LinearProgress variant="determinate" value={j.progress} color="primary" sx={{ height: 5, borderRadius: 3, mt: 0.5 }} />
</Grid>
</Grid>
</Box>
<Grid container spacing={2.5}>
{/* Hop-by-hop timeline */}
<Grid item xs={12} lg={7}>
<MainCard title={`Journey · ${doneCount}/${j.hops.length} stages complete`}>
<Box>
{j.hops.map((h, i) => {
const Icon = HOP_ICONS[h.icon] || WarehouseOutlinedIcon;
const last = i === j.hops.length - 1;
const newMile = i === 0 || j.hops[i - 1].mile !== h.mile;
return (
<Box key={h.key}>
{newMile && (
<Typography variant="overline" color="text.secondary" sx={{ display: 'block', mt: i === 0 ? 0 : 1.5, mb: 0.5, letterSpacing: '0.1em' }}>
{h.mile}
</Typography>
)}
<Stack direction="row" spacing={1.75}>
{/* rail */}
<Stack alignItems="center" sx={{ width: 36 }}>
<Avatar
sx={{
width: 34,
height: 34,
bgcolor: h.status === 'done' ? 'success.main' : h.status === 'active' ? 'primary.main' : 'grey.100',
color: h.status === 'pending' ? 'grey.500' : '#fff',
border: h.status === 'pending' ? '1px solid' : 'none',
borderColor: 'grey.300'
}}
>
{h.status === 'done' ? <CheckIcon sx={{ fontSize: 18 }} /> : <Icon sx={{ fontSize: 18 }} />}
</Avatar>
{!last && <Box sx={{ flexGrow: 1, width: 2, minHeight: 26, bgcolor: h.status === 'done' ? 'success.light' : 'grey.200', my: 0.25 }} />}
</Stack>
{/* content */}
<Box sx={{ pb: last ? 0 : 2, flexGrow: 1, minWidth: 0 }}>
<Stack direction="row" justifyContent="space-between" alignItems="flex-start" spacing={1}>
<Typography variant="subtitle2" sx={{ fontWeight: 700, color: h.status === 'pending' ? 'text.secondary' : 'grey.900' }}>{h.title}</Typography>
{h.status === 'active' && <Chip size="small" label="In progress" sx={{ bgcolor: 'primary.lighter', color: 'primary.dark', fontWeight: 600 }} />}
</Stack>
<Typography variant="caption" sx={{ display: 'block', color: 'grey.800', fontWeight: 600 }}>{h.node}</Typography>
<Typography variant="caption" color="text.secondary" sx={{ display: 'block' }}>{h.handler}</Typography>
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mt: 0.25 }}>{h.detail}</Typography>
<Typography variant="caption" sx={{ color: h.status === 'pending' ? 'grey.400' : 'text.secondary', fontStyle: h.status === 'pending' ? 'italic' : 'normal' }}>{h.time}</Typography>
</Box>
</Stack>
</Box>
);
})}
</Box>
</MainCard>
</Grid>
{/* Map + live monitoring */}
<Grid item xs={12} lg={5}>
<Stack spacing={2.5}>
<MainCard title="Live Route">
<MapPlaceholder
height={240}
label={`${j.from.city}${j.to.city}`}
pins={[
{ x: '16%', y: '74%', label: 'Chennai', color: '#00A854' },
{ x: '48%', y: '50%', label: 'Line-haul', color: '#C01227' },
{ x: '80%', y: '24%', label: 'Bengaluru', color: '#595959' }
]}
/>
</MainCard>
<MainCard
title={
<Stack direction="row" spacing={1} alignItems="center">
<Avatar variant="rounded" sx={{ bgcolor: 'primary.lighter', color: 'primary.main', width: 30, height: 30 }}><AutoAwesomeOutlinedIcon sx={{ fontSize: 18 }} /></Avatar>
<Typography variant="h5">Live Monitoring & Reroute</Typography>
</Stack>
}
>
<Stack divider={<Divider />} spacing={0}>
{j.events.map((e, i) => (
<Stack key={i} direction="row" spacing={1.5} alignItems="flex-start" sx={{ py: 1.25 }}>
<Avatar variant="rounded" sx={{ width: 32, height: 32, bgcolor: e.type === 'reroute' ? 'warning.lighter' : 'grey.100', color: e.type === 'reroute' ? 'warning.dark' : 'grey.600' }}>
{e.type === 'reroute' ? <AltRouteOutlinedIcon sx={{ fontSize: 18 }} /> : <VisibilityOutlinedIcon sx={{ fontSize: 18 }} />}
</Avatar>
<Box sx={{ flexGrow: 1 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>{e.title}</Typography>
<Typography variant="caption" color="text.secondary" sx={{ display: 'block' }}>{e.detail}</Typography>
<Typography variant="caption" color="text.secondary">{e.time}</Typography>
</Box>
</Stack>
))}
</Stack>
</MainCard>
</Stack>
</Grid>
</Grid>
<Toast {...toast} />
</>
);
}
function Summary({ label, value, small }) {
return (
<Box>
<Typography variant="caption" color="text.secondary">{label}</Typography>
<Typography variant={small ? 'subtitle2' : 'h5'} sx={{ fontWeight: 700, color: 'grey.900', lineHeight: 1.2 }}>{value}</Typography>
</Box>
);
}

View File

@@ -0,0 +1,169 @@
import { useState } from 'react';
import { Grid, Card, CardContent, Stack, Typography, Box, Avatar, LinearProgress, Table, TableBody, TableCell, TableHead, TableRow, Button, Divider } from '@mui/material';
import VerifiedUserOutlinedIcon from '@mui/icons-material/VerifiedUserOutlined';
import BadgeOutlinedIcon from '@mui/icons-material/BadgeOutlined';
import GppMaybeOutlinedIcon from '@mui/icons-material/GppMaybeOutlined';
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import LinkOutlinedIcon from '@mui/icons-material/LinkOutlined';
import GavelOutlinedIcon from '@mui/icons-material/GavelOutlined';
import SecurityOutlinedIcon from '@mui/icons-material/SecurityOutlined';
import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
import PageHeader from '@/components/PageHeader';
import StatCard from '@/components/StatCard';
import MainCard from '@/components/MainCard';
import StatusChip from '@/components/StatusChip';
import LayerBanner from '@/components/LayerBanner';
import FormDialog from '@/components/FormDialog';
import { trustChecks, trustQueue } from '@/data/mock';
const ICONS = {
kyc: VerifiedUserOutlinedIcon,
id: BadgeOutlinedIcon,
fraud: GppMaybeOutlinedIcon,
tamper: LockOutlinedIcon,
custody: LinkOutlinedIcon,
compliance: GavelOutlinedIcon
};
export default function TrustCompliance() {
const [audit, setAudit] = useState(false);
const cleared = trustQueue.filter((t) => t.status === 'cleared').length;
const review = trustQueue.filter((t) => t.status === 'review').length;
const hold = trustQueue.filter((t) => t.status === 'hold').length;
return (
<>
<PageHeader
title="Trust & Identity Layer"
breadcrumbs={[{ label: 'Trust & Identity' }]}
action={<Button variant="outlined" startIcon={<FileDownloadOutlinedIcon />} onClick={() => setAudit(true)}>Audit Log</Button>}
/>
<LayerBanner
no={2}
icon={SecurityOutlinedIcon}
color="#5B5BD6"
title="Trust, Security & Compliance"
subtitle="Every shipment is verified, screened and sealed before it moves."
steps={['KYC Verification', 'ID Verification', 'Fraud Detection', 'Tamper Protection', 'Chain of Custody', 'Compliance Engine']}
/>
<Grid container spacing={2.5} sx={{ mb: 0.5 }}>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Cleared Rate" value="99.2%" icon={VerifiedUserOutlinedIcon} color="success" trend={0.4} caption="this week" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="In Review" value={review} icon={BadgeOutlinedIcon} color="warning" caption="awaiting verification" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="On Hold" value={hold} icon={GppMaybeOutlinedIcon} color="error" caption="fraud flags" /></Grid>
<Grid item xs={12} sm={6} lg={3}><StatCard title="Cleared Today" value={cleared} icon={LockOutlinedIcon} color="info" caption="seals logged" /></Grid>
</Grid>
<Grid container spacing={2.5} sx={{ mt: 0 }}>
{trustChecks.map((c) => {
const Icon = ICONS[c.icon] || VerifiedUserOutlinedIcon;
return (
<Grid item xs={12} sm={6} lg={4} key={c.key}>
<Card sx={{ height: '100%' }}>
<CardContent>
<Stack direction="row" spacing={1.5} alignItems="center" sx={{ mb: 1.5 }}>
<Avatar variant="rounded" sx={{ bgcolor: 'primary.lighter', color: 'primary.main', width: 44, height: 44 }}>
<Icon fontSize="small" />
</Avatar>
<Box>
<Typography variant="subtitle1" sx={{ fontWeight: 700 }}>{c.title}</Typography>
<Typography variant="caption" color="text.secondary">{c.desc}</Typography>
</Box>
</Stack>
<Stack direction="row" justifyContent="space-between" sx={{ mb: 0.5 }}>
<Typography variant="caption" color="text.secondary">Pass rate</Typography>
<Typography variant="caption" sx={{ fontWeight: 700, color: 'grey.900' }}>{c.passRate}%</Typography>
</Stack>
<LinearProgress
variant="determinate"
value={c.passRate}
sx={{ height: 6, borderRadius: 3, bgcolor: 'grey.100', '& .MuiLinearProgress-bar': { bgcolor: 'grey.800' } }}
/>
<Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: 'block' }}>
{c.pending} pending review
</Typography>
</CardContent>
</Card>
</Grid>
);
})}
</Grid>
<Box sx={{ mt: 2.5 }}>
<MainCard title="Verification & Fraud Queue" noPadding>
<Table>
<TableHead>
<TableRow>
<TableCell>Shipment</TableCell>
<TableCell>Entity</TableCell>
<TableCell>Check</TableCell>
<TableCell>Risk</TableCell>
<TableCell>Risk Score</TableCell>
<TableCell>Signal</TableCell>
<TableCell>Status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{trustQueue.map((r) => (
<TableRow key={r.id} hover>
<TableCell sx={{ fontWeight: 600, color: 'primary.main' }}>{r.id}</TableCell>
<TableCell>
<Typography variant="subtitle2">{r.entity}</Typography>
<Typography variant="caption" color="text.secondary">{r.type}</Typography>
</TableCell>
<TableCell>{r.check}</TableCell>
<TableCell><StatusChip status={r.risk} /></TableCell>
<TableCell>
<Stack direction="row" spacing={1} alignItems="center">
<Box sx={{ width: 60 }}>
<LinearProgress
variant="determinate"
value={r.score}
color={r.score > 60 ? 'error' : r.score > 35 ? 'warning' : 'success'}
sx={{ height: 6, borderRadius: 3 }}
/>
</Box>
<Typography variant="caption" sx={{ fontWeight: 600 }}>{r.score}</Typography>
</Stack>
</TableCell>
<TableCell><Typography variant="caption" color="text.secondary">{r.flagged}</Typography></TableCell>
<TableCell><StatusChip status={r.status} /></TableCell>
</TableRow>
))}
</TableBody>
</Table>
</MainCard>
</Box>
<FormDialog open={audit} onClose={() => setAudit(false)} title="Chain-of-Custody Audit Log" maxWidth="md" hideActions>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1.5 }}>
Immutable, tamper-evident log of every verification & custody event.
</Typography>
<Stack divider={<Divider />} spacing={0}>
{trustQueue.map((r, i) => (
<Stack key={r.id} direction="row" spacing={1.5} alignItems="center" sx={{ py: 1.1 }}>
<Box sx={{ width: 70 }}>
<Typography variant="caption" color="text.secondary">10:{42 - i}:0{i}</Typography>
</Box>
<Box sx={{ flexGrow: 1 }}>
<Typography variant="subtitle2">{r.id} · {r.check}</Typography>
<Typography variant="caption" color="text.secondary">{r.entity} {r.flagged}</Typography>
</Box>
<StatusChip status={r.status} />
<Typography variant="caption" sx={{ fontFamily: 'monospace', color: 'grey.500', display: { xs: 'none', sm: 'block' } }}>
0x{(r.id.length * 7 + r.score).toString(16)}a{i}f3
</Typography>
</Stack>
))}
</Stack>
</FormDialog>
</>
);
}
const hexA = (hex, a) => {
const n = parseInt(hex.replace('#', ''), 16);
return `rgba(${n >> 16}, ${(n >> 8) & 255}, ${n & 255}, ${a})`;
};