feat: real boxes page added
This commit is contained in:
368
src/routes/private/real-boxes/DashboardRealBoxesPage.tsx
Normal file
368
src/routes/private/real-boxes/DashboardRealBoxesPage.tsx
Normal file
@@ -0,0 +1,368 @@
|
||||
'use client';
|
||||
|
||||
import ActionPopMenu from '@/components/common/ActionPopMenu';
|
||||
import { MyTable, ColumnData } from '@/components/common/MyTable';
|
||||
import StatusChangePopup from '@/components/common/StatusChangePopup';
|
||||
import BaseButton from '@/components/ui-kit/BaseButton';
|
||||
import BaseIconButton from '@/components/ui-kit/BaseIconButton';
|
||||
import BaseInput from '@/components/ui-kit/BaseInput';
|
||||
import BasePagination from '@/components/ui-kit/BasePagination';
|
||||
import { useAuthContext } from '@/context/auth-context';
|
||||
import { BoxStatus, BoxStatusList, IBox } from '@/data/box/box.model';
|
||||
import { box_requests } from '@/data/box/box.requests';
|
||||
import { DEFAULT_PAGE_SIZE, pageLinks } from '@/helpers/constants';
|
||||
import useDebouncedInput from '@/hooks/useDebouncedInput';
|
||||
import useInput from '@/hooks/useInput';
|
||||
import { useMyNavigation } from '@/hooks/useMyNavigation';
|
||||
import { useMyTranslation } from '@/hooks/useMyTranslation';
|
||||
import useRequest from '@/hooks/useRequest';
|
||||
import { file_service } from '@/services/file-service';
|
||||
import { notifyUnknownError } from '@/services/notification';
|
||||
import { getBoxStatusStyles, getStatusColor } from '@/theme/getStatusBoxStyles';
|
||||
import { Add, AddCircleOutline, Circle, Delete, Download, Edit, FilterList, FilterListOff, Search, PlusOne } from '@mui/icons-material';
|
||||
import { Box, Button, Stack, Tooltip, Typography } from '@mui/material';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
type Props = {};
|
||||
|
||||
const DashboardRealBoxesPage = (props: Props) => {
|
||||
const t = useMyTranslation();
|
||||
const navigation = useMyNavigation();
|
||||
const { isAdmin } = useAuthContext();
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize] = useState(DEFAULT_PAGE_SIZE);
|
||||
const { value: keyword, onChange: handleKeyword, setValue: setKeyword } = useInput('');
|
||||
const [boxStatusFilter, setBoxStatusFilter] = useState<BoxStatus | undefined>(undefined);
|
||||
|
||||
const [deleteIds, setDeleteIds] = useState<number[]>([]);
|
||||
const [downloadIds, setDownloadIds] = useState<number[]>([]);
|
||||
const [changeStatusIds, setChangeStatusIds] = useState<number[]>([]);
|
||||
|
||||
const boxStatusOptions = useMemo(() => {
|
||||
const p = ['READY_TO_INVOICE'] as BoxStatus[];
|
||||
if (isAdmin) {
|
||||
p.push('READY');
|
||||
}
|
||||
return p;
|
||||
}, [isAdmin]);
|
||||
|
||||
const getBoxesQuery = useRequest(
|
||||
() =>
|
||||
box_requests.getAll({
|
||||
page: page,
|
||||
cargoId: keyword,
|
||||
status: boxStatusFilter,
|
||||
direction: 'desc',
|
||||
sort: 'id',
|
||||
}),
|
||||
{
|
||||
dependencies: [page, boxStatusFilter],
|
||||
selectData(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const {
|
||||
data: list,
|
||||
totalElements,
|
||||
totalPages,
|
||||
} = useMemo(() => {
|
||||
if (getBoxesQuery.data?.data) {
|
||||
return {
|
||||
data: getBoxesQuery.data.data,
|
||||
totalElements: getBoxesQuery.data.totalElements,
|
||||
totalPages: getBoxesQuery.data.totalPages,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
data: [],
|
||||
totalElements: 0,
|
||||
totalPages: 0,
|
||||
};
|
||||
}
|
||||
}, [getBoxesQuery]);
|
||||
const loading = getBoxesQuery.loading;
|
||||
|
||||
const handleChange = (newPage: number) => {
|
||||
setTimeout(() => {
|
||||
setPage(newPage);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
const resetFilter = () => {
|
||||
setPage(1);
|
||||
setKeyword('');
|
||||
setBoxStatusFilter(undefined);
|
||||
};
|
||||
|
||||
const onDelete = async (id: number) => {
|
||||
if (deleteIds.includes(id)) return;
|
||||
|
||||
try {
|
||||
setDeleteIds(p => [...p, id]);
|
||||
await box_requests.delete({ packetId: id });
|
||||
getBoxesQuery.refetch();
|
||||
} catch (error) {
|
||||
notifyUnknownError(error);
|
||||
} finally {
|
||||
setDeleteIds(prev => prev.filter(i => i !== id));
|
||||
}
|
||||
};
|
||||
|
||||
const onDownloadExcel = async (id: number) => {
|
||||
if (downloadIds.includes(id)) return;
|
||||
|
||||
try {
|
||||
setDownloadIds(p => [...p, id]);
|
||||
const response = await box_requests.downloadExcel({ packetId: id });
|
||||
const file = new File([response.data], 'Box-excel.xlsx', { type: response.data.type });
|
||||
file_service.download(file);
|
||||
} catch (error) {
|
||||
notifyUnknownError(error);
|
||||
} finally {
|
||||
setDownloadIds(prev => prev.filter(i => i !== id));
|
||||
}
|
||||
};
|
||||
|
||||
const onChangeStatus = async (id: number, newStatus: BoxStatus) => {
|
||||
if (changeStatusIds.includes(id)) return;
|
||||
|
||||
try {
|
||||
setChangeStatusIds(p => [...p, id]);
|
||||
await box_requests.changeStatus({ packetId: id, status: newStatus });
|
||||
getBoxesQuery.refetch();
|
||||
} catch (error) {
|
||||
notifyUnknownError(error);
|
||||
} finally {
|
||||
setChangeStatusIds(prev => prev.filter(i => i !== id));
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
setPage(1);
|
||||
getBoxesQuery.refetch();
|
||||
}, 350);
|
||||
return () => clearTimeout(timeoutId);
|
||||
}, [keyword]);
|
||||
|
||||
// No, PartyName, PacketName, PartyTozaOg'irlik, CountOfItems, WeightOfItems, CargoID, PassportNameFamily - PacketStatusForInvoice
|
||||
const columns: ColumnData<IBox>[] = [
|
||||
{
|
||||
label: t('No'),
|
||||
width: 100,
|
||||
renderCell(data, rowIndex) {
|
||||
return (page - 1) * pageSize + rowIndex + 1;
|
||||
},
|
||||
},
|
||||
{
|
||||
dataKey: 'id',
|
||||
label: "Qo'shish",
|
||||
width: 120,
|
||||
renderCell: data => {
|
||||
return <Button onClick={() => navigation.push(pageLinks.dashboard.boxes.edit(data.id))}>
|
||||
<Add />
|
||||
</Button>;
|
||||
},
|
||||
},
|
||||
{
|
||||
dataKey: 'partyName',
|
||||
label: t('party_name'),
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
dataKey: 'name',
|
||||
label: t('name'),
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
dataKey: 'packetNetWeight',
|
||||
label: t("weight"),
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
dataKey: 'totalItems',
|
||||
label: t('count_of_items'),
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
dataKey: 'totalNetWeight',
|
||||
label: t("party_weight"),
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
dataKey: 'cargoId',
|
||||
label: t('cargo_id'),
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
dataKey: 'passportName',
|
||||
label: t('client'),
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
dataKey: 'status',
|
||||
label: t('status'),
|
||||
width: 240,
|
||||
renderHeaderCell(rowIndex) {
|
||||
return (
|
||||
<Stack direction={'row'} alignItems={'center'}>
|
||||
<span>{t('box_status')}</span>
|
||||
<ActionPopMenu
|
||||
buttons={BoxStatusList.map(stat => {
|
||||
return {
|
||||
icon: <Circle sx={{ path: { color: getStatusColor(stat) } }} />,
|
||||
label: t(stat),
|
||||
onClick() {
|
||||
setBoxStatusFilter(stat);
|
||||
setPage(1);
|
||||
},
|
||||
};
|
||||
})}
|
||||
mainIcon={<FilterList />}
|
||||
placement={{
|
||||
anchorOrigin: {
|
||||
vertical: 'bottom',
|
||||
horizontal: 'center',
|
||||
},
|
||||
transformOrigin: {
|
||||
horizontal: 'center',
|
||||
vertical: 'top',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
renderCell(data) {
|
||||
return (
|
||||
<StatusChangePopup
|
||||
anchor={{
|
||||
status: data.status,
|
||||
text: t(data.status),
|
||||
}}
|
||||
loading={changeStatusIds.includes(data.id)}
|
||||
buttons={boxStatusOptions.map(stat => {
|
||||
return {
|
||||
icon: (
|
||||
<Circle
|
||||
sx={{
|
||||
path: {
|
||||
color: getStatusColor(stat),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
),
|
||||
label: t(stat),
|
||||
onClick: () => {
|
||||
onChangeStatus(data.id, stat);
|
||||
},
|
||||
};
|
||||
})}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
width: 100,
|
||||
numeric: true,
|
||||
renderCell(data, rowIndex) {
|
||||
return (
|
||||
<ActionPopMenu
|
||||
buttons={[
|
||||
{
|
||||
icon: <Edit sx={{ path: { color: '#3489E4' } }} />,
|
||||
label: t('edit'),
|
||||
onClick: () => {
|
||||
navigation.push(pageLinks.dashboard.boxes.edit(data.id));
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: <Delete sx={{ path: { color: '#3489E4' } }} />,
|
||||
label: t('delete'),
|
||||
onClick: () => {
|
||||
onDelete(data.id);
|
||||
},
|
||||
dontCloseOnClick: true,
|
||||
loading: deleteIds.includes(data.id),
|
||||
},
|
||||
...(data.status === 'READY'
|
||||
? [
|
||||
{
|
||||
icon: <Download sx={{ path: { color: '#3489E4' } }} />,
|
||||
label: t('download_excel'),
|
||||
onClick: () => {
|
||||
onDownloadExcel(data.id);
|
||||
},
|
||||
loading: downloadIds.includes(data.id),
|
||||
dontCloseOnClick: true,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Stack direction={'row'} mb={3} spacing={3}>
|
||||
<BaseButton colorVariant='blue' startIcon={<Add />} href={pageLinks.dashboard.boxes.create}>
|
||||
{t('create_packet')}
|
||||
</BaseButton>
|
||||
</Stack>
|
||||
<Box
|
||||
width={1}
|
||||
mb={3}
|
||||
sx={{
|
||||
padding: '28px',
|
||||
borderRadius: '16px',
|
||||
backgroundColor: '#fff',
|
||||
}}
|
||||
>
|
||||
<Stack mb={3.5} direction={'row'} justifyContent={'space-between'} alignItems={'center'}>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '20px',
|
||||
lineHeight: '24px',
|
||||
fontWeight: 600,
|
||||
textTransform: 'capitalize',
|
||||
color: '#000',
|
||||
}}
|
||||
>
|
||||
{t('packet')}
|
||||
</Typography>
|
||||
<Stack direction={'row'} alignItems={'center'} spacing={2}>
|
||||
<BaseInput
|
||||
InputProps={{
|
||||
startAdornment: <Search color='primary' />,
|
||||
}}
|
||||
placeholder={t('filter_packet_name')}
|
||||
value={keyword}
|
||||
onChange={e => {
|
||||
setKeyword(e.target.value);
|
||||
}}
|
||||
/>
|
||||
|
||||
<BaseButton colorVariant='gray' startIcon={<FilterListOff />} size='small' onClick={resetFilter}>
|
||||
{t('reset_filter')}
|
||||
</BaseButton>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Box mb={6}>
|
||||
<MyTable columns={columns} data={list} loading={loading} />
|
||||
</Box>
|
||||
<Stack direction={'row'} justifyContent={'center'}>
|
||||
<BasePagination page={page} pageSize={pageSize} totalCount={totalElements} onChange={handleChange} />
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardRealBoxesPage;
|
||||
Reference in New Issue
Block a user