Merge branch 'dev' into 'main'

Dev

See merge request azizziy/cpost!4
This commit is contained in:
Azizbek Usmonov
2025-05-26 09:41:20 +05:00
14 changed files with 552 additions and 846 deletions

View File

@@ -15,7 +15,6 @@
"safe_warehouse": "安全仓库",
"we_proud_to_serve_global": "我们以为全球提供最佳的货运和运输解决方案感到自豪。",
"our_services": "我们的服务",
"air_transport": "空运",
"air_transport__subtitle": "CPost提供各类大型和重型货物的空运服务。",
"car_transport": "汽车运输",
@@ -28,13 +27,11 @@
"warehouse_yivu_and_guanchjou__subtitle": "CPost在中国提供仓储物流服务与从中国运输的车辆同时进行。",
"customs_office_service": "海关清关服务",
"customs_office_service__subtitle": "CPost重视客户的时间并提供货物的海关申报服务。按照HS和TN编码对货物进行分类。",
"pricing": {
"title": "价格",
"limit": "交货时间:",
"min_weight": "最小重量",
"more": "更多详情",
"pricing1": {
"title": "空运",
"price": "每千克从$10起",
@@ -60,7 +57,6 @@
"min_weight": "1立方米"
}
},
"our_partners": "我们的合作伙伴",
"news_subtitle": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
"contacts_subtitle": "Creative mouse is here for you to make sure you can build a more resilient portfolio. We curate bespoke offerings across alternative.",
@@ -70,7 +66,6 @@
"telegram": "Telegram",
"instagram": "Instagram",
"real_address": "塔什干市奥尔马佐尔区_______街",
"faq": {
"title": "常见问题",
"subtitle": "Creative mouse is here for you to make sure you can build a more resilient portfolio. We curate bespoke offerings across alternative.",
@@ -79,7 +74,6 @@
"answer": "大多数物流公司通过其网站或移动应用提供在线追踪服务。您可以输入跟踪号以获取货物的实时位置和状态更新。"
}
},
"footer_site_info_text": "可靠且快速的物流服务提供商",
"privacy_policy": "隐私政策",
"terms_of_use": "使用条款",
@@ -89,7 +83,6 @@
"info": "信息",
"join_tg": "加入我们的Telegram",
"all_rights_reserved": "版权所有",
"calc_obj": {
"title": "计算器",
"subtitle": "选择您的订单的运输类型和重量,以了解运费。",
@@ -107,7 +100,6 @@
"delivery_type_car": "汽车"
},
"price": "价格",
"__dash": "------------------------------------DASHBOARD------------------------------------------------------",
"dashboard": "仪表板",
"parties": "派对",
@@ -179,13 +171,10 @@
"count_of_boxes": "箱子数量",
"count_of_items": "物品数量",
"download_excel": "下载Excel",
"enter_party_name_to_find": "输入要查找的派对名称...",
"enter_box_name_to_find": "输入要查找的箱子名称...",
"enter_client_name_to_find": "输入要查找的客户名称...",
"filter_packet_name": "封包名稱過濾器",
"filter_party_name": "派对名称过滤器",
"filter_box_name": "箱子名称过滤器",
"filter_track_id": "跟踪ID过滤器",
@@ -193,7 +182,6 @@
"filter_item_name": "产品名称过滤器",
"filter_by_box_status": "箱子状态过滤器",
"filter_by_party_status": "派对状态过滤器",
"COLLECTING": "收集中",
"ON_THE_WAY": "在路上",
"ARRIVED": "已到达",
@@ -202,7 +190,6 @@
"IN_CUSTOMS": "在海關",
"IN_WAREHOUSE": "接觸客戶",
"DELIVERED": "發表",
"add_photo_to_item": "將照片加入項目",
"photo": "照片",
"summa": "總結",
@@ -221,5 +208,7 @@
"passport": "護照",
"weight_of_items": "產品重量",
"update_packet": "編輯套件",
"party_weight": "大量重量"
}
"party_weight": "大量重量",
"qr_code": "二维码",
"created_at": "添加日期"
}

View File

