updated the ui

This commit is contained in:
2026-06-11 18:09:10 +05:30
parent 251be9afd2
commit f50b4b010c
33 changed files with 1527 additions and 2302 deletions

View File

@@ -52,28 +52,26 @@ const LocationAutocomplete = forwardRef(
// Helpers (only used by pill variant) — match the deliveries page's
// token shorthand so the same opacity ramp is applied here.
const a = (suffix) => `${accentColor}${suffix}`;
const tint = a('08');
const ring = a('26');
const edge = a('55');
const soft = a('18');
const pillSx = pill
? {
cursor: 'pointer',
'& .MuiOutlinedInput-root': {
borderRadius: '999px',
bgcolor: tint,
borderRadius: '10px',
bgcolor: '#ffffff',
fontWeight: 600,
color: '#0f172a',
paddingRight: '8px',
cursor: 'pointer',
transition: 'border-color 0.15s, box-shadow 0.15s, background-color 0.2s',
'& fieldset': { borderColor: edge, borderWidth: 1.5 },
'&:hover fieldset': { borderColor: accentColor },
transition: 'border-color 0.15s, box-shadow 0.15s',
'& fieldset': { borderColor: '#e2e8f0', borderWidth: 1 },
'&:hover fieldset': { borderColor: '#cbd5e1' },
'&.Mui-focused': { boxShadow: `0 0 0 3px ${ring}` },
'&.Mui-focused fieldset': { borderColor: accentColor, borderWidth: 2 }
'&.Mui-focused fieldset': { borderColor: accentColor, borderWidth: 1.5 }
},
'& .MuiAutocomplete-endAdornment .MuiSvgIcon-root': { color: accentColor }
'& .MuiAutocomplete-endAdornment .MuiSvgIcon-root': { color: '#94a3b8' }
}
: {};

View File

@@ -0,0 +1,51 @@
import PropTypes from 'prop-types';
import { Box, Stack, Typography } from '@mui/material';
// ==============================|| PAGE HEADER (Doormile-style clean title) ||============================== //
// A plain, un-boxed page title + optional live subtitle + an action slot
// (zone selector, primary button, etc.). Replaces the heavy boxed/gradient
// header so every operator page opens with a calm, corporate heading.
export default function PageHeader({ title, subtitle, live = false, action }) {
return (
<Stack
direction={{ xs: 'column', sm: 'row' }}
justifyContent="space-between"
alignItems={{ xs: 'flex-start', sm: 'center' }}
spacing={1.5}
sx={{ mb: { xs: 2, md: 2.5 } }}
>
<Box sx={{ minWidth: 0 }}>
<Typography variant="h3" sx={{ fontWeight: 700, letterSpacing: '-0.02em', color: 'grey.800' }}>
{title}
</Typography>
{subtitle && (
<Stack direction="row" alignItems="center" spacing={0.75} sx={{ mt: 0.5 }}>
{live && (
<Box
sx={{
width: 7,
height: 7,
borderRadius: '50%',
bgcolor: 'success.main',
boxShadow: '0 0 0 3px rgba(16,185,129,0.18)'
}}
/>
)}
<Typography variant="caption" sx={{ color: 'text.secondary', fontWeight: 500 }}>
{subtitle}
</Typography>
</Stack>
)}
</Box>
{action && <Box sx={{ flexShrink: 0, width: { xs: '100%', sm: 'auto' } }}>{action}</Box>}
</Stack>
);
}
PageHeader.propTypes = {
title: PropTypes.node,
subtitle: PropTypes.node,
live: PropTypes.bool,
action: PropTypes.node
};

View File

@@ -0,0 +1,77 @@
import PropTypes from 'prop-types';
import { Avatar, Box, Card, CardContent, Skeleton, Stack, Typography } from '@mui/material';
// ==============================|| STAT / KPI CARD (Doormile-style) ||============================== //
// Clean metric card: muted label, large value, a single soft-tinted rounded
// avatar holding the icon. No coloured top-stripe, no rainbow — colour appears
// only in the small avatar so a row of cards reads calm and corporate.
//
// `icon` is a rendered node, e.g. icon={<MdLocalShipping size={20} />}.
// `color` is a hex accent (defaults to brand purple); `caption` is the small
// muted line under the value (e.g. "96% of total").
export default function StatCard({ title, value, icon, color = '#662582', caption, loading = false }) {
return (
<Card sx={{ height: '100%' }}>
<CardContent sx={{ p: { xs: 1.75, md: 2 }, '&:last-child': { pb: { xs: 1.75, md: 2 } } }}>
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={1}>
<Box sx={{ minWidth: 0, flex: 1 }}>
<Typography
variant="body2"
sx={{
fontWeight: 500,
color: 'text.secondary',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis'
}}
>
{title}
</Typography>
{loading ? (
<Skeleton variant="rounded" sx={{ width: 72, height: 30, mt: 0.5 }} animation="wave" />
) : (
<Typography
sx={{
mt: 0.25,
fontWeight: 700,
letterSpacing: '-0.015em',
color: 'grey.800',
lineHeight: 1.15,
fontSize: { xs: '1.375rem', md: '1.5rem' },
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis'
}}
>
{value}
</Typography>
)}
{caption && (
<Typography variant="caption" sx={{ display: 'block', mt: 0.5, color: 'text.secondary' }}>
{caption}
</Typography>
)}
</Box>
{icon && (
<Avatar
variant="rounded"
sx={{ width: 42, height: 42, borderRadius: 2, bgcolor: `${color}14`, color, flexShrink: 0 }}
>
{icon}
</Avatar>
)}
</Stack>
</CardContent>
</Card>
);
}
StatCard.propTypes = {
title: PropTypes.node,
value: PropTypes.node,
icon: PropTypes.node,
color: PropTypes.string,
caption: PropTypes.node,
loading: PropTypes.bool
};

View File

@@ -0,0 +1,74 @@
import PropTypes from 'prop-types';
import { Chip } from '@mui/material';
// ==============================|| STATUS CHIP (Doormile-style soft badge) ||============================== //
// One consistent soft-filled chip for every lifecycle status across orders,
// deliveries, riders, tenants, invoices. Colour encodes meaning; the chip stays
// quiet (soft bg + readable dark text) so tables don't turn into a rainbow.
// Soft background + readable foreground per semantic tone.
const TONE = {
amber: { bg: '#FEF3C7', fg: '#92400E' },
indigo: { bg: '#E0E7FF', fg: '#3730A3' },
cyan: { bg: '#CFFAFE', fg: '#155E75' },
violet: { bg: '#EDE9FE', fg: '#5B21B6' },
teal: { bg: '#CCFBF1', fg: '#115E59' },
emerald: { bg: '#D1FAE5', fg: '#065F46' },
red: { bg: '#FEE2E2', fg: '#991B1B' },
orange: { bg: '#FFEDD5', fg: '#9A3412' },
sky: { bg: '#E0F2FE', fg: '#075985' },
slate: { bg: '#F1F5F9', fg: '#475569' },
brand: { bg: '#F1E6F7', fg: '#5B1F73' }
};
// Status keyword -> tone + display label.
const MAP = {
pending: { tone: 'amber', label: 'Pending' },
created: { tone: 'sky', label: 'Created' },
assigned: { tone: 'indigo', label: 'Assigned' },
accepted: { tone: 'indigo', label: 'Accepted' },
arrived: { tone: 'cyan', label: 'Arrived' },
picked: { tone: 'violet', label: 'Picked' },
'picked-up':{ tone: 'violet', label: 'Picked Up' },
started: { tone: 'cyan', label: 'Started' },
active: { tone: 'teal', label: 'Active' },
'in-transit': { tone: 'teal', label: 'In Transit' },
delivered: { tone: 'emerald', label: 'Delivered' },
completed: { tone: 'emerald', label: 'Completed' },
skipped: { tone: 'orange', label: 'Skipped' },
failed: { tone: 'red', label: 'Failed' },
cancelled: { tone: 'red', label: 'Cancelled' },
// riders / tenants
online: { tone: 'emerald', label: 'Online' },
offline: { tone: 'slate', label: 'Offline' },
inactive: { tone: 'slate', label: 'Inactive' },
idle: { tone: 'amber', label: 'Idle' },
unknown: { tone: 'slate', label: 'Unknown' },
// invoices
paid: { tone: 'emerald', label: 'Paid' },
unpaid: { tone: 'amber', label: 'Unpaid' },
open: { tone: 'sky', label: 'Open' },
overdue: { tone: 'red', label: 'Overdue' },
prepaid: { tone: 'emerald', label: 'Prepaid' },
cod: { tone: 'amber', label: 'COD' }
};
export default function StatusChip({ status, label, size = 'small', sx }) {
const key = String(status || '').toLowerCase().trim().replace(/\s+/g, '-');
const cfg = MAP[key] || { tone: 'slate', label: status || '—' };
const tone = TONE[cfg.tone] || TONE.slate;
return (
<Chip
size={size}
label={label || cfg.label}
sx={{ bgcolor: tone.bg, color: tone.fg, border: 'none', fontWeight: 600, ...sx }}
/>
);
}
StatusChip.propTypes = {
status: PropTypes.string,
label: PropTypes.node,
size: PropTypes.string,
sx: PropTypes.object
};