From 57be849be299ebcc09104f70dea474be5fd85fa9 Mon Sep 17 00:00:00 2001 From: azizziy Date: Tue, 20 May 2025 17:44:47 +0500 Subject: [PATCH] feat: real boxes page added --- .../dashboard/boxes/create/loading.tsx | 9 + .../[locale]/dashboard/boxes/create/page.tsx | 22 + .../dashboard/boxes/edit/[box_id]/loading.tsx | 9 + .../dashboard/boxes/edit/[box_id]/page.tsx | 5 + src/app/[locale]/dashboard/boxes/page.tsx | 5 + .../layout/dashboard-layout/routes.tsx | 15 + src/helpers/constants.ts | 5 + .../private/boxes/DashboardBoxesPage.tsx | 2 +- .../DashboardCreateRealBox.tsx | 752 ++++++++++++++++++ .../DashboardEditRealBox.tsx | 86 ++ src/routes/private/real-boxes-create/index.ts | 1 + .../real-boxes/DashboardRealBoxesPage.tsx | 368 +++++++++ src/routes/private/real-boxes/index.ts | 1 + 13 files changed, 1279 insertions(+), 1 deletion(-) create mode 100644 src/app/[locale]/dashboard/boxes/create/loading.tsx create mode 100644 src/app/[locale]/dashboard/boxes/create/page.tsx create mode 100644 src/app/[locale]/dashboard/boxes/edit/[box_id]/loading.tsx create mode 100644 src/app/[locale]/dashboard/boxes/edit/[box_id]/page.tsx create mode 100644 src/app/[locale]/dashboard/boxes/page.tsx create mode 100644 src/routes/private/real-boxes-create/DashboardCreateRealBox.tsx create mode 100644 src/routes/private/real-boxes-create/DashboardEditRealBox.tsx create mode 100644 src/routes/private/real-boxes-create/index.ts create mode 100644 src/routes/private/real-boxes/DashboardRealBoxesPage.tsx create mode 100644 src/routes/private/real-boxes/index.ts diff --git a/src/app/[locale]/dashboard/boxes/create/loading.tsx b/src/app/[locale]/dashboard/boxes/create/loading.tsx new file mode 100644 index 0000000..3fbec00 --- /dev/null +++ b/src/app/[locale]/dashboard/boxes/create/loading.tsx @@ -0,0 +1,9 @@ +import { CircularProgress, Stack } from '@mui/material'; + +export default function DashboardLoading() { + return ( + + + + ); +} diff --git a/src/app/[locale]/dashboard/boxes/create/page.tsx b/src/app/[locale]/dashboard/boxes/create/page.tsx new file mode 100644 index 0000000..f9ffc19 --- /dev/null +++ b/src/app/[locale]/dashboard/boxes/create/page.tsx @@ -0,0 +1,22 @@ +'use client'; + +import useRequest from '@/hooks/useRequest'; +import { party_requests } from '@/data/party/party.requests'; +import Loader from '@/components/common/Loader'; +import React from 'react'; +import DashboardCreateRealBoxPage from '@/routes/private/real-boxes-create/DashboardCreateRealBox'; + +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 ; + } + + return ; +} diff --git a/src/app/[locale]/dashboard/boxes/edit/[box_id]/loading.tsx b/src/app/[locale]/dashboard/boxes/edit/[box_id]/loading.tsx new file mode 100644 index 0000000..3fbec00 --- /dev/null +++ b/src/app/[locale]/dashboard/boxes/edit/[box_id]/loading.tsx @@ -0,0 +1,9 @@ +import { CircularProgress, Stack } from '@mui/material'; + +export default function DashboardLoading() { + return ( + + + + ); +} diff --git a/src/app/[locale]/dashboard/boxes/edit/[box_id]/page.tsx b/src/app/[locale]/dashboard/boxes/edit/[box_id]/page.tsx new file mode 100644 index 0000000..d06c55b --- /dev/null +++ b/src/app/[locale]/dashboard/boxes/edit/[box_id]/page.tsx @@ -0,0 +1,5 @@ +import DashboardEditRealBoxPage from '@/routes/private/real-boxes-create/DashboardEditRealBox'; + +export default function Home() { + return ; +} diff --git a/src/app/[locale]/dashboard/boxes/page.tsx b/src/app/[locale]/dashboard/boxes/page.tsx new file mode 100644 index 0000000..80d8121 --- /dev/null +++ b/src/app/[locale]/dashboard/boxes/page.tsx @@ -0,0 +1,5 @@ +import DashboardRealBoxesPage from "@/routes/private/real-boxes/DashboardRealBoxesPage"; + +export default function Home() { + return ; +} diff --git a/src/components/layout/dashboard-layout/routes.tsx b/src/components/layout/dashboard-layout/routes.tsx index 238668d..8f04871 100644 --- a/src/components/layout/dashboard-layout/routes.tsx +++ b/src/components/layout/dashboard-layout/routes.tsx @@ -33,6 +33,21 @@ export const routes = [ ), roles: [UserRoleEnum.ADMIN, UserRoleEnum.CHINA_WORKER], }, + { + title: 'boxes', + path: pageLinks.dashboard.real_boxes.index, + icon: ( + + + + + + ), + roles: [UserRoleEnum.ADMIN, UserRoleEnum.CHINA_WORKER], + }, { title: 'packet', path: pageLinks.dashboard.boxes.index, diff --git a/src/helpers/constants.ts b/src/helpers/constants.ts index 6e4db66..3853f3e 100644 --- a/src/helpers/constants.ts +++ b/src/helpers/constants.ts @@ -18,6 +18,11 @@ export const pageLinks = { create: '/dashboard/packets/create', edit: (slug: string | number) => '/dashboard/packets/edit/' + slug, }, + real_boxes: { + index: '/dashboard/boxes', + create: '/dashboard/boxes/create', + edit: (slug: string | number) => '/dashboard/boxes/edit/' + slug, + }, items: { index: '/dashboard/items', create: '/dashboard/items/create', diff --git a/src/routes/private/boxes/DashboardBoxesPage.tsx b/src/routes/private/boxes/DashboardBoxesPage.tsx index 71588e0..ad35b65 100644 --- a/src/routes/private/boxes/DashboardBoxesPage.tsx +++ b/src/routes/private/boxes/DashboardBoxesPage.tsx @@ -335,7 +335,7 @@ const DashboardBoxesPage = (props: Props) => { color: '#000', }} > - {t('packets')} + {t('packet')} * { + 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 DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => { + const [cargoIdValue, setCargoIdValue] = useState(''); + const { user, isAdmin: isAdminUser } = useAuthContext(); + const editMode = !!initialValues && !!initialValues.id; + const isAdmin = isAdminUser && editMode; + const t = useMyTranslation(); + const params = useSearchParams(); + const navigation = useMyNavigation(); + 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({ + 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(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', ''); + }, [cargoId]); + + 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, + status: values.status, + 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, + // cargoId: item.cargoId, + trekId: item.trekId, + // name: item.name + (item.nameRu ? ` / ${item.nameRu}` : ''), + 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, + passportId: values.passportId.value, + partyId: values.partyId, + items: values.products_list.map((item: any) => { + return { + trekId: item.trekId, + name: item.name, + weight: +item.weight, + 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 ( + + + + {editMode ? t('update_packet') : t('create_packet')} + + + + + + {t('party_name')} + + { + return ( + { + 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 && {errors.partyId?.message}} + + + + + {t('status')} + + { + return ( + 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 && {errors.box_type?.message}} + + + + + {t('cargo_id')} + + + {!!errors.net_weight?.message && ( + // @ts-expect-error + {errors.net_weight?.message} + )} + + + + {t('passport')} + + + { + return ( + { + 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')} + /> + ); + }} + /> + + + + + {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 ( + + + + + {t('track_id')} + + + ID + + ), + }} + fullWidth + placeholder={t('id')} + sx={{ + '.MuiInputBase-root': { + paddingLeft: 0, + }, + }} + {...register(`products_list.${index}.trekId`, { required: requiredText })} + /> + {!!get(errors, `products_list.${index}.trekId`) && ( + {requiredText} + )} + + + + {t('name')} + + { + translateAndUpdateRussianName(event.target.value, index); + }} + /> + {!!get(errors, `products_list.${index}.name`) && ( + {requiredText} + )} + + + + {'NAME_RU'} + + + {!!get(errors, `products_list.${index}.name`) && ( + {requiredText} + )} + + + + {t('quantity')} + + + {!!get(errors, `products_list.${index}.amount`) && ( + {requiredText} + )} + + + + {t('weight')} + + + {!!get(errors, `products_list.${index}.amount`) && ( + {requiredText} + )} + + {isAdmin && ( + + + + {t('weight')} + + + + + + + + {t('price')} + + + {!!get(errors, `products_list.${index}.price`) && ( + {requiredText} + )} + + + + {t('total_price')} + + + + + )} + + + removeProduct(index)} + > + + + + + + + ); + })} + + + } onClick={appendProduct}> + {t('add_more')} + + + + + + + + {editMode ? t('update') : t('create')} + + + + ); +}; + +export default DashboardCreateRealBoxPage; diff --git a/src/routes/private/real-boxes-create/DashboardEditRealBox.tsx b/src/routes/private/real-boxes-create/DashboardEditRealBox.tsx new file mode 100644 index 0000000..ef70a4d --- /dev/null +++ b/src/routes/private/real-boxes-create/DashboardEditRealBox.tsx @@ -0,0 +1,86 @@ +'use client'; + +import Loader from '@/components/common/Loader'; +import { box_requests } from '@/data/box/box.requests'; +import useRequest from '@/hooks/useRequest'; +import DashboardCreateBoxPage from '@/routes/private/boxes-create/DashboardCreateBox'; +import { useParams } from 'next/navigation'; +import React from 'react'; + +type Props = {}; + +const DashboardEditRealBoxPage = (props: Props) => { + const params = useParams(); + const box_id = params.box_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 ; + } + + return ( + <> + + + ); +}; + +export default DashboardEditRealBoxPage; diff --git a/src/routes/private/real-boxes-create/index.ts b/src/routes/private/real-boxes-create/index.ts new file mode 100644 index 0000000..83b3a88 --- /dev/null +++ b/src/routes/private/real-boxes-create/index.ts @@ -0,0 +1 @@ +export { default } from './DashboardCreateRealBox'; diff --git a/src/routes/private/real-boxes/DashboardRealBoxesPage.tsx b/src/routes/private/real-boxes/DashboardRealBoxesPage.tsx new file mode 100644 index 0000000..680cf06 --- /dev/null +++ b/src/routes/private/real-boxes/DashboardRealBoxesPage.tsx @@ -0,0 +1,368 @@ +'use client'; + +import ActionPopMenu from '@/components/common/ActionPopMenu'; +import { MyTable, ColumnData } from '@/components/common/MyTable'; +import StatusChangePopup from '@/components/common/StatusChangePopup'; +import BaseButton from '@/components/ui-kit/BaseButton'; +import BaseIconButton from '@/components/ui-kit/BaseIconButton'; +import BaseInput from '@/components/ui-kit/BaseInput'; +import BasePagination from '@/components/ui-kit/BasePagination'; +import { useAuthContext } from '@/context/auth-context'; +import { BoxStatus, BoxStatusList, IBox } from '@/data/box/box.model'; +import { box_requests } from '@/data/box/box.requests'; +import { DEFAULT_PAGE_SIZE, pageLinks } from '@/helpers/constants'; +import useDebouncedInput from '@/hooks/useDebouncedInput'; +import useInput from '@/hooks/useInput'; +import { useMyNavigation } from '@/hooks/useMyNavigation'; +import { useMyTranslation } from '@/hooks/useMyTranslation'; +import useRequest from '@/hooks/useRequest'; +import { file_service } from '@/services/file-service'; +import { notifyUnknownError } from '@/services/notification'; +import { getBoxStatusStyles, getStatusColor } from '@/theme/getStatusBoxStyles'; +import { Add, AddCircleOutline, Circle, Delete, Download, Edit, FilterList, FilterListOff, Search, PlusOne } from '@mui/icons-material'; +import { Box, Button, Stack, Tooltip, Typography } from '@mui/material'; +import { useRouter } from 'next/navigation'; +import React, { useEffect, useMemo, useState } from 'react'; + +type Props = {}; + +const DashboardRealBoxesPage = (props: Props) => { + const t = useMyTranslation(); + const navigation = useMyNavigation(); + const { isAdmin } = useAuthContext(); + const [page, setPage] = useState(1); + const [pageSize] = useState(DEFAULT_PAGE_SIZE); + const { value: keyword, onChange: handleKeyword, setValue: setKeyword } = useInput(''); + const [boxStatusFilter, setBoxStatusFilter] = useState(undefined); + + const [deleteIds, setDeleteIds] = useState([]); + const [downloadIds, setDownloadIds] = useState([]); + const [changeStatusIds, setChangeStatusIds] = useState([]); + + const boxStatusOptions = useMemo(() => { + const p = ['READY_TO_INVOICE'] as BoxStatus[]; + if (isAdmin) { + p.push('READY'); + } + return p; + }, [isAdmin]); + + const getBoxesQuery = useRequest( + () => + box_requests.getAll({ + page: page, + cargoId: keyword, + status: boxStatusFilter, + direction: 'desc', + sort: 'id', + }), + { + dependencies: [page, boxStatusFilter], + selectData(data) { + return data.data.data; + }, + } + ); + + const { + data: list, + totalElements, + totalPages, + } = useMemo(() => { + if (getBoxesQuery.data?.data) { + return { + data: getBoxesQuery.data.data, + totalElements: getBoxesQuery.data.totalElements, + totalPages: getBoxesQuery.data.totalPages, + }; + } else { + return { + data: [], + totalElements: 0, + totalPages: 0, + }; + } + }, [getBoxesQuery]); + const loading = getBoxesQuery.loading; + + const handleChange = (newPage: number) => { + setTimeout(() => { + setPage(newPage); + }, 100); + }; + + const resetFilter = () => { + setPage(1); + setKeyword(''); + setBoxStatusFilter(undefined); + }; + + const onDelete = async (id: number) => { + if (deleteIds.includes(id)) return; + + try { + setDeleteIds(p => [...p, id]); + await box_requests.delete({ packetId: id }); + getBoxesQuery.refetch(); + } catch (error) { + notifyUnknownError(error); + } finally { + setDeleteIds(prev => prev.filter(i => i !== id)); + } + }; + + const onDownloadExcel = async (id: number) => { + if (downloadIds.includes(id)) return; + + try { + setDownloadIds(p => [...p, id]); + const response = await box_requests.downloadExcel({ packetId: id }); + const file = new File([response.data], 'Box-excel.xlsx', { type: response.data.type }); + file_service.download(file); + } catch (error) { + notifyUnknownError(error); + } finally { + setDownloadIds(prev => prev.filter(i => i !== id)); + } + }; + + const onChangeStatus = async (id: number, newStatus: BoxStatus) => { + if (changeStatusIds.includes(id)) return; + + try { + setChangeStatusIds(p => [...p, id]); + await box_requests.changeStatus({ packetId: id, status: newStatus }); + getBoxesQuery.refetch(); + } catch (error) { + notifyUnknownError(error); + } finally { + setChangeStatusIds(prev => prev.filter(i => i !== id)); + } + }; + + useEffect(() => { + const timeoutId = setTimeout(() => { + setPage(1); + getBoxesQuery.refetch(); + }, 350); + return () => clearTimeout(timeoutId); + }, [keyword]); + + // No, PartyName, PacketName, PartyTozaOg'irlik, CountOfItems, WeightOfItems, CargoID, PassportNameFamily - PacketStatusForInvoice + const columns: ColumnData[] = [ + { + label: t('No'), + width: 100, + renderCell(data, rowIndex) { + return (page - 1) * pageSize + rowIndex + 1; + }, + }, + { + dataKey: 'id', + label: "Qo'shish", + width: 120, + renderCell: data => { + return ; + }, + }, + { + dataKey: 'partyName', + label: t('party_name'), + width: 120, + }, + { + dataKey: 'name', + label: t('name'), + width: 120, + }, + { + dataKey: 'packetNetWeight', + label: t("weight"), + width: 120, + }, + { + dataKey: 'totalItems', + label: t('count_of_items'), + width: 120, + }, + { + dataKey: 'totalNetWeight', + label: t("party_weight"), + width: 120, + }, + { + dataKey: 'cargoId', + label: t('cargo_id'), + width: 120, + }, + { + dataKey: 'passportName', + label: t('client'), + width: 120, + }, + { + dataKey: 'status', + label: t('status'), + width: 240, + renderHeaderCell(rowIndex) { + return ( + + {t('box_status')} + { + return { + icon: , + label: t(stat), + onClick() { + setBoxStatusFilter(stat); + setPage(1); + }, + }; + })} + mainIcon={} + placement={{ + anchorOrigin: { + vertical: 'bottom', + horizontal: 'center', + }, + transformOrigin: { + horizontal: 'center', + vertical: 'top', + }, + }} + /> + + ); + }, + renderCell(data) { + return ( + { + return { + icon: ( + + ), + label: t(stat), + onClick: () => { + onChangeStatus(data.id, stat); + }, + }; + })} + /> + ); + }, + }, + { + label: '', + width: 100, + numeric: true, + renderCell(data, rowIndex) { + return ( + , + label: t('edit'), + onClick: () => { + navigation.push(pageLinks.dashboard.boxes.edit(data.id)); + }, + }, + { + icon: , + label: t('delete'), + onClick: () => { + onDelete(data.id); + }, + dontCloseOnClick: true, + loading: deleteIds.includes(data.id), + }, + ...(data.status === 'READY' + ? [ + { + icon: , + label: t('download_excel'), + onClick: () => { + onDownloadExcel(data.id); + }, + loading: downloadIds.includes(data.id), + dontCloseOnClick: true, + }, + ] + : []), + ]} + /> + ); + }, + }, + ]; + + return ( + + + } href={pageLinks.dashboard.boxes.create}> + {t('create_packet')} + + + + + + {t('packet')} + + + , + }} + placeholder={t('filter_packet_name')} + value={keyword} + onChange={e => { + setKeyword(e.target.value); + }} + /> + + } size='small' onClick={resetFilter}> + {t('reset_filter')} + + + + + + + + + + + + ); +}; + +export default DashboardRealBoxesPage; diff --git a/src/routes/private/real-boxes/index.ts b/src/routes/private/real-boxes/index.ts new file mode 100644 index 0000000..9283102 --- /dev/null +++ b/src/routes/private/real-boxes/index.ts @@ -0,0 +1 @@ +export { default } from './DashboardRealBoxesPage';