Merge branch 'samandar' into 'dev'
Samandar See merge request azizziy/cpost!34
This commit is contained in:
@@ -212,5 +212,10 @@
|
|||||||
"qr_code": "二维码",
|
"qr_code": "二维码",
|
||||||
"created_at": "添加日期",
|
"created_at": "添加日期",
|
||||||
"download_all_items_exel": "将所有产品上传至Excel",
|
"download_all_items_exel": "将所有产品上传至Excel",
|
||||||
"select_all": "全选,"
|
"select_all": "全选,",
|
||||||
|
"product_inspection": "产品检验",
|
||||||
|
"enter_product": "请输入产品的追溯ID",
|
||||||
|
"confirmation": "确认",
|
||||||
|
"view_packet": "查看包裹数据",
|
||||||
|
"accepted_number": "已接收"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,5 +212,10 @@
|
|||||||
"download_all_items_exel": "Upload all products in Excel",
|
"download_all_items_exel": "Upload all products in Excel",
|
||||||
"select_all": "Select all",
|
"select_all": "Select all",
|
||||||
"qr_code": "QR Code",
|
"qr_code": "QR Code",
|
||||||
"created_at": "Date of joining"
|
"created_at": "Date of joining",
|
||||||
|
"product_inspection": "Product Inspection",
|
||||||
|
"enter_product": "Enter your product tracking ID",
|
||||||
|
"confirmation": "Confirm",
|
||||||
|
"view_packet": "View package data",
|
||||||
|
"accepted_number": "Accepted"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,7 +224,11 @@
|
|||||||
"party_weight": "Вес партии",
|
"party_weight": "Вес партии",
|
||||||
"download_all_items_exel": "Загрузить все товары в Excel",
|
"download_all_items_exel": "Загрузить все товары в Excel",
|
||||||
"select_all": "Выделить все",
|
"select_all": "Выделить все",
|
||||||
|
"product_inspection": "Проверка товаров",
|
||||||
|
"enter_product": "Enter your product tracking ID",
|
||||||
|
"confirmation": "Подтверждение",
|
||||||
|
"view_packet": "Просмотреть данные посылки",
|
||||||
|
"accepted_number": "Принято",
|
||||||
"qr_code": "QR код",
|
"qr_code": "QR код",
|
||||||
"created_at": "Дата добавления"
|
"created_at": "Дата добавления"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,6 +117,8 @@
|
|||||||
"create_packet": "Paket yaratish",
|
"create_packet": "Paket yaratish",
|
||||||
"update_box": "Qutni yangilash",
|
"update_box": "Qutni yangilash",
|
||||||
"update_package": "Paketni yangilash",
|
"update_package": "Paketni yangilash",
|
||||||
|
"view_packet": "Paketni ma'lumotlarini ko'rish",
|
||||||
|
"accepted_number": "Qabul qilingan",
|
||||||
"update_item": "Mahsulotni yangilash",
|
"update_item": "Mahsulotni yangilash",
|
||||||
"no": "Yo'q",
|
"no": "Yo'q",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
@@ -223,6 +225,9 @@
|
|||||||
"update_packet": "Paketni Tahrirlash",
|
"update_packet": "Paketni Tahrirlash",
|
||||||
"party_weight": "Partiya og'irligi",
|
"party_weight": "Partiya og'irligi",
|
||||||
"select_all": "Hammasini belgilash",
|
"select_all": "Hammasini belgilash",
|
||||||
|
"product_inspection": "Mahsulotlarni tekshirish",
|
||||||
|
"enter_product": "Mahsulotning Trassirovka IDni kiriting",
|
||||||
|
"confirmation": "Tasdiqlash",
|
||||||
|
|
||||||
"qr_code": "QR kod",
|
"qr_code": "QR kod",
|
||||||
"created_at": "Qo‘shilgan sana"
|
"created_at": "Qo‘shilgan sana"
|
||||||
|
|||||||
146
package.json
146
package.json
@@ -1,75 +1,75 @@
|
|||||||
{
|
{
|
||||||
"name": "c-post",
|
"name": "c-post",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cross-env NEXT_PUBLIC_API_URL=https://cpost.felixits.uz next dev --port=3080",
|
"dev": "cross-env NEXT_PUBLIC_API_URL=https://cpcargo.felixits.uz next dev --port=3080",
|
||||||
"build": "cross-env NEXT_PUBLIC_API_URL=https://api.cpost-express.uz next build",
|
"build": "cross-env NEXT_PUBLIC_API_URL=https://api.cpost-express.uz next build",
|
||||||
"build:dev": "cross-env NEXT_PUBLIC_API_URL=https://cpost.felixits.uz next build",
|
"build:dev": "cross-env NEXT_PUBLIC_API_URL=https://cpcargo.felixits.uz next build",
|
||||||
"build:prod": "cross-env NEXT_PUBLIC_API_URL=https://api.cpost-express.uz next build",
|
"build:prod": "cross-env NEXT_PUBLIC_API_URL=https://api.cpost-express.uz next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"check-types": "tsc --noemit",
|
"check-types": "tsc --noemit",
|
||||||
"format-code": "prettier --write .",
|
"format-code": "prettier --write .",
|
||||||
"prepare": "husky install"
|
"prepare": "husky install"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/cache": "^11.11.0",
|
"@emotion/cache": "^11.11.0",
|
||||||
"@emotion/react": "^11.11.3",
|
"@emotion/react": "^11.11.3",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@mui/icons-material": "^5.15.3",
|
"@mui/icons-material": "^5.15.3",
|
||||||
"@mui/material": "^5.15.3",
|
"@mui/material": "^5.15.3",
|
||||||
"@mui/material-nextjs": "^5.15.3",
|
"@mui/material-nextjs": "^5.15.3",
|
||||||
"@tanstack/react-query": "^4.35.0",
|
"@tanstack/react-query": "^4.35.0",
|
||||||
"@tanstack/react-query-next-experimental": "^5.17.9",
|
"@tanstack/react-query-next-experimental": "^5.17.9",
|
||||||
"aos": "^2.3.4",
|
"aos": "^2.3.4",
|
||||||
"axios": "^1.6.5",
|
"axios": "^1.6.5",
|
||||||
"cookies-next": "^4.1.0",
|
"cookies-next": "^4.1.0",
|
||||||
"i18next": "^23.7.16",
|
"i18next": "^23.7.16",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.omit": "^4.5.0",
|
"lodash.omit": "^4.5.0",
|
||||||
"next": "14.0.4",
|
"next": "14.0.4",
|
||||||
"next-i18next": "^15.2.0",
|
"next-i18next": "^15.2.0",
|
||||||
"next-intl": "^3.4.2",
|
"next-intl": "^3.4.2",
|
||||||
"nextjs-progressbar": "^0.0.16",
|
"nextjs-progressbar": "^0.0.16",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-hook-form": "^7.49.3",
|
"react-hook-form": "^7.49.3",
|
||||||
"react-hot-toast": "^2.4.1",
|
"react-hot-toast": "^2.4.1",
|
||||||
"react-i18next": "^14.0.0",
|
"react-i18next": "^14.0.0",
|
||||||
"react-select": "^5.8.0",
|
"react-select": "^5.8.0",
|
||||||
"simplebar-react": "^3.2.4",
|
"simplebar-react": "^3.2.4",
|
||||||
"swiper": "^11.0.5",
|
"swiper": "^11.0.5",
|
||||||
"use-dehydrated-state": "^0.1.0",
|
"use-dehydrated-state": "^0.1.0",
|
||||||
"zustand": "^4.4.7"
|
"zustand": "^4.4.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tanstack/react-query-devtools": "^4.35.3",
|
"@tanstack/react-query-devtools": "^4.35.3",
|
||||||
"@types/aos": "^3.0.7",
|
"@types/aos": "^3.0.7",
|
||||||
"@types/i18next": "^13.0.0",
|
"@types/i18next": "^13.0.0",
|
||||||
"@types/lodash.clonedeep": "^4.5.9",
|
"@types/lodash.clonedeep": "^4.5.9",
|
||||||
"@types/lodash.debounce": "^4.0.9",
|
"@types/lodash.debounce": "^4.0.9",
|
||||||
"@types/lodash.get": "^4.4.9",
|
"@types/lodash.get": "^4.4.9",
|
||||||
"@types/lodash.omit": "^4.5.9",
|
"@types/lodash.omit": "^4.5.9",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/parse-json": "^4.0.2",
|
"@types/parse-json": "^4.0.2",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
"@types/react-i18next": "^8.1.0",
|
"@types/react-i18next": "^8.1.0",
|
||||||
"@types/react-select": "^5.0.1",
|
"@types/react-select": "^5.0.1",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "14.0.4",
|
"eslint-config-next": "14.0.4",
|
||||||
"husky": "^8.0.0",
|
"husky": "^8.0.0",
|
||||||
"jsdom": "^23.2.0",
|
"jsdom": "^23.2.0",
|
||||||
"lint-staged": "^15.2.0",
|
"lint-staged": "^15.2.0",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.1.1",
|
||||||
"typescript": "^5",
|
"typescript": "^5",
|
||||||
"vitest": "^1.1.3"
|
"vitest": "^1.1.3"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||||
}
|
}
|
||||||
9
src/app/[locale]/dashboard/packets/[box_id]/loading.tsx
Normal file
9
src/app/[locale]/dashboard/packets/[box_id]/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>
|
||||||
|
);
|
||||||
|
}
|
||||||
5
src/app/[locale]/dashboard/packets/[box_id]/page.tsx
Normal file
5
src/app/[locale]/dashboard/packets/[box_id]/page.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import DashboardBoxesOnePage from '@/routes/private/boxes-one';
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return <DashboardBoxesOnePage />;
|
||||||
|
}
|
||||||
@@ -1,23 +1,9 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
import Loader from '@/components/common/Loader';
|
import Loader from '@/components/common/Loader';
|
||||||
import { Scrollbar } from '@/components/common/Scrollbar';
|
import { Scrollbar } from '@/components/common/Scrollbar';
|
||||||
import BaseButton from '@/components/ui-kit/BaseButton';
|
import { box_requests } from '@/data/box/box.requests';
|
||||||
import BaseIconButton from '@/components/ui-kit/BaseIconButton';
|
import { Box, styled, SxProps, Table, TableBody, TableCell, TableHead, TableRow, Theme } from '@mui/material';
|
||||||
import { FilterList, Search } from '@mui/icons-material';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
IconButton,
|
|
||||||
Stack,
|
|
||||||
SxProps,
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableHead,
|
|
||||||
TableRow,
|
|
||||||
Theme,
|
|
||||||
Tooltip,
|
|
||||||
Typography,
|
|
||||||
styled,
|
|
||||||
TableCell,
|
|
||||||
} from '@mui/material';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export interface ColumnData<Data, DataKey = keyof Data> {
|
export interface ColumnData<Data, DataKey = keyof Data> {
|
||||||
@@ -65,6 +51,7 @@ const StyledTable = styled(Table)`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledTableRow = styled(TableRow)``;
|
const StyledTableRow = styled(TableRow)``;
|
||||||
const StyledTableCell = styled(TableCell)`
|
const StyledTableCell = styled(TableCell)`
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@@ -75,6 +62,7 @@ type Props<Data> = {
|
|||||||
data: Data[];
|
data: Data[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
onClickRow?: (data: Data) => void;
|
onClickRow?: (data: Data) => void;
|
||||||
|
color?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MyTable = <Data extends { id: number | string }>(props: Props<Data>) => {
|
const MyTable = <Data extends { id: number | string }>(props: Props<Data>) => {
|
||||||
@@ -82,6 +70,43 @@ const MyTable = <Data extends { id: number | string }>(props: Props<Data>) => {
|
|||||||
|
|
||||||
const isEmpty = !data?.length && !loading;
|
const isEmpty = !data?.length && !loading;
|
||||||
|
|
||||||
|
const [boxStatuses, setBoxStatuses] = React.useState<Record<string, boolean>>({});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const fetchBoxStatuses = async () => {
|
||||||
|
const statuses: Record<string, boolean> = {};
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
data.map(async row => {
|
||||||
|
try {
|
||||||
|
const res = await box_requests.find({ packetId: row.id });
|
||||||
|
const boxData = res.data.data;
|
||||||
|
|
||||||
|
const total = boxData.items.reduce(
|
||||||
|
(acc: { totalAmount: number; totalAccepted: number }, item: any) => {
|
||||||
|
acc.totalAmount += +item.amount || 0;
|
||||||
|
acc.totalAccepted += +item.acceptedNumber || 0;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{ totalAmount: 0, totalAccepted: 0 }
|
||||||
|
);
|
||||||
|
|
||||||
|
statuses[row.id] = total.totalAmount === total.totalAccepted;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching box status:', error);
|
||||||
|
statuses[row.id] = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
setBoxStatuses(statuses);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!loading && data.length > 0) {
|
||||||
|
fetchBoxStatuses();
|
||||||
|
}
|
||||||
|
}, [data, loading]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Scrollbar>
|
<Scrollbar>
|
||||||
@@ -90,10 +115,9 @@ const MyTable = <Data extends { id: number | string }>(props: Props<Data>) => {
|
|||||||
<StyledTableRow>
|
<StyledTableRow>
|
||||||
{columns.map((column, index) => (
|
{columns.map((column, index) => (
|
||||||
<StyledTableCell
|
<StyledTableCell
|
||||||
// @ts-expect-error
|
key={String(column.dataKey) + index}
|
||||||
key={column.dataKey + index}
|
|
||||||
variant='head'
|
variant='head'
|
||||||
align={column.numeric || false ? 'right' : 'left'}
|
align={column.numeric ? 'right' : 'left'}
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: 'background.paper',
|
backgroundColor: 'background.paper',
|
||||||
width: column.width,
|
width: column.width,
|
||||||
@@ -107,17 +131,24 @@ const MyTable = <Data extends { id: number | string }>(props: Props<Data>) => {
|
|||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{isEmpty ? (
|
{isEmpty ? (
|
||||||
'Empty'
|
<StyledTableRow>
|
||||||
|
<StyledTableCell colSpan={columns.length}>Empty</StyledTableCell>
|
||||||
|
</StyledTableRow>
|
||||||
) : loading ? (
|
) : loading ? (
|
||||||
<StyledTableCell colSpan={columns.length}>
|
<StyledTableRow>
|
||||||
<Loader p={4} size={96} />
|
<StyledTableCell colSpan={columns.length}>
|
||||||
</StyledTableCell>
|
<Loader p={4} size={96} />
|
||||||
|
</StyledTableCell>
|
||||||
|
</StyledTableRow>
|
||||||
) : (
|
) : (
|
||||||
data.map((row, rowIndex) => {
|
data.map((row: any, rowIndex) => {
|
||||||
|
const status = boxStatuses[row.id];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledTableRow
|
<StyledTableRow
|
||||||
key={row.id}
|
key={row.id}
|
||||||
sx={{
|
sx={{
|
||||||
|
background: !row.hasInvoice ? 'inherit' : status ? '#a3e635' : '#f87171',
|
||||||
...(onClickRow
|
...(onClickRow
|
||||||
? {
|
? {
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
@@ -127,22 +158,18 @@ const MyTable = <Data extends { id: number | string }>(props: Props<Data>) => {
|
|||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => onClickRow?.(row)}
|
||||||
onClickRow?.(row);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{columns.map((column, index) => (
|
{columns.map((column, index) => (
|
||||||
<StyledTableCell
|
<StyledTableCell
|
||||||
// @ts-expect-error
|
key={String(column.dataKey) + index}
|
||||||
key={column.dataKey + index}
|
align={column.numeric ? 'right' : 'left'}
|
||||||
align={column.numeric || false ? 'right' : 'left'}
|
|
||||||
sx={{
|
sx={{
|
||||||
...column.getSxStyles?.(row),
|
...column.getSxStyles?.(row),
|
||||||
width: column.width,
|
width: column.width,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* @ts-expect-error */}
|
{column.renderCell ? column.renderCell(row, rowIndex) : row[column.dataKey as keyof Data]}
|
||||||
{column.renderCell ? column.renderCell(row, rowIndex) : row[column.dataKey]}
|
|
||||||
</StyledTableCell>
|
</StyledTableCell>
|
||||||
))}
|
))}
|
||||||
</StyledTableRow>
|
</StyledTableRow>
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ export interface IBoxDetail {
|
|||||||
nameRu: string;
|
nameRu: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
weight: number;
|
weight: number;
|
||||||
|
acceptedNumber: number;
|
||||||
price: number;
|
price: number;
|
||||||
totalPrice: number;
|
totalPrice: number;
|
||||||
hasImage: boolean;
|
hasImage: boolean;
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ export type Product = {
|
|||||||
amount: number;
|
amount: number;
|
||||||
weight: number;
|
weight: number;
|
||||||
price?: number;
|
price?: number;
|
||||||
|
packetName: string;
|
||||||
totalPrice?: number;
|
totalPrice?: number;
|
||||||
status: BoxStatus;
|
status: BoxStatus;
|
||||||
|
acceptedNumber: number;
|
||||||
hasImage: boolean;
|
hasImage: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,11 +23,11 @@ export type CreateProductBodyType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type UpdateProductBodyType = {
|
export type UpdateProductBodyType = {
|
||||||
itemId: number;
|
itemId: string | number;
|
||||||
|
|
||||||
cargoId: string;
|
|
||||||
trekId: string;
|
trekId: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
nameRu: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
weight: number;
|
weight: number;
|
||||||
|
acceptedNumber: number | null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export const item_requests = {
|
|||||||
packetId?: number | string;
|
packetId?: number | string;
|
||||||
partyId?: number | string;
|
partyId?: number | string;
|
||||||
name?: string;
|
name?: string;
|
||||||
trekId?: string;
|
trekId?: string | number;
|
||||||
page?: number;
|
page?: number;
|
||||||
sort?: string;
|
sort?: string;
|
||||||
status?: BoxStatus;
|
status?: BoxStatus;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export const pageLinks = {
|
|||||||
},
|
},
|
||||||
boxes: {
|
boxes: {
|
||||||
index: '/dashboard/packets',
|
index: '/dashboard/packets',
|
||||||
|
detail: (slug: string | number) => '/dashboard/packets/' + slug,
|
||||||
create: '/dashboard/packets/create',
|
create: '/dashboard/packets/create',
|
||||||
edit: (slug: string | number) => '/dashboard/packets/edit/' + slug,
|
edit: (slug: string | number) => '/dashboard/packets/edit/' + slug,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,34 +1,31 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import BaseButton from '@/components/ui-kit/BaseButton';
|
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 BaseIconButton from '@/components/ui-kit/BaseIconButton';
|
||||||
import { AddCircleRounded, Close } from '@mui/icons-material';
|
import BaseInput from '@/components/ui-kit/BaseInput';
|
||||||
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 BaseReactSelect, { selectDefaultStyles } from '@/components/ui-kit/BaseReactSelect';
|
||||||
import { customer_requests } from '@/data/customers/customer.requests';
|
|
||||||
import { useAuthContext } from '@/context/auth-context';
|
import { useAuthContext } from '@/context/auth-context';
|
||||||
import { useMyNavigation } from '@/hooks/useMyNavigation';
|
import { BoxStatus, CreateBoxBodyType, UpdateBoxBodyType } from '@/data/box/box.model';
|
||||||
import AsyncSelect from 'react-select/async';
|
import { box_requests } from '@/data/box/box.requests';
|
||||||
import { Party } from '@/data/party/party.model';
|
|
||||||
import cloneDeep from 'lodash.clonedeep';
|
|
||||||
import { item_requests } from '@/data/item/item.requests';
|
|
||||||
import get from 'lodash.get';
|
|
||||||
import Loader from '@/components/common/Loader';
|
|
||||||
import { Customer } from '@/data/customers/customer.model';
|
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 } from '@/data/passport/passport.model';
|
||||||
import { passport_requests } from '@/data/passport/passport.request';
|
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)`
|
const StyledCreateBox = styled(Box)`
|
||||||
.item-row {
|
.item-row {
|
||||||
@@ -123,19 +120,19 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
...(editMode
|
...(editMode
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
products_list: [
|
products_list: [
|
||||||
{
|
{
|
||||||
id: '',
|
id: '',
|
||||||
cargoId: '',
|
cargoId: '',
|
||||||
trekId: '',
|
trekId: '',
|
||||||
name: '',
|
name: '',
|
||||||
nameRu: '',
|
nameRu: '',
|
||||||
amount: '',
|
amount: '',
|
||||||
weight: '',
|
weight: '',
|
||||||
price: '',
|
price: '',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
...initialValues,
|
...initialValues,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -160,8 +157,8 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
label: initialValues?.passportName,
|
label: initialValues?.passportName,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const n = "123ds"
|
const n = '123ds';
|
||||||
n.toUpperCase()
|
n.toUpperCase();
|
||||||
const { data: passportOptions } = useRequest(() => passport_requests.getAll({ cargoId: cargoId?.toUpperCase() }), {
|
const { data: passportOptions } = useRequest(() => passport_requests.getAll({ cargoId: cargoId?.toUpperCase() }), {
|
||||||
enabled: !!cargoId,
|
enabled: !!cargoId,
|
||||||
selectData: data => {
|
selectData: data => {
|
||||||
@@ -185,7 +182,7 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
placeholderData: [], // Kerak emas, chunki server PageAble qaytarmayapti
|
placeholderData: [], // Kerak emas, chunki server PageAble qaytarmayapti
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
if (data?.data.data?.[0]?.id) {
|
if (data?.data.data?.[0]?.id) {
|
||||||
setValue("passportId", initialValues?.passportId)
|
setValue('passportId', initialValues?.passportId);
|
||||||
setValue('passport_id', data.data.data[0].id);
|
setValue('passport_id', data.data.data[0].id);
|
||||||
setSelectedPassport(data.data.data[0]); // Birinchi elementni tanlash
|
setSelectedPassport(data.data.data[0]); // Birinchi elementni tanlash
|
||||||
}
|
}
|
||||||
@@ -251,7 +248,6 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit = handleSubmit(async values => {
|
const onSubmit = handleSubmit(async values => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
@@ -377,11 +373,11 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
label: string;
|
label: string;
|
||||||
value: BoxStatus;
|
value: BoxStatus;
|
||||||
}[] = [
|
}[] = [
|
||||||
{
|
{
|
||||||
label: t('READY_TO_INVOICE'),
|
label: t('READY_TO_INVOICE'),
|
||||||
value: 'READY_TO_INVOICE',
|
value: 'READY_TO_INVOICE',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
p.push({
|
p.push({
|
||||||
@@ -426,15 +422,15 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
defaultValue={
|
defaultValue={
|
||||||
editMode
|
editMode
|
||||||
? {
|
? {
|
||||||
value: initialValues.partyId,
|
value: initialValues.partyId,
|
||||||
label: initialValues.partyName,
|
label: initialValues.partyName,
|
||||||
}
|
}
|
||||||
: partiesData?.length
|
: partiesData?.length
|
||||||
? {
|
? {
|
||||||
value: partiesData[0].value,
|
value: partiesData[0].value,
|
||||||
label: partiesData[0].label,
|
label: partiesData[0].label,
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
styles={selectDefaultStyles}
|
styles={selectDefaultStyles}
|
||||||
noOptionsMessage={() => t('not_found')}
|
noOptionsMessage={() => t('not_found')}
|
||||||
@@ -552,7 +548,7 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
if (!Number.isNaN(p)) {
|
if (!Number.isNaN(p)) {
|
||||||
totalPrice = p;
|
totalPrice = p;
|
||||||
}
|
}
|
||||||
} catch (error) { }
|
} catch (error) {}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box key={product.key} mb={1.5}>
|
<Box key={product.key} mb={1.5}>
|
||||||
@@ -707,7 +703,7 @@ const DashboardCreateBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
value={totalPrice}
|
value={totalPrice}
|
||||||
mainBorderColor='#D8D8D8'
|
mainBorderColor='#D8D8D8'
|
||||||
placeholder={t('total_price')}
|
placeholder={t('total_price')}
|
||||||
// {...register(`products_list.${index}.totalPrice`, { required: requiredText })}
|
// {...register(`products_list.${index}.totalPrice`, { required: requiredText })}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { box_requests } from '@/data/box/box.requests';
|
|||||||
import useRequest from '@/hooks/useRequest';
|
import useRequest from '@/hooks/useRequest';
|
||||||
import DashboardCreateBoxPage from '@/routes/private/boxes-create/DashboardCreateBox';
|
import DashboardCreateBoxPage from '@/routes/private/boxes-create/DashboardCreateBox';
|
||||||
import { useParams } from 'next/navigation';
|
import { useParams } from 'next/navigation';
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
type Props = {};
|
type Props = {};
|
||||||
|
|
||||||
@@ -71,7 +70,7 @@ const DashboardEditBoxPage = (props: Props) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(getOneBox, "pkets");
|
console.log(getOneBox, 'pkets');
|
||||||
|
|
||||||
if (getOneBox.loading || !getOneBox.data) {
|
if (getOneBox.loading || !getOneBox.data) {
|
||||||
return <Loader p={8} size={96} />;
|
return <Loader p={8} size={96} />;
|
||||||
|
|||||||
322
src/routes/private/boxes-one/BoxesOne.tsx
Normal file
322
src/routes/private/boxes-one/BoxesOne.tsx
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import Loader from '@/components/common/Loader';
|
||||||
|
import BaseInput from '@/components/ui-kit/BaseInput';
|
||||||
|
import { useAuthContext } from '@/context/auth-context';
|
||||||
|
import { BoxStatus } from '@/data/box/box.model';
|
||||||
|
import { box_requests } from '@/data/box/box.requests';
|
||||||
|
import { useMyTranslation } from '@/hooks/useMyTranslation';
|
||||||
|
import useRequest from '@/hooks/useRequest';
|
||||||
|
import { Box, Divider, Grid, Stack, Typography, styled } from '@mui/material';
|
||||||
|
import { useParams } from 'next/navigation';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
const StyledViewBox = styled(Box)`
|
||||||
|
.item-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-row-field {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const DashboardBoxesOnePage = () => {
|
||||||
|
const params = useParams();
|
||||||
|
const box_id = params.box_id as string;
|
||||||
|
const { isAdmin: isAdminUser } = useAuthContext();
|
||||||
|
const t = useMyTranslation();
|
||||||
|
|
||||||
|
const { data: boxData, loading } = useRequest(() => 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,
|
||||||
|
passportId: boxData.client?.passportId,
|
||||||
|
client_id: boxData.packet?.cargoId,
|
||||||
|
clientName: boxData.client?.passportName,
|
||||||
|
products_list: boxData.items.map(item => ({
|
||||||
|
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,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const boxStatuses = useMemo(() => {
|
||||||
|
const statuses: { label: string; value: BoxStatus }[] = [
|
||||||
|
{
|
||||||
|
label: t('READY_TO_INVOICE'),
|
||||||
|
value: 'READY_TO_INVOICE',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isAdminUser) {
|
||||||
|
statuses.push({
|
||||||
|
label: t('READY'),
|
||||||
|
value: 'READY',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return statuses;
|
||||||
|
}, [isAdminUser, t]);
|
||||||
|
|
||||||
|
if (loading || !boxData) {
|
||||||
|
return <Loader p={8} size={96} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledViewBox
|
||||||
|
width={1}
|
||||||
|
mb={3}
|
||||||
|
sx={{
|
||||||
|
padding: '28px',
|
||||||
|
borderRadius: '16px',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box>
|
||||||
|
<Typography variant='h5' mb={3.5}>
|
||||||
|
{t('view_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>
|
||||||
|
<Typography sx={{ width: '100%', border: '1px solid gray', padding: '8px', borderRadius: '12px' }}>
|
||||||
|
{boxData.partyName || '-'}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid item xs={5}>
|
||||||
|
<Typography fontSize='18px' fontWeight={500} color='#5D5850' mb={2}>
|
||||||
|
{t('cargo_id')}
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ width: '100%', border: '1px solid gray', padding: '8px', borderRadius: '12px' }}>
|
||||||
|
{boxData.client_id || '-'}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid item xs={5}>
|
||||||
|
<Typography fontSize='18px' fontWeight={500} color='#5D5850' mb={2}>
|
||||||
|
{t('passport')}
|
||||||
|
</Typography>
|
||||||
|
<Typography sx={{ width: '100%', border: '1px solid gray', padding: '8px', borderRadius: '12px' }}>
|
||||||
|
{boxData.passportName || '-'}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Stack
|
||||||
|
sx={{
|
||||||
|
borderRadius: '8px',
|
||||||
|
border: '2px solid #3489E4',
|
||||||
|
background: '#FFF',
|
||||||
|
padding: '24px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{boxData.products_list.map((product, index: number) => {
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
let totalPrice = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const p = +product.price * +product.amount;
|
||||||
|
if (!Number.isNaN(p)) {
|
||||||
|
totalPrice = p;
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box key={index} 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')}
|
||||||
|
disabled
|
||||||
|
defaultValue={product.trekId}
|
||||||
|
sx={{
|
||||||
|
'.MuiInputBase-root': {
|
||||||
|
paddingLeft: 0,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</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')}
|
||||||
|
defaultValue={product.name}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box className='item-row-field'>
|
||||||
|
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
||||||
|
{'NAME_RU'}
|
||||||
|
</Typography>
|
||||||
|
<BaseInput
|
||||||
|
defaultValue={product.nameRu}
|
||||||
|
disabled
|
||||||
|
fullWidth
|
||||||
|
mainBorderColor='#D8D8D8'
|
||||||
|
placeholder={t('name')}
|
||||||
|
InputProps={{
|
||||||
|
sx: {
|
||||||
|
'&.Mui-disabled': {
|
||||||
|
color: '!important',
|
||||||
|
},
|
||||||
|
'& input.Mui-disabled': {
|
||||||
|
color: 'black !important',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</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')}
|
||||||
|
defaultValue={product.amount}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box className='item-row-field'>
|
||||||
|
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
||||||
|
{t('accepted_number')}
|
||||||
|
</Typography>
|
||||||
|
<BaseInput
|
||||||
|
fullWidth
|
||||||
|
type='number'
|
||||||
|
inputProps={{ step: 'any', min: 0, type: 'number' }}
|
||||||
|
mainBorderColor='#D8D8D8'
|
||||||
|
placeholder={t('accepted_number')}
|
||||||
|
defaultValue={product.acceptedNumber === null ? 0 : product.acceptedNumber}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<>
|
||||||
|
<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')}
|
||||||
|
defaultValue={product.weight}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</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,
|
||||||
|
}}
|
||||||
|
defaultValue={product.price}
|
||||||
|
disabled
|
||||||
|
mainBorderColor='#D8D8D8'
|
||||||
|
placeholder={t('price')}
|
||||||
|
/>
|
||||||
|
</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,
|
||||||
|
}}
|
||||||
|
disabled
|
||||||
|
value={totalPrice}
|
||||||
|
mainBorderColor='#D8D8D8'
|
||||||
|
placeholder={t('total_price')}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
</Box>
|
||||||
|
<Divider color='#EBEFF6' />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Stack>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</StyledViewBox>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DashboardBoxesOnePage;
|
||||||
1
src/routes/private/boxes-one/index.ts
Normal file
1
src/routes/private/boxes-one/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default } from './BoxesOne';
|
||||||
@@ -9,6 +9,8 @@ import BasePagination from '@/components/ui-kit/BasePagination';
|
|||||||
import { useAuthContext } from '@/context/auth-context';
|
import { useAuthContext } from '@/context/auth-context';
|
||||||
import { BoxStatus, BoxStatusList, IBox } from '@/data/box/box.model';
|
import { BoxStatus, BoxStatusList, IBox } from '@/data/box/box.model';
|
||||||
import { box_requests } from '@/data/box/box.requests';
|
import { box_requests } from '@/data/box/box.requests';
|
||||||
|
import { Product, UpdateProductBodyType } from '@/data/item/item.mode';
|
||||||
|
import { item_requests } from '@/data/item/item.requests';
|
||||||
import { DEFAULT_PAGE_SIZE, pageLinks } from '@/helpers/constants';
|
import { DEFAULT_PAGE_SIZE, pageLinks } from '@/helpers/constants';
|
||||||
import useInput from '@/hooks/useInput';
|
import useInput from '@/hooks/useInput';
|
||||||
import { useMyNavigation } from '@/hooks/useMyNavigation';
|
import { useMyNavigation } from '@/hooks/useMyNavigation';
|
||||||
@@ -17,13 +19,32 @@ import useRequest from '@/hooks/useRequest';
|
|||||||
import { file_service } from '@/services/file-service';
|
import { file_service } from '@/services/file-service';
|
||||||
import { notifyUnknownError } from '@/services/notification';
|
import { notifyUnknownError } from '@/services/notification';
|
||||||
import { getStatusColor } from '@/theme/getStatusBoxStyles';
|
import { getStatusColor } from '@/theme/getStatusBoxStyles';
|
||||||
import { Add, Circle, Delete, Download, Edit, FilterList, FilterListOff, Search } from '@mui/icons-material';
|
import { Add, Circle, Delete, Download, Edit, FilterList, FilterListOff, RemoveRedEye, Search } from '@mui/icons-material';
|
||||||
import { Box, Button, Stack, Typography } from '@mui/material';
|
import { Box, Button, Card, CardContent, Modal, Stack, TextField, Typography } from '@mui/material';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
|
||||||
type Props = {};
|
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 DashboardBoxesPage = (props: Props) => {
|
const DashboardBoxesPage = (props: Props) => {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const handleOpen = () => setOpen(true);
|
||||||
|
const handleClose = () => setOpen(false);
|
||||||
const t = useMyTranslation();
|
const t = useMyTranslation();
|
||||||
const navigation = useMyNavigation();
|
const navigation = useMyNavigation();
|
||||||
const { isAdmin } = useAuthContext();
|
const { isAdmin } = useAuthContext();
|
||||||
@@ -31,10 +52,12 @@ const DashboardBoxesPage = (props: Props) => {
|
|||||||
const [pageSize] = useState(DEFAULT_PAGE_SIZE);
|
const [pageSize] = useState(DEFAULT_PAGE_SIZE);
|
||||||
const { value: keyword, onChange: handleKeyword, setValue: setKeyword } = useInput('');
|
const { value: keyword, onChange: handleKeyword, setValue: setKeyword } = useInput('');
|
||||||
const [boxStatusFilter, setBoxStatusFilter] = useState<BoxStatus | undefined>(undefined);
|
const [boxStatusFilter, setBoxStatusFilter] = useState<BoxStatus | undefined>(undefined);
|
||||||
|
const [trackId, setTrackId] = useState<string>();
|
||||||
|
|
||||||
const [deleteIds, setDeleteIds] = useState<number[]>([]);
|
const [deleteIds, setDeleteIds] = useState<number[]>([]);
|
||||||
const [downloadIds, setDownloadIds] = useState<number[]>([]);
|
const [downloadIds, setDownloadIds] = useState<number[]>([]);
|
||||||
const [changeStatusIds, setChangeStatusIds] = useState<number[]>([]);
|
const [changeStatusIds, setChangeStatusIds] = useState<number[]>([]);
|
||||||
|
const [boxAmounts, setBoxAmounts] = useState<Record<number, { totalAmount: number; totalAccepted: number }>>({});
|
||||||
|
|
||||||
const boxStatusOptions = useMemo(() => {
|
const boxStatusOptions = useMemo(() => {
|
||||||
const p = ['READY_TO_INVOICE'] as BoxStatus[];
|
const p = ['READY_TO_INVOICE'] as BoxStatus[];
|
||||||
@@ -61,6 +84,31 @@ const DashboardBoxesPage = (props: Props) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getListQuery = useRequest(
|
||||||
|
() =>
|
||||||
|
item_requests.getAll({
|
||||||
|
page: page,
|
||||||
|
trekId: trackId,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
dependencies: [page, trackId],
|
||||||
|
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]: '' }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: list,
|
data: list,
|
||||||
totalElements,
|
totalElements,
|
||||||
@@ -145,6 +193,40 @@ const DashboardBoxesPage = (props: Props) => {
|
|||||||
return () => clearTimeout(timeoutId);
|
return () => clearTimeout(timeoutId);
|
||||||
}, [keyword]);
|
}, [keyword]);
|
||||||
|
|
||||||
|
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: { totalAmount: number; totalAccepted: number }, item: any) => {
|
||||||
|
acc.totalAmount += +item.amount || 0;
|
||||||
|
acc.totalAccepted += +item.acceptedNumber || 0;
|
||||||
|
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]);
|
||||||
|
|
||||||
// No, PartyName, PacketName, PartyTozaOg'irlik, CountOfItems, WeightOfItems, CargoID, PassportNameFamily - PacketStatusForInvoice
|
// No, PartyName, PacketName, PartyTozaOg'irlik, CountOfItems, WeightOfItems, CargoID, PassportNameFamily - PacketStatusForInvoice
|
||||||
const columns: ColumnData<IBox>[] = [
|
const columns: ColumnData<IBox>[] = [
|
||||||
{
|
{
|
||||||
@@ -185,6 +267,15 @@ const DashboardBoxesPage = (props: Props) => {
|
|||||||
dataKey: 'totalItems',
|
dataKey: 'totalItems',
|
||||||
label: t('count_of_items'),
|
label: t('count_of_items'),
|
||||||
width: 120,
|
width: 120,
|
||||||
|
renderCell: data => {
|
||||||
|
const total = boxAmounts[data.id];
|
||||||
|
if (!total) return <Typography>...</Typography>;
|
||||||
|
return (
|
||||||
|
<Typography>
|
||||||
|
{total.totalAmount} | {total.totalAccepted}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
dataKey: 'totalNetWeight',
|
dataKey: 'totalNetWeight',
|
||||||
@@ -272,6 +363,13 @@ const DashboardBoxesPage = (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<ActionPopMenu
|
<ActionPopMenu
|
||||||
buttons={[
|
buttons={[
|
||||||
|
{
|
||||||
|
icon: <RemoveRedEye sx={{ path: { color: '#3489E4' } }} />,
|
||||||
|
label: t('view_packet'),
|
||||||
|
onClick: () => {
|
||||||
|
navigation.push(pageLinks.dashboard.boxes.detail(data.id));
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: <Edit sx={{ path: { color: '#3489E4' } }} />,
|
icon: <Edit sx={{ path: { color: '#3489E4' } }} />,
|
||||||
label: t('edit'),
|
label: t('edit'),
|
||||||
@@ -307,6 +405,53 @@ const DashboardBoxesPage = (props: Props) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
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,
|
||||||
|
amount: item.amount,
|
||||||
|
name: item.name,
|
||||||
|
nameRu: item.nameRu,
|
||||||
|
trekId: item.trekId,
|
||||||
|
weight: item.weight,
|
||||||
|
};
|
||||||
|
|
||||||
|
await item_requests.update(updateBody);
|
||||||
|
|
||||||
|
// Ma'lumotni yangilab olamiz
|
||||||
|
getListQuery.refetch();
|
||||||
|
getBoxesQuery.refetch();
|
||||||
|
|
||||||
|
setValues(prev => ({ ...prev, [item.trekId]: '' }));
|
||||||
|
} catch (error) {
|
||||||
|
notifyUnknownError(error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
@@ -314,6 +459,66 @@ const DashboardBoxesPage = (props: Props) => {
|
|||||||
<BaseButton colorVariant='blue' startIcon={<Add />} href={pageLinks.dashboard.boxes.create}>
|
<BaseButton colorVariant='blue' startIcon={<Add />} href={pageLinks.dashboard.boxes.create}>
|
||||||
{t('create_packet')}
|
{t('create_packet')}
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
|
<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}>
|
||||||
|
<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>
|
||||||
|
<TextField
|
||||||
|
id='outlined-basic'
|
||||||
|
label={t('track_id')}
|
||||||
|
variant='outlined'
|
||||||
|
onChange={e => setTrackId(e.target.value)}
|
||||||
|
/>
|
||||||
|
{trackId && trackId.length > 0 && (
|
||||||
|
<>
|
||||||
|
{getListQuery.loading ? (
|
||||||
|
<Typography sx={{ mt: 2 }}>{t('loading')}...</Typography> // yoki <CircularProgress />
|
||||||
|
) : 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>
|
||||||
|
<TextField
|
||||||
|
id={`amount-${e.trekId}`}
|
||||||
|
label='Mahsulot soni'
|
||||||
|
type='number'
|
||||||
|
sx={{ width: '100%' }}
|
||||||
|
value={values[e.trekId] ?? ''}
|
||||||
|
onChange={change => handleAmountChange(change, e.amount, e.trekId)}
|
||||||
|
inputProps={{ min: 1, max: e.amount }}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
sx={{ mt: '10px' }}
|
||||||
|
onClick={() => {
|
||||||
|
if (values[e.trekId] !== '') {
|
||||||
|
updateItems(e, Number(values[e.trekId]));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('confirmation')}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Typography sx={{ mt: 2 }}>{t('no_products_found') || 'Mahsulot topilmadi'}</Typography>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Box
|
<Box
|
||||||
width={1}
|
width={1}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const DashboardClientsPage = (props: Props) => {
|
|||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [pageSize] = useState(DEFAULT_PAGE_SIZE);
|
const [pageSize] = useState(DEFAULT_PAGE_SIZE);
|
||||||
const { value: keyword, onChange: handleKeyword, setValue: setKeyword } = useInput('');
|
const { value: keyword, onChange: handleKeyword, setValue: setKeyword } = useInput('');
|
||||||
|
const { value: name, onChange: handleName, setValue: setName } = useInput('');
|
||||||
const { value: aviaCargoIdValue, onChange: handleAviaCargoIdValue, setValue: setAviaCargoIdValue } = useInput('');
|
const { value: aviaCargoIdValue, onChange: handleAviaCargoIdValue, setValue: setAviaCargoIdValue } = useInput('');
|
||||||
const { value: autoCargoIdValue, onChange: handleAutoCargoIdValue, setValue: setAutoCargoIdValue } = useInput('');
|
const { value: autoCargoIdValue, onChange: handleAutoCargoIdValue, setValue: setAutoCargoIdValue } = useInput('');
|
||||||
|
|
||||||
@@ -42,7 +43,8 @@ const DashboardClientsPage = (props: Props) => {
|
|||||||
() =>
|
() =>
|
||||||
customer_requests.getAll({
|
customer_requests.getAll({
|
||||||
page: page,
|
page: page,
|
||||||
clientName: keyword,
|
clientName: name,
|
||||||
|
autoCargoId: keyword,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
selectData(data) {
|
selectData(data) {
|
||||||
@@ -112,6 +114,7 @@ const DashboardClientsPage = (props: Props) => {
|
|||||||
setPage(1);
|
setPage(1);
|
||||||
setKeyword('');
|
setKeyword('');
|
||||||
setAviaCargoIdValue('');
|
setAviaCargoIdValue('');
|
||||||
|
setName('');
|
||||||
setAutoCargoIdValue('');
|
setAutoCargoIdValue('');
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -136,7 +139,7 @@ const DashboardClientsPage = (props: Props) => {
|
|||||||
getClientsQuery.refetch();
|
getClientsQuery.refetch();
|
||||||
}, 350);
|
}, 350);
|
||||||
return () => clearTimeout(timeoutId);
|
return () => clearTimeout(timeoutId);
|
||||||
}, [keyword, aviaCargoIdValue, autoCargoIdValue]);
|
}, [keyword, aviaCargoIdValue, autoCargoIdValue, name]);
|
||||||
|
|
||||||
const columns: ColumnData<Customer>[] = [
|
const columns: ColumnData<Customer>[] = [
|
||||||
{
|
{
|
||||||
@@ -332,13 +335,21 @@ const DashboardClientsPage = (props: Props) => {
|
|||||||
{/* onChange={handleAutoCargoIdValue}*/}
|
{/* onChange={handleAutoCargoIdValue}*/}
|
||||||
{/* placeholder={t('auto_cargo_id')}*/}
|
{/* placeholder={t('auto_cargo_id')}*/}
|
||||||
{/*/>*/}
|
{/*/>*/}
|
||||||
|
<BaseInput
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: <Search color='primary' />,
|
||||||
|
}}
|
||||||
|
value={name}
|
||||||
|
onChange={handleName}
|
||||||
|
placeholder={t('name')}
|
||||||
|
/>
|
||||||
<BaseInput
|
<BaseInput
|
||||||
InputProps={{
|
InputProps={{
|
||||||
startAdornment: <Search color='primary' />,
|
startAdornment: <Search color='primary' />,
|
||||||
}}
|
}}
|
||||||
value={keyword}
|
value={keyword}
|
||||||
onChange={handleKeyword}
|
onChange={handleKeyword}
|
||||||
placeholder={'Kargo ID'}
|
placeholder={t('id')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<BaseButton colorVariant='gray' startIcon={<FilterListOff />} size='small' onClick={resetFilter}>
|
<BaseButton colorVariant='gray' startIcon={<FilterListOff />} size='small' onClick={resetFilter}>
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
import BaseButton from '@/components/ui-kit/BaseButton';
|
import BaseButton from '@/components/ui-kit/BaseButton';
|
||||||
import BaseInput from '@/components/ui-kit/BaseInput';
|
import BaseInput from '@/components/ui-kit/BaseInput';
|
||||||
import BaseModal from '@/components/ui-kit/BaseModal';
|
import BaseModal from '@/components/ui-kit/BaseModal';
|
||||||
import BaseReactSelect from '@/components/ui-kit/BaseReactSelect';
|
|
||||||
import { Product } from '@/data/item/item.mode';
|
import { Product } from '@/data/item/item.mode';
|
||||||
import { item_requests } from '@/data/item/item.requests';
|
import { item_requests } from '@/data/item/item.requests';
|
||||||
import { staff_requests } from '@/data/staff/staff.requests';
|
|
||||||
import { useMyTranslation } from '@/hooks/useMyTranslation';
|
import { useMyTranslation } from '@/hooks/useMyTranslation';
|
||||||
import { notifyUnknownError } from '@/services/notification';
|
import { notifyUnknownError } from '@/services/notification';
|
||||||
import { Box, Grid, Stack, Typography, styled } from '@mui/material';
|
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';
|
import { useForm } from 'react-hook-form';
|
||||||
|
|
||||||
const StyledBox = styled(Box)`
|
const StyledBox = styled(Box)`
|
||||||
.title {
|
.title {
|
||||||
@@ -52,6 +50,8 @@ const EditItemModal = ({ onClose, open, onSuccess, item }: Props) => {
|
|||||||
name: string;
|
name: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
weight: number;
|
weight: number;
|
||||||
|
acceptedNumber: number | null;
|
||||||
|
nameRu: string;
|
||||||
}>({
|
}>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
amount: item.amount,
|
amount: item.amount,
|
||||||
@@ -59,6 +59,8 @@ const EditItemModal = ({ onClose, open, onSuccess, item }: Props) => {
|
|||||||
name: item.name,
|
name: item.name,
|
||||||
trekId: item.trekId,
|
trekId: item.trekId,
|
||||||
weight: item.weight,
|
weight: item.weight,
|
||||||
|
acceptedNumber: item.acceptedNumber,
|
||||||
|
nameRu: item.nameRu,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { notifyUnknownError } from '@/services/notification';
|
|||||||
import { AddCircleRounded, Close } from '@mui/icons-material';
|
import { AddCircleRounded, Close } from '@mui/icons-material';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
Button,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
Chip,
|
Chip,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
@@ -33,7 +34,7 @@ import {
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import get from 'lodash.get';
|
import get from 'lodash.get';
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { Controller, useFieldArray, useForm } from 'react-hook-form';
|
import { Controller, useFieldArray, useForm } from 'react-hook-form';
|
||||||
|
|
||||||
const StyledCreateBox = styled(Box)`
|
const StyledCreateBox = styled(Box)`
|
||||||
@@ -66,9 +67,26 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
const t = useMyTranslation();
|
const t = useMyTranslation();
|
||||||
const params = useSearchParams();
|
const params = useSearchParams();
|
||||||
const { push } = useMyNavigation();
|
const { push } = useMyNavigation();
|
||||||
const [partyId, setPartyId] = useState<number | string>('');
|
const [partyId, setPartyId] = useState<number | string>(initialValues?.partyId || '');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const selectMenuProps = useMemo(
|
||||||
|
() => ({
|
||||||
|
PaperProps: { style: { maxHeight: 280 } },
|
||||||
|
autoFocus: false,
|
||||||
|
disableAutoFocus: true,
|
||||||
|
disableEnforceFocus: true,
|
||||||
|
disableRestoreFocus: true,
|
||||||
|
disableScrollLock: true,
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Barcha box/packetlarni barcha sahifadan yuklash uchun state
|
||||||
|
const [allPackets, setAllPackets] = useState<any[]>([]);
|
||||||
|
// Barcha mahsulotlarni barcha sahifadan yuklash uchun map
|
||||||
|
const [allItemsMap, setAllItemsMap] = useState<{ [packetId: number]: any[] }>({});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -93,6 +111,65 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Paketlar va mahsulotlarni yuklash
|
||||||
|
const [isLoadingPackets, setIsLoadingPackets] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchAllPackets = async () => {
|
||||||
|
if (!partyId) {
|
||||||
|
setAllPackets([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setIsLoadingPackets(true);
|
||||||
|
let packets: any[] = [];
|
||||||
|
let totalPages = 1;
|
||||||
|
try {
|
||||||
|
const firstRes = await box_requests.getAll({ partyId, page: 1 });
|
||||||
|
const firstData = firstRes?.data?.data;
|
||||||
|
packets = firstData?.data || [];
|
||||||
|
totalPages = firstData?.totalPages || 1;
|
||||||
|
|
||||||
|
if (totalPages > 1) {
|
||||||
|
const promises = [];
|
||||||
|
for (let page = 2; page <= totalPages; page++) {
|
||||||
|
promises.push(box_requests.getAll({ partyId, page }));
|
||||||
|
}
|
||||||
|
const results = await Promise.all(promises);
|
||||||
|
results.forEach(res => {
|
||||||
|
const data = res?.data?.data;
|
||||||
|
packets = [...packets, ...(data?.data || [])];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
setAllPackets(packets);
|
||||||
|
setIsLoadingPackets(false);
|
||||||
|
};
|
||||||
|
fetchAllPackets();
|
||||||
|
}, [partyId]);
|
||||||
|
|
||||||
|
const fetchAllItemsForPacket = async (packetId: number) => {
|
||||||
|
let items: any[] = [];
|
||||||
|
let totalPages = 1;
|
||||||
|
try {
|
||||||
|
const firstRes = await item_requests.getAll({ packetId, page: 1 });
|
||||||
|
const firstData = firstRes?.data?.data;
|
||||||
|
items = firstData?.data || [];
|
||||||
|
totalPages = firstData?.totalPages || 1;
|
||||||
|
if (totalPages > 1) {
|
||||||
|
const promises = [];
|
||||||
|
for (let page = 2; page <= totalPages; page++) {
|
||||||
|
promises.push(item_requests.getAll({ packetId, page }));
|
||||||
|
}
|
||||||
|
const results = await Promise.all(promises);
|
||||||
|
results.forEach(res => {
|
||||||
|
const data = res?.data?.data;
|
||||||
|
items = [...items, ...(data?.data || [])];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
setAllItemsMap(prev => ({ ...prev, [packetId]: items }));
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialValues) {
|
if (initialValues) {
|
||||||
reset({
|
reset({
|
||||||
@@ -110,9 +187,15 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
if (initialValues.partyId) {
|
if (initialValues.partyId) {
|
||||||
setPartyId(initialValues.partyId);
|
setPartyId(initialValues.partyId);
|
||||||
}
|
}
|
||||||
|
if (initialValues.paketIds) {
|
||||||
|
initialValues.paketIds.forEach((paket: any) => {
|
||||||
|
fetchAllItemsForPacket(paket.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [initialValues, reset]);
|
}, [initialValues, reset]);
|
||||||
|
|
||||||
|
// useFieldArray keyName="key" orqali unique key
|
||||||
const { fields, append, remove } = useFieldArray({
|
const { fields, append, remove } = useFieldArray({
|
||||||
control,
|
control,
|
||||||
name: 'packetItemDtos',
|
name: 'packetItemDtos',
|
||||||
@@ -164,21 +247,16 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const handlePacketChange = (index: number, value: number) => {
|
|
||||||
setValue(`packetItemDtos.${index}.packetId`, value);
|
|
||||||
setValue(`packetItemDtos.${index}.itemDtos`, []);
|
|
||||||
};
|
|
||||||
|
|
||||||
const appendPacket = () => {
|
const appendPacket = () => {
|
||||||
append({ packetId: 0, itemDtos: [] });
|
append({ packetId: 0, itemDtos: [] });
|
||||||
|
setTimeout(() => {
|
||||||
|
document.activeElement instanceof HTMLElement && document.activeElement.blur();
|
||||||
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removePacket = (index: number) => {
|
const removePacket = (index: number) => {
|
||||||
remove(index);
|
remove(index);
|
||||||
};
|
};
|
||||||
const [packetSearchTerm, setPacketSearchTerm] = useState('');
|
|
||||||
|
|
||||||
const packetSearchInputRef = useRef<HTMLInputElement>(null);
|
|
||||||
|
|
||||||
const handlePartyChange = (event: any) => {
|
const handlePartyChange = (event: any) => {
|
||||||
const selectedParty = parties.find(p => p.id === event.target.value);
|
const selectedParty = parties.find(p => p.id === event.target.value);
|
||||||
@@ -189,109 +267,76 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const [paketName, setPaketName] = useState<string>('');
|
|
||||||
const PacketRow = ({ index, field }: { index: number; field: any }) => {
|
const PacketRow = ({ index, field }: { index: number; field: any }) => {
|
||||||
const packetId = watch(`packetItemDtos.${index}.packetId`);
|
const packetId = watch(`packetItemDtos.${index}.packetId`);
|
||||||
// Tanlangan paketning name'ini olish
|
|
||||||
const [itemsPage, setItemsPage] = useState(1);
|
|
||||||
const [itemsList, setItemsList] = useState<any[]>([]);
|
|
||||||
const [itemsHasMore, setItemsHasMore] = useState(true);
|
|
||||||
const itemScrollRef = useRef<HTMLDivElement>(null);
|
|
||||||
// Packetlar uchun scroll state
|
|
||||||
const [packetsPage, setPacketsPage] = useState(1);
|
|
||||||
const [packetsList, setPacketsList] = useState<any[]>([]);
|
|
||||||
const selectedPacket = packetsList.find(p => p.id === packetId);
|
|
||||||
const [packetsHasMore, setPacketsHasMore] = useState(true);
|
|
||||||
|
|
||||||
const { isLoading: isLoadingProducts } = useQuery({
|
|
||||||
queryKey: ['product-list', packetId, itemsPage],
|
|
||||||
queryFn: () => item_requests.list({ packetId, page: itemsPage }),
|
|
||||||
enabled: !!packetId,
|
|
||||||
onSuccess: data => {
|
|
||||||
const newItems = data?.data?.data?.data || [];
|
|
||||||
setItemsList(prev => (itemsPage === 1 ? newItems : [...prev, ...newItems]));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const packetScrollRef = useRef<HTMLDivElement>(null);
|
|
||||||
const [keyword, setKeyword] = useState<string>('');
|
const [keyword, setKeyword] = useState<string>('');
|
||||||
|
const [selectedProductNames, setSelectedProductNames] = useState<{ [productId: number]: string }>({});
|
||||||
|
const packetsList = keyword ? allPackets.filter(p => (p.name || '').toLowerCase().includes(keyword.toLowerCase())) : allPackets;
|
||||||
|
const itemsList = allItemsMap[packetId] || [];
|
||||||
|
const loadingItems = !allItemsMap[packetId] && !!packetId;
|
||||||
|
|
||||||
const { isFetching: isLoadingPackets } = useQuery({
|
useEffect(() => {
|
||||||
queryKey: ['packets-list', partyId, keyword, packetsPage],
|
if (packetId && !allItemsMap[packetId]) {
|
||||||
queryFn: () => box_requests.getAll({ partyId, cargoId: keyword, page: packetsPage }),
|
fetchAllItemsForPacket(packetId);
|
||||||
enabled: !!partyId,
|
|
||||||
onSuccess: data => {
|
|
||||||
const newPackets = data?.data?.data?.data || [];
|
|
||||||
setPacketsList(prev => (packetsPage === 1 ? newPackets : [...prev, ...newPackets]));
|
|
||||||
const totalPages = data?.data?.data?.totalPages || 0;
|
|
||||||
setPacketsHasMore(packetsPage < totalPages);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const handlePacketScroll = (e: React.UIEvent<HTMLDivElement>) => {
|
|
||||||
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
|
|
||||||
const isNearBottom = scrollHeight - scrollTop - clientHeight < 50;
|
|
||||||
|
|
||||||
if (isNearBottom && !isLoadingPackets && packetsHasMore) {
|
|
||||||
setPacketsPage(prev => prev + 1);
|
|
||||||
}
|
}
|
||||||
};
|
}, [packetId]);
|
||||||
|
|
||||||
const handleItemScroll = (e: React.UIEvent<HTMLDivElement>) => {
|
useEffect(() => {
|
||||||
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
|
if (packetId && field?.itemDtos?.length && itemsList.length) {
|
||||||
const isNearBottom = scrollHeight - scrollTop - clientHeight < 50;
|
const names: { [productId: number]: string } = {};
|
||||||
|
field.itemDtos.forEach((id: number) => {
|
||||||
if (isNearBottom && !isLoadingProducts && itemsHasMore) {
|
const prod = itemsList.find(p => p.id === id);
|
||||||
setItemsPage(prev => prev + 1);
|
names[id] = prod?.name || prod?.nameRu || String(id);
|
||||||
|
});
|
||||||
|
setSelectedProductNames(names);
|
||||||
}
|
}
|
||||||
|
}, [field?.itemDtos, itemsList, packetId]);
|
||||||
|
|
||||||
|
const handleProductChange = (product: any, checked: boolean) => {
|
||||||
|
setSelectedProductNames(prev => {
|
||||||
|
if (checked) {
|
||||||
|
return { ...prev, [product.id]: product.name || product.nameRu || String(product.id) };
|
||||||
|
} else {
|
||||||
|
const newNames = { ...prev };
|
||||||
|
delete newNames[product.id];
|
||||||
|
return newNames;
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setItemsPage(1);
|
const element = document.getElementById(`packet-select-${index}`);
|
||||||
setItemsList([]);
|
if (element) {
|
||||||
setItemsHasMore(true);
|
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
}, [packetId]);
|
}
|
||||||
|
}, [fields.length]);
|
||||||
|
|
||||||
const [selectedProductNames, setSelectedProductNames] = useState<{ [packetIndex: number]: { [productId: number]: string } }>({});
|
const handleSelectAllProducts = async () => {
|
||||||
|
if (!packetId) return;
|
||||||
const handleSelectAll = (checked: boolean, packetIndex: number, packetId: number, itemsList: any[]) => {
|
let allProducts = allItemsMap[packetId] || [];
|
||||||
setSelectedProductNames(prev => {
|
if (!allProducts.length) {
|
||||||
const prevNames = prev[packetIndex] || {};
|
allProducts = await fetchAllItemsForPacket(packetId);
|
||||||
if (checked) {
|
}
|
||||||
// add all products of current page
|
if (allProducts.length > 0) {
|
||||||
const newNames = { ...prevNames };
|
setValue(
|
||||||
itemsList.forEach(p => {
|
`packetItemDtos.${index}.itemDtos`,
|
||||||
newNames[p.id] = p.nameRu || p.name || String(p.id);
|
allProducts.map((p: any) => p.id)
|
||||||
});
|
);
|
||||||
return { ...prev, [packetIndex]: newNames };
|
setSelectedProductNames(
|
||||||
} else {
|
allProducts.reduce(
|
||||||
// remove all products of current page
|
(acc, p) => ({
|
||||||
const newNames = { ...prevNames };
|
...acc,
|
||||||
itemsList.forEach(p => {
|
[p.id]: p.name || p.nameRu || String(p.id),
|
||||||
delete newNames[p.id];
|
}),
|
||||||
});
|
{}
|
||||||
return { ...prev, [packetIndex]: newNames };
|
)
|
||||||
}
|
);
|
||||||
});
|
}
|
||||||
setValue(
|
|
||||||
`packetItemDtos.${packetIndex}.itemDtos`,
|
|
||||||
checked
|
|
||||||
? Array.from(new Set([...(watch(`packetItemDtos.${packetIndex}.itemDtos`) || []), ...itemsList.map(p => p.id)]))
|
|
||||||
: (watch(`packetItemDtos.${packetIndex}.itemDtos`) || []).filter(id => !itemsList.some(p => p.id === id))
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleProductChange = (packetIndex: number, product: any, checked: boolean) => {
|
const handleClearAll = () => {
|
||||||
setSelectedProductNames(prev => {
|
setValue(`packetItemDtos.${index}.itemDtos`, []);
|
||||||
const prevNames = prev[packetIndex] || {};
|
setSelectedProductNames({});
|
||||||
if (checked) {
|
|
||||||
return { ...prev, [packetIndex]: { ...prevNames, [product.id]: product.nameRu || product.name || String(product.id) } };
|
|
||||||
} else {
|
|
||||||
const newNames = { ...prevNames };
|
|
||||||
delete newNames[product.id];
|
|
||||||
return { ...prev, [packetIndex]: newNames };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -310,54 +355,45 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
justifyContent={'space-between'}
|
justifyContent={'space-between'}
|
||||||
alignItems={'center'}
|
alignItems={'center'}
|
||||||
>
|
>
|
||||||
<OutlinedInput onChange={e => setKeyword(e.target.value)} />
|
<OutlinedInput onChange={e => setKeyword(e.target.value)} autoFocus={false} />
|
||||||
<div style={{ width: '100%' }}>
|
<div style={{ width: '100%' }}>
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<InputLabel id={`packet-select-label-${index}`}>{t('packet')}</InputLabel>
|
<InputLabel id={`packet-select-label-${index}`}>{t('packet')}</InputLabel>
|
||||||
<Controller
|
<Controller
|
||||||
name={`packetItemDtos.${index}.packetId`}
|
name={`packetItemDtos.${index}.packetId`}
|
||||||
control={control}
|
control={control}
|
||||||
rules={{ required: requiredText }}
|
render={({ field: selectField }) => (
|
||||||
render={({ field }) => (
|
<Select
|
||||||
<>
|
{...selectField}
|
||||||
<Select
|
autoFocus={false}
|
||||||
{...field}
|
labelId={`packet-select-label-${index}`}
|
||||||
labelId={`packet-select-label-${index}`}
|
id={`packet-select-${index}`}
|
||||||
label={t('packet')}
|
label={t('packet')}
|
||||||
value={field.value || ''}
|
MenuProps={selectMenuProps}
|
||||||
disabled={isLoadingPackets}
|
onChange={e => {
|
||||||
renderValue={selected =>
|
e.stopPropagation();
|
||||||
paketName || packetsList.find(p => p.id === selected)?.name || t('select')
|
selectField.onChange(e);
|
||||||
}
|
}}
|
||||||
MenuProps={{
|
value={selectField.value || ''}
|
||||||
PaperProps: {
|
renderValue={selected => {
|
||||||
style: { maxHeight: 280 },
|
const selectedPacket = packetsList.find(p => p.id === selected);
|
||||||
ref: packetScrollRef,
|
return selectedPacket ? selectedPacket.name : t('loading');
|
||||||
onScroll: handlePacketScroll,
|
}}
|
||||||
},
|
>
|
||||||
disableAutoFocus: true,
|
{isLoadingPackets ? (
|
||||||
autoFocus: false,
|
<MenuItem disabled>
|
||||||
}}
|
<CircularProgress size={24} />
|
||||||
onClick={e => e.stopPropagation()}
|
</MenuItem>
|
||||||
>
|
) : packetsList.length === 0 ? (
|
||||||
{isLoadingPackets && packetsList.length === 0 ? (
|
<MenuItem disabled>{t('not_found') || 'Paketlar topilmadi'}</MenuItem>
|
||||||
<MenuItem disabled>
|
) : (
|
||||||
<CircularProgress size={24} />
|
packetsList.map(packet => (
|
||||||
|
<MenuItem key={packet.id} value={packet.id}>
|
||||||
|
{packet.name}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
) : (
|
))
|
||||||
packetsList.map(packet => (
|
)}
|
||||||
<MenuItem key={packet.id} value={packet.id} onClick={() => setPaketName(packet.name)}>
|
</Select>
|
||||||
{packet.name}
|
|
||||||
</MenuItem>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
{isLoadingPackets && packetsList.length > 0 && (
|
|
||||||
<MenuItem disabled>
|
|
||||||
<CircularProgress size={20} />
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{!!get(errors, `packetItemDtos.${index}.packetId`) && (
|
{!!get(errors, `packetItemDtos.${index}.packetId`) && (
|
||||||
@@ -378,25 +414,25 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{packetId && (
|
{packetId && (
|
||||||
<Box mt={2} sx={{ width: '100%' }}>
|
<Box mt={2} sx={{ width: '100%' }}>
|
||||||
<Typography fontSize='18px' sx={{ textAlign: 'start' }} fontWeight={500} color='#5D5850' mb={2}>
|
<Box display='flex' justifyContent='space-between' alignItems='center' mb={2}>
|
||||||
{t('products')}
|
<Typography fontSize='18px' sx={{ textAlign: 'start' }} fontWeight={500} color='#5D5850'>
|
||||||
</Typography>
|
{t('products')}
|
||||||
{isLoadingProducts && itemsList.length === 0 ? (
|
</Typography>
|
||||||
|
<Box>
|
||||||
|
<Button size='small' color='primary' variant='outlined' sx={{ mr: 1 }} onClick={handleSelectAllProducts}>
|
||||||
|
{t('select_all') || 'Barcha mahsulotni tanlash'}
|
||||||
|
</Button>
|
||||||
|
<Button size='small' color='error' variant='outlined' onClick={handleClearAll}>
|
||||||
|
{t('cancel') || 'Hammasini bekor qilish'}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
{loadingItems ? (
|
||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
) : (
|
) : (
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
{/* <Box display='flex' alignItems='center' mb={1}>
|
|
||||||
<Checkbox
|
|
||||||
indeterminate={selectedProducts.length > 0 && selectedProducts.length < itemsList.length}
|
|
||||||
checked={itemsList.length > 0 && selectedProducts.length === itemsList.length}
|
|
||||||
onChange={e => handleSelectAll(e.target.checked)}
|
|
||||||
disabled={itemsList.length === 0}
|
|
||||||
/>
|
|
||||||
<Typography variant='body2'>{t('select_all')}</Typography>
|
|
||||||
</Box> */}
|
|
||||||
<Controller
|
<Controller
|
||||||
name={`packetItemDtos.${index}.itemDtos`}
|
name={`packetItemDtos.${index}.itemDtos`}
|
||||||
control={control}
|
control={control}
|
||||||
@@ -404,46 +440,28 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
<Select
|
<Select
|
||||||
{...field}
|
{...field}
|
||||||
multiple
|
multiple
|
||||||
MenuProps={{
|
MenuProps={selectMenuProps}
|
||||||
PaperProps: {
|
|
||||||
style: { maxHeight: 280 },
|
|
||||||
ref: itemScrollRef,
|
|
||||||
onScroll: handleItemScroll,
|
|
||||||
},
|
|
||||||
disableAutoFocus: true,
|
|
||||||
autoFocus: false,
|
|
||||||
}}
|
|
||||||
renderValue={selected => (
|
renderValue={selected => (
|
||||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
|
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
|
||||||
{(selected as any[]).map(id => (
|
{(selected as any[]).map((id: number) => (
|
||||||
<Chip
|
<Chip
|
||||||
key={id}
|
key={id}
|
||||||
label={
|
label={
|
||||||
selectedProductNames[index]?.[id] ||
|
selectedProductNames[id] ||
|
||||||
itemsList.find(p => p.id === id)?.nameRu ||
|
|
||||||
itemsList.find(p => p.id === id)?.name ||
|
itemsList.find(p => p.id === id)?.name ||
|
||||||
|
itemsList.find(p => p.id === id)?.nameRu ||
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
onDelete={e => {
|
onDelete={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
field.onChange(field.value.filter((x: any) => x !== id));
|
field.onChange(field.value.filter((x: any) => x !== id));
|
||||||
handleProductChange(index, { id }, false);
|
handleProductChange({ id }, false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<MenuItem>
|
|
||||||
<Checkbox
|
|
||||||
indeterminate={field.value.length > 0 && field.value.length < itemsList.length}
|
|
||||||
checked={
|
|
||||||
itemsList.length > 0 && itemsList.every((p: any) => field.value.includes(p.id))
|
|
||||||
}
|
|
||||||
onChange={e => handleSelectAll(e.target.checked, index, packetId, itemsList)}
|
|
||||||
/>
|
|
||||||
<Typography variant='body2'>{t('select_all')}</Typography>
|
|
||||||
</MenuItem>
|
|
||||||
{itemsList.map(product => (
|
{itemsList.map(product => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={product.id}
|
key={product.id}
|
||||||
@@ -458,11 +476,11 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
newValue = field.value.filter((x: any) => x !== product.id);
|
newValue = field.value.filter((x: any) => x !== product.id);
|
||||||
}
|
}
|
||||||
field.onChange(newValue);
|
field.onChange(newValue);
|
||||||
handleProductChange(index, product, checked);
|
handleProductChange(product, checked);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Checkbox checked={field.value.includes(product.id)} />
|
<Checkbox checked={field.value.includes(product.id)} />
|
||||||
<ListItemText primary={product.nameRu || product.name || product.id} />
|
<ListItemText primary={product.name || product.nameRu || product.id} />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
@@ -486,11 +504,10 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box component='form' onSubmit={onSubmit}>
|
<Box component='form' onSubmit={onSubmit} sx={{ overflowAnchor: 'none' }}>
|
||||||
<Typography variant='h5' mb={3.5}>
|
<Typography variant='h5' mb={3.5}>
|
||||||
{editMode ? t('update_box') : t('create_box')}
|
{editMode ? t('update_box') : t('create_box')}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Grid container columnSpacing={2.5} rowSpacing={3} mb={3.5}>
|
<Grid container columnSpacing={2.5} rowSpacing={3} mb={3.5}>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Typography fontSize='18px' fontWeight={500} color='#5D5850' mb={2}>
|
<Typography fontSize='18px' fontWeight={500} color='#5D5850' mb={2}>
|
||||||
@@ -507,6 +524,7 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
{...field}
|
{...field}
|
||||||
labelId='party-select-label'
|
labelId='party-select-label'
|
||||||
label={t('party_name')}
|
label={t('party_name')}
|
||||||
|
MenuProps={selectMenuProps}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
field.onChange(e);
|
field.onChange(e);
|
||||||
handlePartyChange(e);
|
handlePartyChange(e);
|
||||||
@@ -531,7 +549,6 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
{!!errors.partyId && <FormHelperText sx={{ color: 'red' }}>{requiredText}</FormHelperText>}
|
{!!errors.partyId && <FormHelperText sx={{ color: 'red' }}>{requiredText}</FormHelperText>}
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Stack
|
<Stack
|
||||||
sx={{
|
sx={{
|
||||||
@@ -549,13 +566,12 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
|||||||
<PacketRow key={field.key} index={index} field={field} />
|
<PacketRow key={field.key} index={index} field={field} />
|
||||||
))}
|
))}
|
||||||
<BaseButton variant='outlined' onClick={appendPacket} startIcon={<AddCircleRounded />} sx={{ mt: 2 }}>
|
<BaseButton variant='outlined' onClick={appendPacket} startIcon={<AddCircleRounded />} sx={{ mt: 2 }}>
|
||||||
{t('add_packet')}
|
{t('add_more')}
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<BaseButton variant='contained' type='submit' disabled={loading} fullWidth sx={{ py: 1.5 }}>
|
<BaseButton variant='contained' type='submit' disabled={loading} fullWidth sx={{ py: 1.5 }}>
|
||||||
{loading ? <CircularProgress size={24} color='inherit' /> : editMode ? t('update_box') : t('create_box')}
|
{loading ? <CircularProgress size={24} color='inherit' /> : editMode ? t('update_box') : t('create_box')}
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
|
|||||||
Reference in New Issue
Block a user