@@ -15,7 +15,6 @@
"safe_warehouse": "Secure Warehouse",
"we_proud_to_serve_global": "We take pride in providing the best freight and transportation solutions worldwide.",
"our_services": "Our Services",
"air_transport": "Air Transport",
"air_transport__subtitle": "CPost offers air transportation for a wide range of large-sized and heavy goods.",
"car_transport": "Car Transport",
@@ -28,13 +27,11 @@
"warehouse_yivu_and_guanchjou__subtitle": "CPost offers the use of warehouse logistics services in China simultaneously with transporting vehicles from China.",
"customs_office_service": "Customs Clearance Services",
"customs_office_service__subtitle": "CPost values its customers' time and offers customs declaration services for goods. Classification of goods by HS and TN codes.",
"pricing": {
"title": "Pricing",
"limit": "Delivery time:",
"min_weight": "Minimum Weight",
"more": "More Details",
"pricing1": {
"title": "Air Transport",
"price": "From $10/KG",
@@ -60,7 +57,6 @@
"min_weight": "1m3"
}
},
"our_partners": "Our Partners",
"news_subtitle": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
"contacts_subtitle": "Creative mouse is here for you to make sure you can build a more resilient portfolio. We curate bespoke offerings across alternative.",
@@ -70,7 +66,6 @@
"telegram": "Telegram",
"instagram": "Instagram",
"real_address": "Tashkent city, Olmazor district, _______ street",
"faq": {
"title": "Frequently Asked Questions",
"subtitle": "Creative mouse is here for you to make sure you can build a more resilient portfolio. We curate bespoke offerings across alternative.",
@@ -79,7 +74,6 @@
"answer": "Most logistics companies provide online tracking services through their websites or mobile apps. You can enter your tracking number to get real-time updates on the delivery location and status."
}
},
"footer_site_info_text": "Reliable and Fast Logistics Service Provider",
"privacy_policy": "Privacy Policy",
"terms_of_use": "Terms of Use",
@@ -89,7 +83,6 @@
"info": "Information",
"join_tg": "Join our telegram",
"all_rights_reserved": "All Rights Reserved",
"calc_obj": {
"title": "Calculator",
"subtitle": "Select the delivery type and weight of your order to find out the delivery cost",
@@ -107,7 +100,6 @@
"delivery_type_car": "Auto"
},
"price": "Price",
"__dash": "------------------------------------DASHBOARD------------------------------------------------------",
"dashboard": "Dashboard",
"parties": "Parties",
@@ -179,12 +171,10 @@
"count_of_boxes": "Count of boxes",
"count_of_items": "Count of items",
"download_excel": "Download excel",
"enter_party_name_to_find": "Enter party name to find...",
"enter_box_name_to_find": "Enter box name to find...",
"enter_client_name_to_find": "Enter client name to find...",
"filter_packet_name": "Packet name filter",
"filter_party_name": "Party name filter",
"filter_box_name": "Box name filter",
"filter_track_id": "Track id filter",
@@ -192,7 +182,6 @@
"filter_item_name": "Product name filter",
"filter_by_box_status": "Box Status filter",
"filter_by_party_status": "Party Status filter",
"COLLECTING": "Collecting",
"ON_THE_WAY": "On the way",
"ARRIVED": "Arrived",
@@ -201,7 +190,6 @@
"IN_CUSTOMS": "At customs",
"IN_WAREHOUSE": "Reaching out to customers",
"DELIVERED": "Delivered",
"add_photo_to_item": "Add photo to item",
"photo": "photo",
"summa": "summa",
@@ -220,5 +208,8 @@
"passport": "Passport",
"weight_of_items": "Products weight",
"update_packet": "Edit Paket",
"party_weight": "Batch weight"
}
"party_weight": "Batch weight",
"qr_code": "QR Code",
"created_at": "Date of joining"
}

View File

@@ -221,5 +221,8 @@
"passport": "Паспорт",
"weight_of_items": "Вес продукта",
"update_packet": "Редактировать пакет",
"party_weight": "Вес партии"
"party_weight": "Вес партии",
"qr_code": "QR код",
"created_at": "Дата добавления"
}

View File

@@ -113,7 +113,7 @@
"parties": "Partiyalar",
"create_party": "Partiya yaratish",
"update_party": "Partiyani yangilash",
"create_box": "Qutni yaratish",
"create_box": "Qutini yaratish",
"create_packet": "Paket yaratish",
"update_box": "Qutni yangilash",
"update_package": "Paketni yangilash",
@@ -220,5 +220,8 @@
"are_you_sure_delete_party": "{name} yuk partiyasini o'chirishga ishonchingiz komilmi?",
"passport": "Pasport",
"update_packet": "Paketni Tahrirlash",
"party_weight": "Partiya og'irligi"
"party_weight": "Partiya og'irligi",
"qr_code": "QR kod",
"created_at": "Qoshilgan sana"
}

View File

@@ -3,9 +3,9 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "cross-env NEXT_PUBLIC_API_URL=https://cpcargo.uz next dev --port=3080",
"dev": "cross-env NEXT_PUBLIC_API_URL=https://cpost.felixits.uz next dev --port=3080",
"build": "next build",
"build:dev": "cross-env NEXT_PUBLIC_API_URL=https://cpcargo.uz next build",
"build:dev": "cross-env NEXT_PUBLIC_API_URL=https://cpost.felixits.uz next build",
"build:prod": "cross-env NEXT_PUBLIC_API_URL=https://api.cpost-express.uz next build",
"start": "next start",
"lint": "next lint",
@@ -72,4 +72,4 @@
"vitest": "^1.1.3"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
}

View File

