642 lines
27 KiB
JavaScript
642 lines
27 KiB
JavaScript
import { React, useState, useEffect, useRef } from 'react';
|
|
import axios from 'axios';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import TaskAltIcon from '@mui/icons-material/TaskAlt';
|
|
import HighlightOffIcon from '@mui/icons-material/HighlightOff';
|
|
import { Empty } from 'antd';
|
|
// material-ui
|
|
import {
|
|
Box,
|
|
Divider,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableContainer,
|
|
TableHead,
|
|
TableRow,
|
|
Dialog,
|
|
DialogTitle,
|
|
Typography,
|
|
DialogContent,
|
|
Stack,
|
|
Button,
|
|
IconButton,
|
|
Tooltip,
|
|
Chip,
|
|
Collapse
|
|
} from '@mui/material';
|
|
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
|
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
|
import dayjs from 'dayjs';
|
|
var utc = require('dayjs/plugin/utc');
|
|
dayjs.extend(utc);
|
|
import { DateRangePicker } from 'mui-daterange-picker';
|
|
import {
|
|
addDays,
|
|
addMonths,
|
|
addWeeks,
|
|
// addYears,
|
|
endOfMonth,
|
|
endOfWeek,
|
|
// endOfYear,
|
|
startOfMonth,
|
|
startOfWeek
|
|
// startOfYear,
|
|
} from 'date-fns';
|
|
// project imports
|
|
import MainCard from 'components/MainCard';
|
|
import Loader from 'components/Loader';
|
|
import TitleCard from '../titleCard';
|
|
import { fetchRidersSummary } from '../api/api';
|
|
import { useTheme } from '@mui/material/styles';
|
|
import { getValueColor } from 'components/nearle_components/getValueColor';
|
|
|
|
// table filter
|
|
function descendingComparator(a, b, orderBy) {
|
|
if (b[orderBy] < a[orderBy]) {
|
|
return -1;
|
|
}
|
|
if (b[orderBy] > a[orderBy]) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
function getComparator(order, orderBy) {
|
|
return order === 'desc' ? (a, b) => descendingComparator(a, b, orderBy) : (a, b) => -descendingComparator(a, b, orderBy);
|
|
}
|
|
|
|
function stableSort(array, comparator) {
|
|
const stabilizedThis = array.map((el, index) => [el, index]);
|
|
stabilizedThis.sort((a, b) => {
|
|
const order = comparator(a[0], b[0]);
|
|
if (order !== 0) return order;
|
|
return a[1] - b[1];
|
|
});
|
|
return stabilizedThis.map((el) => el[0]);
|
|
}
|
|
function formatNumberToRupees(value) {
|
|
return new Intl.NumberFormat('en-IN', {
|
|
style: 'currency',
|
|
currency: 'INR',
|
|
minimumFractionDigits: 2
|
|
}).format(value);
|
|
}
|
|
|
|
// ==============================|| MUI TABLE - ENHANCED ||============================== //
|
|
|
|
export default function RidersSummary() {
|
|
// const [rows, setRows] = useState([]);
|
|
const theme = useTheme();
|
|
const tenantid = localStorage.getItem('tenantid');
|
|
const [order, setOrder] = useState('asc');
|
|
const [orderBy, setOrderBy] = useState('calories');
|
|
const [selected, setSelected] = useState([]);
|
|
const [page, setPage] = useState(0);
|
|
const [dense] = useState(false);
|
|
const [rowsPerPage, setRowsPerPage] = useState(10);
|
|
const [selectedValue, setSelectedValue] = useState([]);
|
|
const [startdate, setStartdate] = useState(dayjs().format('YYYY-MM-DD'));
|
|
const [enddate, setEnddate] = useState(dayjs().format('YYYY-MM-DD'));
|
|
const [locaName, setLocoName] = useState('All');
|
|
const [open, setOpen] = useState(false);
|
|
const [dateselect, setDateselect] = useState('select');
|
|
const [tabstatus1, setTabstatus1] = useState('Today');
|
|
const [datestatus, setDatestatus] = useState('Today');
|
|
const [total, settotal] = useState(0);
|
|
const [id, setid] = useState(-1);
|
|
|
|
const [tenantData, setTenantData] = useState([]);
|
|
const [openRow, setOpenRow] = useState(null); // Initially no row is open
|
|
const [searchword, setSearchword] = useState('');
|
|
const textFieldRef = useRef(null);
|
|
|
|
const [appId, setAppId] = useState(localStorage.getItem('applocationid'));
|
|
const [locations, setLocations] = useState('Select Location');
|
|
const userid = localStorage.getItem('userid');
|
|
|
|
/* ============================================= || handleKeyPress (ctrl+k)| ============================================= */
|
|
useEffect(() => {
|
|
const handleKeyPress = (event) => {
|
|
if (event.key === 'k' && (event.metaKey || event.ctrlKey)) {
|
|
event.preventDefault();
|
|
|
|
textFieldRef.current.focus();
|
|
}
|
|
if (event.key === 'Escape' && document.activeElement === textFieldRef.current) {
|
|
// Remove focus from the TextField
|
|
textFieldRef.current.blur();
|
|
}
|
|
};
|
|
document.addEventListener('keydown', handleKeyPress);
|
|
|
|
return () => {
|
|
document.removeEventListener('keydown', handleKeyPress);
|
|
};
|
|
}, []);
|
|
|
|
// ==============================|| fetchRidersSummary (riders summary)||============================== //
|
|
const {
|
|
isLoading: isLoadingReports,
|
|
isError: isErrorReports, //true or false
|
|
data: rows,
|
|
error: reportsError
|
|
} = useQuery({
|
|
queryKey: [tenantid, startdate, enddate],
|
|
queryFn: fetchRidersSummary
|
|
});
|
|
useEffect(() => {
|
|
rows && console.log('rows', rows);
|
|
}, [rows]);
|
|
// ==============================|| calculate||============================== //
|
|
const calculate = async () => {
|
|
let calculatedTotal = 0;
|
|
rows &&
|
|
rows.forEach((row, index) => {
|
|
console.log(index, row.Deliveryamt);
|
|
calculatedTotal += row.Deliveryamt;
|
|
});
|
|
// Update the state after the calculation is done
|
|
settotal(calculatedTotal);
|
|
console.log('calculatedTotal', calculatedTotal);
|
|
};
|
|
useEffect(() => {
|
|
rows && calculate();
|
|
}, [rows]);
|
|
|
|
if (isLoadingReports) return <Loader />;
|
|
if (isErrorReports) return 'An error has occurred:(isErrorReports) ' + reportsError.message;
|
|
|
|
// ==============================|| fetchTenantSummary by rider (rider summary)||============================== //
|
|
const fetchTenantSummary = async (riderUserid) => {
|
|
try {
|
|
const tenantRes = await axios.get(
|
|
// `${process.env.REACT_APP_URL}/deliveries/getreportlocationsummary/?&fromdate=${startdate}&todate=${enddate}&userid=${riderUserid}&tenantid=${tenantid}`
|
|
`${process.env.REACT_APP_URL}/deliveries/getriderlocationreportsummary/?&fromdate=${startdate}&todate=${enddate}&userid=${riderUserid}&tenantid=${tenantid}`
|
|
);
|
|
console.log('tenantRes', tenantRes.data.details);
|
|
setTenantData(tenantRes.data.details);
|
|
} catch (error) {
|
|
console.log('tenantRes', error);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<TitleCard title="Riders Summary" />
|
|
<MainCard
|
|
content={false}
|
|
title={
|
|
<Stack display={'flex'} flexDirection={'row'} alignItems={'center'} justifyContent={'space-between'} flexWrap={'wrap'} gap={1}>
|
|
<Stack direction="column" alignItems="flex-start" spacing={1} sx={{}}>
|
|
{startdate && enddate && (
|
|
<Stack direction="row" spacing={2} flexWrap={'wrap'} gap={1}>
|
|
<Chip label={`Orders-${datestatus}`} color="primary" variant="light" size="small" />
|
|
<Chip
|
|
label={
|
|
<Typography noWrap color="secondary">
|
|
{dayjs(startdate).format('DD/MM/YYYY')} - {dayjs(enddate).format('DD/MM/YYYY')}
|
|
</Typography>
|
|
}
|
|
variant="combined"
|
|
color="warning"
|
|
size="small"
|
|
/>
|
|
</Stack>
|
|
)}
|
|
{(!startdate || !enddate) && (
|
|
<>
|
|
<Stack direction="row" spacing={2}>
|
|
<Chip label="Orders-All" color="primary" variant="light" size="small" />
|
|
{/* <Chip label={<Typography noWrap color="secondary">ALL</Typography>} variant="combined" color='warning' size='small' /> */}
|
|
</Stack>
|
|
</>
|
|
)}
|
|
</Stack>
|
|
</Stack>
|
|
}
|
|
>
|
|
{/* table */}
|
|
<TableContainer>
|
|
<Table>
|
|
<TableHead>
|
|
<TableRow>
|
|
<TableCell># </TableCell>
|
|
<TableCell> Rider</TableCell>
|
|
<TableCell>Deliveries </TableCell>
|
|
<TableCell> Pending</TableCell>
|
|
<TableCell> Assigned</TableCell>
|
|
<TableCell> Accepted</TableCell>
|
|
<TableCell>Arrived </TableCell>
|
|
<TableCell>Picked </TableCell>
|
|
<TableCell>Active </TableCell>
|
|
<TableCell>Skipped </TableCell>
|
|
<TableCell>Cancelled </TableCell>
|
|
<TableCell>Delivered </TableCell>
|
|
<TableCell>kms </TableCell>
|
|
<TableCell> COD/PLA</TableCell>
|
|
<TableCell> Charges</TableCell>
|
|
<TableCell>Action </TableCell>
|
|
</TableRow>
|
|
</TableHead>
|
|
{/* ============================================ || TableBody || ============================================ */}
|
|
|
|
<TableBody>
|
|
{/* {rows?.length === 0 && (
|
|
<TableRow>
|
|
<TableCell colSpan={11}>
|
|
<Stack width={'100%'} direction={'row'} justifyContent={'center'}>
|
|
<Empty />
|
|
</Stack>
|
|
</TableCell>
|
|
</TableRow>
|
|
)} */}
|
|
|
|
{rows?.map((row, index) => (
|
|
<>
|
|
{/* // ============================================ || tablerow 1 || ============================================ */}
|
|
|
|
<TableRow
|
|
sx={{
|
|
cursor: openRow === row.userid ? 'pointer' : null
|
|
}}
|
|
>
|
|
<TableCell component="th" scope="row" padding="none">
|
|
{index + 1}
|
|
</TableCell>
|
|
<TableCell align="left">
|
|
<Stack sx={{ ml: -2, wrap: 'nowrap' }}>
|
|
<Typography>
|
|
{row.firstname} {row.lastname}
|
|
</Typography>
|
|
<Typography variant="body2">Id : {row.userid}</Typography>
|
|
</Stack>
|
|
</TableCell>
|
|
|
|
<TableCell align="center" sx={{ color: getValueColor(row.totalorders) }}>
|
|
{row.totalorders}
|
|
</TableCell>
|
|
<TableCell align="center">
|
|
{row.pending ? <Chip color="primary" variant="light" label={row.pending} /> : row.pending}
|
|
</TableCell>
|
|
<TableCell align="center">
|
|
{row.assigned ? <Chip color="primary" variant="light" label={row.assigned} /> : row.assigned}
|
|
</TableCell>
|
|
<TableCell align="center">
|
|
{row.accepted ? <Chip color="primary" variant="light" label={row.accepted} /> : row.accepted}
|
|
</TableCell>
|
|
<TableCell align="center">
|
|
{row.arrived ? <Chip color="primary" variant="light" label={row.arrived} /> : row.arrived}
|
|
</TableCell>
|
|
<TableCell align="center">
|
|
{row.picked ? <Chip color="primary" variant="light" label={row.picked} /> : row.picked}
|
|
</TableCell>
|
|
<TableCell align="center">
|
|
{row.active ? <Chip color="primary" variant="light" label={row.active} /> : row.active}
|
|
</TableCell>
|
|
<TableCell align="center">
|
|
{row.skipped ? <Chip color="primary" variant="light" label={row.skipped} /> : row.skipped}
|
|
</TableCell>
|
|
<TableCell align="center">
|
|
{row.cancelled ? <Chip color="primary" variant="light" label={row.cancelled} /> : row.cancelled}
|
|
</TableCell>
|
|
|
|
<TableCell align="center" sx={{ color: getValueColor(row.totalorders) }}>
|
|
{row.delivered}
|
|
</TableCell>
|
|
|
|
<TableCell align="left">
|
|
<Stack direction={'row'}>
|
|
{/* <Tooltip title="kms" placement="top">
|
|
<Chip
|
|
size="small"
|
|
label={row.kms}
|
|
sx={{
|
|
color: '#1976d2',
|
|
bgcolor: '#e3f2fd',
|
|
mr: 1,
|
|
border: '1px solid #1976d2',
|
|
cursor: 'pointer',
|
|
minWidth: 80
|
|
}}
|
|
/>
|
|
</Tooltip> */}
|
|
<Tooltip title="Cumulative Kms" placement="top">
|
|
<Chip
|
|
size="small"
|
|
label={row.cumulativekms ? parseFloat(row.cumulativekms).toFixed(2) : 0}
|
|
sx={{
|
|
color: '#ff8f00',
|
|
bgcolor: '#ffecb3',
|
|
border: '1px solid #ff8f00',
|
|
cursor: 'pointer',
|
|
minWidth: 80
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
</Stack>
|
|
</TableCell>
|
|
<TableCell align="right">
|
|
<Stack direction={'row'}>
|
|
<Tooltip title="Pay on Delivery" placement="top">
|
|
<Chip
|
|
size="small"
|
|
label={formatNumberToRupees(row.payondelivery)}
|
|
sx={{
|
|
color: '#f44336',
|
|
bgcolor: '#ffcdd2',
|
|
border: '1px solid #f44336',
|
|
cursor: 'pointer',
|
|
minWidth: 80,
|
|
mr: 1
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
<Tooltip title="Pay Later" placement="bottom">
|
|
<Chip
|
|
size="small"
|
|
label={formatNumberToRupees(row.Paylater)}
|
|
sx={{
|
|
color: '#43a047',
|
|
bgcolor: '#c8e6c9',
|
|
border: '1px solid #43a047',
|
|
cursor: 'pointer',
|
|
minWidth: 80
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
</Stack>
|
|
</TableCell>
|
|
<TableCell align="left">
|
|
<Tooltip title="Total Charges" placement="top">
|
|
<Chip
|
|
size="small"
|
|
label={formatNumberToRupees(row.deliveryamt)}
|
|
sx={{
|
|
color: '#1976d2',
|
|
bgcolor: '#e3f2fd',
|
|
border: '1px solid #1976d2',
|
|
cursor: 'pointer',
|
|
minWidth: 100
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
</TableCell>
|
|
<TableCell align="center">
|
|
<IconButton
|
|
aria-label="expand row"
|
|
size="small"
|
|
onClick={() => {
|
|
fetchTenantSummary(row.userid);
|
|
setOpenRow(openRow === row.userid ? null : row.userid);
|
|
}}
|
|
sx={{
|
|
bgcolor: openRow === row.userid ? 'primary.main' : null,
|
|
color: openRow === row.userid ? 'white' : null,
|
|
'&:hover': {
|
|
bgcolor: openRow === row.userid ? 'primary.main' : '#e1bee7'
|
|
}
|
|
}}
|
|
>
|
|
{openRow === row.userid ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
|
|
</IconButton>
|
|
</TableCell>
|
|
</TableRow>
|
|
{/* // ============================================ || collapsive row || ============================================ */}
|
|
{openRow === row.userid && (
|
|
<TableRow
|
|
sx={{
|
|
bgcolor: openRow === row.userid ? 'white' : null,
|
|
'&:hover': {
|
|
bgcolor: openRow === row.userid ? 'white!important' : null
|
|
},
|
|
cursor: openRow === row.userid ? 'pointer' : null
|
|
}}
|
|
>
|
|
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={15}>
|
|
<Collapse in={true} timeout="auto" unmountOnExit>
|
|
<Box sx={{ margin: 1 }}>
|
|
<Table size="small" aria-label="purchases">
|
|
<TableHead>
|
|
<TableRow
|
|
sx={{
|
|
bgcolor: theme.palette.secondary.lighter,
|
|
|
|
'&:hover': {
|
|
bgcolor: `${theme.palette.secondary.lighter} !important`
|
|
}
|
|
}}
|
|
>
|
|
<TableCell>#</TableCell>
|
|
<TableCell>Location</TableCell>
|
|
<TableCell align="left">All</TableCell>
|
|
<TableCell align="left">Pending</TableCell>
|
|
<TableCell align="left">Completed</TableCell>
|
|
<TableCell align="left">Cancelled</TableCell>
|
|
<TableCell align="left"> Kms</TableCell>
|
|
<TableCell align="left">COD / PLA</TableCell>
|
|
<TableCell align="left">Amount</TableCell>
|
|
</TableRow>
|
|
</TableHead>
|
|
<TableBody>
|
|
{tenantData?.map((row, index) => (
|
|
<TableRow
|
|
sx={{
|
|
bgcolor: 'white!important',
|
|
'&:onhover': {
|
|
bgcolor: 'white!important'
|
|
}
|
|
}}
|
|
key={row.tenantname}
|
|
>
|
|
<TableCell component="th" scope="row" padding="none">
|
|
{index + 1}
|
|
</TableCell>
|
|
<TableCell align="left">
|
|
<Stack sx={{ ml: -2 }}>
|
|
<Typography> {row.locationname}</Typography>
|
|
<Typography variant="body2"> Id: {row.locationid}</Typography>
|
|
</Stack>
|
|
</TableCell>
|
|
<TableCell align="center" sx={{ color: getValueColor(row.totalorders) }}>
|
|
{row.totalorders}
|
|
</TableCell>
|
|
<TableCell align="center" sx={{ color: getValueColor(row.deliveriespending) }}>
|
|
{row.deliveriespending}
|
|
</TableCell>
|
|
<TableCell align="center" sx={{ color: getValueColor(row.deliveriescompleted) }}>
|
|
{row.deliveriescompleted}
|
|
</TableCell>
|
|
<TableCell align="center" sx={{ color: getValueColor(row.deliveriescancelled) }}>
|
|
{row.deliveriescancelled}
|
|
</TableCell>
|
|
<TableCell align="left">
|
|
{/* <Chip
|
|
size="small"
|
|
label={row.kms}
|
|
sx={{
|
|
color: '#1976d2',
|
|
bgcolor: '#e3f2fd',
|
|
mr: 1,
|
|
border: '1px solid #1976d2',
|
|
minWidth: 80
|
|
}}
|
|
/> */}
|
|
<Tooltip title="Cumulative Kms" placement="top">
|
|
<Chip
|
|
size="small"
|
|
label={row.cumulativekms ? parseFloat(row.cumulativekms).toFixed(2) : 0}
|
|
sx={{
|
|
color: '#ff8f00',
|
|
bgcolor: '#ffecb3',
|
|
border: '1px solid #ff8f00',
|
|
minWidth: 80
|
|
}}
|
|
/>
|
|
</Tooltip>
|
|
</TableCell>
|
|
|
|
<TableCell align="left">
|
|
<Chip
|
|
size="small"
|
|
label={formatNumberToRupees(row.payondelivery)}
|
|
sx={{
|
|
color: '#f44336',
|
|
bgcolor: '#ffcdd2',
|
|
border: '1px solid #f44336',
|
|
minWidth: 80
|
|
}}
|
|
/>{' '}
|
|
<Chip
|
|
size="small"
|
|
label={formatNumberToRupees(row.paylater)}
|
|
sx={{
|
|
color: '#43a047',
|
|
bgcolor: '#c8e6c9',
|
|
border: '1px solid #43a047',
|
|
minWidth: 80
|
|
}}
|
|
/>
|
|
</TableCell>
|
|
|
|
<TableCell align="left">
|
|
<Chip
|
|
size="small"
|
|
label={formatNumberToRupees(row.charges)}
|
|
sx={{
|
|
color: 'primary.main',
|
|
bgcolor: '#e1bee7',
|
|
border: '1px solid #662582 ',
|
|
minWidth: 100
|
|
}}
|
|
/>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</Box>
|
|
</Collapse>
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
</>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</TableContainer>
|
|
<Divider />
|
|
{/* <Stack direction={'row'} sx={{ display: 'flex', justifyContent: 'end', px: 1, py: 2 }}>
|
|
<Typography variant="h5">Total :</Typography>
|
|
<Typography variant="h5" sx={{ ml: 5, mr: 1.5 }}>
|
|
{formatNumberToRupees(total)}
|
|
</Typography>
|
|
</Stack> */}
|
|
</MainCard>
|
|
{/* ================================================ || Date Filter || ================================================ */}
|
|
<Dialog open={open}>
|
|
<DialogTitle align="left">
|
|
<Typography variant="h4">Select Filter Options</Typography>
|
|
</DialogTitle>
|
|
<DialogContent sx={{ width: '100%' }} className="datedialog">
|
|
<DateRangePicker
|
|
open={open}
|
|
toggle={() => setOpen(!open)}
|
|
id="daterange1"
|
|
onChange={(range) => {
|
|
if (range.label === 'All') {
|
|
setDateselect('all');
|
|
setStartdate('');
|
|
setEnddate('');
|
|
setOpen(false);
|
|
} else {
|
|
setDateselect('select');
|
|
setStartdate(dayjs(range.startDate).format('YYYY-MM-DD'));
|
|
setEnddate(dayjs(range.endDate).format('YYYY-MM-DD'));
|
|
if (range.label) {
|
|
setDatestatus(range.label);
|
|
} else {
|
|
setDatestatus('');
|
|
}
|
|
}
|
|
console.log(range);
|
|
}}
|
|
definedRanges={[
|
|
{
|
|
label: 'Today',
|
|
startDate: new Date(),
|
|
endDate: new Date()
|
|
},
|
|
{
|
|
label: 'Yesterday',
|
|
startDate: addDays(new Date(), -1),
|
|
endDate: addDays(new Date(), -1)
|
|
},
|
|
{
|
|
label: 'Tomorrow',
|
|
startDate: addDays(new Date(), +1),
|
|
endDate: addDays(new Date(), +1)
|
|
},
|
|
{
|
|
label: 'This Week',
|
|
startDate: startOfWeek(new Date()),
|
|
endDate: endOfWeek(new Date())
|
|
},
|
|
{
|
|
label: 'Last Week',
|
|
startDate: startOfWeek(addWeeks(new Date(), -1)),
|
|
endDate: endOfWeek(addWeeks(new Date(), -1))
|
|
},
|
|
{
|
|
label: 'Last 7 Days',
|
|
startDate: addWeeks(new Date(), -1),
|
|
endDate: new Date()
|
|
},
|
|
{
|
|
label: 'This Month',
|
|
startDate: startOfMonth(new Date()),
|
|
endDate: endOfMonth(new Date())
|
|
},
|
|
{
|
|
label: 'Last Month',
|
|
startDate: startOfMonth(addMonths(new Date(), -1)),
|
|
endDate: endOfMonth(addMonths(new Date(), -1))
|
|
},
|
|
{
|
|
label: 'All',
|
|
startDate: new Date(),
|
|
endDate: addDays(new Date(), -1)
|
|
}
|
|
]}
|
|
/>
|
|
</DialogContent>
|
|
<Stack direction="row" justifyContent="flex-end" sx={{ width: '100%', p: 2 }}>
|
|
<Button variant="contained" size="small" onClick={() => setOpen(false)}>
|
|
ok
|
|
</Button>
|
|
</Stack>
|
|
</Dialog>
|
|
</>
|
|
);
|
|
}
|