updates on the ui changes and table
This commit is contained in:
@@ -9,7 +9,6 @@ import { HiOutlineArrowLeft } from 'react-icons/hi';
|
||||
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';
|
||||
@@ -22,6 +21,7 @@ import { Table, TableHeader, TableBody, TableRow, TableCell, TableHeaderCell } f
|
||||
|
||||
import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard';
|
||||
import PageHeader from 'components/nearle_components/PageHeader';
|
||||
import { PageShell, KpiGrid, DataCard } from 'components/nearle_components/PageLayout';
|
||||
import HoverSocialCard from 'components/cards/statistics/HoverSocialCard';
|
||||
import { parseDoormileTimestamp } from 'utils/doormileTimestamp';
|
||||
import { fetchPaymentType, fetchRidersList, finalCreatedeliveries, notifyRider } from '../../api/api';
|
||||
@@ -210,7 +210,10 @@ const OrdersPreview = () => {
|
||||
enabled: appId !== 0 // Ensures query runs only when appId is valid
|
||||
});
|
||||
|
||||
const paymentOptions = useMemo(() => paymentModes.map((p) => ({ value: String(p.apptypeid), label: p.label || p.name || `Type #${p.apptypeid}` })), [paymentModes]);
|
||||
const paymentOptions = useMemo(
|
||||
() => paymentModes.map((p) => ({ value: String(p.apptypeid), label: p.label || p.name || `Type #${p.apptypeid}` })),
|
||||
[paymentModes]
|
||||
);
|
||||
const riderOptions = useMemo(() => ridersList.map((r) => ({ value: String(r.userid), label: r.label })), [ridersList]);
|
||||
|
||||
// ======================================================= || notifyRiderMutation || =======================================================
|
||||
@@ -368,104 +371,124 @@ const OrdersPreview = () => {
|
||||
<>
|
||||
{(paymentModesLoading || ridersListLoading || isLoading) && <Loader />}
|
||||
|
||||
<PageHeader
|
||||
title="Assign Orders"
|
||||
subtitle="Review before committing"
|
||||
action={
|
||||
<>
|
||||
<IconButton label="Back to orders" tooltip="Back to orders" variant="secondary" icon={<HiOutlineArrowLeft size={20} />} onClick={() => navigate('/doormile/orders')} />
|
||||
<Button label="Re-Assign" variant="secondary" icon={<MdArrowBack size={16} />} />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<PageShell>
|
||||
<PageHeader
|
||||
title="Assign Orders"
|
||||
subtitle="Review before committing"
|
||||
action={
|
||||
<>
|
||||
<IconButton
|
||||
label="Back to orders"
|
||||
tooltip="Back to orders"
|
||||
variant="secondary"
|
||||
icon={<HiOutlineArrowLeft size={20} />}
|
||||
onClick={() => navigate('/doormile/orders')}
|
||||
/>
|
||||
<Button label="Re-Assign" variant="secondary" icon={<MdArrowBack size={16} />} />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
{aiMode == 1 && (
|
||||
<Grid columns={{ minWidth: 200, max: 4, repeat: 'fit' }} gap={3} style={{ marginBottom: 16 }}>
|
||||
<HoverSocialCard secondary={metaData?.total_orders} primary="Orders" percentage={<MdDirectionsBike />} color={STATUS.delivered} />
|
||||
<HoverSocialCard secondary={metaData?.total_riders} primary="Riders" percentage={<MdDirectionsBike />} color={STATUS.pending} />
|
||||
<HoverSocialCard secondary={zoneData?.length} primary="Zones" percentage={<FaMapLocationDot />} color="#0ea5e9" />
|
||||
<HoverSocialCard secondary={zoneData?.length} primary="Kilometer" percentage={<FaMapLocationDot />} color={STATUS.cancelled} />
|
||||
</Grid>
|
||||
)}
|
||||
{aiMode == 1 && (
|
||||
<KpiGrid>
|
||||
<HoverSocialCard
|
||||
secondary={metaData?.total_orders}
|
||||
primary="Orders"
|
||||
percentage={<MdDirectionsBike />}
|
||||
color={STATUS.delivered}
|
||||
/>
|
||||
<HoverSocialCard secondary={metaData?.total_riders} primary="Riders" percentage={<MdDirectionsBike />} color={STATUS.pending} />
|
||||
<HoverSocialCard secondary={zoneData?.length} primary="Zones" percentage={<FaMapLocationDot />} color="#0ea5e9" />
|
||||
<HoverSocialCard secondary={zoneData?.length} primary="Kilometer" percentage={<FaMapLocationDot />} color={STATUS.cancelled} />
|
||||
</KpiGrid>
|
||||
)}
|
||||
|
||||
<Card padding={0} elevation="none" style={{ overflow: 'hidden' }}>
|
||||
{isMobile ? (
|
||||
<MobileOrdersList list={finaldeliveryList} aiMode={aiMode} />
|
||||
) : (
|
||||
<TableScroll minWidth={aiMode == 1 ? 1400 : 1200}>
|
||||
<Table density="balanced" dividers="rows" hasHover>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHeaderCell>#</TableHeaderCell>
|
||||
{aiMode == 1 && <TableHeaderCell>Zone</TableHeaderCell>}
|
||||
<TableHeaderCell>Tenant</TableHeaderCell>
|
||||
<TableHeaderCell>Order Location</TableHeaderCell>
|
||||
<TableHeaderCell>Pickup</TableHeaderCell>
|
||||
<TableHeaderCell>Delivery</TableHeaderCell>
|
||||
<TableHeaderCell>Notes</TableHeaderCell>
|
||||
{aiMode == 1 && <TableHeaderCell>Rider</TableHeaderCell>}
|
||||
<TableHeaderCell style={{ textAlign: 'center' }}>Type</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center' }}>Profit</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center' }}>KMS</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{finaldeliveryList?.length == 0 && (
|
||||
<DataCard>
|
||||
{isMobile ? (
|
||||
<MobileOrdersList list={finaldeliveryList} aiMode={aiMode} />
|
||||
) : (
|
||||
<TableScroll minWidth={aiMode == 1 ? 1400 : 1200}>
|
||||
<Table density="balanced" dividers="rows" hasHover>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableCell colSpan={10}>
|
||||
<EmptyState title="No orders to preview" icon={<MdDirectionsBike size={28} />} />
|
||||
</TableCell>
|
||||
<TableHeaderCell>#</TableHeaderCell>
|
||||
{aiMode == 1 && <TableHeaderCell>Zone</TableHeaderCell>}
|
||||
<TableHeaderCell>Tenant</TableHeaderCell>
|
||||
<TableHeaderCell>Order Location</TableHeaderCell>
|
||||
<TableHeaderCell>Pickup</TableHeaderCell>
|
||||
<TableHeaderCell>Delivery</TableHeaderCell>
|
||||
<TableHeaderCell>Notes</TableHeaderCell>
|
||||
{aiMode == 1 && <TableHeaderCell>Rider</TableHeaderCell>}
|
||||
<TableHeaderCell style={{ textAlign: 'center' }}>Type</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center' }}>Profit</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center' }}>KMS</TableHeaderCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{finaldeliveryList?.map((val, index) => renderRow(val, index))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableScroll>
|
||||
)}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{finaldeliveryList?.length == 0 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={10}>
|
||||
<EmptyState title="No orders to preview" icon={<MdDirectionsBike size={28} />} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{finaldeliveryList?.map((val, index) => renderRow(val, index))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableScroll>
|
||||
)}
|
||||
|
||||
<Divider />
|
||||
{aiMode == 0 && (
|
||||
<Grid columns={{ minWidth: 240 }} gap={3} style={{ padding: 16 }}>
|
||||
<Selector
|
||||
label="Choose Payment"
|
||||
options={paymentOptions}
|
||||
value={payment ? String(payment.apptypeid) : ''}
|
||||
onChange={(v) => {
|
||||
if (!v) {
|
||||
setPayment(null);
|
||||
return;
|
||||
}
|
||||
const newValue = paymentModes.find((p) => String(p.apptypeid) === v);
|
||||
setPayment(newValue);
|
||||
setFinalDeliveryList((list) => list.map((item) => ({ ...item, paymenttype: newValue.apptypeid })));
|
||||
}}
|
||||
hasClear
|
||||
hasSearch
|
||||
<Divider />
|
||||
{aiMode == 0 && (
|
||||
<Grid columns={{ minWidth: 240, repeat: 'fit' }} gap={3} style={{ padding: 16 }}>
|
||||
<Selector
|
||||
label="Choose Payment"
|
||||
options={paymentOptions}
|
||||
value={payment ? String(payment.apptypeid) : ''}
|
||||
onChange={(v) => {
|
||||
if (!v) {
|
||||
setPayment(null);
|
||||
return;
|
||||
}
|
||||
const newValue = paymentModes.find((p) => String(p.apptypeid) === v);
|
||||
setPayment(newValue);
|
||||
setFinalDeliveryList((list) => list.map((item) => ({ ...item, paymenttype: newValue.apptypeid })));
|
||||
}}
|
||||
hasClear
|
||||
hasSearch
|
||||
/>
|
||||
<Selector
|
||||
label="Choose Rider"
|
||||
options={riderOptions}
|
||||
value={rider ? String(rider.userid) : ''}
|
||||
onChange={(v) => {
|
||||
if (!v) {
|
||||
setRider(null);
|
||||
return;
|
||||
}
|
||||
const newValue = ridersList.find((r) => String(r.userid) === v);
|
||||
setRider(newValue);
|
||||
setFinalDeliveryList((list) =>
|
||||
list.map((item) => ({ ...item, userid: newValue.userid, userfcmtoken: newValue.userfcmtoken }))
|
||||
);
|
||||
}}
|
||||
hasClear
|
||||
hasSearch
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
<Divider />
|
||||
<HStack justify="end" gap={2} padding={3}>
|
||||
<Button label="Back" variant="secondary" icon={<MdArrowBack size={16} />} onClick={() => navigate('/doormile/orders')} />
|
||||
<Button
|
||||
label="Finalise"
|
||||
variant="primary"
|
||||
isDisabled={aiMode === 0 && (!rider || !payment)}
|
||||
onClick={handleManualCreateDelivery}
|
||||
/>
|
||||
<Selector
|
||||
label="Choose Rider"
|
||||
options={riderOptions}
|
||||
value={rider ? String(rider.userid) : ''}
|
||||
onChange={(v) => {
|
||||
if (!v) {
|
||||
setRider(null);
|
||||
return;
|
||||
}
|
||||
const newValue = ridersList.find((r) => String(r.userid) === v);
|
||||
setRider(newValue);
|
||||
setFinalDeliveryList((list) => list.map((item) => ({ ...item, userid: newValue.userid, userfcmtoken: newValue.userfcmtoken })));
|
||||
}}
|
||||
hasClear
|
||||
hasSearch
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
<Divider />
|
||||
<HStack justify="end" gap={2} padding={3}>
|
||||
<Button label="Back" variant="secondary" icon={<MdArrowBack size={16} />} onClick={() => navigate('/doormile/orders')} />
|
||||
<Button label="Finalise" variant="primary" isDisabled={aiMode === 0 && (!rider || !payment)} onClick={handleManualCreateDelivery} />
|
||||
</HStack>
|
||||
</Card>
|
||||
</HStack>
|
||||
</DataCard>
|
||||
</PageShell>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -33,6 +33,7 @@ import { FaLocationDot } from 'react-icons/fa6';
|
||||
|
||||
import Loader from 'components/Loader';
|
||||
import PageHeader from 'components/nearle_components/PageHeader';
|
||||
import { PageShell } from 'components/nearle_components/PageLayout';
|
||||
import { TableScroll } from 'themes/dt/primitives';
|
||||
import './OrdersRedesign.css';
|
||||
|
||||
@@ -752,405 +753,489 @@ const MultipleOrders = () => {
|
||||
<>
|
||||
{(loading || btnLoading) && <Loader />}
|
||||
|
||||
<PageHeader
|
||||
title="Create Multiple Orders"
|
||||
subtitle="Bulk-create deliveries from CSV/Excel or saved customers"
|
||||
action={<Badge variant={dropCust.length ? 'blue' : 'neutral'} label={`${dropCust.length} drop${dropCust.length === 1 ? '' : 's'}`} />}
|
||||
/>
|
||||
<PageShell>
|
||||
<PageHeader
|
||||
title="Create Multiple Orders"
|
||||
subtitle="Bulk-create deliveries from CSV/Excel or saved customers"
|
||||
action={
|
||||
<Badge variant={dropCust.length ? 'blue' : 'neutral'} label={`${dropCust.length} drop${dropCust.length === 1 ? '' : 's'}`} />
|
||||
}
|
||||
/>
|
||||
|
||||
<Grid columns={{ minWidth: 420 }} gap={3}>
|
||||
{/* LEFT — input fields */}
|
||||
<VStack gap={3} padding={0}>
|
||||
<Card padding={3} elevation="none">
|
||||
<Text weight="bold" style={{ marginBottom: 12 }}>
|
||||
Schedule & Pickup
|
||||
</Text>
|
||||
<VStack gap={2} padding={0}>
|
||||
{locationsStatus === 'loading' ? (
|
||||
<Text color="secondary">Loading business locations…</Text>
|
||||
) : locationsStatus === 'no-tenant' && isStaffLogin ? (
|
||||
// Staff login (express-console-api.md: token tenantid
|
||||
// 0/null = "Doormile's own staff... sees everything, all
|
||||
// tenants") — pick which tenant this bulk order is for.
|
||||
<Selector
|
||||
label="Pick a tenant"
|
||||
options={staffTenantOptions}
|
||||
value={selectedTenantId}
|
||||
onChange={(v) => setSelectedTenantId(v || '')}
|
||||
placeholder="Pick a tenant to create orders for"
|
||||
hasSearch
|
||||
isLoading={staffTenantListLoading}
|
||||
/>
|
||||
) : locationsStatus === 'no-tenant' ? (
|
||||
<Text style={{ color: '#b91c1c' }}>This login has no tenant assigned — sign in as a tenant user</Text>
|
||||
) : locationsStatus === 'error' ? (
|
||||
<Text style={{ color: '#b91c1c', cursor: 'pointer' }} onClick={() => gettenantlocations(tenantid)}>
|
||||
Could not load business locations — tap to retry
|
||||
</Text>
|
||||
) : locationsStatus === 'empty' ? (
|
||||
<Text style={{ color: '#b45309' }}>No saved business locations for this tenant</Text>
|
||||
) : tenantLocations?.length === 1 ? (
|
||||
<TextInput label="Business Location" value={tenantLocations[0] ? locationDisplayName(tenantLocations[0]) : ''} isDisabled />
|
||||
) : (
|
||||
<Selector
|
||||
label="Business Location"
|
||||
options={locationOptions}
|
||||
value={locationValue ? String(locationValue.tenantlocationid) : ''}
|
||||
onChange={(v) => {
|
||||
if (!v) {
|
||||
setLocationid(0);
|
||||
setLocationValue(null);
|
||||
setPickCust(null);
|
||||
return;
|
||||
}
|
||||
const value = tenantLocations.find((l) => String(l.tenantlocationid) === v);
|
||||
setLocationid(value?.tenantlocationid || 0);
|
||||
setLocationValue(value || null);
|
||||
setPickCust(value || null);
|
||||
}}
|
||||
placeholder="Select Business Location"
|
||||
hasSearch
|
||||
hasClear
|
||||
/>
|
||||
)}
|
||||
<Grid columns={{ minWidth: 420, repeat: 'fit' }} gap={3}>
|
||||
{/* LEFT — input fields */}
|
||||
<VStack gap={3} padding={0}>
|
||||
<Card padding={3} elevation="none">
|
||||
<Text weight="bold" style={{ marginBottom: 12 }}>
|
||||
Schedule & Pickup
|
||||
</Text>
|
||||
<VStack gap={2} padding={0}>
|
||||
{locationsStatus === 'loading' ? (
|
||||
<Text color="secondary">Loading business locations…</Text>
|
||||
) : locationsStatus === 'no-tenant' && isStaffLogin ? (
|
||||
// Staff login (express-console-api.md: token tenantid
|
||||
// 0/null = "Doormile's own staff... sees everything, all
|
||||
// tenants") — pick which tenant this bulk order is for.
|
||||
<Selector
|
||||
label="Pick a tenant"
|
||||
options={staffTenantOptions}
|
||||
value={selectedTenantId}
|
||||
onChange={(v) => setSelectedTenantId(v || '')}
|
||||
placeholder="Pick a tenant to create orders for"
|
||||
hasSearch
|
||||
isLoading={staffTenantListLoading}
|
||||
/>
|
||||
) : locationsStatus === 'no-tenant' ? (
|
||||
<Text style={{ color: '#b91c1c' }}>This login has no tenant assigned — sign in as a tenant user</Text>
|
||||
) : locationsStatus === 'error' ? (
|
||||
<Text style={{ color: '#b91c1c', cursor: 'pointer' }} onClick={() => gettenantlocations(tenantid)}>
|
||||
Could not load business locations — tap to retry
|
||||
</Text>
|
||||
) : locationsStatus === 'empty' ? (
|
||||
<Text style={{ color: '#b45309' }}>No saved business locations for this tenant</Text>
|
||||
) : tenantLocations?.length === 1 ? (
|
||||
<TextInput
|
||||
label="Business Location"
|
||||
value={tenantLocations[0] ? locationDisplayName(tenantLocations[0]) : ''}
|
||||
isDisabled
|
||||
/>
|
||||
) : (
|
||||
<Selector
|
||||
label="Business Location"
|
||||
options={locationOptions}
|
||||
value={locationValue ? String(locationValue.tenantlocationid) : ''}
|
||||
onChange={(v) => {
|
||||
if (!v) {
|
||||
setLocationid(0);
|
||||
setLocationValue(null);
|
||||
setPickCust(null);
|
||||
return;
|
||||
}
|
||||
const value = tenantLocations.find((l) => String(l.tenantlocationid) === v);
|
||||
setLocationid(value?.tenantlocationid || 0);
|
||||
setLocationValue(value || null);
|
||||
setPickCust(value || null);
|
||||
}}
|
||||
placeholder="Select Business Location"
|
||||
hasSearch
|
||||
hasClear
|
||||
/>
|
||||
)}
|
||||
|
||||
<Grid columns={{ minWidth: 180 }} gap={2}>
|
||||
<DateInput
|
||||
label="Pickup Date"
|
||||
size="sm"
|
||||
value={startdate ? dayjs(startdate).format('YYYY-MM-DD') : undefined}
|
||||
min={dayjs().format('YYYY-MM-DD')}
|
||||
onChange={(v) => {
|
||||
if (!v || !dayjs(v).isValid()) {
|
||||
setStartdate(dayjs().format('MM-DD-YYYY'));
|
||||
return;
|
||||
}
|
||||
const diffDays = dayjs().diff(dayjs(v), 'd');
|
||||
if (diffDays <= 0) {
|
||||
setStartdate(dayjs(v).format('MM-DD-YYYY'));
|
||||
setSelectedtime('');
|
||||
setPickupSlot(null);
|
||||
} else {
|
||||
opentoast('Choose an upcoming date', 'warning');
|
||||
setStartdate(dayjs().format('MM-DD-YYYY'));
|
||||
}
|
||||
}}
|
||||
<Grid columns={{ minWidth: 180, repeat: 'fit' }} gap={2}>
|
||||
<DateInput
|
||||
label="Pickup Date"
|
||||
size="sm"
|
||||
value={startdate ? dayjs(startdate).format('YYYY-MM-DD') : undefined}
|
||||
min={dayjs().format('YYYY-MM-DD')}
|
||||
onChange={(v) => {
|
||||
if (!v || !dayjs(v).isValid()) {
|
||||
setStartdate(dayjs().format('MM-DD-YYYY'));
|
||||
return;
|
||||
}
|
||||
const diffDays = dayjs().diff(dayjs(v), 'd');
|
||||
if (diffDays <= 0) {
|
||||
setStartdate(dayjs(v).format('MM-DD-YYYY'));
|
||||
setSelectedtime('');
|
||||
setPickupSlot(null);
|
||||
} else {
|
||||
opentoast('Choose an upcoming date', 'warning');
|
||||
setStartdate(dayjs().format('MM-DD-YYYY'));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Selector
|
||||
label="Pickup Slot"
|
||||
options={slotOptions}
|
||||
value={selectedtime ? dayjs(selectedtime, 'hh:mm A').format('HH:mm') : ''}
|
||||
onChange={(v) => {
|
||||
if (!v) {
|
||||
setSelectedtime('');
|
||||
setPickupSlot(null);
|
||||
return;
|
||||
}
|
||||
if (!locationid) {
|
||||
OpenToast('Please select a Business Location first.', 'warning', 3000);
|
||||
return;
|
||||
}
|
||||
const formattedTime = dayjs(v, 'HH:mm').format('hh:mm A');
|
||||
setSelectedtime(formattedTime);
|
||||
const finalDateTime = dayjs(`${startdate} ${formattedTime}`, 'MM-DD-YYYY hh:mm A').format('YYYY-MM-DD hh:mm A');
|
||||
setPickupSlot(finalDateTime);
|
||||
}}
|
||||
placeholder="Select Pickup Slot"
|
||||
hasSearch
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{pickCust ? (
|
||||
<HStack
|
||||
gap={2}
|
||||
vAlign="center"
|
||||
padding={2}
|
||||
style={{ border: '1px solid #eef2f6', borderInlineStart: '3px solid #0ea5e9', borderRadius: 10, background: '#fbfcff' }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 8,
|
||||
background: 'linear-gradient(135deg,#0ea5e9,#0284c7)',
|
||||
color: '#fff',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0,
|
||||
fontSize: 12
|
||||
}}
|
||||
>
|
||||
<FaLocationDot />
|
||||
</div>
|
||||
<VStack gap={0} padding={0} minWidth={0}>
|
||||
<Text weight="bold" maxLines={1}>
|
||||
{pickCust.locationname || '—'}
|
||||
</Text>
|
||||
<Text type="supporting" color="secondary" maxLines={1}>
|
||||
{pickCust.address || '—'}
|
||||
</Text>
|
||||
</VStack>
|
||||
</HStack>
|
||||
) : (
|
||||
<Text type="supporting" color="secondary">
|
||||
Pickup auto-fills once a Business Location is selected.
|
||||
</Text>
|
||||
)}
|
||||
</VStack>
|
||||
</Card>
|
||||
|
||||
<Card padding={3} elevation="none">
|
||||
<Text weight="bold" style={{ marginBottom: 4 }}>
|
||||
Order Notes
|
||||
</Text>
|
||||
<Text type="supporting" color="secondary" style={{ marginBottom: 8 }}>
|
||||
Applied to every order
|
||||
</Text>
|
||||
<TextInput
|
||||
label="Special Dispatch Notes"
|
||||
isLabelHidden
|
||||
placeholder="Gate codes, call instructions, special cargo care…"
|
||||
value={otherinstructions}
|
||||
onChange={setOtherinstructions}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card padding={3} elevation="none">
|
||||
<Text weight="bold" style={{ marginBottom: 2 }}>
|
||||
Bulk Summary
|
||||
</Text>
|
||||
<Text type="supporting" color="secondary" style={{ marginBottom: 12 }}>
|
||||
Live totals
|
||||
</Text>
|
||||
<Grid columns={{ minWidth: 140, repeat: 'fit' }} gap={2}>
|
||||
<MetricTile
|
||||
icon={FaRoute}
|
||||
label="Distance"
|
||||
value={totaldist ? `${totaldist} km` : '—'}
|
||||
accent="#0ea5e9"
|
||||
active={!!totaldist}
|
||||
/>
|
||||
<Selector
|
||||
label="Pickup Slot"
|
||||
options={slotOptions}
|
||||
value={selectedtime ? dayjs(selectedtime, 'hh:mm A').format('HH:mm') : ''}
|
||||
onChange={(v) => {
|
||||
if (!v) {
|
||||
setSelectedtime('');
|
||||
setPickupSlot(null);
|
||||
return;
|
||||
}
|
||||
if (!locationid) {
|
||||
OpenToast('Please select a Business Location first.', 'warning', 3000);
|
||||
return;
|
||||
}
|
||||
const formattedTime = dayjs(v, 'HH:mm').format('hh:mm A');
|
||||
setSelectedtime(formattedTime);
|
||||
const finalDateTime = dayjs(`${startdate} ${formattedTime}`, 'MM-DD-YYYY hh:mm A').format('YYYY-MM-DD hh:mm A');
|
||||
setPickupSlot(finalDateTime);
|
||||
}}
|
||||
placeholder="Select Pickup Slot"
|
||||
hasSearch
|
||||
<MetricTile icon={FaBoxes} label="Quantity" value={totalQty || 0} accent="#10b981" active={!!totalQty} />
|
||||
<MetricTile
|
||||
icon={FaMoneyBillWave}
|
||||
label="Cash Collect"
|
||||
value={`₹${Number(totalCash).toFixed(2)}`}
|
||||
accent="#f59e0b"
|
||||
active={!!totalCash}
|
||||
/>
|
||||
<MetricTile icon={FaTruck} label="Deliveries" value={dropCust.length} accent="#8b5cf6" active={dropCust.length > 0} />
|
||||
</Grid>
|
||||
|
||||
{pickCust ? (
|
||||
<HStack gap={2} vAlign="center" padding={2} style={{ border: '1px solid #eef2f6', borderInlineStart: '3px solid #0ea5e9', borderRadius: 10, background: '#fbfcff' }}>
|
||||
<div
|
||||
style={{
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 8,
|
||||
background: 'linear-gradient(135deg,#0ea5e9,#0284c7)',
|
||||
color: '#fff',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0,
|
||||
fontSize: 12
|
||||
}}
|
||||
>
|
||||
<FaLocationDot />
|
||||
{dropCust.length > 0 && (
|
||||
<div className="total-charge-badge" style={{ marginTop: 14, padding: '10px 14px' }}>
|
||||
<div className="total-charge-left">
|
||||
<FaReceipt className="total-charge-icon" />
|
||||
<div className="total-charge-label">Total Charge</div>
|
||||
</div>
|
||||
<VStack gap={0} padding={0} minWidth={0}>
|
||||
<Text weight="bold" maxLines={1}>
|
||||
{pickCust.locationname || '—'}
|
||||
</Text>
|
||||
<Text type="supporting" color="secondary" maxLines={1}>
|
||||
{pickCust.address || '—'}
|
||||
</Text>
|
||||
</VStack>
|
||||
</HStack>
|
||||
) : (
|
||||
<Text type="supporting" color="secondary">
|
||||
Pickup auto-fills once a Business Location is selected.
|
||||
</Text>
|
||||
<div className="total-charge-val" style={{ fontSize: 17 }}>
|
||||
₹{Number(totalAmt).toFixed(2)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</VStack>
|
||||
</Card>
|
||||
|
||||
<Card padding={3} elevation="none">
|
||||
<Text weight="bold" style={{ marginBottom: 4 }}>
|
||||
Order Notes
|
||||
</Text>
|
||||
<Text type="supporting" color="secondary" style={{ marginBottom: 8 }}>
|
||||
Applied to every order
|
||||
</Text>
|
||||
<TextInput
|
||||
label="Special Dispatch Notes"
|
||||
isLabelHidden
|
||||
placeholder="Gate codes, call instructions, special cargo care…"
|
||||
value={otherinstructions}
|
||||
onChange={setOtherinstructions}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card padding={3} elevation="none">
|
||||
<Text weight="bold" style={{ marginBottom: 2 }}>
|
||||
Bulk Summary
|
||||
</Text>
|
||||
<Text type="supporting" color="secondary" style={{ marginBottom: 12 }}>
|
||||
Live totals
|
||||
</Text>
|
||||
<Grid columns={{ minWidth: 140 }} gap={2}>
|
||||
<MetricTile icon={FaRoute} label="Distance" value={totaldist ? `${totaldist} km` : '—'} accent="#0ea5e9" active={!!totaldist} />
|
||||
<MetricTile icon={FaBoxes} label="Quantity" value={totalQty || 0} accent="#10b981" active={!!totalQty} />
|
||||
<MetricTile icon={FaMoneyBillWave} label="Cash Collect" value={`₹${Number(totalCash).toFixed(2)}`} accent="#f59e0b" active={!!totalCash} />
|
||||
<MetricTile icon={FaTruck} label="Deliveries" value={dropCust.length} accent="#8b5cf6" active={dropCust.length > 0} />
|
||||
</Grid>
|
||||
|
||||
{dropCust.length > 0 && (
|
||||
<div className="total-charge-badge" style={{ marginTop: 14, padding: '10px 14px' }}>
|
||||
<div className="total-charge-left">
|
||||
<FaReceipt className="total-charge-icon" />
|
||||
<div className="total-charge-label">Total Charge</div>
|
||||
</div>
|
||||
<div className="total-charge-val" style={{ fontSize: 17 }}>
|
||||
₹{Number(totalAmt).toFixed(2)}
|
||||
</div>
|
||||
<div style={{ marginTop: 14 }}>
|
||||
<Button
|
||||
label={`Dispatch ${dropCust.length || ''} ${dropCust.length === 1 ? 'Order' : 'Orders'}`.trim()}
|
||||
variant="primary"
|
||||
icon={<FaPaperPlane size={12} />}
|
||||
isDisabled={!canSubmit}
|
||||
isLoading={btnLoading}
|
||||
onClick={createorders}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</VStack>
|
||||
|
||||
{/* RIGHT — file / drop preview */}
|
||||
<Card padding={2} elevation="none" style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<HStack justify="between" vAlign="center" gap={1} wrap="wrap" style={{ marginBottom: 8 }}>
|
||||
<HStack gap={2} vAlign="center">
|
||||
<Text weight="bold">{previewMode === 'drops' ? 'Drop List' : previewMode === 'preview' ? 'File Preview' : 'Preview'}</Text>
|
||||
<Badge variant="purple" label={previewMode === 'drops' ? dropCust.length : previewMode === 'preview' ? users.length : 0} />
|
||||
{previewMode === 'drops' && <Badge variant="purple" label={`₹${Number(totalAmt).toFixed(2)} total`} />}
|
||||
{previewMode === 'preview' && <Badge variant="warning" label="Awaiting Continue" />}
|
||||
</HStack>
|
||||
{previewMode !== 'empty' && (
|
||||
<HStack gap={1}>
|
||||
<Button
|
||||
label="Excel / CSV"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon={<MdOutlineCloudUpload size={14} />}
|
||||
onClick={() => pickHeaderSource(0)}
|
||||
/>
|
||||
<Button
|
||||
label="Selection"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon={<FaUsers size={12} />}
|
||||
onClick={() => pickHeaderSource(1)}
|
||||
/>
|
||||
</HStack>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
{fileName && (
|
||||
<Text type="supporting" color="secondary" style={{ marginBottom: 8 }}>
|
||||
{fileName}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
<div style={{ marginTop: 14 }}>
|
||||
<Button
|
||||
label={`Dispatch ${dropCust.length || ''} ${dropCust.length === 1 ? 'Order' : 'Orders'}`.trim()}
|
||||
variant="primary"
|
||||
icon={<FaPaperPlane size={12} />}
|
||||
isDisabled={!canSubmit}
|
||||
isLoading={btnLoading}
|
||||
onClick={createorders}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</VStack>
|
||||
|
||||
{/* RIGHT — file / drop preview */}
|
||||
<Card padding={2} elevation="none" style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<HStack justify="between" vAlign="center" gap={1} wrap="wrap" style={{ marginBottom: 8 }}>
|
||||
<HStack gap={2} vAlign="center">
|
||||
<Text weight="bold">{previewMode === 'drops' ? 'Drop List' : previewMode === 'preview' ? 'File Preview' : 'Preview'}</Text>
|
||||
<Badge variant="purple" label={previewMode === 'drops' ? dropCust.length : previewMode === 'preview' ? users.length : 0} />
|
||||
{previewMode === 'drops' && <Badge variant="purple" label={`₹${Number(totalAmt).toFixed(2)} total`} />}
|
||||
{previewMode === 'preview' && <Badge variant="warning" label="Awaiting Continue" />}
|
||||
</HStack>
|
||||
{previewMode !== 'empty' && (
|
||||
<HStack gap={1}>
|
||||
<Button label="Excel / CSV" variant="secondary" size="sm" icon={<MdOutlineCloudUpload size={14} />} onClick={() => pickHeaderSource(0)} />
|
||||
<Button label="Selection" variant="secondary" size="sm" icon={<FaUsers size={12} />} onClick={() => pickHeaderSource(1)} />
|
||||
{previewMode === 'preview' && users.length >= 1 && (
|
||||
<HStack
|
||||
justify="between"
|
||||
vAlign="center"
|
||||
gap={2}
|
||||
padding={2}
|
||||
style={{
|
||||
marginBottom: 8,
|
||||
background: 'rgba(245,158,11,0.06)',
|
||||
border: '1.5px solid rgba(245,158,11,0.25)',
|
||||
borderRadius: 10
|
||||
}}
|
||||
>
|
||||
<VStack gap={0} padding={0}>
|
||||
<Text weight="bold" style={{ color: '#b45309' }}>
|
||||
Process & Calculate Distances
|
||||
</Text>
|
||||
<Text type="supporting" style={{ color: '#b45309' }}>
|
||||
Click continue to import spreadsheet rows and calculate drop charges.
|
||||
</Text>
|
||||
</VStack>
|
||||
<Button
|
||||
label="Continue"
|
||||
variant="primary"
|
||||
size="sm"
|
||||
onClick={() => users.forEach((customer) => handleCheckboxChange1(customer))}
|
||||
/>
|
||||
</HStack>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
{fileName && (
|
||||
<Text type="supporting" color="secondary" style={{ marginBottom: 8 }}>
|
||||
{fileName}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{previewMode === 'preview' && users.length >= 1 && (
|
||||
<HStack justify="between" vAlign="center" gap={2} padding={2} style={{ marginBottom: 8, background: 'rgba(245,158,11,0.06)', border: '1.5px solid rgba(245,158,11,0.25)', borderRadius: 10 }}>
|
||||
<VStack gap={0} padding={0}>
|
||||
<Text weight="bold" style={{ color: '#b45309' }}>
|
||||
Process & Calculate Distances
|
||||
</Text>
|
||||
<Text type="supporting" style={{ color: '#b45309' }}>
|
||||
Click continue to import spreadsheet rows and calculate drop charges.
|
||||
</Text>
|
||||
</VStack>
|
||||
<Button
|
||||
label="Continue"
|
||||
variant="primary"
|
||||
size="sm"
|
||||
onClick={() => users.forEach((customer) => handleCheckboxChange1(customer))}
|
||||
/>
|
||||
</HStack>
|
||||
)}
|
||||
|
||||
<div style={{ flex: 1, minHeight: 0, overflow: 'auto' }}>
|
||||
{previewMode === 'drops' && (
|
||||
<TableScroll minWidth={720}>
|
||||
<Table density="compact" dividers="rows" hasHover>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHeaderCell>#</TableHeaderCell>
|
||||
<TableHeaderCell>Customer</TableHeaderCell>
|
||||
<TableHeaderCell>Address</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center' }}>Qty</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center' }}>Cash</TableHeaderCell>
|
||||
<TableHeaderCell>Km</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'right' }}>Charge</TableHeaderCell>
|
||||
<TableHeaderCell></TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{dropCust.map((customer, index) => (
|
||||
<TableRow key={customer._rowkey || index}>
|
||||
<TableCell>{index + 1}</TableCell>
|
||||
<div style={{ flex: 1, minHeight: 0, overflow: 'auto' }}>
|
||||
{previewMode === 'drops' && (
|
||||
<TableScroll minWidth={720}>
|
||||
<Table density="compact" dividers="rows" hasHover>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHeaderCell>#</TableHeaderCell>
|
||||
<TableHeaderCell>Customer</TableHeaderCell>
|
||||
<TableHeaderCell>Address</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center' }}>Qty</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center' }}>Cash</TableHeaderCell>
|
||||
<TableHeaderCell>Km</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'right' }}>Charge</TableHeaderCell>
|
||||
<TableHeaderCell></TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{dropCust.map((customer, index) => (
|
||||
<TableRow key={customer._rowkey || index}>
|
||||
<TableCell>{index + 1}</TableCell>
|
||||
<TableCell>
|
||||
<Text weight="semibold" style={{ whiteSpace: 'nowrap' }}>
|
||||
{customer.firstname}
|
||||
</Text>
|
||||
</TableCell>
|
||||
<TableCell style={{ maxWidth: 260 }}>
|
||||
{customer.latitude == null || customer.longitude == null ? (
|
||||
<AddressAutocomplete
|
||||
id={`drop-address-${customer._rowkey}`}
|
||||
placeholder="Search address…"
|
||||
value={customer.address || ''}
|
||||
onChange={(text) =>
|
||||
setDropCust((prev) => prev.map((c) => (c._rowkey === customer._rowkey ? { ...c, address: text } : c)))
|
||||
}
|
||||
onPlaceSelected={(place) => handleRowPlaceSelected(customer._rowkey, place)}
|
||||
bias={pickCust?.latitude ? { lat: pickCust.latitude, lng: pickCust.longitude } : undefined}
|
||||
/>
|
||||
) : (
|
||||
<Tooltip content={customer.address || ''}>
|
||||
<Text maxLines={1} style={{ maxWidth: 260 }}>
|
||||
{customer.address}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell style={{ textAlign: 'center' }}>
|
||||
{uploadType === 0 ? (
|
||||
customer.quantity
|
||||
) : (
|
||||
<NumberInput
|
||||
label="Quantity"
|
||||
isLabelHidden
|
||||
value={customer.quantity || undefined}
|
||||
onChange={(v) => handleQuantityChange(customer._rowkey, v ?? 0)}
|
||||
size="sm"
|
||||
/>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell style={{ textAlign: 'center' }}>
|
||||
{uploadType === 0 ? (
|
||||
`₹${Number(customer.collectionamt || 0).toFixed(2)}`
|
||||
) : (
|
||||
<NumberInput
|
||||
label="Collection amount"
|
||||
isLabelHidden
|
||||
value={customer.collectionamt || undefined}
|
||||
onChange={(v) => handleCollectionAmtChange(customer._rowkey, v && v > 0 ? v : 0)}
|
||||
size="sm"
|
||||
/>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>{customer.distance || '—'}</TableCell>
|
||||
<TableCell style={{ textAlign: 'right' }}>
|
||||
<Text weight="bold">₹{Number(customer?.totalcharge || 0).toFixed(2)}</Text>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<IconButton
|
||||
label="Remove"
|
||||
tooltip="Remove"
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
icon={<MdDeleteOutline size={14} />}
|
||||
onClick={() => handleCheckboxChange1(customer)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Text weight="semibold" style={{ whiteSpace: 'nowrap' }}>
|
||||
{customer.firstname}
|
||||
<Text weight="bold">Total</Text>
|
||||
</TableCell>
|
||||
<TableCell colSpan={2} />
|
||||
<TableCell style={{ textAlign: 'center' }}>
|
||||
<Text weight="bold">{totalQty}</Text>
|
||||
</TableCell>
|
||||
<TableCell style={{ textAlign: 'center' }}>
|
||||
<Text weight="bold">₹{Number(totalCash).toFixed(2)}</Text>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Text weight="bold">{totaldist}</Text>
|
||||
</TableCell>
|
||||
<TableCell style={{ textAlign: 'right' }}>
|
||||
<Text weight="bold" style={{ color: '#8b5cf6' }}>
|
||||
₹{Number(totalAmt).toFixed(2)}
|
||||
</Text>
|
||||
</TableCell>
|
||||
<TableCell style={{ maxWidth: 260 }}>
|
||||
{customer.latitude == null || customer.longitude == null ? (
|
||||
<AddressAutocomplete
|
||||
id={`drop-address-${customer._rowkey}`}
|
||||
placeholder="Search address…"
|
||||
value={customer.address || ''}
|
||||
onChange={(text) => setDropCust((prev) => prev.map((c) => (c._rowkey === customer._rowkey ? { ...c, address: text } : c)))}
|
||||
onPlaceSelected={(place) => handleRowPlaceSelected(customer._rowkey, place)}
|
||||
bias={pickCust?.latitude ? { lat: pickCust.latitude, lng: pickCust.longitude } : undefined}
|
||||
/>
|
||||
) : (
|
||||
<Tooltip content={customer.address || ''}>
|
||||
<Text maxLines={1} style={{ maxWidth: 260 }}>
|
||||
{customer.address}
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableScroll>
|
||||
)}
|
||||
|
||||
{previewMode === 'preview' && (
|
||||
<TableScroll minWidth={640}>
|
||||
<Table density="compact" dividers="rows" hasHover>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHeaderCell>#</TableHeaderCell>
|
||||
<TableHeaderCell>Name</TableHeaderCell>
|
||||
<TableHeaderCell>Contact</TableHeaderCell>
|
||||
<TableHeaderCell>Address</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center' }}>Qty</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center' }}>Cash</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{users.map((u, i) => (
|
||||
<TableRow key={i}>
|
||||
<TableCell>{i + 1}</TableCell>
|
||||
<TableCell>
|
||||
<Text weight="semibold" style={{ whiteSpace: 'nowrap' }}>
|
||||
{u.firstname || '—'}
|
||||
</Text>
|
||||
</TableCell>
|
||||
<TableCell>{u.contactno || '—'}</TableCell>
|
||||
<TableCell style={{ maxWidth: 240 }}>
|
||||
<Tooltip content={u.address || ''}>
|
||||
<Text maxLines={1} style={{ maxWidth: 240 }}>
|
||||
{u.address || '—'}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell style={{ textAlign: 'center' }}>
|
||||
{uploadType === 0 ? (
|
||||
customer.quantity
|
||||
) : (
|
||||
<NumberInput
|
||||
label="Quantity"
|
||||
isLabelHidden
|
||||
value={customer.quantity || undefined}
|
||||
onChange={(v) => handleQuantityChange(customer._rowkey, v ?? 0)}
|
||||
size="sm"
|
||||
/>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell style={{ textAlign: 'center' }}>
|
||||
{uploadType === 0 ? (
|
||||
`₹${Number(customer.collectionamt || 0).toFixed(2)}`
|
||||
) : (
|
||||
<NumberInput
|
||||
label="Collection amount"
|
||||
isLabelHidden
|
||||
value={customer.collectionamt || undefined}
|
||||
onChange={(v) => handleCollectionAmtChange(customer._rowkey, v && v > 0 ? v : 0)}
|
||||
size="sm"
|
||||
/>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>{customer.distance || '—'}</TableCell>
|
||||
<TableCell style={{ textAlign: 'right' }}>
|
||||
<Text weight="bold">₹{Number(customer?.totalcharge || 0).toFixed(2)}</Text>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<IconButton label="Remove" tooltip="Remove" size="sm" variant="destructive" icon={<MdDeleteOutline size={14} />} onClick={() => handleCheckboxChange1(customer)} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Text weight="bold">Total</Text>
|
||||
</TableCell>
|
||||
<TableCell colSpan={2} />
|
||||
<TableCell style={{ textAlign: 'center' }}>
|
||||
<Text weight="bold">{totalQty}</Text>
|
||||
</TableCell>
|
||||
<TableCell style={{ textAlign: 'center' }}>
|
||||
<Text weight="bold">₹{Number(totalCash).toFixed(2)}</Text>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Text weight="bold">{totaldist}</Text>
|
||||
</TableCell>
|
||||
<TableCell style={{ textAlign: 'right' }}>
|
||||
<Text weight="bold" style={{ color: '#8b5cf6' }}>
|
||||
₹{Number(totalAmt).toFixed(2)}
|
||||
</Text>
|
||||
</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableScroll>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell style={{ textAlign: 'center' }}>{u.quantity ?? '—'}</TableCell>
|
||||
<TableCell style={{ textAlign: 'center' }}>
|
||||
{u.collectionamt != null ? `₹${Number(u.collectionamt).toFixed(2)}` : '—'}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableScroll>
|
||||
)}
|
||||
|
||||
{previewMode === 'preview' && (
|
||||
<TableScroll minWidth={640}>
|
||||
<Table density="compact" dividers="rows" hasHover>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHeaderCell>#</TableHeaderCell>
|
||||
<TableHeaderCell>Name</TableHeaderCell>
|
||||
<TableHeaderCell>Contact</TableHeaderCell>
|
||||
<TableHeaderCell>Address</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center' }}>Qty</TableHeaderCell>
|
||||
<TableHeaderCell style={{ textAlign: 'center' }}>Cash</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{users.map((u, i) => (
|
||||
<TableRow key={i}>
|
||||
<TableCell>{i + 1}</TableCell>
|
||||
<TableCell>
|
||||
<Text weight="semibold" style={{ whiteSpace: 'nowrap' }}>
|
||||
{u.firstname || '—'}
|
||||
</Text>
|
||||
</TableCell>
|
||||
<TableCell>{u.contactno || '—'}</TableCell>
|
||||
<TableCell style={{ maxWidth: 240 }}>
|
||||
<Tooltip content={u.address || ''}>
|
||||
<Text maxLines={1} style={{ maxWidth: 240 }}>
|
||||
{u.address || '—'}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell style={{ textAlign: 'center' }}>{u.quantity ?? '—'}</TableCell>
|
||||
<TableCell style={{ textAlign: 'center' }}>{u.collectionamt != null ? `₹${Number(u.collectionamt).toFixed(2)}` : '—'}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableScroll>
|
||||
)}
|
||||
|
||||
{previewMode === 'empty' && (
|
||||
<VStack vAlign="center" gap={2} padding={4} style={{ minHeight: 240, background: 'rgba(24,144,255,0.03)', border: '1px dashed rgba(24,144,255,0.25)', borderRadius: 12, textAlign: 'center' }}>
|
||||
<Text weight="bold">Choose a Drop Source to begin</Text>
|
||||
<Text type="supporting" color="secondary" style={{ maxWidth: 360 }}>
|
||||
Select a source below to import or pick your delivery customers.
|
||||
</Text>
|
||||
<HStack gap={2} wrap="wrap" style={{ width: '100%', maxWidth: 480, marginTop: 8 }}>
|
||||
<SourceTile icon={MdOutlineCloudUpload} title="Excel / CSV" sub="Bulk upload a sheet" accent="#0ea5e9" disabled={!prereqOk} onClick={() => pickEmptySource(0)} />
|
||||
<SourceTile icon={FaUsers} title="Selection" sub="Pick saved customers" accent="#8b5cf6" disabled={!prereqOk} onClick={() => pickEmptySource(1)} />
|
||||
</HStack>
|
||||
</VStack>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</Grid>
|
||||
{previewMode === 'empty' && (
|
||||
<VStack
|
||||
vAlign="center"
|
||||
gap={2}
|
||||
padding={4}
|
||||
style={{
|
||||
minHeight: 240,
|
||||
background: 'rgba(24,144,255,0.03)',
|
||||
border: '1px dashed rgba(24,144,255,0.25)',
|
||||
borderRadius: 12,
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
<Text weight="bold">Choose a Drop Source to begin</Text>
|
||||
<Text type="supporting" color="secondary" style={{ maxWidth: 360 }}>
|
||||
Select a source below to import or pick your delivery customers.
|
||||
</Text>
|
||||
<HStack gap={2} wrap="wrap" style={{ width: '100%', maxWidth: 480, marginTop: 8 }}>
|
||||
<SourceTile
|
||||
icon={MdOutlineCloudUpload}
|
||||
title="Excel / CSV"
|
||||
sub="Bulk upload a sheet"
|
||||
accent="#0ea5e9"
|
||||
disabled={!prereqOk}
|
||||
onClick={() => pickEmptySource(0)}
|
||||
/>
|
||||
<SourceTile
|
||||
icon={FaUsers}
|
||||
title="Selection"
|
||||
sub="Pick saved customers"
|
||||
accent="#8b5cf6"
|
||||
disabled={!prereqOk}
|
||||
onClick={() => pickEmptySource(1)}
|
||||
/>
|
||||
</HStack>
|
||||
</VStack>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</Grid>
|
||||
</PageShell>
|
||||
|
||||
<input
|
||||
accept=".csv, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
@@ -1188,7 +1273,13 @@ const MultipleOrders = () => {
|
||||
const rowkey = `sel-${customer.appcustomerid}`;
|
||||
const checked = dropCust.some((c) => c._rowkey === rowkey);
|
||||
return (
|
||||
<div key={customer.appcustomerid || index} style={{ padding: '8px 4px', borderBottom: index < filteredCustomerList.length - 1 ? '1px solid var(--color-border-subtle)' : 'none' }}>
|
||||
<div
|
||||
key={customer.appcustomerid || index}
|
||||
style={{
|
||||
padding: '8px 4px',
|
||||
borderBottom: index < filteredCustomerList.length - 1 ? '1px solid var(--color-border-subtle)' : 'none'
|
||||
}}
|
||||
>
|
||||
<CheckboxInput
|
||||
label={`${customer.name} (${customer.phone})`}
|
||||
description="Address not on file — add one after selecting"
|
||||
@@ -1206,7 +1297,11 @@ const MultipleOrders = () => {
|
||||
footer={
|
||||
<LayoutFooter hasDivider>
|
||||
<HStack justify="end">
|
||||
<Button label={dropCust.length === 0 ? 'Close' : 'Continue'} variant={dropCust.length === 0 ? 'destructive' : 'primary'} onClick={() => setIsCustomerOpen(false)} />
|
||||
<Button
|
||||
label={dropCust.length === 0 ? 'Close' : 'Continue'}
|
||||
variant={dropCust.length === 0 ? 'destructive' : 'primary'}
|
||||
onClick={() => setIsCustomerOpen(false)}
|
||||
/>
|
||||
</HStack>
|
||||
</LayoutFooter>
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user