@@ -34,13 +34,13 @@ export const routes = [
roles: [UserRoleEnum.ADMIN, UserRoleEnum.CHINA_WORKER],
},
{
title: 'boxes',
path: pageLinks.dashboard.real_boxes.index,
title: 'packet',
path: pageLinks.dashboard.boxes.index,
icon: (
<SvgIcon fontSize='small'>
<svg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none'>
<path
d='M19 3H5C3.9 3 3 3.9 3 5V19C3 20.1 3.9 21 5 21H19C20.1 21 21 20.1 21 19V5C21 3.9 20.1 3 19 3ZM19 14H15.44C15.08 14 14.76 14.19 14.58 14.5C14.06 15.4 13.11 16 12 16C10.89 16 9.94 15.4 9.42 14.5C9.24 14.19 8.91 14 8.56 14H5V5H19V14ZM14.79 10H13V7C13 6.45 12.55 6 12 6C11.45 6 11 6.45 11 7V10H9.21C8.76 10 8.54 10.54 8.86 10.85L11.65 13.64C11.85 13.84 12.16 13.84 12.36 13.64L15.15 10.85C15.46 10.54 15.24 10 14.79 10Z'
d='M17.578 4.432L15.578 3.382C13.822 2.461 12.944 2 12 2C11.056 2 10.178 2.46 8.422 3.382L8.101 3.551L17.024 8.65L21.04 6.64C20.394 5.908 19.352 5.361 17.578 4.43M21.748 7.964L17.75 9.964V13C17.75 13.1989 17.671 13.3897 17.5303 13.5303C17.3897 13.671 17.1989 13.75 17 13.75C16.8011 13.75 16.6103 13.671 16.4697 13.5303C16.329 13.3897 16.25 13.1989 16.25 13V10.714L12.75 12.464V21.904C13.468 21.725 14.285 21.297 15.578 20.618L17.578 19.568C19.729 18.439 20.805 17.875 21.403 16.86C22 15.846 22 14.583 22 12.06V11.943C22 10.05 22 8.866 21.748 7.964ZM11.25 21.904V12.464L2.252 7.964C2 8.866 2 10.05 2 11.941V12.058C2 14.583 2 15.846 2.597 16.86C3.195 17.875 4.271 18.44 6.422 19.569L8.422 20.618C9.715 21.297 10.532 21.725 11.25 21.904ZM2.96 6.641L12 11.161L15.411 9.456L6.525 4.378L6.422 4.432C4.649 5.362 3.606 5.909 2.96 6.642'
fill='currentColor'
/>
</svg>
@@ -49,13 +49,13 @@ export const routes = [
roles: [UserRoleEnum.ADMIN, UserRoleEnum.CHINA_WORKER],
},
{
title: 'packet',
path: pageLinks.dashboard.boxes.index,
title: 'boxes',
path: pageLinks.dashboard.real_boxes.index,
icon: (
<SvgIcon fontSize='small'>
<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none'>
<svg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
d='M17.578 4.432L15.578 3.382C13.822 2.461 12.944 2 12 2C11.056 2 10.178 2.46 8.422 3.382L8.101 3.551L17.024 8.65L21.04 6.64C20.394 5.908 19.352 5.361 17.578 4.43M21.748 7.964L17.75 9.964V13C17.75 13.1989 17.671 13.3897 17.5303 13.5303C17.3897 13.671 17.1989 13.75 17 13.75C16.8011 13.75 16.6103 13.671 16.4697 13.5303C16.329 13.3897 16.25 13.1989 16.25 13V10.714L12.75 12.464V21.904C13.468 21.725 14.285 21.297 15.578 20.618L17.578 19.568C19.729 18.439 20.805 17.875 21.403 16.86C22 15.846 22 14.583 22 12.06V11.943C22 10.05 22 8.866 21.748 7.964ZM11.25 21.904V12.464L2.252 7.964C2 8.866 2 10.05 2 11.941V12.058C2 14.583 2 15.846 2.597 16.86C3.195 17.875 4.271 18.44 6.422 19.569L8.422 20.618C9.715 21.297 10.532 21.725 11.25 21.904ZM2.96 6.641L12 11.161L15.411 9.456L6.525 4.378L6.422 4.432C4.649 5.362 3.606 5.909 2.96 6.642'
d='M19 3H5C3.9 3 3 3.9 3 5V19C3 20.1 3.9 21 5 21H19C20.1 21 21 20.1 21 19V5C21 3.9 20.1 3 19 3ZM19 14H15.44C15.08 14 14.76 14.19 14.58 14.5C14.06 15.4 13.11 16 12 16C10.89 16 9.94 15.4 9.42 14.5C9.24 14.19 8.91 14 8.56 14H5V5H19V14ZM14.79 10H13V7C13 6.45 12.55 6 12 6C11.45 6 11 6.45 11 7V10H9.21C8.76 10 8.54 10.54 8.86 10.85L11.65 13.64C11.85 13.84 12.16 13.84 12.36 13.64L15.15 10.85C15.46 10.54 15.24 10 14.79 10Z'
fill='currentColor'
/>
</svg>

View File

@@ -0,0 +1,44 @@
export type BoxStatus = 'READY_TO_INVOICE' | 'READY';
export const BoxStatusList: BoxStatus[] = ['READY_TO_INVOICE', 'READY'];
export interface IRealBox {
boxName: string;
id: number;
packetsCount: number;
partyName: string;
}
export interface IRealBoxDetail {
message: string;
data: {
boxName: string;
party: {
id: number;
name: string;
};
packets: Array<{
id: number;
packetName: string;
}>;
};
}
export interface RealCreateBoxBodyType {
partyName: string;
packetDtos: number[];
}
export interface UpdateRealBoxBodyType {
boxId: string;
partyName: string;
packetDtos: number[];
}
export interface FormValues {
partyName: string;
paketIds: Array<{ id: string | number }>;
id?: number;
boxId?: string;
partyId?: number;
}

View File

@@ -0,0 +1,67 @@
import { IBox, UpdateBoxBodyType, IBoxDetail, 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';
export const real_box_requests = {
async getAll(params?: {
page?: number;
sort?: string;
direction?: string;
cargoId?: string;
boxName?: string,
partyId?: string | number;
status?: BoxStatus;
}) {
return request.get<CommonResponseType<PageAble<IRealBox>>>('/boxes/list', { params });
},
async create({ ...body }: RealCreateBoxBodyType) {
return request.post<CommonResponseType>('/boxes/create', body);
},
async update({ boxId, ...body }: UpdateRealBoxBodyType) {
return request.put<CommonResponseType>('/boxes/change', body, {
params: { boxId },
});
},
async find(params: { boxId?: number | string }) {
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/`, { params });
},
async downloadQrCode(params: { boxId: number | string }) {
return request.get(`/qr/${params.boxId}`, { responseType: 'blob' });
},
async translateWithGoogleApi(params: { text: string }): Promise<string> {
const response = await axios.post('https://translation.googleapis.com/language/translate/v2', undefined, {
params: {
q: params.text,
target: 'ru',
source: 'zh',
key: 'AIzaSyA5uAPZyjF_yo1hYOWWJ2uP7XgcmohZc8o',
},
});
return response.data.data.translations?.[0]?.translatedText ?? '';
},
async translateWithMemoryApi(params: { text: string }): Promise<string> {
const response = await axios.get<{
responseData: {
translatedText: string;
match: number;
};
}>('https://api.mymemory.translated.net/get', {
params: {
q: params.text,
langpair: 'zh|ru',
// key: '7a4ac2e07cde1ff1e9de',
},
});
return response.data.responseData.translatedText;
},
};

View File

@@ -193,7 +193,7 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
});
useEffect(() => {
setValue('passportId', '');
setValue('passportId', 'AA1234567');
}, [cargoId]);
const { data: defaultParties, status: defaultPartiesStatus } = useRequest(() => party_requests.getAll({ status: 'COLLECTING' }), {

View File

@@ -71,6 +71,7 @@ const DashboardEditBoxPage = (props: Props) => {
}
);
console.log(getOneBox, "pkets");
if (getOneBox.loading || !getOneBox.data) {
return <Loader p={8} size={96} />;

View File

@@ -1,320 +1,182 @@
'use client';
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 { notifyError, notifyUnknownError } from '@/services/notification';
import { Box, Divider, FormHelperText, Grid, Stack, Typography, styled } from '@mui/material';
import { useParams, useRouter, useSearchParams } from 'next/navigation';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { useLocale } from 'use-intl';
import { Controller, useFieldArray, useForm } from 'react-hook-form';
import BaseIconButton from '@/components/ui-kit/BaseIconButton';
import { AddCircleRounded, Close } from '@mui/icons-material';
import { party_requests } from '@/data/party/party.requests';
import { box_requests } from '@/data/box/box.requests';
import useRequest from '@/hooks/useRequest';
import { useMyTranslation } from '@/hooks/useMyTranslation';
import { BoxStatus, CreateBoxBodyType, UpdateBoxBodyType } from '@/data/box/box.model';
import BaseReactSelect, { selectDefaultStyles } from '@/components/ui-kit/BaseReactSelect';
import { customer_requests } from '@/data/customers/customer.requests';
import { real_box_requests } from '@/data/real-box/real-box.requests';
import { pageLinks } from '@/helpers/constants';
import { notifyUnknownError } from '@/services/notification';
import { Box, FormHelperText, Grid, Stack, Typography, styled } from '@mui/material';
import { AddCircleRounded, Close } from '@mui/icons-material';
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';
import { selectDefaultStyles } from '@/components/ui-kit/BaseReactSelect';
import { useAuthContext } from '@/context/auth-context';
import { useMyNavigation } from '@/hooks/useMyNavigation';
import AsyncSelect from 'react-select/async';
import { Party } from '@/data/party/party.model';
import cloneDeep from 'lodash.clonedeep';
import { item_requests } from '@/data/item/item.requests';
import { useMyTranslation } from '@/hooks/useMyTranslation';
import { FormValues, RealCreateBoxBodyType, UpdateRealBoxBodyType } from '@/data/real-box/real-box.model';
import get from 'lodash.get';
import Loader from '@/components/common/Loader';
import { Customer } from '@/data/customers/customer.model';
import { Passport } from '@/data/passport/passport.model';
import { passport_requests } from '@/data/passport/passport.request';
import useRequest from '@/hooks/useRequest';
const StyledCreateBox = styled(Box)`
.item-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.item-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.item-row-field {
}
& > * {
flex: 1 1 1;
}
& > * {
flex: 1 1 1;
}
`;
type Props = {
interface 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;
}[];
id?: number;
boxId?: string;
partyId?: number;
partyName?: string;
paketIds?: Array<{ id: number; packetName: string }>;
};
};
}
const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
const [cargoIdValue, setCargoIdValue] = useState<string>('');
const { user, isAdmin: isAdminUser } = useAuthContext();
const editMode = !!initialValues && !!initialValues.id;
const isAdmin = isAdminUser && editMode;
const editMode = !!initialValues?.id;
const t = useMyTranslation();
const params = useSearchParams();
const navigation = useMyNavigation();
const { push } = useMyNavigation();
const [partyId, setPartyId] = useState<number | string>('');
const [loading, setLoading] = useState(false);
const helperRef = useRef<{
finished: boolean;
partyFinished: boolean;
clientFinished: boolean;
settedDefaultParty: Party | null;
settedDefaultClient: Customer | null;
// settedDefaultPartyValue: { value: number; label: string }[] | null;
settedDefaultParty: any;
}>({
settedDefaultParty: null,
settedDefaultClient: null,
partyFinished: false,
clientFinished: false,
finished: false,
// settedDefaultPartyValue: partiesData?.[0] || null,
});
const {
register,
control,
handleSubmit,
watch,
setValue,
reset,
formState: { errors },
} = useForm<any>({
} = useForm<FormValues>({
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,
partyName: initialValues?.partyName || '',
paketIds: initialValues?.paketIds
? initialValues.paketIds.map((paket) => ({ id: paket.id }))
: params.get('party_id')
? [{ id: +params.get('party_id')! }]
: [{ id: '' }],
id: initialValues?.id,
boxId: initialValues?.boxId,
partyId: initialValues?.partyId,
},
});
const [loading, setLoading] = useState(false);
const products = useFieldArray({
// Reset form when initialValues change (for edit mode)
useEffect(() => {
if (initialValues) {
reset({
partyName: initialValues.partyName || '',
paketIds: initialValues.paketIds
? initialValues.paketIds.map((paket) => ({ id: paket.id }))
: [{ id: '' }],
id: initialValues.id,
boxId: initialValues.boxId,
partyId: initialValues.partyId,
});
if (initialValues.partyId) {
setPartyId(initialValues.partyId);
}
}
}, [initialValues, reset]);
const { fields, append, remove } = useFieldArray({
control,
name: 'products_list',
name: 'paketIds',
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', '');
}, [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(
const getBoxesQuery = useRequest(
() =>
customer_requests.getAll({
page: 1,
box_requests.getAll({
partyId: partyId,
}),
{
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);
selectData: (data) => data?.data?.data ?? [],
enabled: !!partyId,
},
);
const list = useMemo(() => {
return getBoxesQuery.data?.data.filter((box: any) => box.status === 'READY_TO_INVOICE') ?? [];
}, [getBoxesQuery.data]);
useEffect(() => {
if (partyId) {
getBoxesQuery.refetch();
}
}, [partyId, getBoxesQuery.refetch]);
const { data: defaultParties } = useRequest(
() => party_requests.getAll({ status: 'COLLECTING' }),
{
enabled: true,
selectData: (data) =>
data.data.data.data.map((p: any) => ({ value: p.id, label: p.name })),
onSuccess: (data) => {
if (!editMode && data?.data?.data?.data?.[0]) {
const defaultParty = data.data.data.data[0];
helperRef.current.settedDefaultParty = defaultParty;
setValue('partyName', defaultParty.name);
setValue('paketIds.0.id', defaultParty.id); // Use dot notation
setPartyId(defaultParty.id);
}
helperRef.current.clientFinished = true;
if (helperRef.current.partyFinished) {
helperRef.current.partyFinished = true;
if (helperRef.current.clientFinished) {
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 => {
const onSubmit = handleSubmit(async (values) => {
try {
setLoading(true);
const packetDtos = values.paketIds.map((paket) => Number(paket.id)).filter((id) => id);
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 updateBody: UpdateRealBoxBodyType = {
boxId: initialValues!.boxId!,
partyName: values.partyName,
packetDtos,
};
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);
await real_box_requests.update(updateBody);
} 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,
};
}),
const createBody: RealCreateBoxBodyType = {
partyName: values.partyName,
packetDtos,
};
await box_requests.create(createBody);
await real_box_requests.create(createBody);
}
navigation.push(pageLinks.dashboard.boxes.index);
push(pageLinks.dashboard.real_boxes.index);
} catch (error) {
notifyUnknownError(error);
} finally {
@@ -322,79 +184,26 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
}
});
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;
const partyOptions = async (inputValue: string) => {
try {
// const responseText = await box_requests.translateWithGoogleApi({ text });
const responseText = await box_requests.translateWithMemoryApi({ text });
setValue(`products_list.${index}.nameRu`, responseText || '');
const res = await party_requests.getAll({
status: 'COLLECTING',
partyName: inputValue,
});
return res.data.data.data.map((p: any) => ({ label: p.name, value: p.id }));
} catch (error) {
console.error(error);
notifyError('Translation api error');
notifyUnknownError(error);
return [];
}
};
const boxTypes = [
{
label: 'KG',
value: 'KG',
},
{
label: 'GABARIT',
value: 'GABARIT',
},
];
const appendPaket = () => {
append({ id: '' });
};
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]);
const removePaket = (index: number) => {
remove(index);
};
return (
<StyledCreateBox
@@ -406,134 +215,54 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
backgroundColor: '#fff',
}}
>
<Box component={'form'} onSubmit={onSubmit}>
<Typography variant='h5' mb={3.5}>
{editMode ? t('update_packet') : t('create_packet')}
<Box component="form" onSubmit={onSubmit}>
<Typography variant="h5" mb={3.5}>
{editMode ? t('update_box') : t('create_box')}
</Typography>
<Grid container columnSpacing={2.5} rowSpacing={3} mb={3.5}>
<Grid item xs={5}>
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
<Grid item xs={12}>
<Typography fontSize="18px" fontWeight={500} color="#5D5850" mb={2}>
{t('party_name')}
</Typography>
<Controller
name='partyId'
name="partyName"
control={control}
rules={{ required: requiredText }}
render={({ field, fieldState, formState }) => {
return (
<AsyncSelect
onChange={(newValue: any) => {
field.onChange(newValue.value);
}}
defaultValue={
editMode
render={({ field }) => (
<AsyncSelect
onChange={(newValue: any) => {
field.onChange(newValue?.label ?? '');
setPartyId(newValue?.value ?? '');
}}
defaultValue={
editMode
? {
value: initialValues?.partyId,
label: initialValues?.partyName,
}
: partiesData?.length
? {
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')}
/>
);
}}
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>
{!!errors.partyName && (
<FormHelperText sx={{ color: 'red' }}>{requiredText}</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
@@ -544,196 +273,83 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
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>
),
<Typography fontSize="18px" fontWeight={500} color="#5D5850" mb={2}>
{t('packet')}
</Typography>
{fields.map((field, index) => (
<Box key={field.key} className="item-row" mb={2}>
<Box className="item-row-field" flex={1}>
<Controller
name={`paketIds.${index}.id`}
control={control}
rules={{ required: requiredText }}
render={({ field: paketField }) => (
<AsyncSelect
onChange={(newValue: any) => {
paketField.onChange(newValue?.value ?? '');
}}
fullWidth
placeholder={t('id')}
sx={{
'.MuiInputBase-root': {
paddingLeft: 0,
},
defaultValue={
editMode && initialValues?.paketIds?.[index]
? {
value: initialValues.paketIds[index].id,
label:
initialValues.paketIds[index].packetName ||
`Box ${initialValues.paketIds[index].id}`,
}
: null
}
styles={selectDefaultStyles}
noOptionsMessage={() => t('not_found')}
loadingMessage={() => t('loading')}
onBlur={paketField.onBlur}
name={paketField.name}
defaultOptions={list.map((box: any) => ({
value: box.id,
label: box.box_name || box.name || `Box ${box.id}`,
}))}
loadOptions={async (inputValue: string) => {
if (!partyId) return [];
try {
const res = await box_requests.getAll({
partyId,
});
return res.data.data.data.map((box: any) => ({
label: box.box_name || box.name || `Box ${box.id}`,
value: box.id,
}));
} catch (error) {
notifyUnknownError(error);
return [];
}
}}
{...register(`products_list.${index}.trekId`, { required: requiredText })}
placeholder={t('enter_box_name_to_find')}
/>
{!!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' />
/>
{!!get(errors, `paketIds.${index}.id`) && (
<FormHelperText sx={{ color: 'red' }}>{requiredText}</FormHelperText>
)}
</Box>
);
})}
<Stack alignItems={'center'}>
<BaseButton sx={{ backgroundColor: '#239D5F' }} startIcon={<AddCircleRounded />} onClick={appendProduct}>
{fields.length > 1 && (
<Box className="item-row-field">
<BaseIconButton
size="small"
colorVariant="icon-error"
sx={{ flexShrink: 0, height: 'auto', marginTop: 1 }}
onClick={() => removePaket(index)}
>
<Close />
</BaseIconButton>
</Box>
)}
</Box>
))}
<Stack alignItems="center" mt={2}>
<BaseButton
sx={{ backgroundColor: '#239D5F' }}
startIcon={<AddCircleRounded />}
onClick={appendPaket}
>
{t('add_more')}
</BaseButton>
</Stack>
@@ -741,7 +357,7 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
</Grid>
</Grid>
<BaseButton type='submit' colorVariant='blue' loading={loading}>
<BaseButton type="submit" colorVariant="blue" loading={loading}>
{editMode ? t('update') : t('create')}
</BaseButton>
</Box>
@@ -749,4 +365,4 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
);
};
export default DashboardCreateRealBoxPage;
export default DashboardCreateRealBoxPage;

View File

@@ -1,11 +1,11 @@
'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';
import React, { useEffect } from 'react';
import DashboardCreateRealBoxPage from './DashboardCreateRealBox';
import { real_box_requests } from '@/data/real-box/real-box.requests';
type Props = {};
@@ -15,70 +15,39 @@ const DashboardEditRealBoxPage = (props: Props) => {
const getOneBox = useRequest(
() => {
return box_requests.find({ packetId: box_id });
return real_box_requests.find({ boxId: box_id });
},
{
selectData(data) {
const boxData = data.data.data;
const boxData = data.data; // ⬅️ Faqat data.data, data.data.data emas
if (!boxData) return null;
console.log(boxData.data, 'boxdata');
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,
};
}),
],
id: box_id,
boxId: box_id!,
partyId: boxData.data.party.id, // ⬅️ Togri yol
partyName: boxData.data.party.name!,
paketIds: boxData.data.packets!
};
},
}
);
useEffect(() => {
getOneBox.refetch()
}, [box_id])
console.log(getOneBox);
if (getOneBox.loading || !getOneBox.data) {
return <Loader p={8} size={96} />;
}
return (
<>
<DashboardCreateBoxPage initialValues={getOneBox.data} />
<DashboardCreateRealBoxPage initialValues={getOneBox.data as any} />
</>
);
};

View File

@@ -10,6 +10,8 @@ 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 { 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 useDebouncedInput from '@/hooks/useDebouncedInput';
import useInput from '@/hooks/useInput';
@@ -19,9 +21,10 @@ 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 { Add, QrCode, 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 { log } from 'node:console';
import React, { useEffect, useMemo, useState } from 'react';
type Props = {};
@@ -49,7 +52,7 @@ const DashboardRealBoxesPage = (props: Props) => {
const getBoxesQuery = useRequest(
() =>
box_requests.getAll({
real_box_requests.getAll({
page: page,
cargoId: keyword,
status: boxStatusFilter,
@@ -83,8 +86,8 @@ const DashboardRealBoxesPage = (props: Props) => {
};
}
}, [getBoxesQuery]);
const loading = getBoxesQuery.loading;
const loading = getBoxesQuery.loading;
const handleChange = (newPage: number) => {
setTimeout(() => {
setPage(newPage);
@@ -102,7 +105,7 @@ const DashboardRealBoxesPage = (props: Props) => {
try {
setDeleteIds(p => [...p, id]);
await box_requests.delete({ packetId: id });
await real_box_requests.delete({ boxId: id });
getBoxesQuery.refetch();
} catch (error) {
notifyUnknownError(error);
@@ -116,7 +119,7 @@ const DashboardRealBoxesPage = (props: Props) => {
try {
setDownloadIds(p => [...p, id]);
const response = await box_requests.downloadExcel({ packetId: id });
const response = await real_box_requests.downloadExcel({ boxId: id });
const file = new File([response.data], 'Box-excel.xlsx', { type: response.data.type });
file_service.download(file);
} catch (error) {
@@ -126,6 +129,23 @@ const DashboardRealBoxesPage = (props: Props) => {
}
};
const onDownloadQrCode = async (id: number) => {
if (downloadIds.includes(id)) return;
try {
setDownloadIds(p => [...p, id]);
const response = await real_box_requests.downloadQrCode({ boxId: id });
console.log(response, "rres");
const file = new File([response.data], 'qr.png', { type: response.data.type });
file_service.download(file);
} catch (error) {
notifyUnknownError(error);
console.log(error);
} finally {
setDownloadIds(prev => prev.filter(i => i !== id));
}
};
const onChangeStatus = async (id: number, newStatus: BoxStatus) => {
if (changeStatusIds.includes(id)) return;
@@ -149,7 +169,7 @@ const DashboardRealBoxesPage = (props: Props) => {
}, [keyword]);
// No, PartyName, PacketName, PartyTozaOg'irlik, CountOfItems, WeightOfItems, CargoID, PassportNameFamily - PacketStatusForInvoice
const columns: ColumnData<IBox>[] = [
const columns: ColumnData<IRealBox>[] = [
{
label: t('No'),
width: 100,
@@ -158,113 +178,125 @@ const DashboardRealBoxesPage = (props: Props) => {
},
},
{
dataKey: 'id',
label: "Qo'shish",
label: t("qr_code"),
width: 120,
renderCell: data => {
return <Button onClick={() => navigation.push(pageLinks.dashboard.boxes.edit(data.id))}>
return <Button
onClick={() =>
onDownloadQrCode(data.id)
}>
<QrCode />
</Button >;
},
},
{
dataKey: 'id',
label: t("add_more"),
width: 120,
renderCell: data => {
return <Button onClick={() => navigation.push(pageLinks.dashboard.real_boxes.edit(data.id))}>
<Add />
</Button>;
},
},
{
dataKey: 'partyName',
label: t('party_name'),
width: 120,
},
{
dataKey: 'name',
label: t('name'),
width: 120,
},
{
dataKey: 'packetNetWeight',
label: t("weight"),
width: 120,
},
{
dataKey: 'totalItems',
label: t('count_of_items'),
width: 120,
},
{
dataKey: 'totalNetWeight',
label: t("party_weight"),
width: 120,
},
{
dataKey: 'cargoId',
dataKey: 'boxName',
label: t('cargo_id'),
width: 120,
},
{
dataKey: 'passportName',
label: t('client'),
dataKey: "boxName",
label: t('party_name'),
width: 120,
},
// {
// dataKey: 'name',
// label: t('name'),
// width: 120,
// },
// {
// dataKey: 'packetNetWeight',
// label: t("weight"),
// width: 120,
// },
{
dataKey: 'status',
label: t('status'),
width: 240,
renderHeaderCell(rowIndex) {
return (
<Stack direction={'row'} alignItems={'center'}>
<span>{t('box_status')}</span>
<ActionPopMenu
buttons={BoxStatusList.map(stat => {
return {
icon: <Circle sx={{ path: { color: getStatusColor(stat) } }} />,
label: t(stat),
onClick() {
setBoxStatusFilter(stat);
setPage(1);
},
};
})}
mainIcon={<FilterList />}
placement={{
anchorOrigin: {
vertical: 'bottom',
horizontal: 'center',
},
transformOrigin: {
horizontal: 'center',
vertical: 'top',
},
}}
/>
</Stack>
);
},
renderCell(data) {
return (
<StatusChangePopup
anchor={{
status: data.status,
text: t(data.status),
}}
loading={changeStatusIds.includes(data.id)}
buttons={boxStatusOptions.map(stat => {
return {
icon: (
<Circle
sx={{
path: {
color: getStatusColor(stat),
},
}}
/>
),
label: t(stat),
onClick: () => {
onChangeStatus(data.id, stat);
},
};
})}
/>
);
},
dataKey: 'packetsCount',
label: t('count_of_items'),
width: 120,
},
// {
// dataKey: 'totalNetWeight',
// label: t("party_weight"),
// width: 120,
// },
// {
// dataKey: 'passportName',
// label: t('client'),
// width: 120,
// },
// {
// dataKey: 'status',
// label: t('status'),
// width: 240,
// renderHeaderCell(rowIndex) {
// return (
// <Stack direction={'row'} alignItems={'center'}>
// <span>{t('box_status')}</span>
// <ActionPopMenu
// buttons={BoxStatusList.map(stat => {
// return {
// icon: <Circle sx={{ path: { color: getStatusColor(stat) } }} />,
// label: t(stat),
// onClick() {
// setBoxStatusFilter(stat);
// setPage(1);
// },
// };
// })}
// mainIcon={<FilterList />}
// placement={{
// anchorOrigin: {
// vertical: 'bottom',
// horizontal: 'center',
// },
// transformOrigin: {
// horizontal: 'center',
// vertical: 'top',
// },
// }}
// />
// </Stack>
// );
// },
// renderCell(data) {
// return (
// <StatusChangePopup
// anchor={{
// status: data.status,
// text: t(data.status),
// }}
// loading={changeStatusIds.includes(data.id)}
// buttons={boxStatusOptions.map(stat => {
// return {
// icon: (
// <Circle
// sx={{
// path: {
// color: getStatusColor(stat),
// },
// }}
// />
// ),
// label: t(stat),
// onClick: () => {
// onChangeStatus(data.id, stat);
// },
// };
// })}
// />
// );
// },
// },
{
label: '',
width: 100,
@@ -277,7 +309,7 @@ const DashboardRealBoxesPage = (props: Props) => {
icon: <Edit sx={{ path: { color: '#3489E4' } }} />,
label: t('edit'),
onClick: () => {
navigation.push(pageLinks.dashboard.boxes.edit(data.id));
navigation.push(pageLinks.dashboard.real_boxes.edit(data.id));
},
},
{
@@ -289,19 +321,15 @@ const DashboardRealBoxesPage = (props: Props) => {
dontCloseOnClick: true,
loading: deleteIds.includes(data.id),
},
...(data.status === 'READY'
? [
{
icon: <Download sx={{ path: { color: '#3489E4' } }} />,
label: t('download_excel'),
onClick: () => {
onDownloadExcel(data.id);
},
loading: downloadIds.includes(data.id),
dontCloseOnClick: true,
},
]
: []),
{
icon: <Download sx={{ path: { color: '#3489E4' } }} />,
label: t('download_excel'),
onClick: () => {
onDownloadExcel(data.id);
},
loading: downloadIds.includes(data.id),
dontCloseOnClick: true,
},
]}
/>
);
@@ -312,8 +340,8 @@ const DashboardRealBoxesPage = (props: Props) => {
return (
<Box>
<Stack direction={'row'} mb={3} spacing={3}>
<BaseButton colorVariant='blue' startIcon={<Add />} href={pageLinks.dashboard.boxes.create}>
{t('create_packet')}
<BaseButton colorVariant='blue' startIcon={<Add />} href={pageLinks.dashboard.real_boxes.create}>
{t('create_box')}
</BaseButton>
</Stack>
<Box
@@ -335,7 +363,7 @@ const DashboardRealBoxesPage = (props: Props) => {
color: '#000',
}}
>
{t('packet')}
{t('boxes')}
</Typography>
<Stack direction={'row'} alignItems={'center'} spacing={2}>
<BaseInput
@@ -355,7 +383,7 @@ const DashboardRealBoxesPage = (props: Props) => {
</Stack>
</Stack>
<Box mb={6}>
<MyTable columns={columns} data={list} loading={loading} />
<MyTable columns={columns as any} data={list} loading={loading} />
</Box>
<Stack direction={'row'} justifyContent={'center'}>
<BasePagination page={page} pageSize={pageSize} totalCount={totalElements} onChange={handleChange} />

View File

@@ -330,10 +330,10 @@
resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz"
integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==
"@esbuild/darwin-x64@0.21.5":
"@esbuild/win32-x64@0.21.5":
version "0.21.5"
resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz"
integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==
resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz"
integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
"@eslint-community/eslint-utils@^4.2.0":
version "4.4.0"
@@ -592,10 +592,10 @@
dependencies:
glob "7.1.7"
"@next/swc-darwin-x64@14.0.4":
"@next/swc-win32-x64-msvc@14.0.4":
version "14.0.4"
resolved "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.4.tgz"
integrity sha512-IZQ3C7Bx0k2rYtrZZxKKiusMTM9WWcK5ajyhOZkYYTCc8xytmwSzR1skU7qLgVT/EY9xtXDG0WhY6fyujnI3rw==
resolved "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.4.tgz"
integrity sha512-yEh2+R8qDlDCjxVpzOTEpBLQTEFAcP2A8fUFLaWNap9GitYKkKv1//y2S6XY6zsR4rCOPRpU7plYDR+az2n30A==
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
@@ -623,10 +623,10 @@
resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz"
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
"@rollup/rollup-darwin-x64@4.34.8":
"@rollup/rollup-win32-x64-msvc@4.34.8":
version "4.34.8"
resolved "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz"
integrity sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==
resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz"
integrity sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==
"@rushstack/eslint-patch@^1.3.3":
version "1.6.1"
@@ -2085,11 +2085,6 @@ fs.realpath@^1.0.0:
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@~2.3.2, fsevents@~2.3.3:
version "2.3.3"
resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.1, function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"