import React, { useState, useMemo, useEffect, useCallback } from 'react'; import { Box, Typography, Card, CardContent, Avatar, Chip, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, IconButton, Button, Stack, Tooltip, LinearProgress, Dialog, DialogContent, DialogActions, Drawer, TextField, MenuItem, Divider, InputAdornment, Menu, ListItemIcon, Snackbar, Alert, useMediaQuery, Grid, Select, FormControl, InputLabel, Autocomplete, Switch, FormControlLabel, Paper, ToggleButtonGroup, ToggleButton, Stepper, Step, StepLabel, CircularProgress } from '@mui/material'; import { useTheme, alpha } from '@mui/material/styles'; // ── Professional outlined icon set ────────────────────────────────────────────── import AddOutlinedIcon from '@mui/icons-material/AddOutlined'; import SearchOutlinedIcon from '@mui/icons-material/SearchOutlined'; import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; import DeleteOutlineIcon from '@mui/icons-material/DeleteOutlineOutlined'; import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'; import MoreVertOutlinedIcon from '@mui/icons-material/MoreVertOutlined'; import CloseOutlinedIcon from '@mui/icons-material/CloseOutlined'; import VerifiedOutlinedIcon from '@mui/icons-material/VerifiedOutlined'; import CircleRoundedIcon from '@mui/icons-material/CircleRounded'; import StarRoundedIcon from '@mui/icons-material/StarRounded'; import PhoneOutlinedIcon from '@mui/icons-material/PhoneOutlined'; import CallOutlinedIcon from '@mui/icons-material/CallOutlined'; import PlaceOutlinedIcon from '@mui/icons-material/PlaceOutlined'; import BadgeOutlinedIcon from '@mui/icons-material/BadgeOutlined'; import PersonOutlineOutlinedIcon from '@mui/icons-material/PersonOutlineOutlined'; import PersonAddAlt1OutlinedIcon from '@mui/icons-material/PersonAddAlt1Outlined'; import GroupsOutlinedIcon from '@mui/icons-material/GroupsOutlined'; import TwoWheelerOutlinedIcon from '@mui/icons-material/TwoWheelerOutlined'; import ElectricBikeOutlinedIcon from '@mui/icons-material/ElectricBikeOutlined'; import PedalBikeOutlinedIcon from '@mui/icons-material/PedalBikeOutlined'; import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined'; import AirportShuttleOutlinedIcon from '@mui/icons-material/AirportShuttleOutlined'; import InventoryOutlinedIcon from '@mui/icons-material/Inventory2Outlined'; import PaymentsOutlinedIcon from '@mui/icons-material/PaymentsOutlined'; import AccessTimeOutlinedIcon from '@mui/icons-material/AccessTimeOutlined'; import WarningAmberOutlinedIcon from '@mui/icons-material/WarningAmberOutlined'; import CheckCircleOutlinedIcon from '@mui/icons-material/CheckCircleOutlined'; import RouteOutlinedIcon from '@mui/icons-material/RouteOutlined'; import GppGoodOutlinedIcon from '@mui/icons-material/GppGoodOutlined'; import ApartmentOutlinedIcon from '@mui/icons-material/ApartmentOutlined'; import TableRowsOutlinedIcon from '@mui/icons-material/TableRowsOutlined'; import GridViewOutlinedIcon from '@mui/icons-material/GridViewOutlined'; import DownloadOutlinedIcon from '@mui/icons-material/DownloadOutlined'; import HomeWorkOutlinedIcon from '@mui/icons-material/HomeWorkOutlined'; import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded'; import StatCard from '@/components/StatCard'; import { getMilers, createMiler, updateMiler, deleteMiler } from '@/api/hub'; import { getHubContext } from '@/auth/session'; // The backend uses snake-case availability values; this page uses friendlier labels. const API_TO_UI_STATUS = { Available: 'Idle', Assigned: 'On Pickup', On_Break: 'On Break', Offline: 'Offline' }; const UI_TO_API_STATUS = { Idle: 'Available', 'On Pickup': 'Assigned', 'On Break': 'On_Break', 'Returning to Hub': 'Assigned', Offline: 'Offline', Suspended: 'Offline' }; // ── Reference data ────────────────────────────────────────────────────────────── const VEHICLES = { 'Electric Bike': { capacity: 30, icon: ElectricBikeOutlinedIcon }, 'Motorcycle': { capacity: 25, icon: TwoWheelerOutlinedIcon }, 'Cycle': { capacity: 15, icon: PedalBikeOutlinedIcon }, 'Cargo Van': { capacity: 120, icon: AirportShuttleOutlinedIcon }, 'Mini Truck': { capacity: 200, icon: LocalShippingOutlinedIcon }, }; const VEHICLE_TYPES = Object.keys(VEHICLES); const ALL_ZONES = ['Dwarka', 'Janakpuri', 'Saket', 'Malviya Nagar', 'Rohini', 'Vasant Kunj', 'Central Delhi', 'Lajpat Nagar', 'Karol Bagh', 'Connaught Place', 'Mayur Vihar']; const HUB_OPTIONS = ['Delhi Operations Hub', 'Mumbai Hub (BOM-02)', 'Bengaluru Hub (BLR-03)', 'Jaipur Hub (JAI-08)']; const STATUS_OPTIONS = ['On Pickup', 'Idle', 'On Break', 'Returning to Hub', 'Offline', 'Suspended']; const STATUS_META = { 'On Pickup': { color: '#1A73E8', bg: '#E8F0FE', label: 'On Pickup' }, 'Idle': { color: '#B06000', bg: '#FEF7E0', label: 'Idle / Available' }, 'On Break': { color: '#8E24AA', bg: '#F3E5F5', label: 'On Break' }, 'Returning to Hub': { color: '#0B8043', bg: '#E6F4EA', label: 'Returning' }, 'Offline': { color: '#80868B', bg: '#F1F3F4', label: 'Offline' }, 'Suspended': { color: '#D93025', bg: '#FCE8E6', label: 'Suspended' }, }; let idSeq = 8060; const genId = () => `RDR-${idSeq++}`; // Turn a vehicle type name into the 1-based id the backend uses (best effort). const vehicleIdFor = (vehicle) => Math.max(1, VEHICLE_TYPES.indexOf(vehicle) + 1); // Map a raw API miler onto the rich shape this page renders. Fields the API does // not provide (zones, COD, live load) default to empty/zero so the UI still works. // Format an ISO check-in timestamp as HH:MM (or "—"). const checkInLabel = (iso) => { if (!iso) return '—'; const d = new Date(iso); if (Number.isNaN(d.getTime())) return '—'; return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); }; const mapMiler = (m, hubName) => { // Real backend exposes defaultvehicletype (e.g. "Bike"); fall back to id lookup. const vehicle = m.defaultvehicletype || VEHICLE_TYPES[(m.vehicleid || 1) - 1] || 'Motorcycle'; const capacity = m.capacity || VEHICLES[vehicle]?.capacity || 0; return { id: m.userid, userid: m.userid, vehicleid: m.vehicleid, hubid: m.hubid, name: m.displayname || `Miler ${m.userid}`, phone: m.phone || '—', hub: hubName, zones: Array.isArray(m.zones) ? m.zones : m.currentpincode ? [m.currentpincode] : [], vehicle, // Real registration plate from the joined vehicles table; fall back to a // placeholder tag if the miler has no vehicle assigned. vehicleNo: m.vehicleno || (m.vehicleid ? `VEH-${m.vehicleid}` : '—'), status: API_TO_UI_STATUS[m.availabilitystatus] || 'Idle', checkInTime: checkInLabel(m.checkinat), hoursToday: m.hoursactive ?? 0, assigned: m.assignedload ?? 0, capacity, pickupsPending: m.pickupspending ?? 0, deliveriesPending: m.assignedload ?? 0, deliveriesDone: m.totalcompletedpickups ?? m.completedorders ?? 0, deliveriesFailed: m.totalcancelledpickups ?? m.cancelledorders ?? 0, codCollected: m.codcollected ?? 0, codPending: m.codpending ?? 0, rating: m.rating ?? 0, verified: Boolean(m.isverified ?? m.device_token) }; }; // ── Helpers ───────────────────────────────────────────────────────────── const successRate = (r) => { const total = r.deliveriesDone + r.deliveriesFailed; return total === 0 ? 100 : Math.round((r.deliveriesDone / total) * 100); }; const loadPct = (r) => (r.capacity === 0 ? 0 : Math.round((r.assigned / r.capacity) * 100)); const initials = (name) => name.split(' ').map(w => w[0]).slice(0, 2).join('').toUpperCase(); const inr = (n) => `₹${n.toLocaleString('en-IN')}`; const EMPTY_FORM = { name: '', phone: '', hub: 'Delhi Operations Hub', zones: [], vehicle: '', vehicleNo: '', status: 'Idle', verified: false, }; // ════════════════════════════════════════════════════════════════════════════════ // Presentational Components (Improved spacing & visuals) // ════════════════════════════════════════════════════════════════════════════════ function StatusPill({ status, size = 'md' }) { const m = STATUS_META[status] || STATUS_META['Offline']; const small = size === 'sm'; return ( {m.label} ); } function CapacityBar({ rider, showLabel = true }) { const pct = loadPct(rider); const color = pct > 85 ? '#D93025' : pct > 60 ? '#F29900' : '#1A73E8'; return ( {showLabel && ( {rider.assigned}/{rider.capacity} · {pct}% )} ); } function RiderAvatar({ rider, size = 48 }) { const m = STATUS_META[rider.status] || STATUS_META['Offline']; return ( {initials(rider.name)} ); } function VehicleCell({ vehicle, vehicleNo }) { const Icon = VEHICLES[vehicle]?.icon || LocalShippingOutlinedIcon; return ( {vehicle} {vehicleNo} ); } // KPI card — the shared StatCard is the single reference design (see components/StatCard). // Kept as a thin alias so the call sites below stay unchanged; `hover` on for the lift effect. const KpiCard = (props) => ; // ════════════════════════════════════════════════════════════════════════════════ // Dialogs (Cleaner & Better Spaced) // ════════════════════════════════════════════════════════════════════════════════ const STEPS = ['Personal', 'Assignment', 'Vehicle & KYC']; function RiderFormDialog({ open, onClose, onSave, initial, mode }) { const theme = useTheme(); const fullScreen = useMediaQuery(theme.breakpoints.down('sm')); const [activeStep, setActiveStep] = useState(0); const [form, setForm] = useState(initial || EMPTY_FORM); const [errors, setErrors] = useState({}); React.useEffect(() => { if (open) { setForm(initial || EMPTY_FORM); setErrors({}); setActiveStep(0); } }, [open, initial]); const set = (k, v) => { setForm(p => ({ ...p, [k]: v })); setErrors(e => ({ ...e, [k]: undefined })); }; const validateStep = (step) => { const e = {}; if (step === 0) { if (!form.name.trim()) e.name = 'Full name is required'; if (!form.phone.trim()) e.phone = 'Phone number is required'; else if (!/[0-9]{10}/.test(form.phone.replace(/\D/g, ''))) e.phone = 'Enter a valid 10-digit phone number'; } if (step === 1) { if (form.zones.length === 0) e.zones = 'Assign at least one service zone'; } if (step === 2) { if (!form.vehicle) e.vehicle = 'Select a vehicle type'; if (!form.vehicleNo.trim() && form.vehicle !== 'Cycle') e.vehicleNo = 'Vehicle number is required'; } setErrors(e); return Object.keys(e).length === 0; }; const next = () => { if (validateStep(activeStep)) setActiveStep(s => s + 1); }; const back = () => setActiveStep(s => s - 1); const submit = () => { if (!validateStep(2)) return; const cap = VEHICLES[form.vehicle]?.capacity || 30; onSave({ ...form, vehicleNo: form.vehicleNo.trim() || '—', id: mode === 'add' ? genId() : form.id, capacity: cap, checkInTime: mode === 'add' ? '—' : form.checkInTime, hoursToday: mode === 'add' ? 0 : form.hoursToday, assigned: mode === 'add' ? 0 : form.assigned, pickupsPending: mode === 'add' ? 0 : form.pickupsPending, deliveriesPending: mode === 'add' ? 0 : form.deliveriesPending, deliveriesDone: mode === 'add' ? 0 : form.deliveriesDone, deliveriesFailed: mode === 'add' ? 0 : form.deliveriesFailed, codCollected: mode === 'add' ? 0 : form.codCollected, codPending: mode === 'add' ? 0 : form.codPending, rating: mode === 'add' ? 5.0 : form.rating, }); }; return ( {/* Header */} {mode === 'add' ? 'Onboard New Miler' : 'Edit Miler'} {mode === 'add' ? 'Add delivery agent to the fleet' : `Updating ${form.name}`} {STEPS.map(label => ( {label} ))} {/* STEP 0 */} {activeStep === 0 && ( set('name', e.target.value)} error={!!errors.name} helperText={errors.name} InputProps={{ startAdornment: }} /> set('phone', e.target.value)} error={!!errors.phone} helperText={errors.phone} placeholder="+91 XXXXXXXXXX" InputProps={{ startAdornment: }} /> Home Hub )} {/* STEP 1 */} {activeStep === 1 && ( set('zones', v)} renderInput={(params) => } renderTags={(value, getTagProps) => value.map((option, i) => )} /> {mode === 'edit' && ( Status )} )} {/* STEP 2 */} {activeStep === 2 && ( Vehicle Type {errors.vehicle && {errors.vehicle}} set('vehicleNo', e.target.value.toUpperCase())} error={!!errors.vehicleNo} helperText={errors.vehicleNo || (form.vehicle === 'Cycle' ? 'Optional for cycles' : '')} placeholder="DL-00-AB-0000" sx={{ '& input': { fontFamily: 'monospace' } }} /> set('verified', e.target.checked)} color="success" />} label={ KYC Verified Driving licence & ID proof validated } /> )} {activeStep > 0 && } {activeStep < STEPS.length - 1 ? ( ) : ( )} ); } // Profile Drawer - view + inline edit, in the same right-side sheet function ProfileDrawer({ rider: riderProp, onClose, onSave, startInEdit = false }) { const [editing, setEditing] = useState(startInEdit); // Retain the last opened miler so the sheet keeps rendering its content while it // slides OUT — the prop becomes null on close, and unmounting here (an early // `return null`) would kill the exit animation and make closing feel instant. const [retained, setRetained] = useState(riderProp); const [form, setForm] = useState(riderProp || {}); // Reset the editable copy + mode whenever a different miler is opened. useEffect(() => { if (riderProp) { setRetained(riderProp); setForm(riderProp); setEditing(startInEdit); } }, [riderProp, startInEdit]); const open = Boolean(riderProp); // drives the slide; false triggers the exit anim const rider = riderProp || retained; // keep content during the close transition // NOTE: we deliberately do NOT early-return when there's no rider. The Drawer stays // mounted (open=false) from first render, so the first click is a real false→true // transition and animates — otherwise the first open mounts already-open and skips it. const sr = rider ? successRate(rider) : 0; const setField = (key) => (e) => setForm((f) => ({ ...f, [key]: e.target.value })); const cancelEdit = () => { setForm(rider); setEditing(false); }; const saveEdit = () => { const updated = { ...rider, ...form, capacity: VEHICLES[form.vehicle]?.capacity ?? rider.capacity }; onSave(updated); setEditing(false); }; return ( t.zIndex.modal, '& .MuiDrawer-paper': { width: { xs: '100%', sm: 520 }, maxWidth: '100%', // Run the sheet the full height of the viewport so there is no empty // strip above the red header (zIndex.modal keeps Close clickable). top: 0, height: '100%', borderTopLeftRadius: { sm: 16 }, display: 'flex', flexDirection: 'column', overflow: 'hidden', boxShadow: '-8px 0 30px rgba(0,0,0,0.12)', }, }} > {rider && (<> {/* Header */} {form.name || rider.name} {rider.id} {rider.verified && ( } label="Verified" sx={{ bgcolor: 'rgba(255,255,255,0.18)', color: '#fff', fontWeight: 700 }} /> )} {/* Content */} {editing ? ( /* ── Inline edit form (same sheet) ── */ EDIT MILER DETAILS Status Vehicle Hub setForm((f) => ({ ...f, zones: v }))} renderInput={(params) => } /> ) : ( {/* Today's Performance */} TODAY'S PERFORMANCE {[ { label: "Picked up", value: rider.deliveriesDone, icon: CheckCircleOutlinedIcon, color: "#16A34A", bg: "#DCFCE7" }, { label: "Failed", value: rider.deliveriesFailed, icon: WarningAmberOutlinedIcon, color: "#DC2626", bg: "#FEE2E2" }, { label: "COP", value: inr(rider.codCollected), icon: PaymentsOutlinedIcon, color: "#D97706", bg: "#FEF3C7" }, ].map((stat, i) => ( ))} {/* Live Load Panel */} LIVE LOAD Pending pickups {rider.pickupsPending} {/* Miler Details Panel */} MILER DETAILS {[ { icon: PhoneOutlinedIcon, label: "Phone", value: rider.phone }, { icon: HomeWorkOutlinedIcon, label: "Hub", value: rider.hub }, { icon: PlaceOutlinedIcon, label: "Zones", value: rider.zones?.join(', ') || 'N/A' }, { icon: BadgeOutlinedIcon, label: "Vehicle", value: `${rider.vehicle} • ${rider.vehicleNo}` }, { icon: AccessTimeOutlinedIcon, label: "Check-in", value: `${rider.checkInTime} (${rider.hoursToday}h)` }, ].map((detail, idx) => ( {detail.label} {detail.value} ))} )} {/* Footer */} {editing ? ( <> ) : ( <> )} )} ); } // Delete Dialog function DeleteDialog({ rider, onClose, onConfirm }) { return ( Remove Miler? {rider?.name} will be permanently removed from the roster. ); } // Miler Card (Grid) function RiderCard({ rider, onView, onMenu }) { const pct = loadPct(rider); const pctColor = pct > 85 ? "#D93025" : pct > 60 ? "#F29900" : "#1A73E8"; return ( onView(rider)} sx={{ height: "100%", borderRadius: 2, border: "1px solid #E8EAED", cursor: "pointer", transition: "all .25s ease", bgcolor: "#fff", "&:hover": { transform: "translateY(-4px)", borderColor: "#C01227", boxShadow: "0 16px 40px rgba(0,0,0,.08)", }, }} > {/* Header */} {rider.name} {rider.verified && ( )} {rider.id} { e.stopPropagation(); onMenu(e, rider); }} > {/* Status */} {/* Load */} Today's Load {rider.assigned}/{rider.capacity} {pct}% {/* Zones */} SERVICE ZONES {rider.zones.slice(0, 3).map((z) => ( ))} {rider.zones.length > 3 && ( )} ); } // ════════════════════════════════════════════════════════════════════════════════ // MAIN COMPONENT // ════════════════════════════════════════════════════════════════════════════════ export default function Riders() { const theme = useTheme(); const isMdDown = useMediaQuery(theme.breakpoints.down('md')); const isLgDown = useMediaQuery(theme.breakpoints.down('lg')); const hub = getHubContext(); const hubName = hub.hubname || 'This Hub'; const [riders, setRiders] = useState([]); const [loading, setLoading] = useState(true); const [loadError, setLoadError] = useState(''); const [search, setSearch] = useState(''); const [statusFilter, setStatusFilter] = useState('All'); const [vehicleFilter, setVehicleFilter] = useState('All'); const [view, setView] = useState('table'); const loadMilers = useCallback(async () => { setLoading(true); setLoadError(''); try { const res = await getMilers(); setRiders((res?.data || []).map((m) => mapMiler(m, hubName))); } catch (err) { setLoadError(err?.message || 'Could not load milers.'); } finally { setLoading(false); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { loadMilers(); }, [loadMilers]); const [formDialog, setFormDialog] = useState({ open: false, mode: 'add', initial: null }); const [profile, setProfile] = useState(null); const [profileEdit, setProfileEdit] = useState(false); const [deleteTarget, setDeleteTarget] = useState(null); const [menuAnchor, setMenuAnchor] = useState(null); const [menuRider, setMenuRider] = useState(null); const [snack, setSnack] = useState({ open: false, msg: '', severity: 'success' }); const toast = (msg, severity = 'success') => setSnack({ open: true, msg, severity }); const effectiveView = isLgDown ? 'grid' : view; const kpi = useMemo(() => { const onDuty = riders.filter(r => !['Offline', 'Suspended'].includes(r.status)).length; const onField = riders.filter(r => ['On Pickup', 'Returning to Hub'].includes(r.status)).length; const idle = riders.filter(r => r.status === 'Idle').length; const slaRisk = riders.filter(r => loadPct(r) > 85 || r.deliveriesFailed >= 5).length; const codToCollect = riders.reduce((s, r) => s + r.codPending, 0); return { onDuty, onField, idle, slaRisk, codToCollect, total: riders.length }; }, [riders]); const filtered = useMemo(() => { const q = search.trim().toLowerCase(); return riders.filter(r => { const matchSearch = !q || r.name.toLowerCase().includes(q) || r.id.toLowerCase().includes(q) || r.phone.includes(q) || r.zones.some(z => z.toLowerCase().includes(q)); const matchStatus = statusFilter === 'All' || r.status === statusFilter; const matchVehicle = vehicleFilter === 'All' || r.vehicle === vehicleFilter; return matchSearch && matchStatus && matchVehicle; }); }, [riders, search, statusFilter, vehicleFilter]); // Build the API payload from the rich form object. const toApiPayload = (rider) => ({ displayname: rider.name, phone: rider.phone, hubid: rider.hubid || hub.hubid, vehicleid: rider.vehicleid || vehicleIdFor(rider.vehicle), availabilitystatus: UI_TO_API_STATUS[rider.status] || 'Available' }); const handleSave = async (rider) => { try { if (formDialog.mode === 'add') { await createMiler(toApiPayload(rider)); toast(`${rider.name} onboarded successfully`); } else { await updateMiler(rider.userid ?? rider.id, toApiPayload(rider)); toast(`${rider.name} updated`); } setFormDialog({ open: false, mode: 'add', initial: null }); loadMilers(); } catch (err) { toast(err?.message || 'Could not save this miler.', 'error'); } }; const handleDelete = async () => { const target = deleteTarget; try { await deleteMiler(target.userid ?? target.id); setRiders((p) => p.filter((r) => r.id !== target.id)); toast(`${target.name} removed`, 'info'); } catch (err) { toast(err?.message || 'Could not remove this miler.', 'error'); } finally { setDeleteTarget(null); setProfile(null); } }; const openAdd = () => setFormDialog({ open: true, mode: 'add', initial: null }); // Edit now opens the same right-side sheet in edit mode (no separate modal). const openEdit = (r) => { setMenuAnchor(null); setProfile(r); setProfileEdit(true); }; const openView = (r) => { setMenuAnchor(null); setProfile(r); setProfileEdit(false); }; const saveProfileEdit = async (updated) => { try { await updateMiler(updated.userid ?? updated.id, toApiPayload(updated)); setRiders((p) => p.map((r) => (r.id === updated.id ? updated : r))); setProfile(updated); setProfileEdit(false); toast(`${updated.name} updated`); } catch (err) { toast(err?.message || 'Could not update this miler.', 'error'); } }; const openMenu = (e, r) => { setMenuAnchor(e.currentTarget); setMenuRider(r); }; const STATUS_CHIPS = ['All', 'On Pickup', 'Idle', 'On Break', 'Offline', 'Suspended']; return ( {/* Header */} {/* ── Improved Header with better gaps ── */} {/* Title & Avatar Branding Group */} Milers {kpi.total} milers • who's working and where {/* Actions Group - pushed to the far right with clear spacing between buttons */} {/* KPIs */} {[ { icon: GroupsOutlinedIcon, label: 'On Duty', value: kpi.onDuty, sub: `of ${kpi.total}`, color: '#1A73E8', bg: '#E8F0FE', trend: true }, { icon: RouteOutlinedIcon, label: 'In Field', value: kpi.onField, sub: 'Active pickups', color: '#1E8E3E', bg: '#E6F4EA' }, { icon: AccessTimeOutlinedIcon, label: 'Available', value: kpi.idle, sub: 'Ready for assignment', color: '#B06000', bg: '#FEF7E0' }, { icon: WarningAmberOutlinedIcon, label: 'SLA Risk', value: kpi.slaRisk, sub: 'Overloaded milers', color: '#D93025', bg: '#FCE8E6' }, { icon: PaymentsOutlinedIcon, label: 'COP Pending', value: inr(kpi.codToCollect), sub: 'To be collected', color: '#8E24AA', bg: '#F3E5F5' }, ].map((k, i) => ( ))} {/* Main Card */} {/* Toolbar */} setSearch(e.target.value)} InputProps={{ startAdornment: ( ), sx: { borderRadius: 2 }, }} sx={{ maxWidth: { lg: 420 }, }} /> {!isLgDown && ( v && setView(v)} sx={{ borderRadius: 2 }} > )} {/* Status Chips */} {STATUS_CHIPS.map(s => { const active = statusFilter === s; const meta = STATUS_META[s] || {}; // Each status carries its own colour; "All" falls back to a neutral dark. const accent = meta.color || '#1A1A2E'; const tint = meta.bg || '#F8F9FB'; return ( setStatusFilter(s)} icon={meta.color ? : undefined} sx={{ height: 38, px: 1, borderRadius: 2, fontSize: '0.82rem', fontWeight: active ? 700 : 600, bgcolor: active ? accent : tint, color: active ? '#fff' : accent, border: active ? 'none' : `1px solid ${alpha(accent, 0.25)}`, '& .MuiChip-icon': { color: active ? '#fff' : meta.color }, '& .MuiChip-label': { px: 1.25 }, '&:hover': { bgcolor: active ? accent : alpha(accent, 0.14) } }} /> ); })} {/* Content Area */} {loadError && ( setLoadError('')} sx={{ m: 2, borderRadius: 2 }}> {loadError} )} {loading ? ( ) : filtered.length === 0 ? ( No milers match your filters ) : effectiveView === 'table' ? ( Miler Status Zones Vehicle Load Today {filtered.map(rider => ( openView(rider)}> {rider.name} {rider.id} {rider.zones.map(z => )} Done {rider.deliveriesDone} COP {inr(rider.codCollected)} { e.stopPropagation(); openView(rider); }}> { e.stopPropagation(); openEdit(rider); }}> { e.stopPropagation(); openMenu(e, rider); }}> ))}
) : ( {filtered.map(r => ( ))} )} {filtered.length > 0 && ( Showing {filtered.length} of {riders.length} milers )}
{/* Context Menu */} setMenuAnchor(null)} PaperProps={{ sx: { borderRadius: 2 } }}> openView(menuRider)}>View Profile openEdit(menuRider)}>Edit Call { setDeleteTarget(menuRider); setMenuAnchor(null); }} sx={{ color: '#D93025' }}> Remove Miler {/* Dialogs */} setFormDialog({ open: false, mode: 'add', initial: null })} onSave={handleSave} /> { setProfile(null); setProfileEdit(false); }} onSave={saveProfileEdit} /> setDeleteTarget(null)} onConfirm={handleDelete} /> {/* Toast */} setSnack(s => ({ ...s, open: false }))}> {snack.msg}
); }