initial commit
This commit is contained in:
501
src/pages/nearle/riders/riders.js
Normal file
501
src/pages/nearle/riders/riders.js
Normal file
@@ -0,0 +1,501 @@
|
||||
import * as React from 'react';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import Geocode from 'react-geocode';
|
||||
import { Empty } from 'antd';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { FaRegEdit } from 'react-icons/fa';
|
||||
import {
|
||||
Avatar,
|
||||
Stack,
|
||||
Chip,
|
||||
Typography,
|
||||
Table,
|
||||
TableCell,
|
||||
TableBody,
|
||||
TableHead,
|
||||
IconButton,
|
||||
Tabs,
|
||||
Tab,
|
||||
TableRow,
|
||||
Tooltip,
|
||||
TableContainer,
|
||||
Divider,
|
||||
Backdrop,
|
||||
Collapse
|
||||
} from '@mui/material';
|
||||
var utc = require('dayjs/plugin/utc');
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { DownOutlined, UpOutlined } from '@ant-design/icons';
|
||||
import dayjs from 'dayjs';
|
||||
dayjs.extend(utc);
|
||||
import MainCard from 'components/MainCard';
|
||||
import TitleCard from 'components/nearle_components/TitleCard';
|
||||
import LocationAutocomplete from 'components/nearle_components/LocationAutocomplete';
|
||||
import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar';
|
||||
import CircularLoader from 'components/CircularLoader';
|
||||
import { fetchAllRiders, getallridersummary, getriderstatus } from 'pages/api/api';
|
||||
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
|
||||
import LoaderWithImage from 'components/nearle_components/LoaderWithImage';
|
||||
import { OrdersTableSkeleton } from '../orders/OrdersTableSkeleton';
|
||||
import { OpenToast } from 'components/third-party/OpenToast';
|
||||
import axios from 'axios';
|
||||
import LocationOnIcon from '@mui/icons-material/LocationOn';
|
||||
import BatteryStdIcon from '@mui/icons-material/BatteryStd';
|
||||
import SpeedIcon from '@mui/icons-material/Speed';
|
||||
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||
import PowerIcon from '@mui/icons-material/Power';
|
||||
import GpsFixedIcon from '@mui/icons-material/GpsFixed';
|
||||
|
||||
const Riders = () => {
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
const loadMoreRef = useRef();
|
||||
const containerRef = useRef();
|
||||
const [searchword, setSearchword] = useState('');
|
||||
const [debouncedSearch, setDebouncedSearch] = useState('');
|
||||
const [locaName, setLocoName] = useState('All');
|
||||
const [appId, setAppId] = useState(0);
|
||||
const [tabvalue, setTabvalue] = useState(0);
|
||||
const roleid = localStorage.getItem('roleid');
|
||||
const [logsRow, setLogsRow] = useState(null);
|
||||
const [riderLogsdata, setRiderLogsdata] = useState(null);
|
||||
|
||||
Geocode.setApiKey(process.env.REACT_APP_GOOGLE_MAPS_API_KEY);
|
||||
|
||||
|
||||
|
||||
const handleChangetab = (e, i) => {
|
||||
setTabvalue(i);
|
||||
setLogsRow(null);
|
||||
};
|
||||
|
||||
// ==============================|| getallridersummary||============================== //
|
||||
|
||||
const { data: allRidersSummary, isLoading: riderSummarysLoading } = useQuery({
|
||||
queryKey: ['allriders', appId, tabvalue],
|
||||
queryFn: getallridersummary
|
||||
});
|
||||
// ==============================|| getRiderLogs (riders)||============================== //
|
||||
const getRiderLogs = async (userid) => {
|
||||
try {
|
||||
const res = await axios.get(`${process.env.REACT_APP_URL}/utils/getriderperiodiclogs?userid=${userid}`);
|
||||
if (res.data.data.length == 0) {
|
||||
setLogsRow(null);
|
||||
OpenToast(res.data.message, 'error', 2000);
|
||||
} else {
|
||||
setRiderLogsdata(res.data.data);
|
||||
}
|
||||
} catch (err) {
|
||||
OpenToast(err.message, 'error', 2000);
|
||||
}
|
||||
};
|
||||
// ==============================|| getriderstatus||============================== //
|
||||
const {
|
||||
data: ridersStatus,
|
||||
isLoading: riderStatusLoading,
|
||||
isError: riderstatusIsError,
|
||||
error: riderStatusError
|
||||
} = useQuery({
|
||||
queryKey: ['ridersStatus'],
|
||||
queryFn: getriderstatus
|
||||
});
|
||||
useEffect(() => {
|
||||
if (ridersStatus) {
|
||||
console.log('Success:', ridersStatus);
|
||||
}
|
||||
}, [ridersStatus]);
|
||||
// ==============================|| fetchAllRiders||============================== //
|
||||
const {
|
||||
data: allRidersData,
|
||||
isLoading: allRidersLoading,
|
||||
isFetchingNextPage,
|
||||
fetchNextPage,
|
||||
hasNextPage
|
||||
} = useInfiniteQuery({
|
||||
queryKey: ['allriders', appId, debouncedSearch, tabvalue],
|
||||
queryFn: fetchAllRiders,
|
||||
getNextPageParam: (lastPage, pages) => (lastPage.details?.length ? pages.length + 1 : undefined)
|
||||
});
|
||||
|
||||
const rows = allRidersData?.pages.flatMap((page) => page.details) || [];
|
||||
useEffect(() => {
|
||||
if (!hasNextPage) return;
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
if (entries[0].isIntersecting) {
|
||||
fetchNextPage();
|
||||
}
|
||||
},
|
||||
{
|
||||
root: document.querySelector('.MuiTableContainer-root'), // 👈 or explicitly TableContainer
|
||||
rootMargin: '0px',
|
||||
threshold: 1.0
|
||||
}
|
||||
);
|
||||
if (loadMoreRef.current) observer.observe(loadMoreRef.current);
|
||||
return () => {
|
||||
if (loadMoreRef.current) observer.unobserve(loadMoreRef.current);
|
||||
};
|
||||
}, [hasNextPage, fetchNextPage]);
|
||||
|
||||
const handleScroll = (event) => {
|
||||
const { scrollTop, scrollHeight, clientHeight } = event.currentTarget;
|
||||
if (scrollTop + clientHeight >= scrollHeight - 50) {
|
||||
if (hasNextPage && !isFetchingNextPage) {
|
||||
fetchNextPage();
|
||||
}
|
||||
}
|
||||
};
|
||||
const errMessage = riderstatusIsError ? riderStatusError : null;
|
||||
useEffect(() => {
|
||||
if (errMessage) {
|
||||
OpenToast(errMessage, 'error', 2000);
|
||||
}
|
||||
}, [errMessage]);
|
||||
return (
|
||||
<>
|
||||
{
|
||||
<Backdrop
|
||||
sx={{
|
||||
color: '#fff',
|
||||
zIndex: (theme) => theme.zIndex.drawer + 1
|
||||
}}
|
||||
open={allRidersLoading || riderSummarysLoading || riderStatusLoading} // when loader = true, backdrop covers the page
|
||||
>
|
||||
<CircularLoader color="inherit" />
|
||||
</Backdrop>
|
||||
}
|
||||
{/* ============================================= || titlecard | ============================================= */}
|
||||
|
||||
<TitleCard title="Riders">
|
||||
<LocationAutocomplete
|
||||
locaName={locaName}
|
||||
setAppId={setAppId}
|
||||
setLocoName={setLocoName}
|
||||
sx={{ width: { xs: '100%', custom450: 300 }, zIndex: '100' }}
|
||||
/>
|
||||
</TitleCard>
|
||||
<Stack
|
||||
minWidth={'100%'}
|
||||
flexDirection="row"
|
||||
sx={{
|
||||
p: 1.5,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
flexWrap: 'wrap-reverse',
|
||||
gap: 1,
|
||||
border: '1px solid',
|
||||
borderColor: 'bg.main',
|
||||
mt: 2
|
||||
}}
|
||||
>
|
||||
<Tabs value={tabvalue} onChange={handleChangetab} variant="scrollable" scrollButtons="auto">
|
||||
<Tab
|
||||
label="ALL"
|
||||
iconPosition="end"
|
||||
icon={searchword ? null : <Chip label={allRidersSummary?.total} color="primary" variant="light" size="small" />}
|
||||
/>
|
||||
<Tab
|
||||
label="Active"
|
||||
iconPosition="end"
|
||||
icon={searchword ? null : <Chip label={allRidersSummary?.active} color="primary" variant="light" size="small" />}
|
||||
/>
|
||||
</Tabs>
|
||||
<DebounceSearchBar
|
||||
value={searchword}
|
||||
onChange={setSearchword}
|
||||
onDebouncedChange={setDebouncedSearch}
|
||||
sx={{ width: { xs: '100%', custom600: 275 }, m: 0 }}
|
||||
/>
|
||||
</Stack>
|
||||
<MainCard content={false}>
|
||||
<TableContainer
|
||||
ref={containerRef}
|
||||
onScroll={handleScroll}
|
||||
sx={{
|
||||
maxHeight: 'calc(100vh - 185px)',
|
||||
overflow: 'auto',
|
||||
'&::-webkit-scrollbar': {
|
||||
width: '12px', // scroll bar width
|
||||
cursor: 'pointer'
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb': {
|
||||
backgroundColor: theme.palette.primary.main, // thumb color
|
||||
borderRadius: '8px',
|
||||
cursor: 'pointer'
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb:hover': {
|
||||
backgroundColor: theme.palette.primary.dark, // hover color
|
||||
cursor: 'pointer'
|
||||
},
|
||||
'&::-webkit-scrollbar-track': {
|
||||
backgroundColor: theme.palette.primary.lighter,
|
||||
cursor: 'pointer'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Table stickyHeader>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell sx={{ position: 'sticky !important' }}>S.NO</TableCell>
|
||||
<TableCell sx={{ position: 'sticky !important', whiteSpace: 'nowrap', minWidth: 100 }}>User ID</TableCell>
|
||||
<TableCell sx={{ position: 'sticky !important' }}> Rider </TableCell>
|
||||
<TableCell sx={{ position: 'sticky !important' }}> Address </TableCell>
|
||||
<TableCell sx={{ position: 'sticky !important' }}> Vehicle </TableCell>
|
||||
<TableCell sx={{ position: 'sticky !important' }}> Shift </TableCell>
|
||||
<TableCell align="center" sx={{ position: 'sticky !important' }}>
|
||||
Time
|
||||
</TableCell>
|
||||
<TableCell sx={{ position: 'sticky !important' }}> Fare </TableCell>
|
||||
<TableCell sx={{ position: 'sticky !important' }}> Fuel </TableCell>
|
||||
<TableCell align="center" sx={{ position: 'sticky !important' }}>
|
||||
Status
|
||||
</TableCell>
|
||||
{roleid == 1 && (
|
||||
<TableCell align="center" sx={{ position: 'sticky !important' }}>
|
||||
Action{' '}
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<Divider />
|
||||
<TableBody>
|
||||
{allRidersLoading && <OrdersTableSkeleton col={6} />}
|
||||
{rows?.length == 0 && !allRidersLoading && (
|
||||
<>
|
||||
<TableCell colSpan={11}>
|
||||
<Stack width={'100%'} direction={'row'} justifyContent={'center'}>
|
||||
<Empty />
|
||||
</Stack>
|
||||
</TableCell>
|
||||
</>
|
||||
)}
|
||||
{rows?.length != 0 &&
|
||||
rows?.map((row, index) => {
|
||||
return (
|
||||
<>
|
||||
<TableRow key={index + 1} sx={{ cursor: 'pointer' }}>
|
||||
<TableCell>{index + 1}</TableCell>
|
||||
<TableCell>
|
||||
<Chip label={row?.userid} color="primary" variant="light" />
|
||||
</TableCell>
|
||||
<TableCell align="left" sx={{ paddingLeft: '0px !important' }}>
|
||||
<Stack direction="row" alignItems="center" gap={2} justifyContent="flex-start">
|
||||
<Avatar
|
||||
sx={{
|
||||
width: 35,
|
||||
height: 35,
|
||||
fontSize: 20
|
||||
}}
|
||||
>
|
||||
{row.fullname?.charAt(0).toUpperCase()}
|
||||
</Avatar>
|
||||
<Stack direction="column" gap={1}>
|
||||
<Typography>{`${row.username}`}</Typography>
|
||||
<Typography variant="caption">{row.contactno}</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
<TableCell align="left">
|
||||
<Tooltip title={row.address}>
|
||||
<Stack direction="column">
|
||||
<Typography variant="caption">{row.suburb || row.address.slice(0, 20)}</Typography>
|
||||
<Typography>{row.city}</Typography>
|
||||
</Stack>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell align="left">{row.vehicleno}</TableCell>
|
||||
<TableCell>{row.shiftid}</TableCell>
|
||||
<TableCell align="center">
|
||||
<Stack display={'flex'} flexDirection={'column'} gap={1}>
|
||||
<Chip
|
||||
size="small"
|
||||
color="success"
|
||||
variant="light"
|
||||
label={dayjs(`${dayjs().format('MM-DD-YYYY')} ${row.starttime}`).format('hh:mm A')}
|
||||
sx={{ width: 100 }}
|
||||
/>
|
||||
<Chip
|
||||
size="small"
|
||||
color="error"
|
||||
variant="light"
|
||||
label={dayjs(`${dayjs().format('MM-DD-YYYY')} ${row.endtime}`).format('hh:mm A')}
|
||||
sx={{ width: 100 }}
|
||||
/>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="left">{row.basefare}</TableCell>
|
||||
<TableCell align="left">{row.fuelcharge}</TableCell>
|
||||
{tabvalue == 0 ? (
|
||||
<TableCell>
|
||||
{row.status == 'Active' && (
|
||||
<Chip label="Active" color="success" size="small" sx={{ width: 80 }} variant="light" />
|
||||
)}
|
||||
{row.status == 'InActive' && (
|
||||
<Chip label="In Active" color="error" size="small" sx={{ width: 80 }} variant="light" />
|
||||
)}
|
||||
</TableCell>
|
||||
) : (
|
||||
<TableCell>
|
||||
{(() => {
|
||||
const state = ridersStatus?.find((status) => status.userid === row.userid);
|
||||
const statusText = state?.status;
|
||||
console.log('statusText', state);
|
||||
return (
|
||||
<Chip
|
||||
label={statusText || 'Unknown'}
|
||||
color={
|
||||
statusText === 'Active'
|
||||
? 'primary'
|
||||
: statusText === 'Offline'
|
||||
? 'error'
|
||||
: statusText === 'Online'
|
||||
? 'success'
|
||||
: 'default'
|
||||
}
|
||||
size="small"
|
||||
sx={{ width: 80 }}
|
||||
variant="light"
|
||||
/>
|
||||
);
|
||||
})()}
|
||||
</TableCell>
|
||||
)}
|
||||
|
||||
{roleid == 1 && (
|
||||
<TableCell align="center">
|
||||
<Stack direction={'row'}>
|
||||
<Tooltip title="Edit Rider">
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
navigate('/nearle/riders/edit', { state: { riderdata: row } });
|
||||
}}
|
||||
>
|
||||
<FaRegEdit />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
{tabvalue != 0 && (
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
if (row.userid == logsRow) {
|
||||
setLogsRow(null);
|
||||
} else {
|
||||
setLogsRow(row.userid);
|
||||
getRiderLogs(row.userid);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{row.userid == logsRow ? <UpOutlined /> : <DownOutlined />}
|
||||
</IconButton>
|
||||
)}
|
||||
</Stack>
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
{logsRow === row.userid && tabvalue !== 0 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={11} sx={{ p: 0 }}>
|
||||
<Collapse in={logsRow === row.userid} timeout="auto" unmountOnExit>
|
||||
<MainCard content={false}>
|
||||
<Table size="small">
|
||||
{/* Header */}
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Location</TableCell>
|
||||
<TableCell>Battery</TableCell>
|
||||
<TableCell>Charging</TableCell>
|
||||
<TableCell>Speed</TableCell>
|
||||
<TableCell>Accuracy</TableCell>
|
||||
<TableCell>Time</TableCell>
|
||||
<TableCell>Order</TableCell>
|
||||
<TableCell>Status</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
{/* Body */}
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Stack direction="row" spacing={1}>
|
||||
<LocationOnIcon color="error" fontSize="small" />
|
||||
<Typography variant="body2">
|
||||
{riderLogsdata?.latitude}, {riderLogsdata?.longitude}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Stack direction="row" spacing={1}>
|
||||
<BatteryStdIcon fontSize="small" />
|
||||
<Typography>{riderLogsdata?.battery || 'N/A'}</Typography>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Stack direction="row" spacing={1}>
|
||||
<PowerIcon color={riderLogsdata?.is_charging ? 'success' : 'disabled'} fontSize="small" />
|
||||
<Typography>{riderLogsdata?.is_charging ? 'Charging' : 'Not Charging'}</Typography>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Stack direction="row" spacing={1}>
|
||||
<SpeedIcon fontSize="small" />
|
||||
<Typography>{riderLogsdata?.speed} km/h</Typography>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Stack direction="row" spacing={1}>
|
||||
<GpsFixedIcon fontSize="small" />
|
||||
<Typography>{riderLogsdata?.accuracy} m</Typography>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Stack direction="row" spacing={1}>
|
||||
<AccessTimeIcon fontSize="small" />
|
||||
<Typography>{riderLogsdata?.logdate}</Typography>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>{riderLogsdata?.orderid || 'N/A'}</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<Chip
|
||||
label={riderLogsdata?.status || 'unknown'}
|
||||
color={riderLogsdata?.status === 'idle' ? 'warning' : 'success'}
|
||||
size="small"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</MainCard>
|
||||
</Collapse>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
{rows?.length != 0 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={15} rowSpan={3}>
|
||||
<div ref={loadMoreRef} style={{ height: 40, textAlign: 'center' }}>
|
||||
{isFetchingNextPage ? <LoaderWithImage /> : hasNextPage ? <LoaderWithImage /> : 'No More Riders'}
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</MainCard>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Riders;
|
||||
Reference in New Issue
Block a user