From f18b6802477b83499c8105aa627490dc16b903de Mon Sep 17 00:00:00 2001 From: Thiru-tenext Date: Tue, 9 Jun 2026 13:12:18 +0530 Subject: [PATCH] updated the ui for the mobile view --- .../nearle_components/MobileCard.js | 140 ++++++ src/pages/nearle/accountsettings.js | 8 +- .../nearle/clientPricing/clientPricing.js | 136 +++++- src/pages/nearle/clients/Tenants.js | 219 ++++++++- src/pages/nearle/clients/createCustomer.js | 18 +- src/pages/nearle/clients/createclient.js | 19 +- src/pages/nearle/customers/customers.js | 115 ++++- src/pages/nearle/deliveries/deliveries.js | 449 ++++++++++++++---- src/pages/nearle/dispatch/CompareDataPanel.js | 13 +- src/pages/nearle/dispatch/Dispatch.css | 111 +++++ src/pages/nearle/dispatch/Preview.js | 69 ++- src/pages/nearle/invoice/invoice.js | 204 +++++++- src/pages/nearle/invoice/invoicePreview.js | 22 +- src/pages/nearle/login1.js | 15 +- src/pages/nearle/orders/OrdersPreview.js | 167 ++++++- src/pages/nearle/orders/createorder1.js | 12 +- src/pages/nearle/orders/details.js | 325 ++++++++++++- src/pages/nearle/orders/multipleOrders.js | 157 +++++- .../nearle/orders/optimisedOrderPreview.js | 149 +++++- src/pages/nearle/orders/orders.js | 230 +++++++++ src/pages/nearle/reports/ordersDetails.js | 272 ++++++++++- src/pages/nearle/reports/ordersSummary.js | 254 +++++++++- src/pages/nearle/reports/ridersLogs.js | 63 ++- src/pages/nearle/reports/ridersSummary.js | 252 +++++++++- src/pages/nearle/requests/requests.js | 100 +++- src/pages/nearle/riders/createrider.js | 22 +- src/pages/nearle/riders/editRider.js | 29 +- src/pages/nearle/riders/riders.js | 309 +++++++++++- src/pages/nearle/viewProfile.js | 10 +- 29 files changed, 3664 insertions(+), 225 deletions(-) create mode 100644 src/components/nearle_components/MobileCard.js diff --git a/src/components/nearle_components/MobileCard.js b/src/components/nearle_components/MobileCard.js new file mode 100644 index 0000000..31e2f69 --- /dev/null +++ b/src/components/nearle_components/MobileCard.js @@ -0,0 +1,140 @@ +import PropTypes from 'prop-types'; +import { Box, Paper, Stack, Typography } from '@mui/material'; + +// ============================================================================ +// MobileCard — shared primitives that turn a desktop data-table row into an +// app-style card on phones. Used by every operator list page (deliveries, +// orders, customers, riders, tenants, …) so the mobile experience is +// consistent. Purely presentational: pages keep their own data + handlers and +// just slot content into these shells. Desktop layouts are untouched — these +// only render inside an `isMobile` branch. +// +// Tokens mirror the `DT` block in deliveries.js so cards match page surfaces. +// ============================================================================ +const BORDER = '#e2e8f0'; +const MUTED = '#94a3b8'; +const PRIMARY_TEXT = '#0f172a'; + +// Vertical list wrapper — drop-in replacement for / +// on mobile. `scroll` makes it an internal scroll region (matches the table's +// maxHeight behaviour); omit it to let the page scroll naturally. +export const MobileCardList = ({ children, scroll = false, onScroll, sx, ...rest }) => ( + + {children} + +); + +MobileCardList.propTypes = { + children: PropTypes.node, + scroll: PropTypes.bool, + onScroll: PropTypes.func, + sx: PropTypes.object +}; + +// Card shell — coloured accent rail on the left, a header slot (status badge / +// title / action buttons), then any field grid / collapse content as children. +export const MobileCard = ({ accent = '#662582', header, footer, selected = false, onClick, children, sx }) => ( + + + + {header} + {children} + {footer} + + +); + +MobileCard.propTypes = { + accent: PropTypes.string, + header: PropTypes.node, + footer: PropTypes.node, + selected: PropTypes.bool, + onClick: PropTypes.func, + children: PropTypes.node, + sx: PropTypes.object +}; + +// Grid wrapper for MobileField cells. Two columns by default; pass `columns` +// to change. Keeps every card's body alignment identical. +export const MobileFieldGrid = ({ children, columns = 2, sx }) => ( + + {children} + +); + +MobileFieldGrid.propTypes = { + children: PropTypes.node, + columns: PropTypes.number, + sx: PropTypes.object +}; + +// A single label/value cell. `full` makes it span the whole row; `value` can be +// a string/number or any node (chip, stack, etc.). +export const MobileField = ({ label, value, children, full = false, align = 'left' }) => ( + + + {label} + + + {children !== undefined ? ( + children + ) : ( + + {value ?? '—'} + + )} + + +); + +MobileField.propTypes = { + label: PropTypes.node, + value: PropTypes.node, + children: PropTypes.node, + full: PropTypes.bool, + align: PropTypes.string +}; diff --git a/src/pages/nearle/accountsettings.js b/src/pages/nearle/accountsettings.js index b372a4b..5ba6cdb 100644 --- a/src/pages/nearle/accountsettings.js +++ b/src/pages/nearle/accountsettings.js @@ -1,4 +1,4 @@ -import { Grid } from '@mui/material'; +import { Grid, useMediaQuery, useTheme } from '@mui/material'; import { useQuery } from '@tanstack/react-query'; import CircularLoader from 'components/CircularLoader'; import Loader from 'components/Loader'; @@ -6,6 +6,8 @@ import MainCard from 'components/MainCard'; import { getusers } from 'pages/api/api'; const ViewProfile = () => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const { data: userdata, isLoading, @@ -23,8 +25,8 @@ const ViewProfile = () => { )} - - + + {userdata?.firstname} diff --git a/src/pages/nearle/clientPricing/clientPricing.js b/src/pages/nearle/clientPricing/clientPricing.js index 1307f93..3df92c5 100644 --- a/src/pages/nearle/clientPricing/clientPricing.js +++ b/src/pages/nearle/clientPricing/clientPricing.js @@ -11,7 +11,9 @@ import { TableContainer, TableHead, TableRow, - Typography + Typography, + useMediaQuery, + useTheme } from '@mui/material'; import { MdLocalOffer, @@ -29,6 +31,7 @@ import { useQuery } from '@tanstack/react-query'; import Loader from 'components/Loader'; import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar'; import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete'; +import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton'; import { getallpricing } from 'pages/api/api'; @@ -125,6 +128,8 @@ const MetricPill = ({ color, icon, label, width }) => ( // ==============================|| Pricing page ||============================== // const ClientsPricing = () => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const containerRef = useRef(); const [appId, setAppId] = useState(0); const [locaName, setLocoName] = useState('All'); @@ -248,7 +253,7 @@ const ClientsPricing = () => { {KPI_META.map((item) => { const Icon = item.icon; return ( - + { background: '#fff' }} > + {isMobile ? ( + rows.length === 0 && !isLoading ? ( + + + + + + No pricing to show + + + {searchword ? 'Try a different keyword.' : 'Pick a zone above to load the catalog.'} + + + ) : ( + + {rows.map((row, index) => ( + + + + + + + + {row.appname || '—'} + + + ID #{row.pricingid} + + + + + {String(index + 1).padStart(2, '0')} + + + } + > + + {row.applocation ? ( + + {row.applocation} + + ) : null} + + {row.slab || '—'} + + + + + + } + label={formatRupees(row.baseprice)} + /> + + + } + label={formatRupees(row.priceperkm)} + /> + + + } + label={`${formatDecimal(row.minkm)} km`} + /> + + + } + label={`${formatDecimal(row.maxkm)} km`} + /> + + + + + + {row.minorder ?? '—'} + + + + + + ))} + + ) + ) : ( { + )} ); diff --git a/src/pages/nearle/clients/Tenants.js b/src/pages/nearle/clients/Tenants.js index 1fd8c5a..502cbc9 100644 --- a/src/pages/nearle/clients/Tenants.js +++ b/src/pages/nearle/clients/Tenants.js @@ -41,8 +41,10 @@ import { TablePagination, Skeleton, Avatar, - Paper + Paper, + useMediaQuery } from '@mui/material'; +import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; import { EyeOutlined, EyeInvisibleOutlined, @@ -172,6 +174,7 @@ const Clients1 = () => { const [rowsPerPage, setRowsPerPage] = React.useState(10); // const [tenantList, settenantList] = useState([]); const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const [isloader, setisloader] = useState(false); const [appId, setAppId] = useState(0); const [locaName, setLocoName] = useState(''); @@ -886,6 +889,7 @@ const Clients1 = () => { > { + + {/* ============================================= || Mobile cards (xs only) || ============================================= */} + {isMobile && ( + + {getalltenantsIsLoading && ( + + + + + )} + + {tenantList?.length == 0 && !isloader ? ( + + + + + + No tenants to show + + + {`No ${activeTabMeta.label.toLowerCase()} tenants for this filter.`} + + + ) : ( + tenantList?.map((row, index) => { + const rowStatusKey = value0 === 0 ? 'active' : value0 === 1 ? 'pending' : 'inactive'; + const rowStatusMeta = STATUS_META[rowStatusKey]; + const RowStatusIcon = rowStatusMeta.icon; + const expanded = openRowIndex1 === index; + return ( + + + + + + + + {row.tenantname} + + + + + + + {rowStatusMeta.label} + + + + + + {value0 == 0 && ( + { + setSelectedCustomer(row); + setSelectedtenid(row.tenantid); + setAppId(row.applocationid); + setTimeout(() => { + tenantupdate(row.tenantid); + }, 100); + }} + > + + + )} + {value0 == 1 && ( + { + setSelectedCustomer(row); + setDialogopen(true); + setSelectedtenid(row.tenantid); + setAppId(row.applocationid); + getAppPricing(row.applolcationid); + }} + > + + + )} + {value0 == 2 && ( + { + setSelectedCustomer(row); + setSelectedtenid(row.tenantid); + setAppId(row.applocationid); + setTimeout(() => { + tenantupdate(row.tenantid); + }, 100); + }} + > + + + )} + { + setSelectedCustomer(row); + handleCollapseToggle1(index); + setOpenRowIndex2(-1); + setSelectedtenid(row.tenantid); + }} + > + {expanded ? : } + + {value0 !== 1 && ( + { + setSelectedCustomer(row); + setSelectedtenid(row.tenantid); + setAppId(row.applocationid); + getAppPricing(row.applolcationid); + setDialogopen(true); + }} + > + + + )} + + + } + > + + + + + + + {row.address || '—'} + + + + + {expanded && ( + + + + + + + + + + )} + + ); + }) + )} + + )} + {/* ============================================= || Pagination| ============================================= */} {!searchword && tenantList?.length > 0 && ( <> @@ -2003,7 +2209,16 @@ const Clients1 = () => { )} {/* // ==============================||( Client Pricing ) dialog (dialogopen) ||============================== // */} - + { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const [appId, setAppId] = useState(0); const locationRef = useRef(null); const [mobilenumber, setMobilenumber] = useState(''); @@ -268,10 +270,10 @@ const CreateCustomer = () => { Create Customer - - + + - + {/* ===================================================== || Choose location || ===================================================== */} @@ -491,9 +493,15 @@ const CreateCustomer = () => { - + diff --git a/src/pages/nearle/customers/customers.js b/src/pages/nearle/customers/customers.js index 54bb707..a8e57d1 100644 --- a/src/pages/nearle/customers/customers.js +++ b/src/pages/nearle/customers/customers.js @@ -25,7 +25,9 @@ import { TextField, Autocomplete, Avatar, - Paper + Paper, + useMediaQuery, + useTheme } from '@mui/material'; import { MdPeopleAlt, @@ -50,6 +52,7 @@ import { useInfiniteQuery, useQuery } from '@tanstack/react-query'; import { getallcustomers, getcustomersummary } from 'pages/api/api'; import { OpenToast } from 'components/third-party/OpenToast'; import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton'; +import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; // ============================================================================ // Design tokens — shared with the deliveries / tenants / pricing pages so every @@ -124,6 +127,8 @@ const autocompleteService = { current: null }; // ==============================|| MUI TABLE - ENHANCED ||============================== // export default function Customers() { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const containerRef = useRef(); const loadMoreRef = useRef(); const [rowsPerPage] = useState(50); @@ -474,7 +479,7 @@ export default function Customers() { {KPI_META.map((item) => { const Icon = item.icon; return ( - + + {isMobile ? ( + + {customersIsLoading && } + {rows?.length === 0 && !customersIsLoading ? ( + + + + + + No customers to show + + + {searchword ? 'Try a different keyword.' : 'Pick a zone above to load the directory.'} + + + ) : ( + rows?.map((row, index) => ( + + + + + + + + {row.firstname || '—'} + + + ID #{row.customerid} + + + + + { + setSelectedCustomer(row); + setTimeout(() => setOpen(true), 0); + }} + sx={{ + bgcolor: soft('#8b5cf6'), + color: '#8b5cf6', + border: `1px solid ${edge('#8b5cf6')}`, + flexShrink: 0, + '&:hover': { bgcolor: '#8b5cf6', color: '#fff' } + }} + > + + + + + } + > + + + + {row.suburb ? ( + + {row.suburb} + + ) : ( + + )} + + + + {row.address || '—'} + + + + + )) + )} + {rows?.length !== 0 && ( +
+ {isFetchingNextPage || hasNextPage ? ( + + ) : ( + + No more customers + + )} +
+ )} + + ) : ( + )} {/* ======================================== || Edit Dialog || ======================================== */} ( const Deliveries = () => { const userid = localStorage.getItem('userid'); const theme = useTheme(); + // Below `md` we swap the wide data table for an app-style card list. Desktop + // (md and up) keeps the exact same table — no behaviour change either way. + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const loadMoreRef = useRef(); const containerRef = useRef(); const [deliverylist, setDeliverylist] = useState([]); @@ -1592,7 +1597,263 @@ const Deliveries = () => { const showAction = tabstatus !== 'Cancelled' && tabstatus !== 'Delivered'; const showSelect = tabstatus == 'Created'; const totalCols = 15 + (showAction ? 1 : 0) + (showSelect ? 1 : 0); - return ( + return isMobile ? ( + /* ===================== MOBILE: card list ===================== */ + + {filteredRows.length === 0 && !loading1 && !countSourceLoading && ( + + + + + + No deliveries to show + + + {selectedBatch === 'all' + ? `No ${(STATUS_META[currentStatus]?.label || tabstatus).toLowerCase()} orders for this filter.` + : `No ${(STATUS_META[currentStatus]?.label || tabstatus).toLowerCase()} orders in ${BATCH_OPTIONS.find((b) => b.id === selectedBatch)?.label || 'this batch'}.`} + + + )} + {filteredRows.length === 0 && countSourceLoading && ( + + + + Loading deliveries… + + + )} + {filteredRows.map((row, index) => { + const rowStatusMeta = STATUS_META[String(row.orderstatus || '').toLowerCase()] || { + label: row.orderstatus || '—', + color: '#94a3b8', + icon: MdHistoryToggleOff + }; + const RowStatusIcon = rowStatusMeta.icon; + const isSelected = !!deliverylist.find((res1) => res1.orderheaderid == row.orderheaderid); + const isOpen = productCollapse?.orderid === row?.orderid; + const chipSx = (c) => ({ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', px: 1, py: 0.25, borderRadius: 999, bgcolor: tint(c), color: c, fontWeight: 700, fontSize: 11, border: `1px solid ${edge(c)}`, whiteSpace: 'nowrap' }); + return ( + + + + {showSelect && ( + { + if (e.target.checked) { + let arr = deliverylist; + arr.push({ ...row, sno: deliverylist.length + 1 }); + setDeliverylist([...arr]); + } else { + let res = deliverylist.find((res1) => res1.orderheaderid == row.orderheaderid); + if (res) { + let arr = deliverylist; + arr.splice(res.sno - 1, 1); + arr.map((val, i) => { + val.sno = i + 1; + }); + setDeliverylist([...arr]); + } + } + }} + checked={!!deliverylist.find((res1) => res1.orderheaderid == row.orderheaderid)} + /> + )} + + {String(page * rowsPerPage + index + 1).padStart(2, '0')} + + + + + + + {rowStatusMeta.label} + + + + + {row.deliverytype == 'C' && ( + { + if (productCollapse?.orderid === row.orderid) { + setProductCollapse(null); + setOrderHeaderId(null); + } else { + setProductCollapse(row); + setOrderHeaderId(row.orderheaderid); + } + }} + sx={{ borderRadius: 999, bgcolor: tint('#06b6d4'), color: '#06b6d4', border: `1px solid ${edge('#06b6d4')}`, '&:hover': { bgcolor: soft('#06b6d4') } }} + > + {isOpen ? : } + + )} + {showAction && ( + handleMenuOpen(e, row)} + sx={{ borderRadius: 999, bgcolor: tint('#6366f1'), color: '#6366f1', border: `1px solid ${edge('#6366f1')}`, '&:hover': { bgcolor: soft('#6366f1') } }} + > + + + )} + + + + + {row.tenantname} + + + {[row.tenantsuburb, row.applocation].filter(Boolean).join(' · ') || '—'} + + + + } + > + + + + {`${row.locationname}-(${row.locationsuburb})`} + + + {row.orderid} · {row.deliveryid} + + + + + {row.pickupcustomer || '—'} + + + {row.pickupcontactno} + + + + + {row.deliverycustomer || '—'} + + + {row.deliverycontactno} + + + + {row.ridername ? ( + + + + + + + {row.ridername} + + + ID #{row.userid} · {row.ridercontact || '—'} + + + + ) : ( + + Unassigned + + )} + + + + {row.transitminutes || 0}m + + + + {row.kms || 0} km + {row.cumulativekms || 0} km + + + + + ₹ {row.deliverycharges?.toFixed(2) ?? '0.00'} + ₹ {row.deliveryamt?.toFixed(2) ?? '0.00'} + + + + + + {row.collectionamt ? `₹ ${row.collectionamt.toFixed(2)}` : '—'} + + + + {row.step ? ( + {row.step} + ) : ( + + )} + + {row.notes && ( + + {row.notes} + + )} + + {isOpen && ( + + + + + Product Details + + + + {orderdetails?.details?.map((product, idx2) => ( + + + + + {product?.productname || 'Unnamed'} + + + Qty {product?.orderqty || 0} · ₹ {product?.price || 0} + + + + ₹ {(product?.productsumprice + product?.taxamount).toFixed(2) || 0} + + + ))} + + Total Amount + + ₹ {orderdetails?.pricedetails?.orderamount?.toFixed(2)} + + + + + )} + + ); + })} + {countSourceRows?.length != 0 && ( +
+ {countIsFetchingNext || countHasNext ? ( + + ) : ( + + · End of list · + + )} +
+ )} +
+ ) : ( { - - - {selectedRow?.orderstatus !== 'delivered' && ( - { - notifyRiderMutation.mutate(selectedRow.userfcmtoken); - handleMenuClose(); - }} - > - - Notify Rider - - )} - {['pending', 'accepted', 'arrived'].includes(selectedRow?.orderstatus) && ( - { - if (!appId) { - opentoast('Please select a location first!', 'warning'); - locationRef.current?.focus(); - return; - } - setChangeDialogOpen(true); - handleMenuClose(); - }} - > - - Change Rider - - )} - {(roleid == 1 || roleid == 2) && ( - { - setKms(selectedRow.kms); - setCumulativeKms(selectedRow.cumulativekms); - setDeliverylat(selectedRow.droplat); - setDeliverylong(selectedRow.droplon); - setNotes(selectedRow.notes); - setDeliveryamount(selectedRow.deliveryamount); - setUpdateStatus(selectedRow.orderstatus || 'delivered'); - setCurrentorder(selectedRow); - setDialogopen(true); - handleMenuClose(); - }} - > - - Update Status - - )} - {selectedRow?.orderstatus !== 'cancelled' && selectedRow?.orderstatus !== 'delivered' && ( - { - setCancelDeliveryOpen(true); - handleMenuClose(); - }} - > - - Cancel Delivery - - )} - )} @@ -2238,6 +2411,96 @@ const Deliveries = () => { + {/* Shared row-action menu — single instance reused by both the desktop + table rows and the mobile cards (both trigger handleMenuOpen). */} + + {selectedRow?.orderstatus !== 'delivered' && ( + { + notifyRiderMutation.mutate(selectedRow.userfcmtoken); + handleMenuClose(); + }} + > + + Notify Rider + + )} + {['pending', 'accepted', 'arrived'].includes(selectedRow?.orderstatus) && ( + { + if (!appId) { + opentoast('Please select a location first!', 'warning'); + locationRef.current?.focus(); + return; + } + setChangeDialogOpen(true); + handleMenuClose(); + }} + > + + Change Rider + + )} + {(roleid == 1 || roleid == 2) && ( + { + setKms(selectedRow.kms); + setCumulativeKms(selectedRow.cumulativekms); + setDeliverylat(selectedRow.droplat); + setDeliverylong(selectedRow.droplon); + setNotes(selectedRow.notes); + setDeliveryamount(selectedRow.deliveryamount); + setUpdateStatus(selectedRow.orderstatus || 'delivered'); + setCurrentorder(selectedRow); + setDialogopen(true); + handleMenuClose(); + }} + > + + Update Status + + )} + {selectedRow?.orderstatus !== 'cancelled' && selectedRow?.orderstatus !== 'delivered' && ( + { + setCancelDeliveryOpen(true); + handleMenuClose(); + }} + > + + Cancel Delivery + + )} + + {/* =============================== || cancel dialog || =============================== */} { setOpen(false)} + fullScreen={isMobile} PaperProps={{ elevation: 0, sx: { - borderRadius: 3, + borderRadius: { xs: 0, sm: 3 }, border: '1px solid', borderColor: DT.borderSubtle, boxShadow: DT.shadowPop, @@ -2559,11 +2823,12 @@ const Deliveries = () => { onClose={dialogclose} scroll="paper" maxWidth="sm" + fullScreen={isMobile} TransitionComponent={PopupTransition} PaperProps={{ elevation: 0, sx: { - borderRadius: 3, + borderRadius: { xs: 0, sm: 3 }, border: '1px solid', borderColor: DT.borderSubtle, boxShadow: DT.shadowPop, diff --git a/src/pages/nearle/dispatch/CompareDataPanel.js b/src/pages/nearle/dispatch/CompareDataPanel.js index 1e53258..9a8dc5c 100644 --- a/src/pages/nearle/dispatch/CompareDataPanel.js +++ b/src/pages/nearle/dispatch/CompareDataPanel.js @@ -1,4 +1,5 @@ import React, { useMemo } from 'react'; +import { useTheme, useMediaQuery } from '@mui/material'; import { MdPublic, MdSwapHoriz, @@ -60,6 +61,13 @@ function CompareDataPanel({ setExpandedSeqGroups, onClose }) { + // Mobile flag — gates only layout (a className on the root
+ {isMobile ? ( + <> + {isDeliveryLoading ? ( + + + + ) : pagedList.length === 0 ? ( + + + + + + No invoices to show + + + {searchword + ? 'Try a different keyword.' + : `No ${activeMeta.label.toLowerCase()} invoices for this filter.`} + + + ) : ( + + {pagedList.map((item, index) => { + const overdue = + billStatus === 2 || + (item.duedate && dayjs(item.duedate).isBefore(dayjs(), 'day') && billStatus !== 3); + return ( + + + + + + + + {item.tenantname || '—'} + + {item.contactperson && ( + + {item.contactperson} + + )} + + + + { + setIsLoader(true); + setTimeout(() => { + setIsLoader(false); + navigate('/nearle/invoice/preview', { state: item }); + }, 500); + }} + sx={{ + flexShrink: 0, + bgcolor: soft(BRAND), + color: BRAND, + border: `1px solid ${edge(BRAND)}`, + '&:hover': { bgcolor: BRAND, color: '#fff' } + }} + > + + + + + } + > + + + + {item.invoiceno || '—'} + + + + + + {formatNumberToRupees(item.totalamount).replace('₹', '').trim()} + + + + + + + {item.transactiondate ? dayjs(item.transactiondate).format('DD/MM/YYYY') : '—'} + + + + + + + + {item.duedate ? dayjs(item.duedate).format('DD/MM/YYYY') : '—'} + + + + + + {item.itemcount ?? 0} + + + + + ); + })} + + )} + + ) : ( + +
{ ); }) )} - -
- + + + + )} { const [refnumber, setRefnumber] = useState(''); const [remarks, setRemarks] = useState(''); const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); useEffect(() => { setselected(location.state); }, []); @@ -96,7 +98,13 @@ const InvoicePreview = () => { return ( <> - + { - + - diff --git a/src/pages/nearle/orders/createorder1.js b/src/pages/nearle/orders/createorder1.js index 8f6a549..ed8583d 100644 --- a/src/pages/nearle/orders/createorder1.js +++ b/src/pages/nearle/orders/createorder1.js @@ -22,7 +22,8 @@ import { FormGroup, FormControlLabel, Box, - Card + Card, + useMediaQuery } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; import { Empty } from 'antd'; @@ -185,6 +186,7 @@ const Createorder1 = () => { const loaded = React.useRef(false); const navigate = useNavigate(); const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const locationRef = useRef(null); const tenantRef = useRef(null); const [inputValue1, setInputValue1] = React.useState(''); @@ -2439,9 +2441,10 @@ const Createorder1 = () => { setSearchCustList(''); }} fullWidth + fullScreen={isMobile} sx={{ '& .MuiDialog-paper': { - borderRadius: '16px', + borderRadius: { xs: 0, sm: '16px' }, overflow: 'hidden' } }} @@ -2567,10 +2570,13 @@ const Createorder1 = () => { onClose={() => { setOpen(false); }} + fullWidth sx={{ '& .MuiDialog-paper': { - borderRadius: '16px', + borderRadius: { xs: 3, sm: '16px' }, p: 1.5, + m: { xs: 1.5, sm: 4 }, + width: { xs: 'calc(100% - 24px)', sm: 'auto' }, maxWidth: '400px' } }} diff --git a/src/pages/nearle/orders/details.js b/src/pages/nearle/orders/details.js index 032a727..f52c6ed 100644 --- a/src/pages/nearle/orders/details.js +++ b/src/pages/nearle/orders/details.js @@ -88,6 +88,8 @@ dayjs.extend(utc); // import HeartFilled from '@mui/icons-material/Favorite'; import { useTheme } from '@mui/material/styles'; +import useMediaQuery from '@mui/material/useMediaQuery'; +import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; import { CloseOutlined, WarningOutlined, @@ -177,6 +179,7 @@ const Details = () => { }, [state.orderheaderid, state.tenantid]); const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); // const fetchorderdetails = async () => { // setLoading(true); @@ -704,7 +707,13 @@ const Details = () => { const [deletepassword, setDeletepassword] = useState(''); return ( - handleClose(false)} maxWidth="xs"> + handleClose(false)} + maxWidth="xs" + fullScreen={isMobile} + PaperProps={{ sx: { borderRadius: { xs: 0, sm: 3 } } }} + > @@ -778,7 +787,8 @@ const Details = () => { open={dialogopen} onClose={dialogclose} scroll={'paper'} - // fullScreen + fullScreen={isMobile} + PaperProps={{ sx: { borderRadius: { xs: 0, sm: 3 } } }} TransitionComponent={PopupTransition} > @@ -880,6 +890,115 @@ const Details = () => { No Staffs Available )} + ) : isMobile ? ( + + {stafflist.map((val, i) => { + const isSelected = staffarr.find((res) => res.userid == val.userid) ? true : false; + return ( + + + + + + {val.firstname} + + {val.contactno} + + + + {val.orderdetailid !== orderdetailid ? ( + { + console.log(currentshiftobj); + if (currentshiftobj.remaining >= 0) { + if (e.target.checked && currentshiftobj.remaining != 0) { + let arr = staffarr; + arr.push({ + userid: val.userid, + orderdetailid, + productid, + shiftid: currentshiftobj.shiftid, + userrate: currentshiftobj.price, + productrate: val.rolecost, + firstname: val.firstname + }); + setStaffarr([...arr]); + let obj = currentshiftobj; + obj.assigned++; + obj.remaining = obj.shifts - obj.assigned; + setCurrentshiftobj({ ...obj }); + } else if ( + currentshiftobj.assigned != currentshiftobj.shifts || + (currentshiftobj.remaining === 0 && !e.target.checked) + ) { + let arr = staffarr; + let index = arr.findIndex((val1) => val1.userid === val.userid); + arr.splice(index, 1); + setStaffarr([...arr]); + let obj = currentshiftobj; + obj.assigned--; + obj.remaining = obj.shifts - obj.assigned; + setCurrentshiftobj({ ...obj }); + } + console.log(staffarr); + } + }} + /> + ) : ( + + + + { + console.log(val); + unassign(val); + }} + > + + + + + { + console.log(val); + notificationpush(val); + }} + > + + + + + )} + + + + + + {val.cateoryname} + + + + + + + + + + + {val.orderid && ( + + + + )} + + + ); + })} + ) : ( @@ -1081,14 +1200,19 @@ const Details = () => { )} - + {stafflist.length > 0 && ( <> - + OK { @@ -1107,7 +1231,7 @@ const Details = () => { )} { @@ -1195,7 +1319,11 @@ const Details = () => { - + {/* {dayjs(startdate).$d.toString()} */} {/* {startdate} */} {/* {dayjs().$d.toString()} */} @@ -1206,7 +1334,7 @@ const Details = () => { } onClick={(e) => { e.stopPropagation(); @@ -1269,7 +1397,7 @@ const Details = () => { setOpen(true); } }} - sx={{ borderRadius: '40px', mt: { xs: 2, sm: 0 } }} + sx={{ borderRadius: '40px', mt: { xs: 2, sm: 0 }, width: { xs: '100%', sm: 'auto' } }} startIcon={} > Cancel Order @@ -1418,6 +1546,184 @@ const Details = () => { })} + {isMobile ? ( + + {val5.orderdetails.length === 0 && ( + + + + + + )} + {val5.orderdetails.map((row, i) => ( + + + + #{i + 1} + {row.productname} + + + + { + setStafflist([]); + setExpandopen(expandopen[0] === j && expandopen[1] === i ? ['', ''] : [j, i]); + fetchstafflist(row.orderdetailid); + }} + > + {expandopen[0] === j && expandopen[1] === i ? : } + + + {orderstatus === 'cancelled' && ( + + + + )} + {row.status === 1 && ( + + + + + + )} + {row.supplyqty > row.orderqty && ( + + + + + + )} + + + + + + {dayjs(row.starttime).format('MM/DD/YYYY')} + {dayjs(row.starttime).format('hh:mm A')} + + + + + {dayjs(row.endtime).format('MM/DD/YYYY')} + {dayjs(row.endtime).format('hh:mm A')} + + + + + + + + + + + + + + + {stafflist.length === 0 ? ( + loading ? ( + + + + ) : ( + + No Staffs has been Assigned + + ) + ) : ( + + {stafflist.map((val, si) => ( + + + + {val.staffname} + + + + + + {val.orderstatus === 'pending' && } + {val.orderstatus === 'cancelled' && ( + + )} + {val.orderstatus === 'completed' && ( + + )} + {val.orderstatus === 'processing' && ( + + )} + {val.orderstatus === 'assigned' && } + {val.orderstatus === 'confirmed' && ( + + )} + {val.orderstatus === 'active' && } + {val.orderstatus === 'closed' && } + + + + + + {dayjs(val.Starttime).format('MM/DD/YYYY')} + {dayjs(val.Starttime).format('hh:mm A')} + + + + + {dayjs(val.Endtime).format('MM/DD/YYYY')} + {dayjs(val.Endtime).format('hh:mm A')} + + + + + + + + + + + + + + + + + + + ))} + + )} + + + ))} + + ) : (
@@ -1870,6 +2176,7 @@ const Details = () => {
+ )}
diff --git a/src/pages/nearle/orders/multipleOrders.js b/src/pages/nearle/orders/multipleOrders.js index d46512d..0c61574 100644 --- a/src/pages/nearle/orders/multipleOrders.js +++ b/src/pages/nearle/orders/multipleOrders.js @@ -5,6 +5,7 @@ import * as XLSX from 'xlsx'; import dayjs from 'dayjs'; import { useNavigate } from 'react-router'; import { useTheme } from '@mui/material/styles'; +import useMediaQuery from '@mui/material/useMediaQuery'; import { enqueueSnackbar } from 'notistack'; import { @@ -65,6 +66,7 @@ import { MdOutlineCloudUpload } from 'react-icons/md'; import Loader from 'components/Loader'; import CircularLoader from 'components/CircularLoader'; import AnimateButton from 'components/@extended/AnimateButton'; +import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; import './OrdersRedesign.css'; var utc = require('dayjs/plugin/utc'); @@ -88,6 +90,7 @@ const cellBodySx = { const MultipleOrders = () => { const navigate = useNavigate(); const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const locationRef = useRef(null); const tenantRef = useRef(null); const userid = localStorage.getItem('userid'); @@ -711,11 +714,12 @@ const MultipleOrders = () => { @@ -752,7 +756,7 @@ const MultipleOrders = () => { {/* ============================== LEFT 50% : Input fields ============================== */} { xs={12} md={6} sx={{ - height: '100%', + height: { xs: 'auto', md: '100%' }, minHeight: 0, display: 'flex', flexDirection: 'column', - gap: 2.25, - overflowY: 'auto', - pr: 0.75 + gap: { xs: 1.25, md: 2.25 }, + overflowY: { xs: 'visible', md: 'auto' }, + pr: { xs: 0, md: 0.75 } }} > {/* Card: Setup (Location / Client / Business) */} @@ -1283,7 +1287,7 @@ const MultipleOrders = () => { item xs={12} md={6} - sx={{ height: '100%', minHeight: 0, display: 'flex', flexDirection: 'column' }} + sx={{ height: { xs: 'auto', md: '100%' }, minHeight: 0, display: 'flex', flexDirection: 'column' }} > { flexDirection: 'column', overflow: 'hidden', p: 1.25, - maxHeight: 685 + maxHeight: { xs: 'none', md: 685 } }} > {/* Preview header (sticky inside card) */} @@ -1483,7 +1487,101 @@ const MultipleOrders = () => { {/* Scrollable preview body */} - {previewMode === 'drops' && ( + {previewMode === 'drops' && isMobile && ( + + {dropCust.map((customer, index) => ( + + + + {index + 1}. {customer.firstname} + + + {customer.address} + + + + handleCheckboxChange1(customer)} + sx={{ color: '#ef4444', p: 0.5, flexShrink: 0 }} + > + + + + + } + > + + + {uploadType === 0 ? ( + + {customer.quantity ?? '—'} + + ) : ( + handleQuantityChange(customer.customerid, e.target.value)} + inputProps={{ min: 0 }} + fullWidth + sx={{ '& .MuiOutlinedInput-root': { borderRadius: '8px', height: 34 } }} + /> + )} + + + {uploadType === 0 ? ( + + {`₹${Number(customer.collectionamt || 0).toFixed(2)}`} + + ) : ( + { + const v = Number(e.target.value); + handleCollectionAmtChange(customer.customerid, v > 0 ? v : 0); + }} + inputProps={{ min: 0 }} + InputProps={{ + startAdornment: + }} + fullWidth + sx={{ '& .MuiOutlinedInput-root': { borderRadius: '8px', height: 34 } }} + /> + )} + + + + + ₹{Number(customer?.totalcharge || 0).toFixed(2)} + + + + + ))} + + + + + + + + ₹{Number(totalAmt).toFixed(2)} + + + + + + )} + + {previewMode === 'drops' && !isMobile && ( { )} - {previewMode === 'preview' && ( + {previewMode === 'preview' && isMobile && ( + + {users.map((u, i) => ( + + + {i + 1}. {u.firstname || '—'} + + + {u.address || '—'} + + + } + > + + + + + + + ))} + + )} + + {previewMode === 'preview' && !isMobile && ( { open={isCustomerOpen} onClose={() => setIsCustomerOpen(false)} fullWidth + fullScreen={isMobile} sx={{ '& .MuiDialog-paper': { - borderRadius: '16px', + borderRadius: { xs: 0, sm: '16px' }, overflow: 'hidden' } }} @@ -1863,7 +1992,7 @@ const MultipleOrders = () => {
- + {customerlist?.length === 0 ? ( diff --git a/src/pages/nearle/orders/optimisedOrderPreview.js b/src/pages/nearle/orders/optimisedOrderPreview.js index 1e55842..a80efe4 100644 --- a/src/pages/nearle/orders/optimisedOrderPreview.js +++ b/src/pages/nearle/orders/optimisedOrderPreview.js @@ -20,10 +20,12 @@ import { Accordion, AccordionSummary, AccordionActions, - AccordionDetails + AccordionDetails, + useMediaQuery } from '@mui/material'; import React, { Fragment, useEffect, useMemo, useState } from 'react'; import { useTheme } from '@mui/material/styles'; +import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; import { useLocation, useNavigate } from 'react-router-dom'; import dayjs from 'dayjs'; import MainCard from 'components/MainCard'; @@ -60,6 +62,7 @@ dayjs.extend(utc); const OptimisedOrderPreview = () => { const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const navigate = useNavigate(); const location = useLocation(); console.log('location.state', location.state); @@ -266,7 +269,13 @@ const OptimisedOrderPreview = () => { )} - + { ); }} - style={{ width: 400 }} + sx={{ width: { xs: '100%', md: 400 } }} renderInput={(params) => ( { - - + + {isMobile ? ( + + {orders.map((val, i) => { + const typeAccent = + val.ordertype === 'Economy' ? '#10b981' : val.ordertype === 'Risky' ? '#ef4444' : '#662582'; + return ( + + + + + + {`${val.locationname}-(${val.locationsuburb})`} + + + + ) : val.ordertype === 'Risky' ? ( + + ) : ( + + ) + } + color={val.ordertype === 'Economy' ? 'success' : val.ordertype === 'Risky' ? 'error' : 'primary'} + variant="light" + /> + + + + + + {val.rider} + + ID : {val.userid} + + + + + {val.pickupcustomer} + + + {val.pickupcontactno} + + + + {val.pickupsuburb || val.pickupaddress.slice(0, 20)} + + + + + + + + + {val.deliverycustomer} + + + {val.deliverycontactno} + + + + {val.deliverysuburb || val.deliveryaddress.slice(0, 20)} + + + + + + {val.ordernotes ? : null} + + + + + + + + + + + + + + + ); + })} + + ) : ( + @@ -579,16 +697,24 @@ const OptimisedOrderPreview = () => { ))}
-
+
+ )}
); })} - + - diff --git a/src/pages/nearle/orders/orders.js b/src/pages/nearle/orders/orders.js index 7162080..deafa24 100644 --- a/src/pages/nearle/orders/orders.js +++ b/src/pages/nearle/orders/orders.js @@ -110,6 +110,7 @@ import { getallriders } from '../../api/api'; import LoaderWithImage from 'components/nearle_components/LoaderWithImage'; +import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; import { useNavigate } from 'react-router'; import CSVExport from 'components/third-party/ReactTable'; import Dispatch from '../dispatch/Dispatch'; @@ -204,6 +205,7 @@ const ORDERS_STATUS_TABS = [ const Orders = () => { const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const navigate = useNavigate(); const loadMoreRef = useRef(); const containerRef = useRef(); @@ -1419,6 +1421,233 @@ const Orders = () => { '&::-webkit-scrollbar-track': { backgroundColor: DT.surfaceAlt } }} > + {isMobile ? ( + /* ===================== MOBILE: card list ===================== */ + + {rows?.length === 0 && !fetchOrdersIsLoading && ( + + + + + + No {currentStatus} orders + + + {searchword ? 'Try a different keyword.' : 'Adjust the filters above to load orders.'} + + + )} + {fetchOrdersIsLoading && ( + + + + Loading orders… + + + )} + {rows?.map((row, index) => { + const isItemSelected = !!deliverylist.find((res) => res.orderheaderid === row.orderheaderid); + const handleCheckbox = (e) => { + if (!appId) { + OpenToast('Please select a location first!', 'warning', 2000); + locationRef.current?.focus(); + return; + } + if (e.target.checked) { + setDeliverylist((prev) => [...prev, { ...row, sno: prev.length + 1 }]); + } else { + setDeliverylist((prev) => + prev.filter((item) => item.orderheaderid !== row.orderheaderid).map((item, i) => ({ ...item, sno: i + 1 })) + ); + } + }; + const meta = ROW_STATUS_META[String(row.orderstatus || '').toLowerCase()] || { + label: row.orderstatus || '—', + color: BRAND, + icon: MdHistoryToggleOff + }; + const StatusIcon = meta.icon; + const chipSx = (c) => ({ + display: 'inline-flex', + alignItems: 'center', + justifyContent: 'center', + px: 1, + py: 0.25, + borderRadius: 999, + bgcolor: dtTint(c), + color: c, + fontWeight: 700, + fontSize: 11, + border: `1px solid ${dtEdge(c)}`, + whiteSpace: 'nowrap' + }); + return ( + + + + {currentStatus === 'created' && ( + + )} + + {String(page * rowsPerPage + index + 1).padStart(2, '0')} + + + {meta.label} + + + {row.orderstatus === 'created' && ( + + {row.deliverytype === 'C' && ( + { + if (productCollapse?.orderid === row.orderid) { + setProductCollapse(null); + } else { + setProductCollapse(row); + } + }} + sx={{ + borderRadius: 999, + bgcolor: dtTint('#06b6d4'), + color: '#06b6d4', + border: `1px solid ${dtEdge('#06b6d4')}`, + '&:hover': { bgcolor: dtSoft('#06b6d4') } + }} + > + {productCollapse?.orderid === row.orderid ? ( + + ) : ( + + )} + + )} + { + setCancelDialog(true); + setOrderheaderid(row.orderheaderid); + }} + sx={{ + borderRadius: 999, + bgcolor: dtTint('#ef4444'), + color: '#ef4444', + border: `1px solid ${dtEdge('#ef4444')}`, + '&:hover': { bgcolor: dtSoft('#ef4444') }, + '&.Mui-disabled': { color: theme.palette.secondary.main } + }} + > + + + + )} + + + + {row.tenantname} + + + {[row.tenantsuburb, row.applocation].filter(Boolean).join(' · ') || '—'} + + + + } + > + + + + {`${row.locationname}-(${row.locationsuburb})`} + + + {row.orderid} · {dayjs(row.pickupslot).utc().format('DD/MM/YYYY')} {dayjs(row.pickupslot).format('hh:mm A')} + + + + + {row.pickupcustomer || '—'} + + + {row.pickupcontactno} + + + {row.pickupsuburb || row.pickupaddress} + + + + + {row.deliverycustomer || '—'} + + + {row.deliverycontactno} + + + {row.deliverysuburb || row.deliveryaddress} + + + + + {row.kms || 0} km + + + + {row.collectionamt ? `₹ ${row.collectionamt.toFixed(2)}` : '—'} + + + + + {row.deliverycharge ? `₹ ${row.deliverycharge.toFixed(2)}` : '—'} + + + {row.ordernotes && ( + + + {row.ordernotes} + + + )} + + + ); + })} + {rows?.length != 0 && ( +
+ {isFetchingNextPage || hasNextPage ? ( + + ) : ( + + No more orders + + )} +
+ )} + + ) : ( { )}
+ )} {/* ============================================= || Orders Preview Dialog | ============================================= */} diff --git a/src/pages/nearle/reports/ordersDetails.js b/src/pages/nearle/reports/ordersDetails.js index d83fead..6da8692 100644 --- a/src/pages/nearle/reports/ordersDetails.js +++ b/src/pages/nearle/reports/ordersDetails.js @@ -30,8 +30,10 @@ import { TextField, Tooltip, Typography, - Autocomplete + Autocomplete, + useMediaQuery } from '@mui/material'; +import { useTheme } from '@mui/material/styles'; import { MdAssignment, MdMyLocation, @@ -71,6 +73,7 @@ import LocationAutocomplete from 'components/nearle_components/LocationAutocompl import dayjs from 'dayjs'; import { OpenToast } from 'components/third-party/OpenToast'; import TableLoader from 'components/nearle_components/TableLoader'; +import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; var utc = require('dayjs/plugin/utc'); dayjs.extend(utc); @@ -418,6 +421,8 @@ function kalmanSmoothGps(pings, options = {}) { } export default function OrdersDetails() { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const loadMoreRef = useRef(); const containerRef = useRef(); const locationRef = useRef(null); @@ -1313,6 +1318,267 @@ export default function OrdersDetails() { '&::-webkit-scrollbar-track': { backgroundColor: DT.surfaceAlt } }} > + {isMobile ? ( + /* ===================== MOBILE: card list ===================== */ + + {fetchDeliveriesIsLoading ? ( + + + + Loading orders… + + + ) : rows?.length == 0 ? ( + + + + + + No orders to show + + + {searchword ? 'Try a different keyword.' : 'Adjust the filters above to load orders.'} + + + ) : ( + rows?.map((row, index) => { + const statusKey = String(row.orderstatus || '').toLowerCase(); + const rowStatusMeta = STATUS_META[statusKey] || { + label: row.orderstatus || '—', + color: BRAND, + icon: MdAssignment + }; + const StatusIcon = rowStatusMeta.icon; + const cancelled = statusKey === 'cancelled'; + const isDelivered = row.orderstatus === 'delivered'; + return ( + + + + + {String(page * rowsPerPage + index + 1).padStart(2, '0')} + + + + + + + {row.tenantname} + + + #{row.orderid} + + + + + + { + if (isDelivered) { + getdeliverylogs(row.deliveryid); + setMapTenant(row); + } + }} + sx={{ + flexShrink: 0, + bgcolor: isDelivered ? soft(BRAND) : soft('#94a3b8'), + color: isDelivered ? BRAND : DT.textMuted, + border: `1px solid ${isDelivered ? edge(BRAND) : edge('#94a3b8')}`, + '&:hover': { + bgcolor: isDelivered ? BRAND : soft('#94a3b8'), + color: isDelivered ? '#fff' : DT.textMuted + } + }} + > + + + + + + + + {rowStatusMeta.label} + + {row.ridername && ( + + {row.ridername} + + )} + + {dayjs(row.deliverydate).utc().format('DD/MM/YYYY · hh:mm A')} + + +
+ } + > + + + + + {row.pickupcustomer || '—'} + + + {row.pickupcontactno} + + + {row.pickupsuburb || (row.Pickupaddress ? row.Pickupaddress.slice(0, 22) + '…' : '')} + + {row.applocation && ( + + {row.applocation} + + )} + + + + + + {row.deliverycustomer || '—'} + + + {row.deliverycontactno} + + + {row.deliverysuburb || (row.deliveryaddress ? row.deliveryaddress.slice(0, 22) + '…' : '')} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + label={cancelled || row.kms == '' ? '0 km' : `${row.kms} km`} + tooltip="KMS" + /> + } + label={`${row.cumulativekms ?? 0} km`} + tooltip="Actual KMS" + /> + } + label={`${row.previouskms || (cancelled ? '0.00' : row.kms) || 0} km`} + tooltip="Rider KMS" + /> + + + + + } + label={cancelled || row.deliverycharges == '' ? `0.00` : `${row.deliverycharges}.00`} + tooltip="Delivery Charge" + /> + } + label={row.deliveryamt == '' ? `0.00` : `${row.deliveryamt}.00`} + tooltip="Delivery Amount" + /> + + + + {row.ordernotes && ( + + + + + {row.ordernotes} + + + + )} + + + ); + }) + )} + + ) : (
+ )} {rows?.length !== 0 && ( @@ -1682,7 +1949,8 @@ export default function OrdersDetails() { onClose={() => setReportDialog(false)} fullWidth maxWidth="sm" - PaperProps={{ sx: { borderRadius: 2.5, overflow: 'hidden' } }} + fullScreen={isMobile} + PaperProps={{ sx: { borderRadius: { xs: 0, sm: 3 }, overflow: 'hidden' } }} > + {isMobile ? ( + <> + {isLoadingReports && ( + + {[0, 1, 2, 3].map((i) => ( + + + + ))} + + )} + + {!isLoadingReports && filteredRows.length === 0 && ( + + + + + + No {isLocationGroup ? 'locations' : 'tenants'} to show + + + {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.'} + + + )} + + {!isLoadingReports && filteredRows.length > 0 && ( + + {filteredRows.map((row, index) => { + const rowKey = isLocationGroup ? row.locationname : row.tenantname; + const isOpen = openRow === rowKey; + const amount = Math.max(Number(row.charges) || 0, Number(row.deliveryamt) || 0); + const rowAccent = isLocationGroup ? '#0ea5e9' : BRAND; + + return ( + + + {isLocationGroup ? : } + + + + {(isLocationGroup ? row.locationname : row.tenantname) || '—'} + + + ID #{isLocationGroup ? row.locationid : row.tenantid} + + + + { + const isOpening = !isOpen; + setOpenRow(isOpening ? rowKey : null); + if (!isOpening) return; + setRidersdata([]); + if (isLocationGroup) { + getriderlocationsummary(row.locationid); + } else { + getuserreportsummary(row.tenantid); + } + }} + sx={{ + flexShrink: 0, + bgcolor: isOpen ? BRAND : soft(BRAND), + color: isOpen ? '#fff' : BRAND, + border: `1px solid ${edge(BRAND)}`, + '&:hover': { bgcolor: BRAND, color: '#fff' } + }} + > + {isOpen ? : } + + + + } + > + + + } /> + + + } /> + + + } /> + + + } /> + + + } /> + + + } /> + + + } /> + + + } + label={Number(row.collectionamt || 0).toFixed(2)} + tooltip="Collection Amount" + minWidth={90} + /> + + + } + label={formatNumberToRupees(amount).replace('₹', '').trim()} + tooltip="Total Amount" + minWidth={100} + /> + + + } + label={`${Number(row.kms || 0).toFixed(2)} km`} + tooltip="KMS" + /> + + + } + label={`${Number(row.cumulativekms || 0).toFixed(2)} km`} + tooltip="Actual KMS" + /> + + + + {/* Collapsible rider breakdown — mobile */} + + + + + + + + Rider Breakdown + + + + {!loading && (!ridersdata || ridersdata.length === 0) ? ( + + No rider activity for this row. + + ) : ( + + {ridersdata.map((sub, sidx) => { + const subAmount = Math.max(Number(sub.charges) || 0, Number(sub.deliveryamt) || 0); + return ( + + + + + + + + {`${sub.firstname || ''} ${sub.lastname || ''}`.trim() || '—'} + + + {sub.ridercontact || `ID #${sub.userid}`} + + + + + + } /> + + + } /> + + + } /> + + + } /> + + + } /> + + + } + label={Number(sub.collectionamt || 0).toFixed(2)} + tooltip="Collection" + minWidth={80} + /> + + + } + label={`${Number(sub.kms || 0).toFixed(2)} km`} + tooltip="KMS" + /> + + + } + label={`${Number(sub.cumulativekms || 0).toFixed(2)} km`} + tooltip="Actual KMS" + /> + + + } + label={formatNumberToRupees(subAmount).replace('₹', '').trim()} + tooltip="Total Amount" + minWidth={100} + /> + + + + ); + })} + + )} + + + + ); + })} + + )} + + ) : ( + )} {/* ============================================= || Total Bar || ============================================= */} {filteredRows.length > 0 && ( diff --git a/src/pages/nearle/reports/ridersLogs.js b/src/pages/nearle/reports/ridersLogs.js index d2e1441..aed4c63 100644 --- a/src/pages/nearle/reports/ridersLogs.js +++ b/src/pages/nearle/reports/ridersLogs.js @@ -25,6 +25,7 @@ import { useQuery } from '@tanstack/react-query'; import { fetchRidersLogs } from 'pages/api/api'; import RiderLocationMap from './RiderLocationMap'; import MainCard from 'components/MainCard'; +import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; import dayjs from 'dayjs'; import error500 from 'assets/images/maintenance/Error500.png'; @@ -34,6 +35,7 @@ const drawerWidth = 350; const RidersLogs = () => { const theme = useTheme(); const isDesktop = useMediaQuery('(min-width:900px)'); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const [open, setOpen] = useState(false); const [selectedRiders, setSelectedRiders] = useState([]); const [riderSearch, setRiderSearch] = useState(''); @@ -74,7 +76,8 @@ const RidersLogs = () => { ModalProps={{ keepMounted: true }} sx={{ '& .MuiDrawer-paper': { - width: drawerWidth, + width: isMobile ? '100vw' : drawerWidth, + maxWidth: isMobile ? '100vw' : drawerWidth, position: 'absolute', left: 0, top: 0, @@ -142,7 +145,8 @@ const RidersLogs = () => { )) - : riders?.map((row) => { + : !isMobile && + riders?.map((row) => { return ( { ); })} + + {/* Mobile: rider rows rendered as app-style cards (same selection behaviour) */} + {isMobile && !ridersIsLoading && !riderIsFetching && ( + + {riders?.map((row) => { + const isActive = row.status == 'active'; + const isSelected = selectedRiders?.length === 1 && selectedRiders[0]?.userid === row?.userid; + return ( + + { + if (e.target.checked) { + setSelectedRiders([row]); + } else { + setSelectedRiders(riders); + } + }} + /> + + + {row.username?.slice(0, 25) || ''} + {row.username?.length > 25 && '...'} + + + {row.contactno || '##########'} + + +
+ } + > + + + + {row.userid} + + + + + + + ); + })} + + )} {/* AppBar */} diff --git a/src/pages/nearle/reports/ridersSummary.js b/src/pages/nearle/reports/ridersSummary.js index da92a42..cc77f1e 100644 --- a/src/pages/nearle/reports/ridersSummary.js +++ b/src/pages/nearle/reports/ridersSummary.js @@ -22,7 +22,9 @@ import { TableHead, TableRow, Tooltip, - Typography + Typography, + useMediaQuery, + useTheme } from '@mui/material'; import { MdDirectionsBike, @@ -53,6 +55,7 @@ import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar'; import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton'; import RidersRoutes from './RidersRoutes'; import { OpenToast } from 'components/third-party/OpenToast'; +import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; // ============================================================================ // Design tokens — shared with deliveries / tenants / customers / pricing / @@ -182,6 +185,9 @@ function formatNumberToRupees(value) { // ==============================|| Riders Summary ||============================== // export default function RidersSummary() { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); + const [startdate, setStartdate] = useState(dayjs().format('YYYY-MM-DD')); const [enddate, setEnddate] = useState(dayjs().format('YYYY-MM-DD')); const [locaName, setLocoName] = useState('All'); @@ -584,6 +590,249 @@ export default function RidersSummary() { background: '#fff' }} > + {isMobile ? ( + + {isLoadingReports && ( + + + Loading riders… + + + )} + {(!filteredRows || filteredRows.length === 0) && !isLoadingReports ? ( + + + + + + No riders to show + + + {searchword ? 'Try a different keyword.' : 'Pick a zone or date range to load the summary.'} + + + ) : ( + filteredRows.map((row, index) => { + const isOpen = openRow === row.userid; + const amount = Math.max(Number(row.charges) || 0, Number(row.deliveryamt) || 0); + const riderName = `${row?.firstname || ''} ${row?.lastname || ''}`.trim() || '—'; + return ( + + + + + + + + {riderName} + + + #{String(index + 1).padStart(2, '0')} · ID #{row.userid} + + + + + + { + setSelectedRider({ + userid: row?.userid, + name: `${row?.firstname || ''} ${row?.lastname || ''}`.trim() || `Rider ${row?.userid}` + }); + setLogDetails(null); + setMapOpen(true); + getuserdeliverylogs(row?.userid); + }} + sx={{ + bgcolor: soft('#0ea5e9'), + color: '#0ea5e9', + border: `1px solid ${edge('#0ea5e9')}`, + '&:hover': { bgcolor: '#0ea5e9', color: '#fff' } + }} + > + + + + + { + const isOpening = !isOpen; + setOpenRow(isOpening ? row.userid : null); + if (isOpening) fetchTenantSummary(row.userid); + }} + sx={{ + bgcolor: isOpen ? BRAND : soft(BRAND), + color: isOpen ? '#fff' : BRAND, + border: `1px solid ${edge(BRAND)}`, + '&:hover': { bgcolor: BRAND, color: '#fff' } + }} + > + {isOpen ? : } + + + +
+ } + > + + + } /> + + + } /> + + + } /> + + + } /> + + + } + label={`${Number(row.kms || 0).toFixed(2)} km`} + tooltip="KMS" + /> + + + } + label={`${Number(row.cumulativekms || 0).toFixed(2)} km`} + tooltip="Actual KMS" + /> + + + } + label={formatNumberToRupees(amount).replace('₹', '').trim()} + tooltip="Total Amount" + minWidth={100} + /> + + + + {/* per-tenant breakdown */} + {isOpen && ( + + + + + + + + Tenant Breakdown + + + }> + {loading && ( + + Loading… + + )} + {!loading && (!tenantData || tenantData.length === 0) ? ( + + No tenant breakdown available. + + ) : ( + tenantData?.map((sub, sidx) => ( + + + + + + + {sub.tenantname || '—'} + + + + + } /> + + + } /> + + + } /> + + + } /> + + + } + label={`${Number(sub.kms || 0).toFixed(2)} km`} + tooltip="KMS" + /> + + + } + label={`${Number(sub.cumulativekms || 0).toFixed(2)} km`} + tooltip="Actual KMS" + /> + + + } + label={formatNumberToRupees( + Math.max(Number(sub.charges) || 0, Number(sub.deliveryamt) || 0) + ) + .replace('₹', '') + .trim()} + tooltip="Total Amount" + minWidth={100} + /> + + + + )) + )} + + + + )} + + ); + }) + )} + + ) : ( + )} {/* ============================================= || Total Bar || ============================================= */} {filteredRows.length > 0 && ( diff --git a/src/pages/nearle/requests/requests.js b/src/pages/nearle/requests/requests.js index 2c4c23e..7d13a77 100644 --- a/src/pages/nearle/requests/requests.js +++ b/src/pages/nearle/requests/requests.js @@ -29,8 +29,11 @@ import { CircularProgress, DialogTitle, FormLabel, - DialogActions + DialogActions, + useMediaQuery, + useTheme } from '@mui/material'; +import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard'; import { Autocomplete as Autocomplete1 } from '@mui/material'; import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'; @@ -178,6 +181,8 @@ const Requests = () => { }; function EnhancedTable() { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const [order, setOrder] = React.useState('asc'); const [orderBy, setOrderBy] = React.useState('calories'); const [selected, setSelected] = React.useState([]); @@ -741,9 +746,10 @@ const Requests = () => { open={dialogopen} onClose={dialogclose} scroll={'paper'} - // fullScreen + fullScreen={isMobile} maxWidth="sm" TransitionComponent={PopupTransition} + PaperProps={{ sx: { borderRadius: { xs: 0, sm: 3 } } }} > Create Request @@ -892,7 +898,77 @@ const Requests = () => { width: '100%' }} > - + {isMobile && ( + + {loading && + [0, 1, 2, 3, 4].map((item) => ( + + + + + + + + + + } /> + } /> + + + ))} + + {!loading && + visibleRows.map((row, index) => { + const isItemSelected = isSelected(row.sno); + return ( + handleClick(event, row.sno)} + header={ + + + + {row.requestor ? String(row.requestor).charAt(0).toUpperCase() : '#'} + + + + {row.requestor || '—'} + + + #{row.sno} + + + + {row.amount != null && ( + + )} + + } + > + + + + + + + + + ); + })} + + {!loading && visibleRows.length === 0 && ( + + + No requests to show + Requests will appear here once available. + + )} + + )} + + { ); } + const outerTheme = useTheme(); + const isMobile = useMediaQuery(outerTheme.breakpoints.down('md')); const [tabvalue, setTabvalue] = useState(0); const [rows, setRows] = useState([]); const [clientapproved, setClientApproved] = useState([]); @@ -1860,10 +1938,16 @@ const Requests = () => { xs={12} // sx={{ mb: -2.25 }} > - + Payment Requests + ); }; diff --git a/src/pages/nearle/riders/editRider.js b/src/pages/nearle/riders/editRider.js index b52c333..f965bd8 100644 --- a/src/pages/nearle/riders/editRider.js +++ b/src/pages/nearle/riders/editRider.js @@ -13,7 +13,9 @@ import { Stack, TextField, Typography, - Autocomplete + Autocomplete, + useMediaQuery, + useTheme } from '@mui/material'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; @@ -34,6 +36,8 @@ import dayjs from 'dayjs'; import CircularLoader from 'components/CircularLoader'; const EditRider = () => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const location = useLocation(); const [riderdata, setRiderdata] = useState(null); const navigate = useNavigate(); @@ -347,15 +351,17 @@ const EditRider = () => { Edit Rider
{ )}
+ )}
diff --git a/src/pages/nearle/viewProfile.js b/src/pages/nearle/viewProfile.js index 6d0412b..836d418 100644 --- a/src/pages/nearle/viewProfile.js +++ b/src/pages/nearle/viewProfile.js @@ -1,4 +1,5 @@ -import { Grid, TextField, Typography } from '@mui/material'; +import { Grid, TextField, Typography, useMediaQuery } from '@mui/material'; +import { useTheme } from '@mui/material/styles'; import { useQuery } from '@tanstack/react-query'; import CircularLoader from 'components/CircularLoader'; import Loader from 'components/Loader'; @@ -6,6 +7,8 @@ import MainCard from 'components/MainCard'; import { getusers } from 'pages/api/api'; const ViewProfile = () => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); const { data: userData, isLoading @@ -23,13 +26,14 @@ const ViewProfile = () => { )} + User Profile } > - + {/* ==============================|| userid ||============================== */}