updates on the ui changes and table
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -25,11 +25,11 @@ import {
|
||||
var utc = require('dayjs/plugin/utc');
|
||||
dayjs.extend(utc);
|
||||
|
||||
import { Grid } from '@astryxdesign/core/Grid';
|
||||
import { HStack } from '@astryxdesign/core/HStack';
|
||||
import { VStack } from '@astryxdesign/core/VStack';
|
||||
import { Card } from '@astryxdesign/core/Card';
|
||||
import { Text } from '@astryxdesign/core/Text';
|
||||
import { Button } from '@astryxdesign/core/Button';
|
||||
import { IconButton } from '@astryxdesign/core/IconButton';
|
||||
import { Tooltip } from '@astryxdesign/core/Tooltip';
|
||||
import { Badge } from '@astryxdesign/core/Badge';
|
||||
@@ -46,6 +46,7 @@ import PageHeader from 'components/nearle_components/PageHeader';
|
||||
import StatCard from 'components/nearle_components/StatCard';
|
||||
import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar';
|
||||
import TableLoader from 'components/nearle_components/TableLoader';
|
||||
import { PageShell, KpiGrid, FilterToolbar, DataCard } from 'components/nearle_components/PageLayout';
|
||||
import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard';
|
||||
import { useIsNarrowViewport } from 'hooks/useIsNarrowViewport';
|
||||
import { DT, tint, edge, ring } from 'themes/dt/tokens';
|
||||
@@ -183,7 +184,10 @@ export default function OrdersReport() {
|
||||
enabled: tenantid !== 0
|
||||
});
|
||||
|
||||
const tenantOptions = useMemo(() => (tenantlist || []).map((t) => ({ value: String(t.tenantid), label: t.tenantname || t.label || `Tenant #${t.tenantid}` })), [tenantlist]);
|
||||
const tenantOptions = useMemo(
|
||||
() => (tenantlist || []).map((t) => ({ value: String(t.tenantid), label: t.tenantname || t.label || `Tenant #${t.tenantid}` })),
|
||||
[tenantlist]
|
||||
);
|
||||
const locationOptions = useMemo(
|
||||
() =>
|
||||
(locationlist || []).map((l) => ({
|
||||
@@ -321,9 +325,25 @@ export default function OrdersReport() {
|
||||
<CountCell value={sub.deliveriespending} color="#f59e0b" icon={<MdHourglassEmpty size={10} />} />
|
||||
<CountCell value={sub.deliveriescancelled} color="#ef4444" icon={<MdCancel size={10} />} />
|
||||
<CountCell value={sub.deliveriescompleted} color="#10b981" icon={<MdCheckCircle size={10} />} />
|
||||
<MetricPill color="#f59e0b" icon={<MdCurrencyRupee size={10} />} label={Number(sub.collectionamt || 0).toFixed(2)} tooltip="Collection" minWidth={80} />
|
||||
<MetricPill color="#ef4444" icon={<MdStraighten size={10} />} label={`${Number(sub.kms || 0).toFixed(2)} km`} tooltip="KMS" />
|
||||
<MetricPill color="#10b981" icon={<MdStraighten size={10} />} label={`${Number(sub.cumulativekms || 0).toFixed(2)} km`} tooltip="Actual KMS" />
|
||||
<MetricPill
|
||||
color="#f59e0b"
|
||||
icon={<MdCurrencyRupee size={10} />}
|
||||
label={Number(sub.collectionamt || 0).toFixed(2)}
|
||||
tooltip="Collection"
|
||||
minWidth={80}
|
||||
/>
|
||||
<MetricPill
|
||||
color="#ef4444"
|
||||
icon={<MdStraighten size={10} />}
|
||||
label={`${Number(sub.kms || 0).toFixed(2)} km`}
|
||||
tooltip="KMS"
|
||||
/>
|
||||
<MetricPill
|
||||
color="#10b981"
|
||||
icon={<MdStraighten size={10} />}
|
||||
label={`${Number(sub.cumulativekms || 0).toFixed(2)} km`}
|
||||
tooltip="Actual KMS"
|
||||
/>
|
||||
<MetricPill
|
||||
color={BRAND}
|
||||
icon={<MdCurrencyRupee size={10} />}
|
||||
@@ -344,108 +364,110 @@ export default function OrdersReport() {
|
||||
<>
|
||||
{(loading || isLoadingReports) && <Loader />}
|
||||
|
||||
<PageHeader
|
||||
title="Orders Summary"
|
||||
subtitle={`Live · ${locaName || 'All Zones'} · ${datestatus}`}
|
||||
live
|
||||
action={<LocationAutocomplete locaName={locaName} setAppId={setAppId} setLocoName={setLocoName} setPage={setPage} placeholder="Select Zone" />}
|
||||
/>
|
||||
<PageShell>
|
||||
<PageHeader
|
||||
title="Orders Summary"
|
||||
subtitle={`Live · ${locaName || 'All Zones'} · ${datestatus}`}
|
||||
live
|
||||
action={
|
||||
<LocationAutocomplete
|
||||
locaName={locaName}
|
||||
setAppId={setAppId}
|
||||
setLocoName={setLocoName}
|
||||
setPage={setPage}
|
||||
placeholder="Select Zone"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<Grid columns={{ minWidth: 180 }} gap={3}>
|
||||
{KPI_META.map((item) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<StatCard key={item.key} title={item.label} value={item.value ?? 0} icon={<Icon size={20} />} color={item.color} loading={isLoadingReports} />
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
|
||||
<VStack gap={0} padding={0} style={{ marginTop: 16 }}>
|
||||
<Card padding={3} elevation="none" style={{ borderBottomLeftRadius: 0, borderBottomRightRadius: 0, borderBottom: 'none' }}>
|
||||
<HStack justify="between" vAlign="center" gap={3} wrap="wrap">
|
||||
<HStack gap={2} vAlign="center" wrap="wrap">
|
||||
<Tooltip content="Date Filter">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(true)}
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
padding: '6px 10px',
|
||||
borderRadius: 999,
|
||||
cursor: 'pointer',
|
||||
background: tint('#f59e0b'),
|
||||
border: `1.5px solid ${edge('#f59e0b')}`,
|
||||
color: '#f59e0b',
|
||||
fontWeight: 800,
|
||||
fontSize: 12
|
||||
}}
|
||||
>
|
||||
<MdCalendarMonth size={14} />
|
||||
Orders · {datestatus} · {dayjs(startdate).format('DD/MM/YY')} – {dayjs(enddate).format('DD/MM/YY')}
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<div style={{ minWidth: 200 }}>
|
||||
<Selector
|
||||
label="Tenant"
|
||||
isLabelHidden
|
||||
options={tenantOptions}
|
||||
value={tenantid ? String(tenantid) : ''}
|
||||
onChange={(v) => {
|
||||
if (!v) {
|
||||
setTenantid(0);
|
||||
setLocationid(0);
|
||||
return;
|
||||
}
|
||||
const t = (tenantlist || []).find((x) => String(x.tenantid) === v);
|
||||
setTenantid(t?.tenantid || 0);
|
||||
setLocationid(0);
|
||||
}}
|
||||
placeholder="Select Tenant"
|
||||
hasSearch
|
||||
hasClear
|
||||
isDisabled={!appId}
|
||||
disabledMessage="Please select a Zone first!"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div style={{ minWidth: 200 }}>
|
||||
<Selector
|
||||
label="Location"
|
||||
isLabelHidden
|
||||
options={locationOptions}
|
||||
value={locationid ? String(locationid) : ''}
|
||||
onChange={(v) => {
|
||||
if (!v) {
|
||||
setLocationid(0);
|
||||
return;
|
||||
}
|
||||
const l = (locationlist || []).find((x) => String(x.locationid) === v);
|
||||
setLocationid(l?.locationid || 0);
|
||||
}}
|
||||
placeholder="Select Location"
|
||||
hasSearch
|
||||
hasClear
|
||||
isDisabled={!tenantid}
|
||||
disabledMessage={!appId ? 'Please select Zone and Tenant first!' : 'Please select a Tenant first!'}
|
||||
/>
|
||||
</div>
|
||||
</HStack>
|
||||
|
||||
<div style={{ width: 320, maxWidth: '100%' }}>
|
||||
<DebounceSearchBar
|
||||
value={searchword}
|
||||
onChange={setSearchword}
|
||||
onDebouncedChange={setDebouncedSearch}
|
||||
placeholder={`Search ${isLocationGroup ? 'locations' : 'tenants'} (ctrl+k)`}
|
||||
<KpiGrid>
|
||||
{KPI_META.map((item) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<StatCard
|
||||
key={item.key}
|
||||
title={item.label}
|
||||
value={item.value ?? 0}
|
||||
icon={<Icon size={20} />}
|
||||
color={item.color}
|
||||
loading={isLoadingReports}
|
||||
/>
|
||||
</div>
|
||||
</HStack>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</KpiGrid>
|
||||
|
||||
<Card padding={0} elevation="none" style={{ borderTopLeftRadius: 0, borderTopRightRadius: 0, overflow: 'hidden' }}>
|
||||
{/* No status tabs on this report (it's a tenant/location breakdown,
|
||||
not a lifecycle list), so the toolbar just holds every filter
|
||||
including search — same shape as hubs.js/vehicles.js when there's
|
||||
nothing to pair search with. The two-row grouped Orders/Deliveries
|
||||
header below (rowSpan/colSpan) stays a hand-rolled table rather
|
||||
than a TanStack Table v9 column-group conversion — a materially
|
||||
larger, riskier change than this pass's chrome scope. */}
|
||||
<DataCard
|
||||
toolbar={
|
||||
<FilterToolbar>
|
||||
<Button
|
||||
label={`${datestatus} · ${dayjs(startdate).format('DD/MM/YY')}–${dayjs(enddate).format('DD/MM/YY')}`}
|
||||
variant="secondary"
|
||||
icon={<MdCalendarMonth size={16} />}
|
||||
onClick={() => setOpen(true)}
|
||||
/>
|
||||
|
||||
<Selector
|
||||
label="Tenant"
|
||||
isLabelHidden
|
||||
options={tenantOptions}
|
||||
value={tenantid ? String(tenantid) : ''}
|
||||
onChange={(v) => {
|
||||
if (!v) {
|
||||
setTenantid(0);
|
||||
setLocationid(0);
|
||||
return;
|
||||
}
|
||||
const t = (tenantlist || []).find((x) => String(x.tenantid) === v);
|
||||
setTenantid(t?.tenantid || 0);
|
||||
setLocationid(0);
|
||||
}}
|
||||
placeholder="Select Tenant"
|
||||
hasSearch
|
||||
hasClear
|
||||
isDisabled={!appId}
|
||||
disabledMessage="Please select a Zone first!"
|
||||
/>
|
||||
|
||||
<Selector
|
||||
label="Location"
|
||||
isLabelHidden
|
||||
options={locationOptions}
|
||||
value={locationid ? String(locationid) : ''}
|
||||
onChange={(v) => {
|
||||
if (!v) {
|
||||
setLocationid(0);
|
||||
return;
|
||||
}
|
||||
const l = (locationlist || []).find((x) => String(x.locationid) === v);
|
||||
setLocationid(l?.locationid || 0);
|
||||
}}
|
||||
placeholder="Select Location"
|
||||
hasSearch
|
||||
hasClear
|
||||
isDisabled={!tenantid}
|
||||
disabledMessage={!appId ? 'Please select Zone and Tenant first!' : 'Please select a Tenant first!'}
|
||||
/>
|
||||
|
||||
<HStack justify="end">
|
||||
<VStack width={260} padding={0} gap={0}>
|
||||
<DebounceSearchBar
|
||||
value={searchword}
|
||||
onChange={setSearchword}
|
||||
onDebouncedChange={setDebouncedSearch}
|
||||
placeholder={`Search ${isLocationGroup ? 'locations' : 'tenants'} (ctrl+k)`}
|
||||
/>
|
||||
</VStack>
|
||||
</HStack>
|
||||
</FilterToolbar>
|
||||
}
|
||||
>
|
||||
{isMobile ? (
|
||||
<>
|
||||
{!isLoadingReports && filteredRows.length === 0 && (
|
||||
@@ -455,8 +477,8 @@ export default function OrdersReport() {
|
||||
debouncedSearch
|
||||
? 'Try a different keyword.'
|
||||
: isErrorReports
|
||||
? 'Something went wrong fetching the summary. Try a different filter.'
|
||||
: 'Pick a zone, tenant, or date range to load the summary.'
|
||||
? 'Something went wrong fetching the summary. Try a different filter.'
|
||||
: 'Pick a zone, tenant, or date range to load the summary.'
|
||||
}
|
||||
icon={<MdAssignment size={28} />}
|
||||
/>
|
||||
@@ -522,7 +544,13 @@ export default function OrdersReport() {
|
||||
<CountCell value={row.deliveriescompleted} color="#10b981" icon={<MdCheckCircle size={11} />} />
|
||||
</MobileField>
|
||||
<MobileField label="Collection" align="center">
|
||||
<MetricPill color="#f59e0b" icon={<MdCurrencyRupee size={11} />} label={Number(row.collectionamt || 0).toFixed(2)} tooltip="Collection Amount" minWidth={90} />
|
||||
<MetricPill
|
||||
color="#f59e0b"
|
||||
icon={<MdCurrencyRupee size={11} />}
|
||||
label={Number(row.collectionamt || 0).toFixed(2)}
|
||||
tooltip="Collection Amount"
|
||||
minWidth={90}
|
||||
/>
|
||||
</MobileField>
|
||||
<MobileField label="Amount" align="center">
|
||||
<MetricPill
|
||||
@@ -534,15 +562,25 @@ export default function OrdersReport() {
|
||||
/>
|
||||
</MobileField>
|
||||
<MobileField label="KMS" align="center">
|
||||
<MetricPill color="#ef4444" icon={<MdStraighten size={11} />} label={`${Number(row.kms || 0).toFixed(2)} km`} tooltip="KMS" />
|
||||
<MetricPill
|
||||
color="#ef4444"
|
||||
icon={<MdStraighten size={11} />}
|
||||
label={`${Number(row.kms || 0).toFixed(2)} km`}
|
||||
tooltip="KMS"
|
||||
/>
|
||||
</MobileField>
|
||||
<MobileField label="Actual KMS" align="center">
|
||||
<MetricPill color="#10b981" icon={<MdStraighten size={11} />} label={`${Number(row.cumulativekms || 0).toFixed(2)} km`} tooltip="Actual KMS" />
|
||||
<MetricPill
|
||||
color="#10b981"
|
||||
icon={<MdStraighten size={11} />}
|
||||
label={`${Number(row.cumulativekms || 0).toFixed(2)} km`}
|
||||
tooltip="Actual KMS"
|
||||
/>
|
||||
</MobileField>
|
||||
</MobileFieldGrid>
|
||||
|
||||
{isOpen && (
|
||||
<div style={{ marginTop: 12 }}>
|
||||
<div style={{ marginTop: 'var(--spacing-6)' }}>
|
||||
<RiderBreakdown />
|
||||
</div>
|
||||
)}
|
||||
@@ -580,12 +618,24 @@ export default function OrdersReport() {
|
||||
<TableHeaderCell rowSpan={2}>Action</TableHeaderCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHeaderCell style={{ textAlign: 'center', backgroundColor: tint('#ef4444'), color: '#f59e0b' }}>Pending</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center', backgroundColor: tint('#ef4444'), color: '#ef4444' }}>Cancelled</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center', backgroundColor: tint('#ef4444'), color: '#10b981' }}>Completed</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center', backgroundColor: tint('#10b981'), color: '#f59e0b' }}>Pending</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center', backgroundColor: tint('#10b981'), color: '#ef4444' }}>Cancelled</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center', backgroundColor: tint('#10b981'), color: '#10b981' }}>Completed</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center', backgroundColor: tint('#ef4444'), color: '#f59e0b' }}>
|
||||
Pending
|
||||
</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center', backgroundColor: tint('#ef4444'), color: '#ef4444' }}>
|
||||
Cancelled
|
||||
</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center', backgroundColor: tint('#ef4444'), color: '#10b981' }}>
|
||||
Completed
|
||||
</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center', backgroundColor: tint('#10b981'), color: '#f59e0b' }}>
|
||||
Pending
|
||||
</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center', backgroundColor: tint('#10b981'), color: '#ef4444' }}>
|
||||
Cancelled
|
||||
</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center', backgroundColor: tint('#10b981'), color: '#10b981' }}>
|
||||
Completed
|
||||
</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
|
||||
@@ -601,8 +651,8 @@ export default function OrdersReport() {
|
||||
debouncedSearch
|
||||
? 'Try a different keyword.'
|
||||
: isErrorReports
|
||||
? 'Something went wrong fetching the summary. Try a different filter.'
|
||||
: 'Pick a zone, tenant, or date range to load the summary.'
|
||||
? 'Something went wrong fetching the summary. Try a different filter.'
|
||||
: 'Pick a zone, tenant, or date range to load the summary.'
|
||||
}
|
||||
icon={<MdAssignment size={28} />}
|
||||
/>
|
||||
@@ -662,13 +712,29 @@ export default function OrdersReport() {
|
||||
</TableCell>
|
||||
|
||||
<TableCell style={{ textAlign: 'center' }}>
|
||||
<MetricPill color="#f59e0b" icon={<MdCurrencyRupee size={11} />} label={Number(row.collectionamt || 0).toFixed(2)} tooltip="Collection Amount" minWidth={90} />
|
||||
<MetricPill
|
||||
color="#f59e0b"
|
||||
icon={<MdCurrencyRupee size={11} />}
|
||||
label={Number(row.collectionamt || 0).toFixed(2)}
|
||||
tooltip="Collection Amount"
|
||||
minWidth={90}
|
||||
/>
|
||||
</TableCell>
|
||||
|
||||
<TableCell style={{ textAlign: 'center' }}>
|
||||
<VStack gap={1} padding={0} vAlign="center">
|
||||
<MetricPill color="#ef4444" icon={<MdStraighten size={11} />} label={`${Number(row.kms || 0).toFixed(2)} km`} tooltip="KMS" />
|
||||
<MetricPill color="#10b981" icon={<MdStraighten size={11} />} label={`${Number(row.cumulativekms || 0).toFixed(2)} km`} tooltip="Actual KMS" />
|
||||
<MetricPill
|
||||
color="#ef4444"
|
||||
icon={<MdStraighten size={11} />}
|
||||
label={`${Number(row.kms || 0).toFixed(2)} km`}
|
||||
tooltip="KMS"
|
||||
/>
|
||||
<MetricPill
|
||||
color="#10b981"
|
||||
icon={<MdStraighten size={11} />}
|
||||
label={`${Number(row.cumulativekms || 0).toFixed(2)} km`}
|
||||
tooltip="Actual KMS"
|
||||
/>
|
||||
</VStack>
|
||||
</TableCell>
|
||||
|
||||
@@ -741,8 +807,8 @@ export default function OrdersReport() {
|
||||
</HStack>
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
</VStack>
|
||||
</DataCard>
|
||||
</PageShell>
|
||||
|
||||
<DateFilterDialog
|
||||
open={open}
|
||||
|
||||
@@ -5,7 +5,6 @@ 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';
|
||||
|
||||
@@ -15,6 +14,7 @@ import { parseDoormileTimestamp } from 'utils/doormileTimestamp';
|
||||
import { fetchDeliveries } from 'pages/api/api';
|
||||
import Loader from 'components/Loader';
|
||||
import PageHeader from 'components/nearle_components/PageHeader';
|
||||
import { PageShell } from 'components/nearle_components/PageLayout';
|
||||
import ProfitabilitySection, {
|
||||
BASE_REVENUE,
|
||||
BASE_KM_LIMIT,
|
||||
@@ -139,19 +139,19 @@ const Profitability = () => {
|
||||
<>
|
||||
{isLoading && <Loader />}
|
||||
|
||||
<PageHeader
|
||||
title="Profitability"
|
||||
subtitle={`Live · ${dayjs(selectedDate).format('DD MMM YYYY')}`}
|
||||
live
|
||||
action={
|
||||
<HStack gap={2} vAlign="center">
|
||||
<MdCalendarMonth size={16} color={BRAND} />
|
||||
<DateInput label="Report date" isLabelHidden size="sm" value={selectedDate} onChange={(v) => v && setSelectedDate(v)} />
|
||||
</HStack>
|
||||
}
|
||||
/>
|
||||
<PageShell>
|
||||
<PageHeader
|
||||
title="Profitability"
|
||||
subtitle={`Live · ${dayjs(selectedDate).format('DD MMM YYYY')}`}
|
||||
live
|
||||
action={
|
||||
<HStack gap={2} vAlign="center">
|
||||
<MdCalendarMonth size={16} color={BRAND} />
|
||||
<DateInput label="Report date" isLabelHidden size="sm" value={selectedDate} onChange={(v) => v && setSelectedDate(v)} />
|
||||
</HStack>
|
||||
}
|
||||
/>
|
||||
|
||||
<VStack gap={0} padding={0} style={{ marginTop: 16 }}>
|
||||
{!isLoading && riders.length === 0 ? (
|
||||
<Card padding={6}>
|
||||
<EmptyState title={`No deliveries for ${dayjs(selectedDate).format('DD MMM YYYY')}`} />
|
||||
@@ -159,7 +159,7 @@ const Profitability = () => {
|
||||
) : (
|
||||
<ProfitabilitySection riders={riders} totalDailyProfit={totalDailyProfit} selectedDate={selectedDate} />
|
||||
)}
|
||||
</VStack>
|
||||
</PageShell>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -23,11 +23,11 @@ import {
|
||||
var utc = require('dayjs/plugin/utc');
|
||||
dayjs.extend(utc);
|
||||
|
||||
import { Grid } from '@astryxdesign/core/Grid';
|
||||
import { HStack } from '@astryxdesign/core/HStack';
|
||||
import { VStack } from '@astryxdesign/core/VStack';
|
||||
import { Card } from '@astryxdesign/core/Card';
|
||||
import { Text } from '@astryxdesign/core/Text';
|
||||
import { Button } from '@astryxdesign/core/Button';
|
||||
import { IconButton } from '@astryxdesign/core/IconButton';
|
||||
import { Tooltip } from '@astryxdesign/core/Tooltip';
|
||||
import { Divider } from '@astryxdesign/core/Divider';
|
||||
@@ -43,6 +43,7 @@ import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar';
|
||||
import PageHeader from 'components/nearle_components/PageHeader';
|
||||
import StatCard from 'components/nearle_components/StatCard';
|
||||
import TableLoader from 'components/nearle_components/TableLoader';
|
||||
import { PageShell, KpiGrid, FilterToolbar, DataCard } from 'components/nearle_components/PageLayout';
|
||||
import RidersRoutes from './RidersRoutes';
|
||||
import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard';
|
||||
import { useIsNarrowViewport } from 'hooks/useIsNarrowViewport';
|
||||
@@ -257,7 +258,12 @@ export default function RidersSummary() {
|
||||
<CountCell value={sub.deliveriespending} color="#f59e0b" icon={<MdHourglassEmpty size={10} />} />
|
||||
<CountCell value={sub.deliveriescompleted} color="#10b981" icon={<MdCheckCircle size={10} />} />
|
||||
<CountCell value={sub.deliveriescancelled} color="#ef4444" icon={<MdCancel size={10} />} />
|
||||
<MetricPill color="#ef4444" icon={<MdStraighten size={10} />} label={`${Number(sub.kms || 0).toFixed(2)} km`} tooltip="KMS" />
|
||||
<MetricPill
|
||||
color="#ef4444"
|
||||
icon={<MdStraighten size={10} />}
|
||||
label={`${Number(sub.kms || 0).toFixed(2)} km`}
|
||||
tooltip="KMS"
|
||||
/>
|
||||
<MetricPill
|
||||
color="#10b981"
|
||||
icon={<MdStraighten size={10} />}
|
||||
@@ -267,7 +273,9 @@ export default function RidersSummary() {
|
||||
<MetricPill
|
||||
color={BRAND}
|
||||
icon={<MdCurrencyRupee size={10} />}
|
||||
label={formatNumberToRupees(Math.max(Number(sub.charges) || 0, Number(sub.deliveryamt) || 0)).replace('₹', '').trim()}
|
||||
label={formatNumberToRupees(Math.max(Number(sub.charges) || 0, Number(sub.deliveryamt) || 0))
|
||||
.replace('₹', '')
|
||||
.trim()}
|
||||
tooltip="Total Amount"
|
||||
minWidth={100}
|
||||
/>
|
||||
@@ -283,68 +291,69 @@ export default function RidersSummary() {
|
||||
<>
|
||||
{(isLoadingReports || loading) && <Loader />}
|
||||
|
||||
<PageHeader
|
||||
title="Riders Summary"
|
||||
subtitle={`Live · ${locaName || 'All Zones'} · ${datestatus}`}
|
||||
live
|
||||
action={<LocationAutocomplete locaName={locaName} setAppId={setAppId} setLocoName={setLocoName} placeholder="Select Zone" />}
|
||||
/>
|
||||
<PageShell>
|
||||
<PageHeader
|
||||
title="Riders Summary"
|
||||
subtitle={`Live · ${locaName || 'All Zones'} · ${datestatus}`}
|
||||
live
|
||||
action={<LocationAutocomplete locaName={locaName} setAppId={setAppId} setLocoName={setLocoName} placeholder="Select Zone" />}
|
||||
/>
|
||||
|
||||
<Grid columns={{ minWidth: 180 }} gap={3}>
|
||||
{KPI_META.map((item) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<StatCard key={item.key} title={item.label} value={item.value ?? 0} icon={<Icon size={20} />} color={item.color} loading={isLoadingReports} />
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
<KpiGrid>
|
||||
{KPI_META.map((item) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<StatCard
|
||||
key={item.key}
|
||||
title={item.label}
|
||||
value={item.value ?? 0}
|
||||
icon={<Icon size={20} />}
|
||||
color={item.color}
|
||||
loading={isLoadingReports}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</KpiGrid>
|
||||
|
||||
<VStack gap={0} padding={0} style={{ marginTop: 16 }}>
|
||||
<Card padding={3} elevation="none" style={{ borderBottomLeftRadius: 0, borderBottomRightRadius: 0, borderBottom: 'none' }}>
|
||||
<HStack justify="between" vAlign="center" gap={3} wrap="wrap">
|
||||
<HStack gap={2} vAlign="center" wrap="wrap">
|
||||
<AccentAvatar color={BRAND} size={32}>
|
||||
<MdDirectionsBike size={18} />
|
||||
</AccentAvatar>
|
||||
<VStack gap={0} padding={0}>
|
||||
<Text type="label" weight="bold" color="secondary" style={{ letterSpacing: 0.6, textTransform: 'uppercase' }}>
|
||||
Orders · {datestatus}
|
||||
</Text>
|
||||
<Text weight="bold">
|
||||
{filteredRows.length} riders · {stats.orders} orders
|
||||
</Text>
|
||||
</VStack>
|
||||
<Tooltip content="Date Filter">
|
||||
<button
|
||||
type="button"
|
||||
{/* No status tabs on this report — toolbar holds the date range and
|
||||
search, same shape as hubs.js/vehicles.js when there's nothing to
|
||||
pair search with. */}
|
||||
<DataCard
|
||||
toolbar={
|
||||
<FilterToolbar max={2}>
|
||||
<HStack gap={2} vAlign="center">
|
||||
<AccentAvatar color={BRAND} size={32}>
|
||||
<MdDirectionsBike size={18} />
|
||||
</AccentAvatar>
|
||||
<VStack gap={0} padding={0}>
|
||||
<Text type="label" weight="bold" color="secondary" style={{ letterSpacing: 0.6, textTransform: 'uppercase' }}>
|
||||
Orders · {datestatus}
|
||||
</Text>
|
||||
<Text weight="bold">
|
||||
{filteredRows.length} riders · {stats.orders} orders
|
||||
</Text>
|
||||
</VStack>
|
||||
<Button
|
||||
label={`${dayjs(startdate).format('DD/MM/YY')} – ${dayjs(enddate).format('DD/MM/YY')}`}
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon={<MdCalendarMonth size={14} />}
|
||||
onClick={() => setOpen(true)}
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
padding: '6px 10px',
|
||||
borderRadius: 999,
|
||||
cursor: 'pointer',
|
||||
background: tint('#f59e0b'),
|
||||
border: `1.5px solid ${edge('#f59e0b')}`,
|
||||
color: '#f59e0b',
|
||||
fontWeight: 800,
|
||||
fontSize: 12,
|
||||
marginInlineStart: 8
|
||||
}}
|
||||
>
|
||||
<MdCalendarMonth size={14} />
|
||||
{dayjs(startdate).format('DD/MM/YY')} – {dayjs(enddate).format('DD/MM/YY')}
|
||||
</button>
|
||||
</Tooltip>
|
||||
</HStack>
|
||||
<div style={{ width: 300, maxWidth: '100%' }}>
|
||||
<DebounceSearchBar value={searchword} onChange={setSearchword} onDebouncedChange={setDebouncedSearch} placeholder="Search riders (ctrl+k)" />
|
||||
</div>
|
||||
</HStack>
|
||||
</Card>
|
||||
|
||||
<Card padding={0} elevation="none" style={{ borderTopLeftRadius: 0, borderTopRightRadius: 0, overflow: 'hidden' }}>
|
||||
/>
|
||||
</HStack>
|
||||
<HStack justify="end">
|
||||
<VStack width={260} padding={0} gap={0}>
|
||||
<DebounceSearchBar
|
||||
value={searchword}
|
||||
onChange={setSearchword}
|
||||
onDebouncedChange={setDebouncedSearch}
|
||||
placeholder="Search riders (ctrl+k)"
|
||||
/>
|
||||
</VStack>
|
||||
</HStack>
|
||||
</FilterToolbar>
|
||||
}
|
||||
>
|
||||
{isMobile ? (
|
||||
<MobileCardList scroll>
|
||||
{(!filteredRows || filteredRows.length === 0) && !isLoadingReports ? (
|
||||
@@ -379,7 +388,14 @@ export default function RidersSummary() {
|
||||
</VStack>
|
||||
</HStack>
|
||||
<HStack gap={0.75}>
|
||||
<IconButton label="View planned route" tooltip="View planned route" size="sm" variant="ghost" icon={<MdMap size={14} />} onClick={() => openRoutePreview(row)} />
|
||||
<IconButton
|
||||
label="View planned route"
|
||||
tooltip="View planned route"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
icon={<MdMap size={14} />}
|
||||
onClick={() => openRoutePreview(row)}
|
||||
/>
|
||||
<IconButton
|
||||
label={isOpen ? 'Collapse breakdown' : 'Expand breakdown'}
|
||||
tooltip={isOpen ? 'Collapse breakdown' : 'Expand breakdown'}
|
||||
@@ -406,7 +422,12 @@ export default function RidersSummary() {
|
||||
<CountCell value={row.cancelled} color="#ef4444" icon={<MdCancel size={11} />} />
|
||||
</MobileField>
|
||||
<MobileField label="KMS">
|
||||
<MetricPill color="#ef4444" icon={<MdStraighten size={11} />} label={`${Number(row.riderkms || 0).toFixed(2)} km`} tooltip="KMS" />
|
||||
<MetricPill
|
||||
color="#ef4444"
|
||||
icon={<MdStraighten size={11} />}
|
||||
label={`${Number(row.riderkms || 0).toFixed(2)} km`}
|
||||
tooltip="KMS"
|
||||
/>
|
||||
</MobileField>
|
||||
<MobileField label="Amount" full>
|
||||
<MetricPill
|
||||
@@ -497,7 +518,12 @@ export default function RidersSummary() {
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<MetricPill color="#ef4444" icon={<MdStraighten size={11} />} label={`${Number(row.riderkms || 0).toFixed(2)} km`} tooltip="KMS" />
|
||||
<MetricPill
|
||||
color="#ef4444"
|
||||
icon={<MdStraighten size={11} />}
|
||||
label={`${Number(row.riderkms || 0).toFixed(2)} km`}
|
||||
tooltip="KMS"
|
||||
/>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
@@ -512,7 +538,14 @@ export default function RidersSummary() {
|
||||
|
||||
<TableCell>
|
||||
<HStack gap={0.75} justify="end">
|
||||
<IconButton label="View planned route" tooltip="View planned route" size="sm" variant="ghost" icon={<MdMap size={14} />} onClick={() => openRoutePreview(row)} />
|
||||
<IconButton
|
||||
label="View planned route"
|
||||
tooltip="View planned route"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
icon={<MdMap size={14} />}
|
||||
onClick={() => openRoutePreview(row)}
|
||||
/>
|
||||
<IconButton
|
||||
label={isOpen ? 'Collapse breakdown' : 'Expand breakdown'}
|
||||
tooltip={isOpen ? 'Collapse breakdown' : 'Expand breakdown'}
|
||||
@@ -566,8 +599,8 @@ export default function RidersSummary() {
|
||||
</HStack>
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
</VStack>
|
||||
</DataCard>
|
||||
</PageShell>
|
||||
|
||||
<Dialog
|
||||
isOpen={mapOpen}
|
||||
|
||||
Reference in New Issue
Block a user