added cargoType
This commit is contained in:
@@ -3,9 +3,9 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "cross-env NEXT_PUBLIC_API_URL=https://cpcargo.felixits.uz next dev --port=3080",
|
||||
"dev": "cross-env NEXT_PUBLIC_API_URL=http://141.105.64.233:7723 next dev --port=3080",
|
||||
"build": "cross-env NEXT_PUBLIC_API_URL=https://api.cpcargo.uz next build",
|
||||
"build:dev": "cross-env NEXT_PUBLIC_API_URL=https://cpcargo.felixits.uz next build",
|
||||
"build:dev": "cross-env NEXT_PUBLIC_API_URL=http://141.105.64.233:7723 next build",
|
||||
"build:prod": "cross-env NEXT_PUBLIC_API_URL=https://api.cpcargo.uz next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
|
||||
9
src/app/[locale]/dashboard/forbidden/create/loading.tsx
Normal file
9
src/app/[locale]/dashboard/forbidden/create/loading.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { CircularProgress, Stack } from '@mui/material';
|
||||
|
||||
export default function DashboardLoading() {
|
||||
return (
|
||||
<Stack justifyContent={'center'} alignItems={'center'} p={8}>
|
||||
<CircularProgress size={'64px'} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
21
src/app/[locale]/dashboard/forbidden/create/page.tsx
Normal file
21
src/app/[locale]/dashboard/forbidden/create/page.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
'use client';
|
||||
|
||||
import Loader from '@/components/common/Loader';
|
||||
import { party_requests } from '@/data/party/party.requests';
|
||||
import useRequest from '@/hooks/useRequest';
|
||||
import DashboardCreateForbiddenPage from '@/routes/private/forbidden-create/DashboardCreateForbiddenPage';
|
||||
|
||||
export default function Home() {
|
||||
const partiesData = useRequest(() => party_requests.getAll({ status: 'COLLECTING' }), {
|
||||
selectData(data) {
|
||||
return data.data.data.data.map(p => ({ value: p.id, label: p.name }));
|
||||
},
|
||||
placeholderData: [],
|
||||
});
|
||||
|
||||
if (partiesData.loading || !partiesData.data) {
|
||||
return <Loader p={8} size={96} />;
|
||||
}
|
||||
|
||||
return <DashboardCreateForbiddenPage partiesData={partiesData.data} />;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { CircularProgress, Stack } from '@mui/material';
|
||||
|
||||
export default function DashboardLoading() {
|
||||
return (
|
||||
<Stack justifyContent={'center'} alignItems={'center'} p={8}>
|
||||
<CircularProgress size={'64px'} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import DashboardEditForbidden from '@/routes/private/forbidden-create/DashboardEditForbidden';
|
||||
|
||||
export default function Home() {
|
||||
return <DashboardEditForbidden />;
|
||||
}
|
||||
5
src/app/[locale]/dashboard/forbidden/page.tsx
Normal file
5
src/app/[locale]/dashboard/forbidden/page.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import DashboardForbiddenPage from '@/routes/private/forbidden';
|
||||
|
||||
export default function Home() {
|
||||
return <DashboardForbiddenPage />;
|
||||
}
|
||||
@@ -14,6 +14,7 @@ export interface ColumnData<Data, DataKey = keyof Data> {
|
||||
getSxStyles?: (data: Data) => SxProps<Theme>;
|
||||
renderCell?: (data: Data, rowIndex: number) => React.ReactNode;
|
||||
renderHeaderCell?: (rowIndex: number) => React.ReactNode;
|
||||
isComplated?: (data: Data, rowIndex: number) => any;
|
||||
}
|
||||
|
||||
const StyledTable = styled(Table)`
|
||||
@@ -63,17 +64,19 @@ type Props<Data> = {
|
||||
loading: boolean;
|
||||
onClickRow?: (data: Data) => void;
|
||||
color?: string;
|
||||
disableSortingAndStatusFetch?: boolean;
|
||||
};
|
||||
|
||||
const MyTable = <Data extends { id: number | string }>(props: Props<Data>) => {
|
||||
const MyTable = <Data extends { id: number | string }>({ disableSortingAndStatusFetch = true, ...props }: Props<Data>) => {
|
||||
const { columns, data, loading, onClickRow } = props;
|
||||
|
||||
const isEmpty = !data?.length && !loading;
|
||||
|
||||
const [boxStatuses, setBoxStatuses] = React.useState<Record<string, boolean>>({});
|
||||
|
||||
// Enhanced sorting: completed boxes (green) go to the bottom
|
||||
const sortedData = React.useMemo(() => {
|
||||
if (disableSortingAndStatusFetch) return data;
|
||||
|
||||
return [...data].sort((a: any, b: any) => {
|
||||
const aPrint = a.print ? 1 : 0;
|
||||
const bPrint = b.print ? 1 : 0;
|
||||
@@ -91,11 +94,11 @@ const MyTable = <Data extends { id: number | string }>(props: Props<Data>) => {
|
||||
|
||||
return Number(a.id) - Number(b.id); // fallback sort
|
||||
});
|
||||
}, [data, boxStatuses]);
|
||||
}, [data, boxStatuses, disableSortingAndStatusFetch]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const fetchBoxStatuses = async () => {
|
||||
if (!data.length || loading) return;
|
||||
if (disableSortingAndStatusFetch || !data.length || loading) return;
|
||||
|
||||
const statuses: Record<string, boolean> = {};
|
||||
|
||||
@@ -131,7 +134,7 @@ const MyTable = <Data extends { id: number | string }>(props: Props<Data>) => {
|
||||
};
|
||||
|
||||
fetchBoxStatuses();
|
||||
}, [data, loading]);
|
||||
}, [data, loading, disableSortingAndStatusFetch]);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
@@ -168,35 +171,30 @@ const MyTable = <Data extends { id: number | string }>(props: Props<Data>) => {
|
||||
</StyledTableRow>
|
||||
) : (
|
||||
sortedData.map((row: any, rowIndex) => {
|
||||
const isCompleted = boxStatuses[row.id];
|
||||
|
||||
const isCompleted = columns.some(col => col.isComplated?.(row, rowIndex)) || boxStatuses[row.id];
|
||||
return (
|
||||
<StyledTableRow
|
||||
key={row.id}
|
||||
sx={{
|
||||
background: row.print
|
||||
? '#dbeafe' // ko‘k rang (bg-blue-100)
|
||||
? '#dbeafe'
|
||||
: !row.hasInvoice
|
||||
? 'inherit'
|
||||
: isCompleted
|
||||
? '#dcfce7' // yashil (bg-green-100)
|
||||
: '#fef2f2', // qizil (bg-red-100)
|
||||
? '#c5ffc0ff'
|
||||
: '#fef2f2',
|
||||
borderLeft: !row.hasInvoice
|
||||
? 'inherit'
|
||||
: row.print
|
||||
? '4px solid #3b82f6' // ko‘k chegara (border-blue-500)
|
||||
? '4px solid #3b82f6'
|
||||
: isCompleted
|
||||
? '4px solid #22c55e' // yashil chegara
|
||||
: '4px solid #ef4444', // qizil chegara
|
||||
? '4px solid #22c55e'
|
||||
: '4px solid #ef4444',
|
||||
...(onClickRow && !row.hasInvoice
|
||||
? {
|
||||
cursor: 'pointer',
|
||||
'&:hover': {
|
||||
backgroundColor: row.print
|
||||
? '#bfdbfe' // hover ko‘k (bg-blue-200)
|
||||
: isCompleted
|
||||
? '#bbf7d0' // hover yashil (bg-green-200)
|
||||
: '#fee2e2', // hover qizil (bg-red-200)
|
||||
backgroundColor: row.print ? '#bfdbfe' : isCompleted ? '#1ea34e' : '#fee2e2',
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
|
||||
@@ -126,6 +126,56 @@ export const routes = [
|
||||
</svg>
|
||||
</SvgIcon>
|
||||
),
|
||||
roles: [UserRoleEnum.ADMIN, UserRoleEnum.CHINA_WORKER],
|
||||
roles: [UserRoleEnum.ADMIN],
|
||||
},
|
||||
// {
|
||||
// title: 'Taqiqlangan buyumlar',
|
||||
// path: pageLinks.dashboard.forbidden.index,
|
||||
// icon: (
|
||||
// <SvgIcon fontSize='small' style={{ width: '28px', height: '28px' }}>
|
||||
// <svg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 24 24' fill='none'>
|
||||
// <path
|
||||
// d='M3.16992 7.43994L11.9999 12.5499L20.7699 7.46991'
|
||||
// stroke='currentColor'
|
||||
// stroke-width='1.5'
|
||||
// stroke-linecap='round'
|
||||
// stroke-linejoin='round'
|
||||
// />
|
||||
// <path d='M12 21.61V12.54' stroke='currentColor' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' />
|
||||
// <path
|
||||
// d='M21.6101 9.17V14.83C21.6101 14.88 21.6101 14.92 21.6001 14.97C20.9001 14.36 20.0001 14 19.0001 14C18.0601 14 17.1901 14.33 16.5001 14.88C15.5801 15.61 15.0001 16.74 15.0001 18C15.0001 18.75 15.2101 19.46 15.5801 20.06C15.6701 20.22 15.7801 20.37 15.9001 20.51L14.0701 21.52C12.9301 22.16 11.0701 22.16 9.93011 21.52L4.59012 18.56C3.38012 17.89 2.39014 16.21 2.39014 14.83V9.17C2.39014 7.79 3.38012 6.11002 4.59012 5.44002L9.93011 2.48C11.0701 1.84 12.9301 1.84 14.0701 2.48L19.4101 5.44002C20.6201 6.11002 21.6101 7.79 21.6101 9.17Z'
|
||||
// stroke='currentColor'
|
||||
// stroke-width='1.5'
|
||||
// stroke-linecap='round'
|
||||
// stroke-linejoin='round'
|
||||
// />
|
||||
// <path
|
||||
// d='M23 18C23 18.75 22.79 19.46 22.42 20.06C22.21 20.42 21.94 20.74 21.63 21C20.93 21.63 20.01 22 19 22C17.54 22 16.27 21.22 15.58 20.06C15.21 19.46 15 18.75 15 18C15 16.74 15.58 15.61 16.5 14.88C17.19 14.33 18.06 14 19 14C21.21 14 23 15.79 23 18Z'
|
||||
// stroke='currentColor'
|
||||
// stroke-width='1.5'
|
||||
// stroke-miterlimit='10'
|
||||
// stroke-linecap='round'
|
||||
// stroke-linejoin='round'
|
||||
// />
|
||||
// <path
|
||||
// d='M20.0702 19.0399L17.9502 16.9299'
|
||||
// stroke='currentColor'
|
||||
// stroke-width='1.5'
|
||||
// stroke-miterlimit='10'
|
||||
// stroke-linecap='round'
|
||||
// stroke-linejoin='round'
|
||||
// />
|
||||
// <path
|
||||
// d='M20.0502 16.96L17.9302 19.0699'
|
||||
// stroke='currentColor'
|
||||
// stroke-width='1.5'
|
||||
// stroke-miterlimit='10'
|
||||
// stroke-linecap='round'
|
||||
// stroke-linejoin='round'
|
||||
// />
|
||||
// </svg>
|
||||
// </SvgIcon>
|
||||
// ),
|
||||
// roles: [UserRoleEnum.ADMIN],
|
||||
// },
|
||||
];
|
||||
|
||||
@@ -56,6 +56,7 @@ export interface IBoxDetail {
|
||||
amount: number;
|
||||
weight: number;
|
||||
acceptedNumber: number;
|
||||
cargoType: 'AUTO' | 'AVIA';
|
||||
price: number;
|
||||
totalPrice: number;
|
||||
hasImage: boolean;
|
||||
@@ -70,12 +71,14 @@ export type CreateBoxBodyType = {
|
||||
status: BoxStatus;
|
||||
cargoId: string;
|
||||
passportId: string;
|
||||
cargoType: 'AUTO' | 'AVIA';
|
||||
partyId: string;
|
||||
items: {
|
||||
trekId: string;
|
||||
name: string;
|
||||
amount: number;
|
||||
weight: number;
|
||||
cargoType: 'AUTO' | 'AVIA';
|
||||
}[];
|
||||
};
|
||||
|
||||
@@ -86,6 +89,7 @@ export type UpdateBoxBodyType = {
|
||||
print?: boolean;
|
||||
// clientId: number;
|
||||
cargoId?: string;
|
||||
cargoType: 'AUTO' | 'AVIA';
|
||||
|
||||
// type: string;
|
||||
// name: string;
|
||||
@@ -98,7 +102,7 @@ export type UpdateBoxBodyType = {
|
||||
name: string;
|
||||
amount: number;
|
||||
weight: number;
|
||||
|
||||
cargoType: 'AUTO' | 'AVIA';
|
||||
id: number;
|
||||
price: number;
|
||||
totalPrice: number;
|
||||
|
||||
@@ -12,6 +12,7 @@ export const box_requests = {
|
||||
cargoId?: string;
|
||||
partyId?: string | number;
|
||||
status?: BoxStatus;
|
||||
cargoType?: 'AVIA' | 'AUTO';
|
||||
}) {
|
||||
return request.get<CommonResponseType<PageAble<IBox>>>('/packets/list', { params });
|
||||
},
|
||||
|
||||
@@ -10,6 +10,7 @@ export type Product = {
|
||||
nameRu: string;
|
||||
amount: number;
|
||||
weight: number;
|
||||
cargoType: 'AUTO' | 'AVIA';
|
||||
price?: number;
|
||||
packetName: string;
|
||||
totalPrice?: number;
|
||||
@@ -26,6 +27,7 @@ export type UpdateProductBodyType = {
|
||||
itemId: string | number;
|
||||
trekId: string;
|
||||
name: string;
|
||||
cargoType: 'AUTO' | 'AVIA';
|
||||
nameRu: string;
|
||||
amount: number;
|
||||
weight: number;
|
||||
|
||||
@@ -10,6 +10,7 @@ export const item_requests = {
|
||||
name?: string;
|
||||
trekId?: string | number;
|
||||
page?: number;
|
||||
cargoType?: 'AVIA' | 'AUTO';
|
||||
sort?: string;
|
||||
status?: BoxStatus;
|
||||
direction?: 'asc' | 'desc';
|
||||
|
||||
@@ -3,6 +3,7 @@ export type Party = {
|
||||
name: string;
|
||||
partyStatus: PartyStatus;
|
||||
totalBoxes: number;
|
||||
cargoType: 'AUTO' | 'AVIA';
|
||||
};
|
||||
|
||||
export type PartyStatus = 'COLLECTING' | 'ON_THE_WAY' | 'IN_CUSTOMS' | 'IN_WAREHOUSE' | 'ARRIVED' | 'DELIVERED';
|
||||
|
||||
@@ -3,19 +3,26 @@ import { CommonResponseType, PageAble } from '@/helpers/types';
|
||||
import { request } from '@/services/request';
|
||||
|
||||
export const party_requests = {
|
||||
async getAll(params?: { page?: number; sort?: string; direction?: string; partyName?: string; status?: PartyStatus }) {
|
||||
async getAll(params?: {
|
||||
page?: number;
|
||||
sort?: string;
|
||||
direction?: string;
|
||||
partyName?: string;
|
||||
status?: PartyStatus;
|
||||
cargoType?: 'AUTO' | 'AVIA';
|
||||
}) {
|
||||
return request.get<CommonResponseType<PageAble<Party>>>('/parties/list', { params });
|
||||
},
|
||||
async changeStatus(params: { partyId: string | number; status: PartyStatus }) {
|
||||
return request.put('/parties/change', undefined, { params });
|
||||
},
|
||||
async create(body: { name: string }) {
|
||||
async create(body: { name: string; cargoType: 'AUTO' | 'AVIA' }) {
|
||||
return request.post<CommonResponseType>('/parties/create', body);
|
||||
},
|
||||
async update(body: { id: number | string; name: string }) {
|
||||
async update(body: { id: number | string; name: string; cargoType: 'AUTO' | 'AVIA' }) {
|
||||
return request.put<CommonResponseType<Party[]>>(
|
||||
'/parties/update',
|
||||
{ name: body.name },
|
||||
{ name: body.name, cargoType: body.cargoType },
|
||||
{
|
||||
params: {
|
||||
partyId: body.id,
|
||||
|
||||
@@ -26,6 +26,7 @@ export interface IRealBoxDetail {
|
||||
|
||||
export interface RealCreateBoxBodyType {
|
||||
partyName: string;
|
||||
cargoType: 'AUTO' | 'AVIA';
|
||||
packetItemDtos: { packetId: number; itemDtos: number[] }[];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { IBox, UpdateBoxBodyType, IBoxDetail, BoxStatus } from '@/data/box/box.model';
|
||||
import { BoxStatus } from '@/data/box/box.model';
|
||||
import { CommonResponseType, PageAble } from '@/helpers/types';
|
||||
import { request } from '@/services/request';
|
||||
import axios from 'axios';
|
||||
import { IRealBox, IRealBoxDetail, RealCreateBoxBodyType, UpdateRealBoxBodyType } from './real-box.model';
|
||||
import { IRealBox, RealCreateBoxBodyType, UpdateRealBoxBodyType } from './real-box.model';
|
||||
|
||||
export const real_box_requests = {
|
||||
async getAll(params?: {
|
||||
@@ -10,9 +10,10 @@ export const real_box_requests = {
|
||||
sort?: string;
|
||||
direction?: string;
|
||||
cargoId?: string;
|
||||
boxName?: string,
|
||||
boxName?: string;
|
||||
partyId?: string | number;
|
||||
status?: BoxStatus;
|
||||
cargoType?: 'AVIA' | 'AUTO';
|
||||
}) {
|
||||
return request.get<CommonResponseType<PageAble<IRealBox>>>('/boxes/list', { params });
|
||||
},
|
||||
@@ -25,13 +26,13 @@ export const real_box_requests = {
|
||||
});
|
||||
},
|
||||
async find(params: { boxId?: number | string }) {
|
||||
return request.get<any>(`/boxes/find/${params.boxId}`,);
|
||||
return request.get<any>(`/boxes/find/${params.boxId}`);
|
||||
},
|
||||
async delete(params: { boxId: number | string }) {
|
||||
return request.delete<CommonResponseType>('/boxes/delete', { params });
|
||||
},
|
||||
async downloadExcel(params: { boxId: number | string }) {
|
||||
return request.get<Blob>(`/boxes/download/packets/${params.boxId}`, { responseType: "blob" });
|
||||
return request.get<Blob>(`/boxes/download/packets/${params.boxId}`, { responseType: 'blob' });
|
||||
},
|
||||
async downloadQrCode(params: { boxId: number | string }) {
|
||||
return request.get(`/qr/${params.boxId}`, { responseType: 'blob' });
|
||||
|
||||
@@ -5,4 +5,5 @@ export type CreateStaffBodyType = {
|
||||
role: string;
|
||||
phone: string;
|
||||
address: string;
|
||||
cargoType: 'AUTO' | 'AVIA';
|
||||
};
|
||||
|
||||
@@ -13,4 +13,5 @@ export type User = {
|
||||
address?: string;
|
||||
role: UserRoleEnum;
|
||||
active: boolean;
|
||||
cargoType: 'AUTO' | 'AVIA';
|
||||
};
|
||||
|
||||
@@ -37,6 +37,11 @@ export const pageLinks = {
|
||||
customers: {
|
||||
index: '/dashboard/customers',
|
||||
},
|
||||
forbidden: {
|
||||
index: '/dashboard/forbidden',
|
||||
create: '/dashboard/forbidden/create',
|
||||
edit: (slug: string | number) => '/dashboard/forbidden/edit/' + slug,
|
||||
},
|
||||
staffs: {
|
||||
index: '/dashboard/staffs',
|
||||
},
|
||||
|
||||
@@ -74,6 +74,7 @@ const DashboardAcceptancePage = () => {
|
||||
const searchParams = useSearchParams();
|
||||
const boxId = searchParams.get('boxId');
|
||||
const { boxesIdAccepted, setBoxIdAccepted } = useBoxIdStore();
|
||||
const [cargoType, setCargoType] = useState<'AUTO' | 'AVIA'>('AVIA');
|
||||
|
||||
// Print related state
|
||||
const [selectedBoxForPrint, setSelectedBoxForPrint] = useState<IBox | null>(null);
|
||||
@@ -91,12 +92,13 @@ const DashboardAcceptancePage = () => {
|
||||
});
|
||||
|
||||
// Fetch party options
|
||||
const { data: defaultPartyOptions } = useRequest(() => party_requests.getAll({}), {
|
||||
const { data: defaultPartyOptions } = useRequest(() => party_requests.getAll({ cargoType }), {
|
||||
enabled: true,
|
||||
selectData(data) {
|
||||
return data.data.data.data.map(p => ({ value: p.id, label: p.name }));
|
||||
},
|
||||
placeholderData: [],
|
||||
dependencies: [cargoType],
|
||||
});
|
||||
|
||||
const partyOptions = (inputValue: string) => {
|
||||
@@ -170,10 +172,11 @@ const DashboardAcceptancePage = () => {
|
||||
partyId: partyFilter?.value,
|
||||
status: boxStatusFilter,
|
||||
direction: 'desc',
|
||||
cargoType,
|
||||
sort: 'id',
|
||||
}),
|
||||
{
|
||||
dependencies: [page, boxStatusFilter, keyword, partyFilter],
|
||||
dependencies: [page, cargoType, boxStatusFilter],
|
||||
selectData(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
@@ -202,10 +205,11 @@ const DashboardAcceptancePage = () => {
|
||||
page: page,
|
||||
trekId: trackId,
|
||||
packetId: boxFilter?.value,
|
||||
cargoType,
|
||||
partyId: partyFilter?.value,
|
||||
}),
|
||||
{
|
||||
dependencies: [page, trackId, boxFilter?.value, partyFilter?.value],
|
||||
dependencies: [page, trackId, boxFilter?.value, partyFilter?.value, cargoType],
|
||||
selectData(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
@@ -251,6 +255,7 @@ const DashboardAcceptancePage = () => {
|
||||
() =>
|
||||
box_requests.getAll({
|
||||
partyId: partyFilter?.value,
|
||||
cargoType,
|
||||
}),
|
||||
{
|
||||
enabled: !!partyFilter,
|
||||
@@ -258,7 +263,7 @@ const DashboardAcceptancePage = () => {
|
||||
return data.data.data.data.map(p => ({ value: p.id, label: p.name }));
|
||||
},
|
||||
placeholderData: [],
|
||||
dependencies: [partyFilter],
|
||||
dependencies: [partyFilter, cargoType],
|
||||
}
|
||||
);
|
||||
|
||||
@@ -289,6 +294,7 @@ const DashboardAcceptancePage = () => {
|
||||
packetId: String(res.data.data.packet.id),
|
||||
passportId: res.data.data.client.passportId,
|
||||
status: res.data.data.packet.status,
|
||||
cargoType: res.data.data.items[0].cargoType,
|
||||
});
|
||||
setPrintStatuses(prev => ({
|
||||
...prev,
|
||||
@@ -436,6 +442,12 @@ const DashboardAcceptancePage = () => {
|
||||
dataKey: 'totalItems',
|
||||
label: t('count_of_items'),
|
||||
width: 120,
|
||||
isComplated: data => {
|
||||
const total = boxAmounts[data.id];
|
||||
if (!total) return <Typography>...</Typography>;
|
||||
const isCompleted = total.totalAmount === total.totalAccepted && total.totalAmount > 0;
|
||||
return isCompleted;
|
||||
},
|
||||
renderCell: data => {
|
||||
const total = boxAmounts[data.id];
|
||||
|
||||
@@ -512,7 +524,33 @@ const DashboardAcceptancePage = () => {
|
||||
value={currentValue}
|
||||
onChange={async e => {
|
||||
const newValue = e.target.value as PrintStatus;
|
||||
|
||||
// Local state yangilanadi
|
||||
setPrintStatuses(prev => ({
|
||||
...prev,
|
||||
[data.id]: newValue,
|
||||
}));
|
||||
|
||||
// Callback chaqiriladi
|
||||
onChangePrint(data.id, newValue);
|
||||
|
||||
// Serverga request yuboriladi
|
||||
try {
|
||||
const res = await box_requests.find({ packetId: data.id });
|
||||
await box_requests.update({
|
||||
cargoId: String(res.data.data.packet.cargoId),
|
||||
items: res.data.data.items,
|
||||
print: newValue === 'true' ? true : newValue === 'false' && false,
|
||||
packetId: String(res.data.data.packet.id),
|
||||
passportId: res.data.data.client.passportId,
|
||||
status: res.data.data.packet.status,
|
||||
cargoType: res.data.data.items[0].cargoType,
|
||||
});
|
||||
refetch();
|
||||
getBoxesQuery.refetch();
|
||||
} catch (error) {
|
||||
console.error('Print status update failed:', error);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MenuItem value='true'>Chop etildi</MenuItem>
|
||||
@@ -579,6 +617,7 @@ const DashboardAcceptancePage = () => {
|
||||
nameRu: item.nameRu,
|
||||
trekId: item.trekId,
|
||||
weight: item.weight,
|
||||
cargoType: item.cargoType,
|
||||
};
|
||||
await item_requests.update(updateBody);
|
||||
getListQuery.refetch();
|
||||
@@ -693,6 +732,20 @@ const DashboardAcceptancePage = () => {
|
||||
loadOptions={partyOptions}
|
||||
placeholder={t('filter_party_name')}
|
||||
/>
|
||||
<FormControl size='small' sx={{ minWidth: 200 }}>
|
||||
<Select
|
||||
value={cargoType ?? ''}
|
||||
onChange={e => {
|
||||
const value = e.target.value || undefined;
|
||||
setCargoType(value as 'AVIA' | 'AUTO');
|
||||
setPage(1);
|
||||
}}
|
||||
displayEmpty
|
||||
>
|
||||
<MenuItem value='AVIA'>AVIA</MenuItem>
|
||||
<MenuItem value='AUTO'>AUTO</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<BaseButton colorVariant='gray' startIcon={<FilterListOff />} size='small' onClick={resetFilter}>
|
||||
{t('reset_filter')}
|
||||
</BaseButton>
|
||||
|
||||
@@ -20,7 +20,7 @@ import { useMyTranslation } from '@/hooks/useMyTranslation';
|
||||
import useRequest from '@/hooks/useRequest';
|
||||
import { notifyError, notifyUnknownError } from '@/services/notification';
|
||||
import { AddCircleRounded, Close } from '@mui/icons-material';
|
||||
import { Box, Divider, FormHelperText, Grid, Stack, Typography, styled } from '@mui/material';
|
||||
import { Box, Divider, FormControl, FormHelperText, Grid, MenuItem, Select, Stack, Typography, styled } from '@mui/material';
|
||||
import get from 'lodash.get';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
@@ -67,7 +67,7 @@ type Props = {
|
||||
products_list: {
|
||||
id: number;
|
||||
price: number | string;
|
||||
|
||||
cargoType: 'AUTO' | 'AVIA';
|
||||
cargoId: string;
|
||||
trekId: string;
|
||||
name: string;
|
||||
@@ -79,7 +79,7 @@ type Props = {
|
||||
};
|
||||
|
||||
const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
const [cargoIdValue, setCargoIdValue] = useState<string>('');
|
||||
const [cargoType, setCargoType] = useState<'AUTO' | 'AVIA'>('AVIA');
|
||||
const { user, isAdmin: isAdminUser } = useAuthContext();
|
||||
const editMode = !!initialValues && !!initialValues.id;
|
||||
const isAdmin = isAdminUser && editMode;
|
||||
@@ -193,7 +193,13 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
setValue('passportId', 'AA1234567');
|
||||
}, [cargoId]);
|
||||
|
||||
const { data: defaultParties, status: defaultPartiesStatus } = useRequest(() => party_requests.getAll({ status: 'COLLECTING' }), {
|
||||
const { data: defaultParties, status: defaultPartiesStatus } = useRequest(
|
||||
() =>
|
||||
party_requests.getAll({
|
||||
status: 'COLLECTING',
|
||||
cargoType: user?.role === 'ADMIN' ? cargoType : user?.cargoType || 'AVIA',
|
||||
}),
|
||||
{
|
||||
enabled: true,
|
||||
selectData(data) {
|
||||
return data.data.data.data.map(p => ({ value: p.id, label: p.name }));
|
||||
@@ -208,8 +214,34 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
helperRef.current.finished = true;
|
||||
}
|
||||
},
|
||||
dependencies: [cargoType, initialValues],
|
||||
placeholderData: [],
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const { data: editParties } = useRequest(
|
||||
() =>
|
||||
party_requests.find({
|
||||
partyId: initialValues?.partyId!,
|
||||
}),
|
||||
{
|
||||
enabled: true,
|
||||
selectData(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
onSuccess(data) {
|
||||
if (!editMode && data?.data?.data) {
|
||||
helperRef.current.settedDefaultParty = data.data.data;
|
||||
setValue('partyId', data.data.data.id);
|
||||
}
|
||||
helperRef.current.partyFinished = true;
|
||||
if (helperRef.current.clientFinished) {
|
||||
helperRef.current.finished = true;
|
||||
}
|
||||
},
|
||||
dependencies: [initialValues],
|
||||
}
|
||||
);
|
||||
|
||||
const { data: defaultClients } = useRequest(
|
||||
() =>
|
||||
@@ -256,6 +288,7 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
cargoId: values.client_id,
|
||||
passportId: values.passportId?.value || values.passport_id,
|
||||
status: values.status,
|
||||
cargoType: editParties?.cargoType!,
|
||||
packetId: initialValues?.packetId,
|
||||
items: values.products_list.map((item: any) => {
|
||||
const _price = +item.price ? +item.price : 0;
|
||||
@@ -269,6 +302,7 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
nameRu: item?.nameRu,
|
||||
weight: +item.weight,
|
||||
amount: +item.amount,
|
||||
cargoType: editParties?.cargoType!,
|
||||
price: _price,
|
||||
totalPrice: _total_price,
|
||||
};
|
||||
@@ -293,6 +327,7 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
const createBody: CreateBoxBodyType = {
|
||||
status: values.status,
|
||||
cargoId: values.cargoId,
|
||||
cargoType: user?.role === 'ADMIN' ? cargoType : user?.cargoType || 'AVIA',
|
||||
passportId: values.passportId.value,
|
||||
partyId: values.partyId,
|
||||
items: values.products_list.map((item: any) => {
|
||||
@@ -301,6 +336,7 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
name: item.name,
|
||||
weight: +item.weight,
|
||||
amount: +item.amount,
|
||||
cargoType: user?.role === 'ADMIN' ? cargoType : user?.cargoType || 'AVIA',
|
||||
};
|
||||
}),
|
||||
};
|
||||
@@ -403,7 +439,22 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
<Typography variant='h5' mb={3.5}>
|
||||
{editMode ? t('update_packet') : t('create_packet')}
|
||||
</Typography>
|
||||
|
||||
{user?.role === 'ADMIN' && (
|
||||
<FormControl size='small' sx={{ minWidth: 200, marginBottom: 4 }}>
|
||||
<Select
|
||||
value={editMode && editParties ? editParties.cargoType : cargoType}
|
||||
onChange={e => {
|
||||
const value = e.target.value || undefined;
|
||||
setCargoType(value as 'AVIA' | 'AUTO');
|
||||
}}
|
||||
displayEmpty
|
||||
disabled={editMode}
|
||||
>
|
||||
<MenuItem value='AVIA'>AVIA</MenuItem>
|
||||
<MenuItem value='AUTO'>AUTO</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
<Grid container columnSpacing={2.5} rowSpacing={3} mb={3.5}>
|
||||
<Grid item xs={5}>
|
||||
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
||||
@@ -414,21 +465,25 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
control={control}
|
||||
rules={{ required: requiredText }}
|
||||
render={({ field, fieldState, formState }) => {
|
||||
const currentValue =
|
||||
defaultParties?.find(p => p.value === field.value) ||
|
||||
(editMode ? { value: initialValues.partyId, label: initialValues.partyName } : null);
|
||||
return (
|
||||
<AsyncSelect
|
||||
onChange={(newValue: any) => {
|
||||
field.onChange(newValue.value);
|
||||
}}
|
||||
value={currentValue}
|
||||
defaultValue={
|
||||
editMode
|
||||
? {
|
||||
value: initialValues.partyId,
|
||||
label: initialValues.partyName,
|
||||
}
|
||||
: partiesData?.length
|
||||
: defaultParties?.length
|
||||
? {
|
||||
value: partiesData[0].value,
|
||||
label: partiesData[0].label,
|
||||
value: defaultParties[0].value,
|
||||
label: defaultParties[0].label,
|
||||
}
|
||||
: null
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ const DashboardEditBoxPage = (props: Props) => {
|
||||
return {
|
||||
id: item.id,
|
||||
price: item.price,
|
||||
|
||||
cargoType: item.cargoType,
|
||||
cargoId: item.cargoId,
|
||||
trekId: item.trekId,
|
||||
name: name,
|
||||
@@ -69,6 +69,7 @@ const DashboardEditBoxPage = (props: Props) => {
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log(getOneBox);
|
||||
|
||||
if (getOneBox.loading || !getOneBox.data) {
|
||||
return <Loader p={8} size={96} />;
|
||||
|
||||
@@ -107,6 +107,7 @@ const DashboardBoxesOnePage = () => {
|
||||
cargoId: item.cargoId,
|
||||
trekId: item.trekId,
|
||||
name: item.name,
|
||||
cargoType: item.cargoType,
|
||||
nameRu: item.nameRu,
|
||||
amount: +item.amount,
|
||||
acceptedNumber: item.acceptedNumber,
|
||||
@@ -142,6 +143,7 @@ const DashboardBoxesOnePage = () => {
|
||||
trekId: trackId,
|
||||
packetId: boxData?.packetId,
|
||||
partyId: boxData?.partyId,
|
||||
cargoType: boxData?.products_list[0].cargoType,
|
||||
}),
|
||||
{
|
||||
dependencies: [page, trackId, boxData],
|
||||
@@ -204,6 +206,7 @@ const DashboardBoxesOnePage = () => {
|
||||
packetId: String(res.data.data.packet.id),
|
||||
passportId: res.data.data.client.passportId,
|
||||
status: res.data.data.packet.status,
|
||||
cargoType: res.data.data.items[0].cargoType,
|
||||
});
|
||||
setPrintStatus(newStatus);
|
||||
await refetch();
|
||||
@@ -225,6 +228,7 @@ const DashboardBoxesOnePage = () => {
|
||||
name: item.name,
|
||||
nameRu: item.nameRu,
|
||||
trekId: item.trekId,
|
||||
cargoType: item.cargoType,
|
||||
weight: item.weight,
|
||||
};
|
||||
await item_requests.update(updateBody);
|
||||
|
||||
@@ -16,6 +16,7 @@ import type { Product, UpdateProductBodyType } from '@/data/item/item.mode';
|
||||
import { item_requests } from '@/data/item/item.requests';
|
||||
import { party_requests } from '@/data/party/party.requests';
|
||||
import { DEFAULT_PAGE_SIZE, pageLinks } from '@/helpers/constants';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import useInput from '@/hooks/useInput';
|
||||
import { useMyNavigation } from '@/hooks/useMyNavigation';
|
||||
import { useMyTranslation } from '@/hooks/useMyTranslation';
|
||||
@@ -68,6 +69,7 @@ const DashboardBoxesPage = (props: Props) => {
|
||||
const handleClose = () => setOpen(false);
|
||||
const t = useMyTranslation();
|
||||
const navigation = useMyNavigation();
|
||||
const [cargoType, setCargoType] = useState<'AUTO' | 'AVIA'>('AVIA');
|
||||
const { isAdmin } = useAuthContext();
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize] = useState(DEFAULT_PAGE_SIZE);
|
||||
@@ -76,7 +78,7 @@ const DashboardBoxesPage = (props: Props) => {
|
||||
const [trackId, setTrackId] = useState<string>();
|
||||
const [partyFilter, setPartyFilter] = useState<{ label: string; value: number } | undefined>(undefined);
|
||||
const [boxFilter, setBoxFilter] = useState<{ label: string; value: number } | undefined>(undefined);
|
||||
|
||||
const { user } = useAuth();
|
||||
const [deleteIds, setDeleteIds] = useState<number[]>([]);
|
||||
const [downloadIds, setDownloadIds] = useState<number[]>([]);
|
||||
const [changeStatusIds, setChangeStatusIds] = useState<number[]>([]);
|
||||
@@ -100,13 +102,17 @@ const DashboardBoxesPage = (props: Props) => {
|
||||
setSelectedBoxDetails(null);
|
||||
},
|
||||
});
|
||||
const { data: defaultPartyOptions } = useRequest(() => party_requests.getAll({}), {
|
||||
const { data: defaultPartyOptions } = useRequest(
|
||||
() => party_requests.getAll({ cargoType: user?.role === 'ADMIN' ? cargoType : user?.cargoType || 'AVIA' }),
|
||||
{
|
||||
enabled: true,
|
||||
selectData(data) {
|
||||
return data.data.data.data.map(p => ({ value: p.id, label: p.name }));
|
||||
},
|
||||
dependencies: [cargoType],
|
||||
placeholderData: [],
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const partyOptions = (inputValue: string) => {
|
||||
return party_requests.getAll({ partyName: inputValue }).then(res => {
|
||||
@@ -187,9 +193,10 @@ const DashboardBoxesPage = (props: Props) => {
|
||||
status: boxStatusFilter,
|
||||
direction: 'desc',
|
||||
sort: 'id',
|
||||
cargoType: user?.role === 'ADMIN' ? cargoType : user?.cargoType || 'AVIA',
|
||||
}),
|
||||
{
|
||||
dependencies: [page, boxStatusFilter],
|
||||
dependencies: [page, boxStatusFilter, cargoType, user?.cargoType],
|
||||
selectData(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
@@ -230,7 +237,7 @@ const DashboardBoxesPage = (props: Props) => {
|
||||
setPartyFilter(selected);
|
||||
}
|
||||
}
|
||||
}, [boxId, defaultPartyOptions]);
|
||||
}, [boxId, defaultPartyOptions, cargoType]);
|
||||
|
||||
useEffect(() => {
|
||||
if (boxesId) {
|
||||
@@ -444,6 +451,13 @@ const DashboardBoxesPage = (props: Props) => {
|
||||
dataKey: 'totalItems',
|
||||
label: t('count_of_items'),
|
||||
width: 120,
|
||||
isComplated: data => {
|
||||
const total = boxAmounts[data.id];
|
||||
if (!total) return <Typography>...</Typography>;
|
||||
|
||||
const isCompleted = total.totalAmount === total.totalAccepted && total.totalAmount > 0;
|
||||
return isCompleted;
|
||||
},
|
||||
renderCell: data => {
|
||||
const total = boxAmounts[data.id];
|
||||
if (!total) return <Typography>...</Typography>;
|
||||
@@ -602,6 +616,7 @@ const DashboardBoxesPage = (props: Props) => {
|
||||
packetId: String(res.data.data.packet.id),
|
||||
passportId: res.data.data.client.passportId,
|
||||
status: res.data.data.packet.status,
|
||||
cargoType: res.data.data.items[0].cargoType,
|
||||
});
|
||||
refetch();
|
||||
getBoxesQuery.refetch();
|
||||
@@ -694,6 +709,7 @@ const DashboardBoxesPage = (props: Props) => {
|
||||
|
||||
const updateBody: UpdateProductBodyType = {
|
||||
itemId: item.id,
|
||||
cargoType: item.cargoType,
|
||||
acceptedNumber: item.amount,
|
||||
amount: item.amount,
|
||||
name: item.name,
|
||||
@@ -722,7 +738,7 @@ const DashboardBoxesPage = (props: Props) => {
|
||||
<BaseButton colorVariant='blue' startIcon={<Add />} href={pageLinks.dashboard.boxes.create}>
|
||||
{t('create_packet')}
|
||||
</BaseButton>
|
||||
<Button onClick={handleOpen}>{t('product_inspection')}</Button>
|
||||
{/* <Button onClick={handleOpen}>{t('product_inspection')}</Button> */}
|
||||
|
||||
<Modal open={open} onClose={handleClose} aria-labelledby='modal-modal-title' aria-describedby='modal-modal-description'>
|
||||
<Box sx={style}>
|
||||
@@ -863,6 +879,22 @@ const DashboardBoxesPage = (props: Props) => {
|
||||
loadOptions={partyOptions}
|
||||
placeholder={t('filter_party_name')}
|
||||
/>
|
||||
{user?.role === 'ADMIN' && (
|
||||
<FormControl size='small' sx={{ minWidth: 200 }}>
|
||||
<Select
|
||||
value={cargoType ?? ''}
|
||||
onChange={e => {
|
||||
const value = e.target.value || undefined;
|
||||
setCargoType(value as 'AVIA' | 'AUTO');
|
||||
setPage(1);
|
||||
}}
|
||||
displayEmpty
|
||||
>
|
||||
<MenuItem value='AVIA'>AVIA</MenuItem>
|
||||
<MenuItem value='AUTO'>AUTO</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
<BaseButton colorVariant='gray' startIcon={<FilterListOff />} size='small' onClick={resetFilter}>
|
||||
{t('reset_filter')}
|
||||
</BaseButton>
|
||||
|
||||
@@ -0,0 +1,773 @@
|
||||
'use client';
|
||||
|
||||
import BaseButton from '@/components/ui-kit/BaseButton';
|
||||
import BaseIconButton from '@/components/ui-kit/BaseIconButton';
|
||||
import BaseInput from '@/components/ui-kit/BaseInput';
|
||||
import BaseReactSelect, { selectDefaultStyles } from '@/components/ui-kit/BaseReactSelect';
|
||||
import { useAuthContext } from '@/context/auth-context';
|
||||
import { BoxStatus, CreateBoxBodyType, UpdateBoxBodyType } from '@/data/box/box.model';
|
||||
import { box_requests } from '@/data/box/box.requests';
|
||||
import { Customer } from '@/data/customers/customer.model';
|
||||
import { customer_requests } from '@/data/customers/customer.requests';
|
||||
import { item_requests } from '@/data/item/item.requests';
|
||||
import { Party } from '@/data/party/party.model';
|
||||
import { party_requests } from '@/data/party/party.requests';
|
||||
import { Passport } from '@/data/passport/passport.model';
|
||||
import { passport_requests } from '@/data/passport/passport.request';
|
||||
import { pageLinks } from '@/helpers/constants';
|
||||
import { useMyNavigation } from '@/hooks/useMyNavigation';
|
||||
import { useMyTranslation } from '@/hooks/useMyTranslation';
|
||||
import useRequest from '@/hooks/useRequest';
|
||||
import { notifyError, notifyUnknownError } from '@/services/notification';
|
||||
import { AddCircleRounded, Close } from '@mui/icons-material';
|
||||
import { Box, Divider, FormHelperText, Grid, Stack, Typography, styled } from '@mui/material';
|
||||
import get from 'lodash.get';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Controller, useFieldArray, useForm } from 'react-hook-form';
|
||||
import AsyncSelect from 'react-select/async';
|
||||
|
||||
const StyledCreateBox = styled(Box)`
|
||||
.item-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.item-row-field {
|
||||
}
|
||||
|
||||
& > * {
|
||||
flex: 1 1 1;
|
||||
}
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
partiesData?: { value: number; label: string }[];
|
||||
initialValues?: {
|
||||
id: number;
|
||||
box_name: string;
|
||||
net_weight: number;
|
||||
box_weight: number;
|
||||
box_type: string;
|
||||
box_size: string;
|
||||
status: BoxStatus;
|
||||
packetId: string;
|
||||
|
||||
passportName: string;
|
||||
passportId: number;
|
||||
partyId: number;
|
||||
partyName: string;
|
||||
|
||||
clientId: number;
|
||||
client_id: string;
|
||||
clientName: string;
|
||||
|
||||
products_list: {
|
||||
id: number;
|
||||
price: number | string;
|
||||
|
||||
cargoId: string;
|
||||
trekId: string;
|
||||
name: string;
|
||||
nameRu: string;
|
||||
amount: number;
|
||||
weight: number;
|
||||
}[];
|
||||
};
|
||||
};
|
||||
|
||||
const DashboardCreateForbiddenPage = ({ initialValues, partiesData }: Props) => {
|
||||
const [cargoIdValue, setCargoIdValue] = useState<string>('');
|
||||
const { user, isAdmin: isAdminUser } = useAuthContext();
|
||||
const editMode = !!initialValues && !!initialValues.id;
|
||||
const isAdmin = isAdminUser && editMode;
|
||||
const t = useMyTranslation();
|
||||
const params = useSearchParams();
|
||||
const navigation = useMyNavigation();
|
||||
const [cargoType, setCargoType] = useState<'AUTO' | 'AVIA'>('AVIA');
|
||||
const helperRef = useRef<{
|
||||
finished: boolean;
|
||||
partyFinished: boolean;
|
||||
clientFinished: boolean;
|
||||
settedDefaultParty: Party | null;
|
||||
settedDefaultClient: Customer | null;
|
||||
// settedDefaultPartyValue: { value: number; label: string }[] | null;
|
||||
}>({
|
||||
settedDefaultParty: null,
|
||||
settedDefaultClient: null,
|
||||
partyFinished: false,
|
||||
clientFinished: false,
|
||||
finished: false,
|
||||
// settedDefaultPartyValue: partiesData?.[0] || null,
|
||||
});
|
||||
const {
|
||||
register,
|
||||
control,
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = useForm<any>({
|
||||
defaultValues: {
|
||||
partyId: params.get('party_id') ? +params.get('party_id')! : '',
|
||||
box_weight: 0.9,
|
||||
box_type: 'KG',
|
||||
box_size: '50x40x40',
|
||||
status: 'READY_TO_INVOICE',
|
||||
cargoId: initialValues?.client_id,
|
||||
passportId: { value: initialValues?.passportId },
|
||||
...(editMode
|
||||
? {}
|
||||
: {
|
||||
products_list: [
|
||||
{
|
||||
id: '',
|
||||
cargoId: '',
|
||||
trekId: '',
|
||||
name: '',
|
||||
nameRu: '',
|
||||
amount: '',
|
||||
weight: '',
|
||||
price: '',
|
||||
},
|
||||
],
|
||||
}),
|
||||
...initialValues,
|
||||
},
|
||||
});
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const products = useFieldArray({
|
||||
control,
|
||||
name: 'products_list',
|
||||
keyName: 'key',
|
||||
});
|
||||
const controlledProductFields = watch('products_list');
|
||||
const partyIdValue = watch('partyId');
|
||||
const clientIdValue = watch('client_id');
|
||||
const cargoId = watch('cargoId');
|
||||
const requiredText = t('required');
|
||||
const [selectedPassport, setSelectedPassport] = useState<Passport | null>(null); // Tanlangan passportni saqlash uchun state (tipi Passport yoki null)
|
||||
|
||||
const passportOptionsInitial = initialValues?.passportId &&
|
||||
initialValues?.passportName && [
|
||||
{
|
||||
value: initialValues?.passportId,
|
||||
label: initialValues?.passportName,
|
||||
},
|
||||
];
|
||||
const n = '123ds';
|
||||
n.toUpperCase();
|
||||
const { data: passportOptions } = useRequest(() => passport_requests.getAll({ cargoId: cargoId?.toUpperCase() }), {
|
||||
enabled: !!cargoId,
|
||||
selectData: data => {
|
||||
// Ma'lumotlarni BaseReactSelect uchun mos formatga o'tkazish
|
||||
const passportOptions = data.data.data.map((passport: Passport) => ({
|
||||
// data.data endi Passport[]
|
||||
value: passport.id, // passport id sini value sifatida
|
||||
label: passport.fullName, // fullName ni label sifatida
|
||||
}));
|
||||
|
||||
const passportId = watch('passportId');
|
||||
|
||||
if (!passportId && initialValues?.passportId && cargoId === initialValues?.client_id) {
|
||||
const currentOption = passportOptions?.find((item: { value: number }) => item.value === initialValues?.passportId);
|
||||
setValue('passportId', currentOption);
|
||||
}
|
||||
|
||||
return passportOptions;
|
||||
},
|
||||
dependencies: [cargoId],
|
||||
placeholderData: [], // Kerak emas, chunki server PageAble qaytarmayapti
|
||||
onSuccess(data) {
|
||||
if (data?.data.data?.[0]?.id) {
|
||||
setValue('passportId', initialValues?.passportId);
|
||||
setValue('passport_id', data.data.data[0].id);
|
||||
setSelectedPassport(data.data.data[0]); // Birinchi elementni tanlash
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setValue('passportId', 'AA1234567');
|
||||
}, [cargoId]);
|
||||
|
||||
const { data: editParties } = useRequest(
|
||||
() =>
|
||||
party_requests.find({
|
||||
partyId: initialValues?.partyId!,
|
||||
}),
|
||||
{
|
||||
enabled: true,
|
||||
selectData(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
onSuccess(data) {
|
||||
if (!editMode && data?.data?.data) {
|
||||
helperRef.current.settedDefaultParty = data.data.data;
|
||||
setValue('partyId', data.data.data.id);
|
||||
}
|
||||
helperRef.current.partyFinished = true;
|
||||
if (helperRef.current.clientFinished) {
|
||||
helperRef.current.finished = true;
|
||||
}
|
||||
},
|
||||
dependencies: [initialValues],
|
||||
}
|
||||
);
|
||||
|
||||
const { data: defaultParties, status: defaultPartiesStatus } = useRequest(() => party_requests.getAll({ status: 'COLLECTING' }), {
|
||||
enabled: true,
|
||||
selectData(data) {
|
||||
return data.data.data.data.map(p => ({ value: p.id, label: p.name }));
|
||||
},
|
||||
onSuccess(data) {
|
||||
if (!editMode && data?.data?.data?.data?.[0]) {
|
||||
helperRef.current.settedDefaultParty = data.data.data.data[0];
|
||||
setValue('partyId', data.data.data.data[0].id);
|
||||
}
|
||||
helperRef.current.partyFinished = true;
|
||||
if (helperRef.current.clientFinished) {
|
||||
helperRef.current.finished = true;
|
||||
}
|
||||
},
|
||||
placeholderData: [],
|
||||
});
|
||||
|
||||
const { data: defaultClients } = useRequest(
|
||||
() =>
|
||||
customer_requests.getAll({
|
||||
page: 1,
|
||||
}),
|
||||
{
|
||||
enabled: !!partyIdValue,
|
||||
selectData(data) {
|
||||
return data.data.data.data.map(p => ({ value: p.aviaCargoId, label: p.fullName }));
|
||||
// return data.data.data.map(p => ({ value: p.id, label: p.fullName }));
|
||||
},
|
||||
dependencies: [partyIdValue],
|
||||
onSuccess(data) {
|
||||
if (!editMode && !clientIdValue && data?.data?.data?.data?.[0]) {
|
||||
helperRef.current.settedDefaultClient = data.data.data?.data?.[0];
|
||||
setValue('client_id', data.data.data.data[0].aviaCargoId);
|
||||
}
|
||||
helperRef.current.clientFinished = true;
|
||||
if (helperRef.current.partyFinished) {
|
||||
helperRef.current.finished = true;
|
||||
}
|
||||
},
|
||||
placeholderData: [],
|
||||
}
|
||||
);
|
||||
|
||||
const onPassportChange = (newValue: Passport | null) => {
|
||||
// Tipi Passport | null
|
||||
setSelectedPassport(newValue);
|
||||
if (newValue) {
|
||||
setValue('passport_id', newValue.id || null);
|
||||
} else {
|
||||
setValue('passport_id', null);
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = handleSubmit(async values => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
if (editMode) {
|
||||
const updateBody: UpdateBoxBodyType = {
|
||||
cargoId: values.client_id,
|
||||
passportId: values.passportId?.value || values.passport_id,
|
||||
status: values.status,
|
||||
cargoType: editParties?.cargoType!,
|
||||
packetId: initialValues?.packetId,
|
||||
items: values.products_list.map((item: any) => {
|
||||
const _price = +item.price ? +item.price : 0;
|
||||
const _amount = +item.amount ? +item.amount : 0;
|
||||
const _total_price = _price ? _price * _amount : 0;
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
trekId: item.trekId,
|
||||
name: item.name,
|
||||
nameRu: item?.nameRu,
|
||||
weight: +item.weight,
|
||||
amount: +item.amount,
|
||||
price: _price,
|
||||
totalPrice: _total_price,
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
const item_delete_promises = initialValues.products_list
|
||||
.filter(item => {
|
||||
if (!updateBody.items?.find(i => String(i.id) === String(item.id))) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.map(item => {
|
||||
return item_requests.delete({ itemId: item.id });
|
||||
});
|
||||
|
||||
await box_requests.update(updateBody);
|
||||
await Promise.all(item_delete_promises);
|
||||
} else {
|
||||
const createBody: CreateBoxBodyType = {
|
||||
status: values.status,
|
||||
cargoId: values.cargoId,
|
||||
cargoType: user?.role === 'ADMIN' ? cargoType : user?.cargoType || 'AVIA',
|
||||
passportId: values.passportId.value,
|
||||
partyId: values.partyId,
|
||||
items: values.products_list.map((item: any) => {
|
||||
return {
|
||||
trekId: item.trekId,
|
||||
name: item.name,
|
||||
weight: +item.weight,
|
||||
cargoType: user?.role === 'ADMIN' ? cargoType : user?.cargoType || 'AVIA',
|
||||
amount: +item.amount,
|
||||
};
|
||||
}),
|
||||
};
|
||||
await box_requests.create(createBody);
|
||||
}
|
||||
|
||||
navigation.push(pageLinks.dashboard.boxes.index);
|
||||
} catch (error) {
|
||||
notifyUnknownError(error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
const partyOptions = (inputValue: string) => {
|
||||
return party_requests.getAll({ status: 'COLLECTING', partyName: inputValue }).then(res => {
|
||||
return res.data.data.data.map(p => ({ label: p.name, value: p.id }));
|
||||
});
|
||||
};
|
||||
|
||||
// const clientOptions = (inputValue: string) => {
|
||||
// return customer_requests.getAll({ clientName: inputValue, page: 1 }).then(res => {
|
||||
// return res.data.data.data.map(p => ({ label: p.fullName, value: p.id }));
|
||||
// });
|
||||
// };
|
||||
|
||||
const appendProduct = () => {
|
||||
products.append({
|
||||
id: '',
|
||||
cargoId: '',
|
||||
trekId: '',
|
||||
name: '',
|
||||
amount: '',
|
||||
weight: '',
|
||||
price: '',
|
||||
totalPrice: '',
|
||||
});
|
||||
};
|
||||
|
||||
const removeProduct = (index: number) => {
|
||||
products.remove(index);
|
||||
};
|
||||
|
||||
const translateAndUpdateRussianName = async (text: string, index: number) => {
|
||||
if (!text) return;
|
||||
|
||||
try {
|
||||
// const responseText = await box_requests.translateWithGoogleApi({ text });
|
||||
const responseText = await box_requests.translateWithMemoryApi({ text });
|
||||
setValue(`products_list.${index}.nameRu`, responseText || '');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
notifyError('Translation api error');
|
||||
}
|
||||
};
|
||||
|
||||
const boxTypes = [
|
||||
{
|
||||
label: 'KG',
|
||||
value: 'KG',
|
||||
},
|
||||
{
|
||||
label: 'GABARIT',
|
||||
value: 'GABARIT',
|
||||
},
|
||||
];
|
||||
|
||||
const boxStatuses = useMemo(() => {
|
||||
const p: {
|
||||
label: string;
|
||||
value: BoxStatus;
|
||||
}[] = [
|
||||
{
|
||||
label: t('READY_TO_INVOICE'),
|
||||
value: 'READY_TO_INVOICE',
|
||||
},
|
||||
];
|
||||
|
||||
if (isAdmin) {
|
||||
p.push({
|
||||
label: t('READY'),
|
||||
value: 'READY',
|
||||
});
|
||||
}
|
||||
|
||||
return p;
|
||||
}, [isAdmin]);
|
||||
|
||||
return (
|
||||
<StyledCreateBox
|
||||
width={1}
|
||||
mb={3}
|
||||
sx={{
|
||||
padding: '28px',
|
||||
borderRadius: '16px',
|
||||
backgroundColor: '#fff',
|
||||
}}
|
||||
>
|
||||
<Box component={'form'} onSubmit={onSubmit}>
|
||||
<Typography variant='h5' mb={3.5}>
|
||||
{editMode ? t('update_packet') : t('create_packet')}
|
||||
</Typography>
|
||||
|
||||
<Grid container columnSpacing={2.5} rowSpacing={3} mb={3.5}>
|
||||
<Grid item xs={5}>
|
||||
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
||||
{t('party_name')}
|
||||
</Typography>
|
||||
<Controller
|
||||
name='partyId'
|
||||
control={control}
|
||||
rules={{ required: requiredText }}
|
||||
render={({ field, fieldState, formState }) => {
|
||||
return (
|
||||
<AsyncSelect
|
||||
onChange={(newValue: any) => {
|
||||
field.onChange(newValue.value);
|
||||
}}
|
||||
defaultValue={
|
||||
editMode
|
||||
? {
|
||||
value: initialValues.partyId,
|
||||
label: initialValues.partyName,
|
||||
}
|
||||
: partiesData?.length
|
||||
? {
|
||||
value: partiesData[0].value,
|
||||
label: partiesData[0].label,
|
||||
}
|
||||
: null
|
||||
}
|
||||
styles={selectDefaultStyles}
|
||||
noOptionsMessage={() => t('not_found')}
|
||||
loadingMessage={() => t('loading')}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
defaultOptions={defaultParties!}
|
||||
loadOptions={partyOptions}
|
||||
placeholder={t('enter_party_name_to_find')}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{/* @ts-expect-error */}
|
||||
{!!errors.partyId?.message && <FormHelperText sx={{ color: 'red' }}>{errors.partyId?.message}</FormHelperText>}
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={5}>
|
||||
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
||||
{t('status')}
|
||||
</Typography>
|
||||
<Controller
|
||||
name='status'
|
||||
control={control}
|
||||
rules={{ required: requiredText }}
|
||||
render={({ field, fieldState, formState }) => {
|
||||
return (
|
||||
<BaseReactSelect
|
||||
value={boxStatuses?.find(p => p.value === field.value)}
|
||||
onChange={(newValue: any) => {
|
||||
field.onChange(newValue.value);
|
||||
}}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
options={boxStatuses}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{/* @ts-expect-error */}
|
||||
{!!errors.box_type?.message && <FormHelperText sx={{ color: 'red' }}>{errors.box_type?.message}</FormHelperText>}
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={5}>
|
||||
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
||||
{t('cargo_id')}
|
||||
</Typography>
|
||||
<BaseInput
|
||||
disabled={!!initialValues}
|
||||
type='text'
|
||||
fullWidth
|
||||
inputProps={{
|
||||
step: 0.1,
|
||||
}}
|
||||
mainBorderColor='#D8D8D8'
|
||||
placeholder={t('cargo_id')}
|
||||
{...register('cargoId')}
|
||||
/>
|
||||
{!!errors.net_weight?.message && (
|
||||
// @ts-expect-error
|
||||
<FormHelperText sx={{ color: 'red' }}>{errors.net_weight?.message}</FormHelperText>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item xs={5}>
|
||||
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
||||
{t('passport')}
|
||||
</Typography>
|
||||
|
||||
<Controller
|
||||
name='passportId'
|
||||
control={control}
|
||||
rules={{ required: requiredText }}
|
||||
render={({ field, fieldState, formState }) => {
|
||||
return (
|
||||
<BaseReactSelect
|
||||
// value={selectedPassport}
|
||||
// onChange={onPassportChange}
|
||||
|
||||
// value={field.value}
|
||||
{...field}
|
||||
onChange={(newValue: any) => {
|
||||
onPassportChange(newValue);
|
||||
field.onChange(newValue);
|
||||
}}
|
||||
// options={passportOptions || passportOptionsInitial || []}
|
||||
options={passportOptions || passportOptionsInitial || []}
|
||||
// isLoading={passportLoading}
|
||||
placeholder={t('passport')}
|
||||
isDisabled={!clientIdValue || !!initialValues}
|
||||
noOptionsMessage={() => t('not_found')}
|
||||
loadingMessage={() => t('loading')}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Stack
|
||||
sx={{
|
||||
borderRadius: '8px',
|
||||
border: '2px solid #3489E4',
|
||||
background: '#FFF',
|
||||
padding: '24px',
|
||||
}}
|
||||
>
|
||||
{controlledProductFields.map((product: any, index: number) => {
|
||||
//
|
||||
|
||||
//
|
||||
let totalPrice = 0;
|
||||
|
||||
try {
|
||||
const p = +product.price * +product.amount;
|
||||
if (!Number.isNaN(p)) {
|
||||
totalPrice = p;
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
return (
|
||||
<Box key={product.key} mb={1.5}>
|
||||
<Box className='item-row' mb={1.5}>
|
||||
<Box className='item-row-field'>
|
||||
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
||||
{t('track_id')}
|
||||
</Typography>
|
||||
<BaseInput
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: '#EBEFF5',
|
||||
color: '#929191',
|
||||
alignSelf: 'stretch',
|
||||
height: 'auto',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
pl: '10px',
|
||||
pr: '10px',
|
||||
borderRadius: '8px 0 0 8px',
|
||||
}}
|
||||
>
|
||||
<span>ID</span>
|
||||
</Box>
|
||||
),
|
||||
}}
|
||||
fullWidth
|
||||
placeholder={t('id')}
|
||||
sx={{
|
||||
'.MuiInputBase-root': {
|
||||
paddingLeft: 0,
|
||||
},
|
||||
}}
|
||||
{...register(`products_list.${index}.trekId`, { required: requiredText })}
|
||||
/>
|
||||
{!!get(errors, `products_list.${index}.trekId`) && (
|
||||
<FormHelperText sx={{ color: 'red' }}>{requiredText}</FormHelperText>
|
||||
)}
|
||||
</Box>
|
||||
<Box className='item-row-field'>
|
||||
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
||||
{t('name')}
|
||||
</Typography>
|
||||
<BaseInput
|
||||
fullWidth
|
||||
mainBorderColor='#D8D8D8'
|
||||
placeholder={t('name')}
|
||||
{...register(`products_list.${index}.name`, { required: requiredText })}
|
||||
onBlur={event => {
|
||||
translateAndUpdateRussianName(event.target.value, index);
|
||||
}}
|
||||
/>
|
||||
{!!get(errors, `products_list.${index}.name`) && (
|
||||
<FormHelperText sx={{ color: 'red' }}>{requiredText}</FormHelperText>
|
||||
)}
|
||||
</Box>
|
||||
<Box className='item-row-field'>
|
||||
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
||||
{'NAME_RU'}
|
||||
</Typography>
|
||||
<BaseInput
|
||||
// disabled
|
||||
fullWidth
|
||||
mainBorderColor='#D8D8D8'
|
||||
placeholder={t('name')}
|
||||
{...register(`products_list.${index}.nameRu`)}
|
||||
/>
|
||||
{!!get(errors, `products_list.${index}.name`) && (
|
||||
<FormHelperText sx={{ color: 'red' }}>{requiredText}</FormHelperText>
|
||||
)}
|
||||
</Box>
|
||||
<Box className='item-row-field'>
|
||||
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
||||
{t('quantity')}
|
||||
</Typography>
|
||||
<BaseInput
|
||||
fullWidth
|
||||
type='number'
|
||||
mainBorderColor='#D8D8D8'
|
||||
placeholder={t('quantity')}
|
||||
{...register(`products_list.${index}.amount`, { required: requiredText })}
|
||||
/>
|
||||
{!!get(errors, `products_list.${index}.amount`) && (
|
||||
<FormHelperText sx={{ color: 'red' }}>{requiredText}</FormHelperText>
|
||||
)}
|
||||
</Box>
|
||||
<Box className='item-row-field'>
|
||||
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
||||
{t('weight')}
|
||||
</Typography>
|
||||
<BaseInput
|
||||
fullWidth
|
||||
type='number'
|
||||
inputProps={{ step: 'any', min: 0, type: 'number' }}
|
||||
mainBorderColor='#D8D8D8'
|
||||
placeholder={t('weight')}
|
||||
{...register(`products_list.${index}.weight`, { required: requiredText })}
|
||||
/>
|
||||
{!!get(errors, `products_list.${index}.amount`) && (
|
||||
<FormHelperText sx={{ color: 'red' }}>{requiredText}</FormHelperText>
|
||||
)}
|
||||
</Box>
|
||||
{isAdmin && (
|
||||
<React.Fragment>
|
||||
<Box className='item-row-field'>
|
||||
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
||||
{t('weight')}
|
||||
</Typography>
|
||||
<Stack direction={'row'} alignItems={'center'} spacing={2.5}>
|
||||
<BaseInput
|
||||
fullWidth
|
||||
type='text'
|
||||
inputProps={{
|
||||
step: 0.1,
|
||||
}}
|
||||
mainBorderColor='#D8D8D8'
|
||||
placeholder={t('weight')}
|
||||
{...register(`products_list.${index}.weight`)}
|
||||
/>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box className='item-row-field'>
|
||||
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
||||
{t('price')}
|
||||
</Typography>
|
||||
<BaseInput
|
||||
fullWidth
|
||||
type='text'
|
||||
inputProps={{
|
||||
step: 0.1,
|
||||
}}
|
||||
mainBorderColor='#D8D8D8'
|
||||
placeholder={t('price')}
|
||||
{...register(`products_list.${index}.price`, { required: requiredText })}
|
||||
/>
|
||||
{!!get(errors, `products_list.${index}.price`) && (
|
||||
<FormHelperText sx={{ color: 'red' }}>{requiredText}</FormHelperText>
|
||||
)}
|
||||
</Box>
|
||||
<Box className='item-row-field'>
|
||||
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
||||
{t('total_price')}
|
||||
</Typography>
|
||||
<BaseInput
|
||||
fullWidth
|
||||
type='number'
|
||||
inputProps={{
|
||||
step: 0.001,
|
||||
}}
|
||||
value={totalPrice}
|
||||
mainBorderColor='#D8D8D8'
|
||||
placeholder={t('total_price')}
|
||||
// {...register(`products_list.${index}.totalPrice`, { required: requiredText })}
|
||||
/>
|
||||
</Box>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
<Box className='item-row-field'>
|
||||
<BaseIconButton
|
||||
size='small'
|
||||
colorVariant='icon-error'
|
||||
sx={{ flexShrink: 0, height: 'auto', marginTop: 4.5 }}
|
||||
onClick={() => removeProduct(index)}
|
||||
>
|
||||
<Close />
|
||||
</BaseIconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
<Divider color='#EBEFF6' />
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
|
||||
<Stack alignItems={'center'}>
|
||||
<BaseButton sx={{ backgroundColor: '#239D5F' }} startIcon={<AddCircleRounded />} onClick={appendProduct}>
|
||||
{t('add_more')}
|
||||
</BaseButton>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<BaseButton type='submit' colorVariant='blue' loading={loading}>
|
||||
{editMode ? t('update') : t('create')}
|
||||
</BaseButton>
|
||||
</Box>
|
||||
</StyledCreateBox>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardCreateForbiddenPage;
|
||||
@@ -0,0 +1,82 @@
|
||||
'use client';
|
||||
import Loader from '@/components/common/Loader';
|
||||
import { box_requests } from '@/data/box/box.requests';
|
||||
import useRequest from '@/hooks/useRequest';
|
||||
import { useParams } from 'next/navigation';
|
||||
import DashboardCreateForbiddenPage from './DashboardCreateForbiddenPage';
|
||||
|
||||
type Props = {};
|
||||
const DashboardEditForbidden = () => {
|
||||
const params = useParams();
|
||||
const box_id = params.forbidden_id as string;
|
||||
|
||||
const getOneBox = useRequest(
|
||||
() => {
|
||||
return box_requests.find({ packetId: box_id });
|
||||
},
|
||||
{
|
||||
selectData(data) {
|
||||
const boxData = data.data.data;
|
||||
|
||||
return {
|
||||
id: +box_id,
|
||||
box_name: boxData.packet.name,
|
||||
net_weight: +boxData.packet.brutto,
|
||||
box_weight: +boxData.packet.boxWeight,
|
||||
box_type: boxData.packet.boxType,
|
||||
box_size: boxData.packet.volume,
|
||||
passportName: boxData.packet.passportName,
|
||||
status: boxData.packet.status,
|
||||
packetId: box_id,
|
||||
|
||||
partyId: +boxData.packet.partyId,
|
||||
partyName: boxData.packet.partyName,
|
||||
|
||||
// client_id: boxData.client?.passportId,
|
||||
passportId: boxData.client?.passportId,
|
||||
client_id: boxData.packet?.cargoId,
|
||||
clientId: boxData.client?.passportId,
|
||||
clientName: boxData.client?.passportName,
|
||||
|
||||
products_list: [
|
||||
...boxData.items.map(item => {
|
||||
let name = item.name;
|
||||
let nameRu = item.nameRu;
|
||||
|
||||
// try {
|
||||
// name = item.name.split(' / ')[0];
|
||||
// nameRu = item.name.split(' / ')[1];
|
||||
// } catch (error) {
|
||||
// console.error('prepare edit values error', error);
|
||||
// }
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
price: item.price,
|
||||
|
||||
cargoId: item.cargoId,
|
||||
trekId: item.trekId,
|
||||
name: name,
|
||||
nameRu: nameRu,
|
||||
amount: +item.amount,
|
||||
weight: +item.weight,
|
||||
};
|
||||
}),
|
||||
],
|
||||
};
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (getOneBox.loading || !getOneBox.data) {
|
||||
return <Loader p={8} size={96} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DashboardCreateForbiddenPage initialValues={getOneBox.data} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardEditForbidden;
|
||||
705
src/routes/private/forbidden/DashboardForbiddenPage.tsx
Normal file
705
src/routes/private/forbidden/DashboardForbiddenPage.tsx
Normal file
@@ -0,0 +1,705 @@
|
||||
'use client';
|
||||
|
||||
import type React from 'react';
|
||||
|
||||
import ActionPopMenu from '@/components/common/ActionPopMenu';
|
||||
import { type ColumnData, MyTable } from '@/components/common/MyTable';
|
||||
import BaseButton from '@/components/ui-kit/BaseButton';
|
||||
import BaseInput from '@/components/ui-kit/BaseInput';
|
||||
import BasePagination from '@/components/ui-kit/BasePagination';
|
||||
import { selectDefaultStyles } from '@/components/ui-kit/BaseReactSelect';
|
||||
import { useAuthContext } from '@/context/auth-context';
|
||||
import { type BoxStatus, type IBox, PrintStatus } from '@/data/box/box.model';
|
||||
import { box_requests } from '@/data/box/box.requests';
|
||||
import type { Product, UpdateProductBodyType } from '@/data/item/item.mode';
|
||||
import { item_requests } from '@/data/item/item.requests';
|
||||
import { party_requests } from '@/data/party/party.requests';
|
||||
import { DEFAULT_PAGE_SIZE, pageLinks } from '@/helpers/constants';
|
||||
import useInput from '@/hooks/useInput';
|
||||
import { useMyNavigation } from '@/hooks/useMyNavigation';
|
||||
import { useMyTranslation } from '@/hooks/useMyTranslation';
|
||||
import useRequest from '@/hooks/useRequest';
|
||||
import { useBoxIdStore } from '@/modalStorage/partyId';
|
||||
import { file_service } from '@/services/file-service';
|
||||
import { notifyUnknownError } from '@/services/notification';
|
||||
import { Add, CheckCircle, Delete, Edit, FilterListOff, Search } from '@mui/icons-material';
|
||||
import { Box, Button, Card, CardContent, Modal, Stack, Typography } from '@mui/material';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import AsyncSelect from 'react-select/async';
|
||||
import { useReactToPrint } from 'react-to-print';
|
||||
|
||||
type Props = {};
|
||||
|
||||
const style = {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 400,
|
||||
bgcolor: 'background.paper',
|
||||
border: '2px solid #000',
|
||||
boxShadow: 24,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '10px',
|
||||
p: 4,
|
||||
};
|
||||
|
||||
const DashboardForbiddenPage = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const handleClose = () => setOpen(false);
|
||||
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 [trackId, setTrackId] = useState<string>();
|
||||
const [partyFilter, setPartyFilter] = useState<{ label: string; value: number } | undefined>(undefined);
|
||||
const [boxFilter, setBoxFilter] = useState<{ label: string; value: number } | undefined>(undefined);
|
||||
|
||||
const [deleteIds, setDeleteIds] = useState<number[]>([]);
|
||||
const [downloadIds, setDownloadIds] = useState<number[]>([]);
|
||||
const [changeStatusIds, setChangeStatusIds] = useState<number[]>([]);
|
||||
const [boxAmounts, setBoxAmounts] = useState<Record<number, { totalAmount: number; totalAccepted: number }>>({});
|
||||
const [printStatuses, setPrintStatuses] = useState<Record<number, string>>({});
|
||||
const searchParams = useSearchParams();
|
||||
const boxId = searchParams.get('boxId');
|
||||
const { boxesId, setBoxId } = useBoxIdStore();
|
||||
|
||||
// Print uchun state
|
||||
const [selectedBoxForPrint, setSelectedBoxForPrint] = useState<IBox | null>(null);
|
||||
const [selectedBoxDetails, setSelectedBoxDetails] = useState<any>(null);
|
||||
|
||||
const printRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Print functionality
|
||||
const handlePrint = useReactToPrint({
|
||||
contentRef: printRef,
|
||||
onAfterPrint: () => {
|
||||
setSelectedBoxForPrint(null);
|
||||
setSelectedBoxDetails(null);
|
||||
},
|
||||
});
|
||||
const { data: defaultPartyOptions } = useRequest(() => party_requests.getAll({}), {
|
||||
enabled: true,
|
||||
selectData(data) {
|
||||
return data.data.data.data.map(p => ({ value: p.id, label: p.name }));
|
||||
},
|
||||
placeholderData: [],
|
||||
});
|
||||
|
||||
const partyOptions = (inputValue: string) => {
|
||||
return party_requests.getAll({ partyName: inputValue }).then(res => {
|
||||
return res.data.data.data.map(p => ({ label: p.name, value: p.id }));
|
||||
});
|
||||
};
|
||||
|
||||
const onPrintBox = async (boxData: IBox) => {
|
||||
try {
|
||||
const response = await box_requests.find({ packetId: boxData.id });
|
||||
const boxOne = response.data.data;
|
||||
const detailedBoxData = {
|
||||
id: +boxData.id,
|
||||
box_name: boxOne.packet.name,
|
||||
net_weight: +boxOne.packet.brutto,
|
||||
box_weight: +boxOne.packet.boxWeight,
|
||||
box_type: boxOne.packet.boxType,
|
||||
box_size: boxOne.packet.volume,
|
||||
passportName: boxOne.packet.passportName,
|
||||
status: boxOne.packet.status,
|
||||
packetId: boxData.id,
|
||||
partyId: +boxOne.packet.partyId,
|
||||
partyName: boxOne.packet.partyName,
|
||||
passportId: boxOne.client?.passportId,
|
||||
client_id: boxOne.packet?.cargoId,
|
||||
clientName: boxOne.client?.passportName,
|
||||
products_list: boxOne.items.map((item: any) => ({
|
||||
id: item.id,
|
||||
price: item.price,
|
||||
cargoId: item.cargoId,
|
||||
trekId: item.trekId,
|
||||
name: item.name,
|
||||
nameRu: item.nameRu,
|
||||
amount: +item.amount,
|
||||
acceptedNumber: item.acceptedNumber,
|
||||
weight: +item.weight,
|
||||
})),
|
||||
};
|
||||
setSelectedBoxDetails(detailedBoxData);
|
||||
setTimeout(() => {
|
||||
handlePrint();
|
||||
}, 100);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch box details:', error);
|
||||
}
|
||||
};
|
||||
const handleOpen = (data: any) => {
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleOpenModal = (data: any) => {
|
||||
setOpenModal(true);
|
||||
setSelectedBoxForPrint(data);
|
||||
};
|
||||
|
||||
const boxStatusOptions = useMemo(() => {
|
||||
const p = ['READY_TO_INVOICE'] as BoxStatus[];
|
||||
if (isAdmin) {
|
||||
p.push('READY');
|
||||
}
|
||||
return p;
|
||||
}, [isAdmin]);
|
||||
|
||||
const printOptions = useMemo(() => {
|
||||
const p = ['false'] as PrintStatus[];
|
||||
if (isAdmin) {
|
||||
p.push('false');
|
||||
}
|
||||
return p;
|
||||
}, [isAdmin]);
|
||||
|
||||
const getBoxesQuery = useRequest(
|
||||
() =>
|
||||
box_requests.getAll({
|
||||
page: page,
|
||||
cargoId: keyword,
|
||||
partyId: partyFilter?.value,
|
||||
status: boxStatusFilter,
|
||||
direction: 'desc',
|
||||
sort: 'id',
|
||||
}),
|
||||
{
|
||||
dependencies: [page, boxStatusFilter],
|
||||
selectData(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const getListQuery = useRequest(
|
||||
() =>
|
||||
item_requests.getAll({
|
||||
page: page,
|
||||
trekId: trackId,
|
||||
packetId: boxFilter?.value,
|
||||
partyId: partyFilter?.value,
|
||||
}),
|
||||
{
|
||||
dependencies: [page, trackId, boxFilter?.value, partyFilter?.value],
|
||||
selectData(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const [values, setValues] = useState<{ [trackId: string]: number | '' }>({});
|
||||
|
||||
const handleAmountChange = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, max: number, trackId: string) => {
|
||||
const val = Number(event.target.value);
|
||||
if (val >= 1 && val <= max) {
|
||||
setValues(prev => ({ ...prev, [trackId]: val }));
|
||||
} else if (event.target.value === '') {
|
||||
setValues(prev => ({ ...prev, [trackId]: '' }));
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (boxId && defaultPartyOptions && defaultPartyOptions.length > 0) {
|
||||
const selected = defaultPartyOptions.find(p => p.value === Number(boxId));
|
||||
if (selected) {
|
||||
setPartyFilter(selected);
|
||||
}
|
||||
}
|
||||
}, [boxId, defaultPartyOptions]);
|
||||
|
||||
useEffect(() => {
|
||||
if (boxesId) {
|
||||
navigation.push(`${pageLinks.dashboard.boxes.index}?boxId=${boxesId}`);
|
||||
console.log(boxesId, 'useeffect');
|
||||
}
|
||||
}, [boxesId]);
|
||||
|
||||
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);
|
||||
setPartyFilter(undefined);
|
||||
};
|
||||
|
||||
const { data: defaultBoxOptions, refetch } = useRequest(
|
||||
() =>
|
||||
box_requests.getAll({
|
||||
partyId: partyFilter?.value,
|
||||
}),
|
||||
{
|
||||
enabled: !!partyFilter,
|
||||
selectData(data) {
|
||||
return data.data.data.data.map(p => ({ value: p.id, label: p.name }));
|
||||
},
|
||||
placeholderData: [],
|
||||
dependencies: [partyFilter],
|
||||
}
|
||||
);
|
||||
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
const onChangePrint = async (id: number, newStatus: PrintStatus) => {
|
||||
if (changeStatusIds.includes(id)) return;
|
||||
|
||||
try {
|
||||
setChangeStatusIds(p => [...p, id]);
|
||||
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, partyFilter?.value, boxFilter?.value]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchAmounts = async () => {
|
||||
const result: Record<number, { totalAmount: number; totalAccepted: number }> = {};
|
||||
|
||||
await Promise.all(
|
||||
list.map(async box => {
|
||||
try {
|
||||
const res = await box_requests.find({ packetId: box.id });
|
||||
const boxData = res.data.data;
|
||||
|
||||
const total = boxData.items.reduce(
|
||||
(acc, item) => {
|
||||
acc.totalAmount = boxData.packet.totalItems ?? 0;
|
||||
if (item.acceptedNumber && item.acceptedNumber > 0) {
|
||||
acc.totalAccepted += 1;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ totalAmount: 0, totalAccepted: 0 }
|
||||
);
|
||||
|
||||
result[box.id] = total;
|
||||
} catch (e) {
|
||||
console.error(`Failed to fetch box ${box.id}`, e);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
setBoxAmounts(result);
|
||||
};
|
||||
|
||||
if (list.length > 0 && !loading) {
|
||||
fetchAmounts();
|
||||
}
|
||||
}, [list, loading]);
|
||||
|
||||
// Calculate completion statistics
|
||||
|
||||
const boxOptions = (inputValue: string) => {
|
||||
return box_requests
|
||||
.getAll({
|
||||
cargoId: inputValue,
|
||||
partyId: partyFilter?.value, // Bu qatorni qo'shing
|
||||
})
|
||||
.then(res => {
|
||||
return res.data.data.data.map(p => ({ label: p.name, value: p.id }));
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setBoxFilter(undefined);
|
||||
}, [partyFilter]);
|
||||
|
||||
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.forbidden.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,
|
||||
renderCell: data => {
|
||||
const total = boxAmounts[data.id];
|
||||
if (!total) return <Typography>...</Typography>;
|
||||
|
||||
const isCompleted = total.totalAmount === total.totalAccepted && total.totalAmount > 0;
|
||||
|
||||
return (
|
||||
<Stack direction='row' alignItems='center' spacing={1}>
|
||||
<Typography>
|
||||
{data.totalItems} | {total.totalAccepted}
|
||||
</Typography>
|
||||
{isCompleted && <CheckCircle sx={{ color: '#22c55e', fontSize: 16 }} />}
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
dataKey: 'totalNetWeight',
|
||||
label: t('party_weight'),
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
dataKey: 'cargoId',
|
||||
label: t('cargo_id'),
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
dataKey: 'passportName',
|
||||
label: t('client'),
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
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.forbidden.edit(data.id));
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: <Delete sx={{ path: { color: '#3489E4' } }} />,
|
||||
label: t('delete'),
|
||||
onClick: () => {
|
||||
onDelete(data.id);
|
||||
},
|
||||
dontCloseOnClick: true,
|
||||
loading: deleteIds.includes(data.id),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const [items, setItems] = useState<Product>();
|
||||
const [loaer, setLoading] = useState(false);
|
||||
const {
|
||||
register,
|
||||
control,
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = useForm<Product>({
|
||||
defaultValues: {
|
||||
trekId: items?.trekId,
|
||||
name: items?.name,
|
||||
nameRu: items?.nameRu,
|
||||
amount: items?.amount,
|
||||
weight: items?.weight,
|
||||
acceptedNumber: Number(values),
|
||||
},
|
||||
});
|
||||
|
||||
const updateItems = async (item: Product, acceptedNumber: number) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
const updateBody: UpdateProductBodyType = {
|
||||
itemId: item.id,
|
||||
acceptedNumber: item.amount,
|
||||
amount: item.amount,
|
||||
cargoType: item.cargoType,
|
||||
name: item.name,
|
||||
nameRu: item.nameRu,
|
||||
trekId: item.trekId,
|
||||
weight: item.weight,
|
||||
};
|
||||
|
||||
await item_requests.update(updateBody);
|
||||
|
||||
getListQuery.refetch();
|
||||
getBoxesQuery.refetch();
|
||||
|
||||
setValues(prev => ({ ...prev, [item.trekId]: '' }));
|
||||
setTrackId('');
|
||||
} catch (error) {
|
||||
notifyUnknownError(error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Stack direction={'row'} mb={3} spacing={3}>
|
||||
<BaseButton colorVariant='blue' startIcon={<Add />} href={pageLinks.dashboard.forbidden.create}>
|
||||
{t('create_packet')}
|
||||
</BaseButton>
|
||||
|
||||
<Modal open={open} onClose={handleClose} aria-labelledby='modal-modal-title' aria-describedby='modal-modal-description'>
|
||||
<Box sx={style}>
|
||||
<Typography id='modal-modal-title' variant='h6' component='h2'>
|
||||
{t('product_inspection')}
|
||||
</Typography>
|
||||
<Typography id='modal-modal-description' sx={{ mt: 2 }}>
|
||||
{t('enter_product')}
|
||||
</Typography>
|
||||
<AsyncSelect
|
||||
isClearable
|
||||
value={partyFilter}
|
||||
onChange={(newValue: any) => {
|
||||
setPartyFilter(newValue);
|
||||
setPage(1);
|
||||
}}
|
||||
styles={selectDefaultStyles}
|
||||
noOptionsMessage={() => t('not_found')}
|
||||
loadingMessage={() => t('loading')}
|
||||
defaultOptions={defaultPartyOptions!}
|
||||
loadOptions={partyOptions}
|
||||
placeholder={t('filter_party_name')}
|
||||
/>
|
||||
|
||||
<AsyncSelect
|
||||
isClearable
|
||||
value={boxFilter}
|
||||
onChange={(newValue: any) => {
|
||||
setBoxFilter(newValue);
|
||||
setPage(1);
|
||||
}}
|
||||
styles={selectDefaultStyles}
|
||||
noOptionsMessage={() => t('enter_box_name_to_find')}
|
||||
loadingMessage={() => t('loading')}
|
||||
defaultOptions={defaultBoxOptions!}
|
||||
loadOptions={boxOptions}
|
||||
placeholder={t('filter_box_name')}
|
||||
/>
|
||||
<BaseInput
|
||||
InputProps={{
|
||||
startAdornment: <Search color='primary' />,
|
||||
}}
|
||||
value={trackId}
|
||||
onChange={e => setTrackId(e.target.value)}
|
||||
placeholder={t('filter_item_name')}
|
||||
/>
|
||||
{trackId && trackId.length > 0 && (
|
||||
<>
|
||||
{getListQuery.loading ? (
|
||||
<Typography sx={{ mt: 2 }}>{t('loading')}...</Typography>
|
||||
) : getListQuery.data?.data && getListQuery.data?.data.length > 0 ? (
|
||||
getListQuery.data?.data.map(e => (
|
||||
<Box key={e.id} sx={{ width: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||
<Card sx={{ minWidth: 275, mb: 2 }}>
|
||||
<CardContent>
|
||||
<Typography sx={{ fontSize: 14 }}>
|
||||
{t('track_id')}: {e.trekId}
|
||||
</Typography>
|
||||
<Typography sx={{ fontSize: 14 }}>Nomi: {e.name || e.nameRu}</Typography>
|
||||
<Typography sx={{ fontSize: 14 }}>Mahsulot soni: {e.amount}</Typography>
|
||||
<Typography sx={{ fontSize: 14 }}>Paket nomi: {e?.packetName}</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Button
|
||||
sx={{ mt: '10px' }}
|
||||
onClick={() => {
|
||||
if (values[e.trekId] !== '') {
|
||||
updateItems(e, Number(values[e.trekId]));
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('confirmation')}
|
||||
</Button>
|
||||
<audio src='/sounds/success-sound.wav' autoPlay style={{ display: 'none' }}></audio>
|
||||
</Box>
|
||||
))
|
||||
) : (
|
||||
<>
|
||||
<Typography sx={{ mt: 2 }}>{t('not_found') || 'Mahsulot topilmadi'}</Typography>
|
||||
<audio src='/sounds/not_found.wav' autoPlay style={{ display: 'none' }}></audio>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Modal>
|
||||
</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);
|
||||
}}
|
||||
/>
|
||||
<AsyncSelect
|
||||
isClearable
|
||||
value={partyFilter}
|
||||
onChange={(newValue: any) => {
|
||||
setPartyFilter(newValue);
|
||||
if (newValue) {
|
||||
setBoxId(newValue.value);
|
||||
navigation.push(`${pageLinks.dashboard.boxes.index}?boxId=${newValue.value}`);
|
||||
} else {
|
||||
setBoxId(undefined);
|
||||
navigation.push(`${pageLinks.dashboard.boxes.index}`);
|
||||
}
|
||||
setPage(1);
|
||||
}}
|
||||
styles={selectDefaultStyles}
|
||||
noOptionsMessage={() => t('not_found')}
|
||||
loadingMessage={() => t('loading')}
|
||||
defaultOptions={defaultPartyOptions!}
|
||||
loadOptions={partyOptions}
|
||||
placeholder={t('filter_party_name')}
|
||||
/>
|
||||
<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} disableSortingAndStatusFetch={false} />
|
||||
</Box>
|
||||
<Stack direction={'row'} justifyContent={'center'}>
|
||||
<BasePagination page={page} pageSize={pageSize} totalCount={totalElements} onChange={handleChange} />
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardForbiddenPage;
|
||||
1
src/routes/private/forbidden/index.ts
Normal file
1
src/routes/private/forbidden/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './DashboardForbiddenPage';
|
||||
@@ -22,7 +22,7 @@ import EditItemModal from '@/routes/private/items/components/EditItemModal';
|
||||
import { notifyUnknownError } from '@/services/notification';
|
||||
import { getBoxStatusStyles, getStatusColor } from '@/theme/getStatusBoxStyles';
|
||||
import { Add, Check, Circle, Delete, Edit, FilterList, FilterListOff, Search } from '@mui/icons-material';
|
||||
import { Box, Stack, Typography } from '@mui/material';
|
||||
import { Box, FormControl, MenuItem, Select, Stack, Typography } from '@mui/material';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import AsyncSelect from 'react-select/async';
|
||||
|
||||
@@ -40,6 +40,7 @@ const DashboardGoodsPage = (props: Props) => {
|
||||
const [partyFilter, setPartyFilter] = useState<{ label: string; value: number } | undefined>(undefined);
|
||||
const [boxFilter, setBoxFilter] = useState<{ label: string; value: number } | undefined>(undefined);
|
||||
const [boxStatusFilter, setBoxStatusFilter] = useState<BoxStatus | undefined>(undefined);
|
||||
const [cargoType, setCargoType] = useState<'AUTO' | 'AVIA'>(user?.role === 'ADMIN' ? 'AVIA' : user?.cargoType || 'AUTO');
|
||||
|
||||
const [deleteIds, setDeleteIds] = useState<number[]>([]);
|
||||
|
||||
@@ -53,13 +54,14 @@ const DashboardGoodsPage = (props: Props) => {
|
||||
partyId: partyFilter?.value,
|
||||
trekId: trackKeyword,
|
||||
direction: 'desc',
|
||||
cargoType,
|
||||
sort: 'id',
|
||||
}),
|
||||
{
|
||||
selectData(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
dependencies: [page, boxStatusFilter, boxFilter, partyFilter],
|
||||
dependencies: [page, boxStatusFilter, boxFilter, partyFilter, cargoType],
|
||||
}
|
||||
);
|
||||
|
||||
@@ -133,11 +135,12 @@ const DashboardGoodsPage = (props: Props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const { data: defaultPartyOptions } = useRequest(() => party_requests.getAll({}), {
|
||||
const { data: defaultPartyOptions } = useRequest(() => party_requests.getAll({ cargoType }), {
|
||||
enabled: true,
|
||||
selectData(data) {
|
||||
return data.data.data.data.map(p => ({ value: p.id, label: p.name }));
|
||||
},
|
||||
dependencies: [cargoType],
|
||||
placeholderData: [],
|
||||
});
|
||||
|
||||
@@ -145,6 +148,7 @@ const DashboardGoodsPage = (props: Props) => {
|
||||
() =>
|
||||
box_requests.getAll({
|
||||
partyId: partyFilter?.value,
|
||||
cargoType,
|
||||
}),
|
||||
{
|
||||
enabled: !!partyFilter,
|
||||
@@ -387,7 +391,22 @@ const DashboardGoodsPage = (props: Props) => {
|
||||
/>
|
||||
|
||||
<BaseInput value={trackKeyword} onChange={handleTrackKeyword} placeholder={t('filter_track_id')} />
|
||||
|
||||
{user?.role === 'ADMIN' && (
|
||||
<FormControl size='small' sx={{ minWidth: 200 }}>
|
||||
<Select
|
||||
value={cargoType ?? ''}
|
||||
onChange={e => {
|
||||
const value = e.target.value || undefined;
|
||||
setCargoType(value as 'AVIA' | 'AUTO');
|
||||
setPage(1);
|
||||
}}
|
||||
displayEmpty
|
||||
>
|
||||
<MenuItem value='AVIA'>AVIA</MenuItem>
|
||||
<MenuItem value='AUTO'>AUTO</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
<BaseButton colorVariant='gray' startIcon={<FilterListOff />} size='small' onClick={resetFilter}>
|
||||
{t('reset_filter')}
|
||||
</BaseButton>
|
||||
|
||||
@@ -67,7 +67,7 @@ const EditItemModal = ({ onClose, open, onSuccess, item }: Props) => {
|
||||
const onSubmit = handleSubmit(async values => {
|
||||
try {
|
||||
setLoading(true);
|
||||
await item_requests.update({ itemId: item.id, ...values });
|
||||
await item_requests.update({ itemId: item.id, cargoType: item.cargoType, ...values });
|
||||
onSuccess();
|
||||
} catch (error) {
|
||||
notifyUnknownError(error);
|
||||
|
||||
@@ -4,10 +4,11 @@ import BaseButton from '@/components/ui-kit/BaseButton';
|
||||
import BaseInput from '@/components/ui-kit/BaseInput';
|
||||
import { party_requests } from '@/data/party/party.requests';
|
||||
import { pageLinks } from '@/helpers/constants';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import useInput from '@/hooks/useInput';
|
||||
import { useMyTranslation } from '@/hooks/useMyTranslation';
|
||||
import { notifyUnknownError } from '@/services/notification';
|
||||
import { Box, Stack, Typography } from '@mui/material';
|
||||
import { Box, FormControl, MenuItem, Select, Stack, Typography } from '@mui/material';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React, { useState } from 'react';
|
||||
import { useLocale } from 'use-intl';
|
||||
@@ -16,6 +17,7 @@ type Props = {
|
||||
initialValues?: {
|
||||
id: number;
|
||||
party_name: string;
|
||||
cargoType: 'AUTO' | 'AVIA';
|
||||
};
|
||||
};
|
||||
|
||||
@@ -23,18 +25,27 @@ const DashboardCreatePartyPage = ({ initialValues }: Props) => {
|
||||
const editMode = !!initialValues && !!initialValues.id;
|
||||
const t = useMyTranslation();
|
||||
const router = useRouter();
|
||||
const { user } = useAuth();
|
||||
const locale = useLocale();
|
||||
const partyNameInput = useInput(editMode ? initialValues.party_name : '');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [cargoType, setCargoType] = useState<'AUTO' | 'AVIA'>('AVIA');
|
||||
|
||||
const onSubmit: React.FormEventHandler<HTMLFormElement> = async event => {
|
||||
event.preventDefault();
|
||||
try {
|
||||
setLoading(true);
|
||||
if (editMode) {
|
||||
await party_requests.update({ name: partyNameInput.value, id: initialValues.id });
|
||||
await party_requests.update({
|
||||
name: partyNameInput.value,
|
||||
id: initialValues.id,
|
||||
cargoType: user?.role === 'ADMIN' ? cargoType : user?.cargoType || 'AVIA',
|
||||
});
|
||||
} else {
|
||||
await party_requests.create({ name: partyNameInput.value });
|
||||
await party_requests.create({
|
||||
name: partyNameInput.value,
|
||||
cargoType: user?.role === 'ADMIN' ? cargoType : user?.cargoType || 'AVIA',
|
||||
});
|
||||
}
|
||||
router.push(`/${locale}` + pageLinks.dashboard.parties.index);
|
||||
} catch (error) {
|
||||
@@ -59,11 +70,29 @@ const DashboardCreatePartyPage = ({ initialValues }: Props) => {
|
||||
{editMode ? t('update_party') : t('create_party')}
|
||||
</Typography>
|
||||
|
||||
<Stack spacing={2} maxWidth={340} mb={3}>
|
||||
<Stack spacing={10} mb={3} direction={'row'} alignItems={'end'}>
|
||||
<div>
|
||||
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850'>
|
||||
{t('party_name')}
|
||||
</Typography>
|
||||
<BaseInput placeholder={t('party_name')} value={partyNameInput.value} onChange={partyNameInput.onChange} />
|
||||
</div>
|
||||
{user?.role === 'ADMIN' && (
|
||||
<FormControl size='small' sx={{ minWidth: 200 }}>
|
||||
<Select
|
||||
value={editMode ? initialValues.cargoType : cargoType}
|
||||
onChange={e => {
|
||||
const value = e.target.value || undefined;
|
||||
setCargoType(value as 'AVIA' | 'AUTO');
|
||||
}}
|
||||
displayEmpty
|
||||
disabled={editMode}
|
||||
>
|
||||
<MenuItem value='AVIA'>AVIA</MenuItem>
|
||||
<MenuItem value='AUTO'>AUTO</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
<BaseButton type='submit' colorVariant='blue' loading={loading}>
|
||||
|
||||
@@ -5,7 +5,6 @@ import { party_requests } from '@/data/party/party.requests';
|
||||
import useRequest from '@/hooks/useRequest';
|
||||
import DashboardCreatePartyPage from '@/routes/private/parties-create/DashboardCreatePartyPage';
|
||||
import { useParams } from 'next/navigation';
|
||||
import React from 'react';
|
||||
|
||||
type Props = {};
|
||||
|
||||
@@ -22,6 +21,7 @@ const DashboardEditPartyPage = (props: Props) => {
|
||||
return {
|
||||
id: +party_id,
|
||||
party_name: data.data.data.name,
|
||||
cargoType: data.data.data.cargoType,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,37 +1,36 @@
|
||||
'use client';
|
||||
|
||||
import ActionPopMenu from '@/components/common/ActionPopMenu';
|
||||
import Loader from '@/components/common/Loader';
|
||||
import { MyTable, ColumnData } from '@/components/common/MyTable';
|
||||
import { ColumnData, MyTable } 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 BaseReactSelect from '@/components/ui-kit/BaseReactSelect';
|
||||
import { useAuthContext } from '@/context/auth-context';
|
||||
import { Party, PartyStatus, PartyStatusList, PartyStatusOptions } from '@/data/party/party.model';
|
||||
import { Party, PartyStatus, PartyStatusList } from '@/data/party/party.model';
|
||||
import { party_requests } from '@/data/party/party.requests';
|
||||
import { DEFAULT_PAGE_SIZE, pageLinks } from '@/helpers/constants';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
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 { getStatusColor } from '@/theme/getStatusBoxStyles';
|
||||
import { Add, AddCircleOutline, Circle, Delete, Download, Edit, FilterList, FilterListOff, Search } from '@mui/icons-material';
|
||||
import { Box, CircularProgress, Stack, SvgIcon, Tooltip, Typography } from '@mui/material';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { getBoxStatusStyles } from '@/theme/getStatusBoxStyles';
|
||||
import { getBoxStatusStyles, getStatusColor } from '@/theme/getStatusBoxStyles';
|
||||
import { Add, Circle, Delete, Download, Edit, FilterList, FilterListOff, Search } from '@mui/icons-material';
|
||||
import { Box, FormControl, MenuItem, Select, Stack, Typography } from '@mui/material';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
type Props = {};
|
||||
|
||||
const DashboardPartiesPage = (props: Props) => {
|
||||
const t = useMyTranslation();
|
||||
const navigation = useMyNavigation();
|
||||
const { user } = useAuth();
|
||||
|
||||
const { isAdmin, isAdminOrUzbek } = useAuthContext();
|
||||
const [cargoType, setCargoType] = useState<'AUTO' | 'AVIA'>('AVIA');
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize] = useState(DEFAULT_PAGE_SIZE);
|
||||
const { value: keyword, onChange: handleKeyword, setValue: setKeyword } = useInput('');
|
||||
@@ -56,13 +55,14 @@ const DashboardPartiesPage = (props: Props) => {
|
||||
() =>
|
||||
party_requests.getAll({
|
||||
page: page,
|
||||
cargoType: user?.role === 'ADMIN' ? cargoType : user?.cargoType,
|
||||
partyName: keyword,
|
||||
status: partyStatusFilter,
|
||||
direction: 'desc',
|
||||
sort: 'id',
|
||||
}),
|
||||
{
|
||||
dependencies: [page, partyStatusFilter],
|
||||
dependencies: [page, partyStatusFilter, cargoType],
|
||||
selectData(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
@@ -383,7 +383,22 @@ const DashboardPartiesPage = (props: Props) => {
|
||||
onChange={handleKeyword}
|
||||
placeholder={t('filter_party_name')}
|
||||
/>
|
||||
|
||||
{user?.role === 'ADMIN' && (
|
||||
<FormControl size='small' sx={{ minWidth: 200 }}>
|
||||
<Select
|
||||
value={cargoType ?? ''}
|
||||
onChange={e => {
|
||||
const value = e.target.value || undefined;
|
||||
setCargoType(value as 'AVIA' | 'AUTO');
|
||||
setPage(1);
|
||||
}}
|
||||
displayEmpty
|
||||
>
|
||||
<MenuItem value='AVIA'>AVIA</MenuItem>
|
||||
<MenuItem value='AUTO'>AUTO</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
<BaseButton colorVariant='gray' startIcon={<FilterListOff />} size='small' onClick={resetFilter}>
|
||||
{t('reset_filter')}
|
||||
</BaseButton>
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
import BaseButton from '@/components/ui-kit/BaseButton';
|
||||
import BaseIconButton from '@/components/ui-kit/BaseIconButton';
|
||||
import { useAuthContext } from '@/context/auth-context';
|
||||
import { box_requests } from '@/data/box/box.requests';
|
||||
import { item_requests } from '@/data/item/item.requests';
|
||||
import { party_requests } from '@/data/party/party.requests';
|
||||
import { FormValues, RealCreateBoxBodyType, UpdateRealBoxBodyType } from '@/data/real-box/real-box.model';
|
||||
import { real_box_requests } from '@/data/real-box/real-box.requests';
|
||||
import { pageLinks } from '@/helpers/constants';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import { useMyNavigation } from '@/hooks/useMyNavigation';
|
||||
import { useMyTranslation } from '@/hooks/useMyTranslation';
|
||||
import { notifyUnknownError } from '@/services/notification';
|
||||
@@ -62,13 +62,14 @@ interface Props {
|
||||
}
|
||||
|
||||
const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
const { user } = useAuthContext();
|
||||
const { user } = useAuth();
|
||||
const editMode = !!initialValues?.id;
|
||||
const t = useMyTranslation();
|
||||
const params = useSearchParams();
|
||||
const { push } = useMyNavigation();
|
||||
const [partyId, setPartyId] = useState<number | string>(initialValues?.partyId || '');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [cargoType, setCargoType] = useState<'AUTO' | 'AVIA'>(user?.role === 'ADMIN' ? 'AVIA' : user?.cargoType || 'AVIA');
|
||||
|
||||
const selectMenuProps = useMemo(
|
||||
() => ({
|
||||
@@ -123,7 +124,11 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
let packets: any[] = [];
|
||||
let totalPages = 1;
|
||||
try {
|
||||
const firstRes = await box_requests.getAll({ partyId, page: 1 });
|
||||
const firstRes = await box_requests.getAll({
|
||||
partyId,
|
||||
page: 1,
|
||||
cargoType: editMode ? initialValues && initialValues?.paketIds?.[0].items[0].cargoType : cargoType,
|
||||
});
|
||||
const firstData = firstRes?.data?.data;
|
||||
packets = firstData?.data || [];
|
||||
totalPages = firstData?.totalPages || 1;
|
||||
@@ -150,7 +155,11 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
let items: any[] = [];
|
||||
let totalPages = 1;
|
||||
try {
|
||||
const firstRes = await item_requests.getAll({ packetId, page: 1 });
|
||||
const firstRes = await item_requests.getAll({
|
||||
packetId,
|
||||
page: 1,
|
||||
cargoType: editMode ? initialValues && initialValues?.paketIds?.[0].items[0].cargoType : cargoType,
|
||||
});
|
||||
const firstData = firstRes?.data?.data;
|
||||
items = firstData?.data || [];
|
||||
totalPages = firstData?.totalPages || 1;
|
||||
@@ -205,8 +214,8 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
const requiredText = t('required');
|
||||
|
||||
const { data: parties = [], isLoading: isLoadingParties } = useQuery({
|
||||
queryKey: ['parties-list', 'COLLECTING'],
|
||||
queryFn: () => party_requests.getAll({ status: 'COLLECTING' }),
|
||||
queryKey: ['parties-list', 'COLLECTING', cargoType],
|
||||
queryFn: () => party_requests.getAll({ status: 'COLLECTING', cargoType }),
|
||||
select: data =>
|
||||
data.data.data.data.map((p: any) => ({
|
||||
id: p.id,
|
||||
@@ -231,6 +240,7 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
} else {
|
||||
const createBody: RealCreateBoxBodyType = {
|
||||
partyName: values.partyName,
|
||||
cargoType,
|
||||
packetItemDtos: values.packetItemDtos.map(packet => ({
|
||||
packetId: packet.packetId,
|
||||
itemDtos: packet.itemDtos,
|
||||
@@ -513,6 +523,22 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
<Typography fontSize='18px' fontWeight={500} color='#5D5850' mb={2}>
|
||||
{t('party_name')}
|
||||
</Typography>
|
||||
<Box display={'flex'} gap={2} flexDirection={'row'} justifyContent={'center'} alignItems={'center'}>
|
||||
{user?.role === 'ADMIN' && (
|
||||
<FormControl size='small' sx={{ minWidth: 200, height: '100%' }}>
|
||||
<Select
|
||||
value={editMode ? initialValues && initialValues?.paketIds?.[0].items[0].cargoType : cargoType}
|
||||
onChange={e => {
|
||||
const value = e.target.value || undefined;
|
||||
setCargoType(value as 'AVIA' | 'AUTO');
|
||||
}}
|
||||
displayEmpty
|
||||
>
|
||||
<MenuItem value='AVIA'>AVIA</MenuItem>
|
||||
<MenuItem value='AUTO'>AUTO</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
<FormControl fullWidth>
|
||||
<InputLabel id='party-select-label'>{t('party_name')}</InputLabel>
|
||||
<Controller
|
||||
@@ -557,6 +583,7 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
||||
/>
|
||||
{!!errors.partyId && <FormHelperText sx={{ color: 'red' }}>{requiredText}</FormHelperText>}
|
||||
</FormControl>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Stack
|
||||
|
||||
@@ -11,6 +11,7 @@ import { box_requests } from '@/data/box/box.requests';
|
||||
import { IRealBox } from '@/data/real-box/real-box.model';
|
||||
import { real_box_requests } from '@/data/real-box/real-box.requests';
|
||||
import { DEFAULT_PAGE_SIZE, pageLinks } from '@/helpers/constants';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import useInput from '@/hooks/useInput';
|
||||
import { useMyNavigation } from '@/hooks/useMyNavigation';
|
||||
import { useMyTranslation } from '@/hooks/useMyTranslation';
|
||||
@@ -18,8 +19,8 @@ import useRequest from '@/hooks/useRequest';
|
||||
import { file_service } from '@/services/file-service';
|
||||
import { notifyUnknownError } from '@/services/notification';
|
||||
import { Add, Delete, Download, Edit, FilterListOff, QrCode, Search } from '@mui/icons-material';
|
||||
import { Box, Button, Stack, Typography } from '@mui/material';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Box, Button, FormControl, MenuItem, Select, Stack, Typography } from '@mui/material';
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
type Props = {};
|
||||
|
||||
@@ -31,8 +32,9 @@ const DashboardRealBoxesPage = (props: Props) => {
|
||||
const [pageSize] = useState(DEFAULT_PAGE_SIZE);
|
||||
const { value: keyword, onChange: handleKeyword, setValue: setKeyword } = useInput('');
|
||||
const [boxStatusFilter, setBoxStatusFilter] = useState<BoxStatus | undefined>(undefined);
|
||||
|
||||
const { user } = useAuth();
|
||||
const [deleteIds, setDeleteIds] = useState<number[]>([]);
|
||||
const [cargoType, setCargoType] = useState<'AUTO' | 'AVIA'>('AVIA');
|
||||
const [downloadIds, setDownloadIds] = useState<number[]>([]);
|
||||
const [changeStatusIds, setChangeStatusIds] = useState<number[]>([]);
|
||||
|
||||
@@ -52,9 +54,10 @@ const DashboardRealBoxesPage = (props: Props) => {
|
||||
status: boxStatusFilter,
|
||||
direction: 'desc',
|
||||
sort: 'id',
|
||||
cargoType: user?.role === 'ADMIN' ? cargoType : user?.cargoType || 'AVIA',
|
||||
}),
|
||||
{
|
||||
dependencies: [page, boxStatusFilter],
|
||||
dependencies: [page, boxStatusFilter, cargoType],
|
||||
selectData(data) {
|
||||
return data.data.data;
|
||||
},
|
||||
@@ -83,9 +86,11 @@ const DashboardRealBoxesPage = (props: Props) => {
|
||||
|
||||
const loading = getBoxesQuery.loading;
|
||||
const handleChange = (newPage: number) => {
|
||||
if (getBoxesQuery.data?.totalElements !== 0) {
|
||||
setTimeout(() => {
|
||||
setPage(newPage);
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
const resetFilter = () => {
|
||||
@@ -152,15 +157,14 @@ const DashboardRealBoxesPage = (props: Props) => {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
setPage(1);
|
||||
getBoxesQuery.refetch();
|
||||
}, 350);
|
||||
return () => clearTimeout(timeoutId);
|
||||
}, [keyword]);
|
||||
// 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<IRealBox>[] = [
|
||||
{
|
||||
label: t('No'),
|
||||
@@ -369,7 +373,22 @@ const DashboardRealBoxesPage = (props: Props) => {
|
||||
setKeyword(e.target.value);
|
||||
}}
|
||||
/>
|
||||
|
||||
{user?.role === 'ADMIN' && (
|
||||
<FormControl size='small' sx={{ minWidth: 200 }}>
|
||||
<Select
|
||||
value={cargoType ?? ''}
|
||||
onChange={e => {
|
||||
const value = e.target.value || undefined;
|
||||
setCargoType(value as 'AVIA' | 'AUTO');
|
||||
setPage(1);
|
||||
}}
|
||||
displayEmpty
|
||||
>
|
||||
<MenuItem value='AVIA'>AVIA</MenuItem>
|
||||
<MenuItem value='AUTO'>AUTO</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
<BaseButton colorVariant='gray' startIcon={<FilterListOff />} size='small' onClick={resetFilter}>
|
||||
{t('reset_filter')}
|
||||
</BaseButton>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
'use client';
|
||||
|
||||
import { MyTable, ColumnData } from '@/components/common/MyTable';
|
||||
import { ColumnData, MyTable } from '@/components/common/MyTable';
|
||||
import BaseButton from '@/components/ui-kit/BaseButton';
|
||||
import BasePagination from '@/components/ui-kit/BasePagination';
|
||||
import { staff_requests } from '@/data/staff/staff.requests';
|
||||
import { User } from '@/data/user/user.model';
|
||||
import { DEFAULT_PAGE_SIZE, pageLinks } from '@/helpers/constants';
|
||||
import { DEFAULT_PAGE_SIZE } from '@/helpers/constants';
|
||||
import { useMyTranslation } from '@/hooks/useMyTranslation';
|
||||
import useRequest from '@/hooks/useRequest';
|
||||
import CreateStaffModal from '@/routes/private/staffs/components/CreateStaffModal';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import { Box, Stack, Typography } from '@mui/material';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
type Props = {};
|
||||
|
||||
@@ -105,6 +105,11 @@ const DashboardStaffsPage = (props: Props) => {
|
||||
label: t('role'),
|
||||
width: 300,
|
||||
},
|
||||
{
|
||||
dataKey: 'cargoType',
|
||||
label: t('cargo type'),
|
||||
width: 300,
|
||||
},
|
||||
{
|
||||
dataKey: 'active',
|
||||
label: t('status'),
|
||||
|
||||
@@ -6,7 +6,7 @@ import { staff_requests } from '@/data/staff/staff.requests';
|
||||
import { useMyTranslation } from '@/hooks/useMyTranslation';
|
||||
import { notifyUnknownError } from '@/services/notification';
|
||||
import { Box, Grid, Stack, Typography, styled } from '@mui/material';
|
||||
import React, { useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
|
||||
const StyledBox = styled(Box)`
|
||||
@@ -49,6 +49,7 @@ const CreateStaffModal = ({ onClose, open, onSuccess }: Props) => {
|
||||
password: string;
|
||||
fullName: string;
|
||||
role: string;
|
||||
cargoType: 'AUTO' | 'AVIA';
|
||||
phone: string;
|
||||
address: string;
|
||||
}>({
|
||||
@@ -84,6 +85,17 @@ const CreateStaffModal = ({ onClose, open, onSuccess }: Props) => {
|
||||
// },
|
||||
];
|
||||
|
||||
const CARGO_TYPES = [
|
||||
{
|
||||
label: 'AUTO',
|
||||
value: 'AUTO',
|
||||
},
|
||||
{
|
||||
label: 'AVIA',
|
||||
value: 'AVIA',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<BaseModal maxWidth='600px' onClose={onClose} open={open}>
|
||||
<StyledBox component={'form'} onSubmit={onSubmit}>
|
||||
@@ -119,6 +131,26 @@ const CreateStaffModal = ({ onClose, open, onSuccess }: Props) => {
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography className='label'>Cargo Type</Typography>
|
||||
<Controller
|
||||
name='cargoType'
|
||||
control={control}
|
||||
render={({ field, fieldState, formState }) => {
|
||||
return (
|
||||
<BaseReactSelect
|
||||
value={CARGO_TYPES.find(p => p.value === field.value)}
|
||||
onChange={(newValue: any) => {
|
||||
field.onChange(newValue.value);
|
||||
}}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
options={CARGO_TYPES}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography className='label'>{t('username')}</Typography>
|
||||
<BaseInput
|
||||
|
||||
Reference in New Issue
Block a user