updates on the ui changes and frully removed the mui and converted into astryx and color change
This commit is contained in:
@@ -2,9 +2,17 @@ import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { MapContainer, TileLayer, Polyline, Marker, Popup, useMap } from 'react-leaflet';
|
||||
import L from 'leaflet';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import { Box, IconButton, Stack, Typography, CircularProgress } from '@mui/material';
|
||||
import { MdClose, MdRoute } from 'react-icons/md';
|
||||
|
||||
import { HStack } from '@astryxdesign/core/HStack';
|
||||
import { VStack } from '@astryxdesign/core/VStack';
|
||||
import { Center } from '@astryxdesign/core/Center';
|
||||
import { Text } from '@astryxdesign/core/Text';
|
||||
import { IconButton } from '@astryxdesign/core/IconButton';
|
||||
import { EmptyState } from '@astryxdesign/core/EmptyState';
|
||||
|
||||
import LoaderWithImage from 'components/nearle_components/LoaderWithImage';
|
||||
|
||||
const containerStyle = { width: '100%', height: '100%' };
|
||||
|
||||
// Numbered step icon — brand red to match the planned-route polyline below.
|
||||
@@ -12,7 +20,7 @@ const containerStyle = { width: '100%', height: '100%' };
|
||||
// the SVG without juggling external marker assets.
|
||||
const stepIcon = (n, isFocused) => {
|
||||
const size = isFocused ? 38 : 32;
|
||||
const color = isFocused ? '#910E1D' : '#C01227';
|
||||
const color = isFocused ? '#262626' : '#000000';
|
||||
const svg = encodeURIComponent(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="${size}" height="${size}">` +
|
||||
`<circle cx="16" cy="16" r="14" fill="${color}" stroke="white" stroke-width="3"/>` +
|
||||
@@ -93,48 +101,51 @@ export default function RidersRoutes({ details, loading, riderName, dateRange, o
|
||||
}, [dropPath]);
|
||||
|
||||
const headerBar = (
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
spacing={1.5}
|
||||
sx={{
|
||||
px: 2,
|
||||
py: 1.25,
|
||||
<HStack
|
||||
gap={3}
|
||||
vAlign="center"
|
||||
paddingInline={4}
|
||||
paddingBlock={3}
|
||||
style={{
|
||||
borderBottom: '1px solid rgba(15, 23, 42, 0.08)',
|
||||
background: 'linear-gradient(135deg, #C01227 0%, #D25463 100%)',
|
||||
background: 'linear-gradient(135deg, #000000 0%, #3A3A3A 100%)',
|
||||
color: '#fff',
|
||||
flexShrink: 0
|
||||
}}
|
||||
>
|
||||
<MdRoute size={20} />
|
||||
<Stack sx={{ flex: 1, minWidth: 0 }}>
|
||||
<Typography sx={{ fontWeight: 700, fontSize: 15, lineHeight: 1.2 }}>Planned route{riderName ? ` — ${riderName}` : ''}</Typography>
|
||||
{dateRange && <Typography sx={{ fontSize: 12, opacity: 0.85 }}>{dateRange}</Typography>}
|
||||
</Stack>
|
||||
<VStack gap={0} padding={0} style={{ flex: 1, minWidth: 0 }}>
|
||||
<Text color="inherit" weight="bold" display="block">
|
||||
Planned route{riderName ? ` — ${riderName}` : ''}
|
||||
</Text>
|
||||
{dateRange && (
|
||||
<Text color="inherit" size="sm" display="block" style={{ opacity: 0.85 }}>
|
||||
{dateRange}
|
||||
</Text>
|
||||
)}
|
||||
</VStack>
|
||||
{details && details.length > 0 && (
|
||||
<Typography sx={{ fontSize: 12, opacity: 0.9, fontWeight: 600 }}>
|
||||
<Text color="inherit" size="sm" weight="semibold" style={{ opacity: 0.9 }}>
|
||||
{details.length} {details.length === 1 ? 'stop' : 'stops'}
|
||||
{routeLoading ? ' · resolving route…' : ''}
|
||||
</Typography>
|
||||
</Text>
|
||||
)}
|
||||
{onClose && (
|
||||
<IconButton size="small" onClick={onClose} sx={{ color: '#fff' }} aria-label="Close">
|
||||
<MdClose />
|
||||
</IconButton>
|
||||
)}
|
||||
</Stack>
|
||||
{onClose && <IconButton label="Close" size="sm" variant="ghost" icon={<MdClose />} onClick={onClose} />}
|
||||
</HStack>
|
||||
);
|
||||
|
||||
// Loading state — parent is still fetching the planned route data.
|
||||
if (loading) {
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||||
<VStack gap={0} padding={0} height="100%">
|
||||
{headerBar}
|
||||
<Stack alignItems="center" justifyContent="center" sx={{ flex: 1, gap: 1.5 }}>
|
||||
<CircularProgress size={32} />
|
||||
<Typography sx={{ color: '#64748b', fontSize: 13 }}>Loading planned route…</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Center axis="both" style={{ flex: 1 }}>
|
||||
<VStack gap={3} hAlign="center" padding={0}>
|
||||
<LoaderWithImage size={44} imgSize={24} />
|
||||
<Text type="supporting">Loading planned route…</Text>
|
||||
</VStack>
|
||||
</Center>
|
||||
</VStack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -142,22 +153,22 @@ export default function RidersRoutes({ details, loading, riderName, dateRange, o
|
||||
// selected window.
|
||||
if (!details || details.length === 0) {
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||||
<VStack gap={0} padding={0} height="100%">
|
||||
{headerBar}
|
||||
<Stack alignItems="center" justifyContent="center" sx={{ flex: 1, gap: 1, p: 3 }}>
|
||||
<Typography sx={{ color: '#1e293b', fontWeight: 700, fontSize: 16 }}>No planned route for this rider</Typography>
|
||||
<Typography sx={{ color: '#64748b', fontSize: 13, textAlign: 'center', maxWidth: 360 }}>
|
||||
There are no deliveries with drop coordinates assigned to this rider for the selected date range.
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Center axis="both" style={{ flex: 1 }}>
|
||||
<EmptyState
|
||||
title="No planned route for this rider"
|
||||
description="There are no deliveries with drop coordinates assigned to this rider for the selected date range."
|
||||
/>
|
||||
</Center>
|
||||
</VStack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||||
<VStack gap={0} padding={0} height="100%">
|
||||
{headerBar}
|
||||
<Box sx={{ flex: 1, minHeight: 0 }}>
|
||||
<div style={{ flex: 1, minHeight: 0 }}>
|
||||
<MapContainer center={dropPath[0]} zoom={14} style={containerStyle} zoomControl={false}>
|
||||
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" attribution="© OpenStreetMap contributors" />
|
||||
<FitBoundsController dropPath={dropPath} />
|
||||
@@ -165,14 +176,14 @@ export default function RidersRoutes({ details, loading, riderName, dateRange, o
|
||||
{routePath.length > 0 ? (
|
||||
<>
|
||||
{/* Translucent backdrop so the route stays legible on busy tiles. */}
|
||||
<Polyline positions={routePath} pathOptions={{ color: '#C01227', opacity: 0.25, weight: 8 }} />
|
||||
<Polyline positions={routePath} pathOptions={{ color: '#000000', opacity: 0.25, weight: 8 }} />
|
||||
{/* Road-following planned route from OSRM. */}
|
||||
<Polyline positions={routePath} pathOptions={{ color: '#C01227', opacity: 0.95, weight: 4 }} />
|
||||
<Polyline positions={routePath} pathOptions={{ color: '#000000', opacity: 0.95, weight: 4 }} />
|
||||
</>
|
||||
) : (
|
||||
// Fallback while OSRM is in flight (or if it fails) — dashed
|
||||
// straight-line skeleton between drop pins in step order.
|
||||
<Polyline positions={dropPath} pathOptions={{ color: '#C01227', opacity: 0.6, weight: 3, dashArray: '2 10', lineCap: 'round' }} />
|
||||
<Polyline positions={dropPath} pathOptions={{ color: '#000000', opacity: 0.6, weight: 3, dashArray: '2 10', lineCap: 'round' }} />
|
||||
)}
|
||||
|
||||
{details.map((d, i) => {
|
||||
@@ -189,22 +200,32 @@ export default function RidersRoutes({ details, loading, riderName, dateRange, o
|
||||
zIndexOffset={isFocused ? 1000 : stepNum}
|
||||
>
|
||||
<Popup onClose={() => setFocusedStep(null)}>
|
||||
<Box sx={{ minWidth: 180, fontFamily: 'inherit' }}>
|
||||
<Typography sx={{ fontWeight: 800, fontSize: 13, color: '#0f172a' }}>
|
||||
<VStack gap={0.5} padding={0} minWidth={180}>
|
||||
<Text weight="bold" size="sm" display="block">
|
||||
Step {stepNum} · {d.customer}
|
||||
</Typography>
|
||||
{d.address && <Typography sx={{ fontSize: 12, color: '#475569', mt: 0.5 }}>{d.address}</Typography>}
|
||||
{d.expectedTime && (
|
||||
<Typography sx={{ fontSize: 12, color: '#64748b', mt: 0.5 }}>ETA {String(d.expectedTime).slice(11, 16) || d.expectedTime}</Typography>
|
||||
</Text>
|
||||
{d.address && (
|
||||
<Text type="supporting" size="sm" display="block">
|
||||
{d.address}
|
||||
</Text>
|
||||
)}
|
||||
{d.orderid && <Typography sx={{ fontSize: 11, color: '#94a3b8', mt: 0.5 }}>Order #{d.orderid}</Typography>}
|
||||
</Box>
|
||||
{d.expectedTime && (
|
||||
<Text type="supporting" size="sm" display="block">
|
||||
ETA {String(d.expectedTime).slice(11, 16) || d.expectedTime}
|
||||
</Text>
|
||||
)}
|
||||
{d.orderid && (
|
||||
<Text type="supporting" size="xsm" display="block">
|
||||
Order #{d.orderid}
|
||||
</Text>
|
||||
)}
|
||||
</VStack>
|
||||
</Popup>
|
||||
</Marker>
|
||||
);
|
||||
})}
|
||||
</MapContainer>
|
||||
</Box>
|
||||
</Box>
|
||||
</div>
|
||||
</VStack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,13 @@ import L from 'leaflet';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import dayjs from 'dayjs';
|
||||
import { parseDoormileTimestamp } from 'utils/doormileTimestamp';
|
||||
import { Chip, Stack, Typography, Box } from '@mui/material';
|
||||
import { CloseCircleOutlined } from '@ant-design/icons';
|
||||
|
||||
import { HStack } from '@astryxdesign/core/HStack';
|
||||
import { Text } from '@astryxdesign/core/Text';
|
||||
import { Token } from '@astryxdesign/core/Token';
|
||||
import { Button } from '@astryxdesign/core/Button';
|
||||
|
||||
import CircularLoader from 'components/CircularLoader';
|
||||
import { DT } from 'themes/dt/tokens';
|
||||
|
||||
@@ -122,38 +127,26 @@ const MapWithRoute = ({ coordinates, additionalProps, order, setMapOpen }) => {
|
||||
const center = coordinates[Math.floor(coordinates.length / 2)];
|
||||
|
||||
const InfoItem = ({ label, value }) => (
|
||||
<Stack direction="row" spacing={1} alignItems="center">
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
||||
{label}:
|
||||
</Typography>
|
||||
<Chip label={value || '0.00'} color="primary" sx={{ fontWeight: 700 }} />
|
||||
</Stack>
|
||||
<HStack gap={1.5} vAlign="center">
|
||||
<Text weight="semibold">{label}:</Text>
|
||||
<Token label={String(value || '0.00')} color="red" size="sm" />
|
||||
</HStack>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%', height: '100vh', position: 'relative', overflow: 'hidden' }}>
|
||||
<div style={{ width: '100%', height: '100vh', position: 'relative', overflow: 'hidden' }}>
|
||||
{loading && <CircularLoader />}
|
||||
{/* CLOSE BUTTON */}
|
||||
<Chip
|
||||
label="Close"
|
||||
icon={<CloseCircleOutlined style={{ fontSize: 18 }} />}
|
||||
onClick={() => setMapOpen(false)}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 12,
|
||||
right: 12,
|
||||
zIndex: 2000,
|
||||
bgcolor: DT.brand,
|
||||
color: '#fff',
|
||||
fontWeight: 600,
|
||||
borderRadius: DT.radiusInner + 'px',
|
||||
px: 1.5,
|
||||
py: 0.5,
|
||||
boxShadow: DT.shadowMd,
|
||||
cursor: 'pointer',
|
||||
'& .MuiChip-icon': { color: '#fff' }
|
||||
}}
|
||||
/>
|
||||
<div style={{ position: 'absolute', top: 12, right: 12, zIndex: 2000 }}>
|
||||
<Button
|
||||
label="Close"
|
||||
variant="primary"
|
||||
size="sm"
|
||||
icon={<CloseCircleOutlined style={{ fontSize: 18 }} />}
|
||||
elevation="low"
|
||||
onClick={() => setMapOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* MAP */}
|
||||
<MapContainer center={center} zoom={14} scrollWheelZoom style={{ height: '100%', width: '100%' }} ref={mapRef}>
|
||||
@@ -174,18 +167,17 @@ const MapWithRoute = ({ coordinates, additionalProps, order, setMapOpen }) => {
|
||||
</MapContainer>
|
||||
|
||||
{/* BOTTOM DETAILS */}
|
||||
<Box
|
||||
sx={{
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
width: '100%',
|
||||
bgcolor: 'rgba(255,255,255,0.96)',
|
||||
p: 2,
|
||||
backgroundColor: 'rgba(255,255,255,0.96)',
|
||||
boxShadow: DT.shadowPop,
|
||||
zIndex: 1500
|
||||
}}
|
||||
>
|
||||
<Stack direction="row" flexWrap="wrap" rowGap={1.5} columnGap={3} alignItems="center">
|
||||
<HStack wrap="wrap" rowGap={2} columnGap={6} vAlign="center" padding={4}>
|
||||
<InfoItem label="Tenant" value={order?.tenantname} />
|
||||
<InfoItem label="Rider" value={order?.ridername} />
|
||||
<InfoItem label="Pickup" value={order?.pickupcustomer} />
|
||||
@@ -193,9 +185,9 @@ const MapWithRoute = ({ coordinates, additionalProps, order, setMapOpen }) => {
|
||||
<InfoItem label="Kms" value={order?.kms} />
|
||||
<InfoItem label="Actual Kms" value={order?.actualkms} />
|
||||
<InfoItem label="Rider Kms" value={order?.riderkms} />
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
</HStack>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,14 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useInfiniteQuery } from '@tanstack/react-query';
|
||||
|
||||
// material-ui
|
||||
import { Box, Paper, TextField, Typography } from '@mui/material';
|
||||
import { MdCalendarMonth } from 'react-icons/md';
|
||||
|
||||
import { Card } from '@astryxdesign/core/Card';
|
||||
import { HStack } from '@astryxdesign/core/HStack';
|
||||
import { VStack } from '@astryxdesign/core/VStack';
|
||||
import { EmptyState } from '@astryxdesign/core/EmptyState';
|
||||
import { DateInput } from '@astryxdesign/core/DateInput';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import { parseDoormileTimestamp } from 'utils/doormileTimestamp';
|
||||
|
||||
@@ -37,14 +41,7 @@ import { SKIPPED_STATUSES } from '../dispatch/dispatchShared';
|
||||
// same pattern as reports/ordersDetails.js and deliveries.js.
|
||||
// ============================================================================
|
||||
|
||||
const DT = {
|
||||
radiusCard: 14,
|
||||
shadowSoft: '0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.05)',
|
||||
textSecondary: '#64748b',
|
||||
borderSubtle: '#e2e8f0',
|
||||
surface: '#ffffff'
|
||||
};
|
||||
const BRAND = '#C01227';
|
||||
const BRAND = '#000000';
|
||||
|
||||
// Profit formulas + batch windows imported from ProfitabilitySection.js
|
||||
// (the single source of truth) rather than duplicated here — used to total
|
||||
@@ -142,47 +139,22 @@ const Profitability = () => {
|
||||
subtitle={`Live · ${dayjs(selectedDate).format('DD MMM YYYY')}`}
|
||||
live
|
||||
action={
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 1,
|
||||
px: 1.5,
|
||||
py: 0.75,
|
||||
borderRadius: DT.radiusCard + 'px',
|
||||
border: '1px solid',
|
||||
borderColor: DT.borderSubtle,
|
||||
boxShadow: DT.shadowSoft,
|
||||
bgcolor: DT.surface
|
||||
}}
|
||||
>
|
||||
<HStack gap={2} vAlign="center">
|
||||
<MdCalendarMonth size={16} color={BRAND} />
|
||||
<TextField
|
||||
type="date"
|
||||
size="small"
|
||||
variant="standard"
|
||||
value={selectedDate}
|
||||
onChange={(e) => e.target.value && setSelectedDate(e.target.value)}
|
||||
InputProps={{ disableUnderline: true }}
|
||||
sx={{ '& input': { fontWeight: 700, color: DT.textSecondary, p: 0 } }}
|
||||
/>
|
||||
</Paper>
|
||||
<DateInput label="Report date" isLabelHidden size="sm" value={selectedDate} onChange={(v) => v && setSelectedDate(v)} />
|
||||
</HStack>
|
||||
}
|
||||
/>
|
||||
|
||||
<Box sx={{ mt: { xs: 1.5, md: 2 } }}>
|
||||
<VStack gap={0} padding={0} style={{ marginTop: 16 }}>
|
||||
{!isLoading && riders.length === 0 ? (
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{ p: 6, textAlign: 'center', borderRadius: DT.radiusCard + 'px', border: '1px solid', borderColor: DT.borderSubtle }}
|
||||
>
|
||||
<Typography sx={{ fontWeight: 700, color: DT.textSecondary }}>No deliveries for {dayjs(selectedDate).format('DD MMM YYYY')}</Typography>
|
||||
</Paper>
|
||||
<Card padding={6}>
|
||||
<EmptyState title={`No deliveries for ${dayjs(selectedDate).format('DD MMM YYYY')}`} />
|
||||
</Card>
|
||||
) : (
|
||||
<ProfitabilitySection riders={riders} totalDailyProfit={totalDailyProfit} selectedDate={selectedDate} />
|
||||
)}
|
||||
</Box>
|
||||
</VStack>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user