Merge branch 'samandar' into 'dev'

added pakets

See merge request azizziy/cpost!26
This commit is contained in:
Azizbek Usmonov
2025-06-19 15:27:25 +05:00
11 changed files with 370 additions and 290 deletions

View File

@@ -211,5 +211,6 @@
"party_weight": "大量重量", "party_weight": "大量重量",
"qr_code": "二维码", "qr_code": "二维码",
"created_at": "添加日期", "created_at": "添加日期",
"download_all_items_exel": "将所有产品上传至Excel" "download_all_items_exel": "将所有产品上传至Excel",
"select_all": "全选,"
} }

View File

@@ -210,6 +210,7 @@
"update_packet": "Edit Paket", "update_packet": "Edit Paket",
"party_weight": "Batch weight", "party_weight": "Batch weight",
"download_all_items_exel": "Upload all products in Excel", "download_all_items_exel": "Upload all products in Excel",
"select_all": "Select all",
"qr_code": "QR Code", "qr_code": "QR Code",
"created_at": "Date of joining" "created_at": "Date of joining"
} }

View File

@@ -223,6 +223,7 @@
"update_packet": "Редактировать пакет", "update_packet": "Редактировать пакет",
"party_weight": "Вес партии", "party_weight": "Вес партии",
"download_all_items_exel": "Загрузить все товары в Excel", "download_all_items_exel": "Загрузить все товары в Excel",
"select_all": "Выделить все",
"qr_code": "QR код", "qr_code": "QR код",
"created_at": "Дата добавления" "created_at": "Дата добавления"

View File

@@ -222,6 +222,7 @@
"passport": "Pasport", "passport": "Pasport",
"update_packet": "Paketni Tahrirlash", "update_packet": "Paketni Tahrirlash",
"party_weight": "Partiya og'irligi", "party_weight": "Partiya og'irligi",
"select_all": "Hammasini belgilash",
"qr_code": "QR kod", "qr_code": "QR kod",
"created_at": "Qoshilgan sana" "created_at": "Qoshilgan sana"

View File

@@ -1,7 +1,7 @@
import * as React from 'react'; import { Divider } from '@mui/material';
import Pagination from '@mui/material/Pagination'; import Pagination from '@mui/material/Pagination';
import Stack from '@mui/material/Stack'; import Stack from '@mui/material/Stack';
import { Divider } from '@mui/material'; import * as React from 'react';
interface BasePaginationProps { interface BasePaginationProps {
page: number; page: number;
@@ -37,14 +37,15 @@ export default function BasePagination({ page, pageSize, totalCount, onChange }:
}, [inputValue, page, totalCount, pageSize, onChange]); }, [inputValue, page, totalCount, pageSize, onChange]);
return ( return (
<Stack spacing={2} direction="row" divider={<Divider orientation="vertical" flexItem />}> <Stack spacing={2} direction='row' divider={<Divider orientation='vertical' flexItem />}>
<Pagination <Pagination
page={page} page={page}
size='large'
count={Math.ceil(totalCount / pageSize)} count={Math.ceil(totalCount / pageSize)}
onChange={(_, newPage) => onChange(newPage)} onChange={(_, newPage) => onChange(newPage)}
variant="outlined" variant='outlined'
shape="rounded" shape='rounded'
color="primary" color='primary'
sx={{ sx={{
'.Mui-selected': { '.Mui-selected': {
backgroundColor: theme => theme.palette.primary.main, backgroundColor: theme => theme.palette.primary.main,
@@ -54,9 +55,9 @@ export default function BasePagination({ page, pageSize, totalCount, onChange }:
/> />
<input <input
value={inputValue} value={inputValue}
type="number" type='number'
style={{ width: "50px", textAlign: "center", outline: "none" }} style={{ width: '50px', textAlign: 'center', outline: 'none' }}
onChange={(e) => setInputValue(Number(e.target.value))} onChange={e => setInputValue(Number(e.target.value))}
/> />
</Stack> </Stack>
); );

View File

@@ -7,6 +7,7 @@ export type Product = {
cargoId: string; cargoId: string;
trekId: string; trekId: string;
name: string; name: string;
nameRu: string;
amount: number; amount: number;
weight: number; weight: number;
price?: number; price?: number;

View File

@@ -1,5 +1,5 @@
import { BoxStatus } from '@/data/box/box.model'; import { BoxStatus } from '@/data/box/box.model';
import { Product, CreateProductBodyType, UpdateProductBodyType } from '@/data/item/item.mode'; import { CreateProductBodyType, Product, UpdateProductBodyType } from '@/data/item/item.mode';
import { CommonResponseType, PageAble } from '@/helpers/types'; import { CommonResponseType, PageAble } from '@/helpers/types';
import { request } from '@/services/request'; import { request } from '@/services/request';
@@ -29,6 +29,9 @@ export const item_requests = {
async find(params: { itemId?: number | string }) { async find(params: { itemId?: number | string }) {
return request.get<CommonResponseType<Product[]>>('/items/find', { params }); return request.get<CommonResponseType<Product[]>>('/items/find', { params });
}, },
async list(params: { packetId?: number | string }) {
return request.get<CommonResponseType<{ data: Product[] }>>('/items/list', { params });
},
async delete(params: { itemId: number | string }) { async delete(params: { itemId: number | string }) {
return request.delete<CommonResponseType>('/items/delete', { params }); return request.delete<CommonResponseType>('/items/delete', { params });
}, },

View File

@@ -5,7 +5,7 @@ export const BoxStatusList: BoxStatus[] = ['READY_TO_INVOICE', 'READY'];
export interface IRealBox { export interface IRealBox {
boxName: string; boxName: string;
id: number; id: number;
packetsCount: number; itemCount: number;
partyName: string; partyName: string;
} }
@@ -26,19 +26,22 @@ export interface IRealBoxDetail {
export interface RealCreateBoxBodyType { export interface RealCreateBoxBodyType {
partyName: string; partyName: string;
packetDtos: number[]; packetItemDtos: { packetId: number; itemDtos: number[] }[];
} }
export interface UpdateRealBoxBodyType { export interface UpdateRealBoxBodyType {
boxId: string; boxId: string;
partyName: string; partyName: string;
packetDtos: number[]; packetItemDtos: { packetId: number; itemDtos: number[] }[];
} }
export interface FormValues { export interface FormValues {
partyName: string;
paketIds: Array<{ id: string | number }>;
id?: number; id?: number;
boxId?: string; boxId?: string;
partyName: string;
partyId?: number; partyId?: number;
packetItemDtos: Array<{
packetId: number;
itemDtos: number[];
}>;
} }

View File

@@ -2,25 +2,38 @@
import BaseButton from '@/components/ui-kit/BaseButton'; import BaseButton from '@/components/ui-kit/BaseButton';
import BaseIconButton from '@/components/ui-kit/BaseIconButton'; import BaseIconButton from '@/components/ui-kit/BaseIconButton';
import { party_requests } from '@/data/party/party.requests'; import { useAuthContext } from '@/context/auth-context';
import { box_requests } from '@/data/box/box.requests'; import { box_requests } from '@/data/box/box.requests';
import { item_requests } from '@/data/item/item.requests';
import { party_requests } from '@/data/party/party.requests';
import { FormValues, RealCreateBoxBodyType, UpdateRealBoxBodyType } from '@/data/real-box/real-box.model';
import { real_box_requests } from '@/data/real-box/real-box.requests'; import { real_box_requests } from '@/data/real-box/real-box.requests';
import { pageLinks } from '@/helpers/constants'; 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 { useMyNavigation } from '@/hooks/useMyNavigation';
import { useMyTranslation } from '@/hooks/useMyTranslation'; import { useMyTranslation } from '@/hooks/useMyTranslation';
import { FormValues, RealCreateBoxBodyType, UpdateRealBoxBodyType } from '@/data/real-box/real-box.model'; import { notifyUnknownError } from '@/services/notification';
import { AddCircleRounded, Close } from '@mui/icons-material';
import {
Box,
Checkbox,
Chip,
CircularProgress,
FormControl,
FormHelperText,
Grid,
InputLabel,
ListItemText,
MenuItem,
Select,
Stack,
styled,
Typography,
} from '@mui/material';
import { useQuery } from '@tanstack/react-query';
import get from 'lodash.get'; import get from 'lodash.get';
import useRequest from '@/hooks/useRequest'; import { useSearchParams } from 'next/navigation';
import useInput from '@/hooks/useInput'; import { useEffect, useState } from 'react';
import { Controller, useFieldArray, useForm } from 'react-hook-form';
const StyledCreateBox = styled(Box)` const StyledCreateBox = styled(Box)`
.item-row { .item-row {
@@ -42,62 +55,55 @@ interface Props {
boxId?: string; boxId?: string;
partyId?: number; partyId?: number;
partyName?: string; partyName?: string;
paketIds?: Array<{ id: number; packetName: string }>; paketIds?: Array<any>;
}; };
} }
const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => { const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
const { user, isAdmin: isAdminUser } = useAuthContext(); const { user } = useAuthContext();
const editMode = !!initialValues?.id; const editMode = !!initialValues?.id;
const [keyword, handleKeyword] = useState(''); const [keyword, setKeyword] = useState('');
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>('');
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const helperRef = useRef<{ console.log(initialValues);
finished: boolean;
partyFinished: boolean;
clientFinished: boolean;
settedDefaultParty: any;
}>({
settedDefaultParty: null,
partyFinished: false,
clientFinished: false,
finished: false,
});
console.log(keyword, "keyword");
const { const {
register, register,
control, control,
handleSubmit, handleSubmit,
setValue, setValue,
reset, reset,
watch,
formState: { errors }, formState: { errors },
} = useForm<FormValues>({ } = useForm<FormValues>({
defaultValues: { defaultValues: {
partyName: initialValues?.partyName || '', partyName: initialValues?.partyName || '',
paketIds: initialValues?.paketIds packetItemDtos: initialValues?.paketIds
? initialValues.paketIds.map((paket) => ({ id: paket.id })) ? initialValues.paketIds.map(paket => ({
packetId: paket.id,
itemDtos: paket.items ? paket.items.map((item: any) => item.id) : [],
}))
: params.get('party_id') : params.get('party_id')
? [{ id: +params.get('party_id')! }] ? [{ packetId: +params.get('party_id')!, itemDtos: [] }]
: [{ id: '' }], : [{ packetId: 0, itemDtos: [] }],
id: initialValues?.id, id: initialValues?.id,
boxId: initialValues?.boxId, boxId: initialValues?.boxId,
partyId: initialValues?.partyId, partyId: initialValues?.partyId,
}, },
}); });
// Reset form when initialValues change (for edit mode)
useEffect(() => { useEffect(() => {
if (initialValues) { if (initialValues) {
reset({ reset({
partyName: initialValues.partyName || '', partyName: initialValues.partyName || '',
paketIds: initialValues.paketIds packetItemDtos: initialValues.paketIds
? initialValues.paketIds.map((paket) => ({ id: paket.id })) ? initialValues.paketIds.map(paket => ({
: [{ id: '' }], packetId: paket.id,
itemDtos: paket.items ? paket.items.map((item: any) => item.id) : [],
}))
: [{ packetId: 0, itemDtos: [] }],
id: initialValues.id, id: initialValues.id,
boxId: initialValues.boxId, boxId: initialValues.boxId,
partyId: initialValues.partyId, partyId: initialValues.partyId,
@@ -110,73 +116,56 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
const { fields, append, remove } = useFieldArray({ const { fields, append, remove } = useFieldArray({
control, control,
name: 'paketIds', name: 'packetItemDtos',
keyName: 'key', keyName: 'key',
}); });
const requiredText = t('required'); const requiredText = t('required');
const getBoxesQuery = useRequest( // Fetch parties data
() => const { data: parties = [], isLoading: isLoadingParties } = useQuery({
queryKey: ['parties-list', 'COLLECTING'],
queryFn: () => party_requests.getAll({ status: 'COLLECTING' }),
select: data =>
data.data.data.data.map((p: any) => ({
id: p.id,
name: p.name,
})),
});
// Fetch packets data
const { data: packets = [], isLoading: isLoadingPackets } = useQuery({
queryKey: ['packets-list', partyId, keyword],
queryFn: () =>
box_requests.getAll({ box_requests.getAll({
partyId: partyId, partyId: partyId,
cargoId: keyword cargoId: keyword,
}), }),
{ select: data => data?.data?.data?.data.filter((box: any) => box.status === 'READY_TO_INVOICE') ?? [],
selectData: (data) => data?.data?.data ?? [],
enabled: !!partyId, enabled: !!partyId,
}, });
);
const list = useMemo(() => { const onSubmit = handleSubmit(async values => {
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.partyFinished = true;
if (helperRef.current.clientFinished) {
helperRef.current.finished = true;
}
},
placeholderData: [],
},
);
const onSubmit = handleSubmit(async (values) => {
try { try {
setLoading(true); setLoading(true);
const packetDtos = values.paketIds.map((paket) => Number(paket.id)).filter((id) => id);
if (editMode) { if (editMode) {
const updateBody: UpdateRealBoxBodyType = { const updateBody: UpdateRealBoxBodyType = {
boxId: initialValues!.boxId!, boxId: initialValues!.boxId!,
partyName: values.partyName, partyName: values.partyName,
packetDtos, packetItemDtos: values.packetItemDtos.map(packet => ({
packetId: packet.packetId,
itemDtos: packet.itemDtos,
})),
}; };
await real_box_requests.update(updateBody); await real_box_requests.update(updateBody);
} else { } else {
const createBody: RealCreateBoxBodyType = { const createBody: RealCreateBoxBodyType = {
partyName: values.partyName, partyName: values.partyName,
packetDtos, packetItemDtos: values.packetItemDtos.map(packet => ({
packetId: packet.packetId,
itemDtos: packet.itemDtos,
})),
}; };
await real_box_requests.create(createBody); await real_box_requests.create(createBody);
} }
@@ -189,27 +178,183 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
} }
}); });
const partyOptions = async (inputValue: string) => { const handlePartyChange = (event: any) => {
try { const selectedParty = parties.find(p => p.id === event.target.value);
const res = await party_requests.getAll({ if (selectedParty) {
status: 'COLLECTING', setValue('partyName', selectedParty.name);
partyName: inputValue, setPartyId(selectedParty.id);
}); setKeyword('');
return res.data.data.data.map((p: any) => ({ label: p.name, value: p.id })); setValue('packetItemDtos', [{ packetId: 0, itemDtos: [] }]);
} catch (error) {
notifyUnknownError(error);
return [];
} }
}; };
const appendPaket = () => { const handlePacketChange = (index: number, value: number) => {
append({ id: '' }); setValue(`packetItemDtos.${index}.packetId`, value);
setValue(`packetItemDtos.${index}.itemDtos`, []);
}; };
const removePaket = (index: number) => { const appendPacket = () => {
append({ packetId: 0, itemDtos: [] });
};
const removePacket = (index: number) => {
remove(index); remove(index);
}; };
// Component for each packet row to isolate product selection
const PacketRow = ({ index, field }: { index: number; field: any }) => {
const packetId = watch(`packetItemDtos.${index}.packetId`);
// Fetch products specific to this packet
const { data: products, isLoading: isLoadingProducts } = useQuery({
queryKey: ['product-list', packetId],
queryFn: () => item_requests.list({ packetId }),
select: data => data?.data?.data.data ?? [],
enabled: !!packetId,
});
return (
<Box className='item-row' mb={2} display={'flex'} flexDirection={'column'}>
<div style={{ width: '100%' }}>
<Typography fontSize='18px' sx={{ textAlign: 'start' }} fontWeight={500} color='#5D5850'>
{t('packet')}
</Typography>
</div>
<Box
className='item-row-field'
sx={{ width: '100%' }}
flex={1}
display={'flex'}
gap={2}
justifyContent={'space-between'}
alignItems={'center'}
>
<div style={{ width: '100%' }}>
<FormControl fullWidth>
<InputLabel id={`packet-select-label-${index}`}>{t('packet')}</InputLabel>
<Controller
name={`packetItemDtos.${index}.packetId`}
control={control}
rules={{ required: requiredText }}
render={({ field }) => (
<Select
{...field}
labelId={`packet-select-label-${index}`}
label={t('packet')}
onChange={e => {
field.onChange(e);
handlePacketChange(index, e.target.value as number);
}}
value={field.value || ''}
disabled={isLoadingPackets}
>
{isLoadingPackets ? (
<MenuItem disabled>
<CircularProgress size={24} />
</MenuItem>
) : (
packets.map(packet => (
<MenuItem key={packet.id} value={packet.id}>
{packet.name || packet.name || `Box ${packet.id}`}
</MenuItem>
))
)}
</Select>
)}
/>
{!!get(errors, `packetItemDtos.${index}.packetId`) && (
<FormHelperText sx={{ color: 'red' }}>{requiredText}</FormHelperText>
)}
</FormControl>
</div>
<div>
{fields.length > 1 && (
<BaseIconButton
size='small'
colorVariant='icon-error'
sx={{ flexShrink: 0, height: 'auto', marginTop: 1 }}
onClick={() => removePacket(index)}
>
<Close />
</BaseIconButton>
)}
</div>
</Box>
{packetId && (
<Box mt={2} sx={{ width: '100%' }}>
<Typography fontSize='18px' sx={{ textAlign: 'start' }} fontWeight={500} color='#5D5850' mb={2}>
{t('products')}
</Typography>
{isLoadingProducts ? (
<CircularProgress />
) : (
<FormControl fullWidth>
<Box display='flex' alignItems='center' mb={1}>
<Controller
name={`packetItemDtos.${index}.itemDtos`}
control={control}
render={({ field: { onChange } }) => (
<Checkbox
checked={watch(`packetItemDtos.${index}.itemDtos`)?.length === products?.length}
onChange={e => {
if (e.target.checked) {
setValue(`packetItemDtos.${index}.itemDtos`, products?.map(p => p.id) || []);
} else {
setValue(`packetItemDtos.${index}.itemDtos`, []);
}
}}
indeterminate={
watch(`packetItemDtos.${index}.itemDtos`)?.length > 0 &&
watch(`packetItemDtos.${index}.itemDtos`)?.length < products?.length!
}
/>
)}
/>
<Typography variant='body2'>{t('select_all')}</Typography>
</Box>
<Controller
name={`packetItemDtos.${index}.itemDtos`}
control={control}
render={({ field }) => (
<Select
{...field}
labelId={`product-select-label-${index}`}
multiple
value={field.value || []}
onChange={e => field.onChange(e.target.value)}
renderValue={selected => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{(selected as any[]).map(value => (
<Chip
key={value}
label={products?.find(p => p.id === value)?.name}
onDelete={() => {
const newValue = (field.value || []).filter(id => id !== value);
field.onChange(newValue);
}}
/>
))}
</Box>
)}
>
{products?.map(product => (
<MenuItem key={product.id} value={product.id}>
<Checkbox checked={(field.value || []).includes(product.id)} />
<ListItemText primary={product.name} />
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
)}
</Box>
)}
</Box>
);
};
return ( return (
<StyledCreateBox <StyledCreateBox
width={1} width={1}
@@ -220,53 +365,50 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
backgroundColor: '#fff', backgroundColor: '#fff',
}} }}
> >
<Box component="form" onSubmit={onSubmit}> <Box component='form' onSubmit={onSubmit}>
<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}>
{t('party_name')} {t('party_name')}
</Typography> </Typography>
<FormControl fullWidth>
<InputLabel id='party-select-label'>{t('party_name')}</InputLabel>
<Controller <Controller
name="partyName" name='partyId'
control={control} control={control}
rules={{ required: requiredText }} rules={{ required: requiredText }}
render={({ field }) => ( render={({ field }) => (
<AsyncSelect <Select
onChange={(newValue: any) => { {...field}
field.onChange(newValue?.label ?? ''); labelId='party-select-label'
setPartyId(newValue?.value ?? ''); label={t('party_name')}
onChange={e => {
field.onChange(e);
handlePartyChange(e);
}} }}
defaultValue={ value={field.value || ''}
editMode disabled={isLoadingParties}
? { >
value: initialValues?.partyId, {isLoadingParties ? (
label: initialValues?.partyName, <MenuItem disabled>
} <CircularProgress size={24} />
: partiesData?.length </MenuItem>
? { ) : (
value: partiesData[0].value, parties.map(party => (
label: partiesData[0].label, <MenuItem key={party.id} value={party.id}>
} {party.name}
: null </MenuItem>
} ))
styles={selectDefaultStyles} )}
noOptionsMessage={() => t('not_found')} </Select>
loadingMessage={() => t('loading')}
onBlur={field.onBlur}
name={field.name}
defaultOptions={defaultParties ?? []}
loadOptions={partyOptions}
placeholder={t('enter_party_name_to_find')}
/>
)} )}
/> />
{!!errors.partyName && ( {!!errors.partyId && <FormHelperText sx={{ color: 'red' }}>{requiredText}</FormHelperText>}
<FormHelperText sx={{ color: 'red' }}>{requiredText}</FormHelperText> </FormControl>
)}
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
@@ -276,95 +418,25 @@ const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
border: '2px solid #3489E4', border: '2px solid #3489E4',
background: '#FFF', background: '#FFF',
padding: '24px', padding: '24px',
gap: '12px',
display: 'flex',
flexDirection: 'column',
}} }}
> >
<Typography fontSize="18px" fontWeight={500} color="#5D5850" mb={2}> <div>
{t('packet')}
</Typography>
{fields.map((field, index) => ( {fields.map((field, index) => (
<Box key={field.key} className="item-row" mb={2}> <PacketRow key={field.key} index={index} field={field} />
<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 ?? '');
handleKeyword(newValue?.label ?? '');
}}
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,
cargoId: inputValue,
});
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 [];
}
}}
placeholder={t('enter_box_name_to_find')}
/>
)}
/>
{!!get(errors, `paketIds.${index}.id`) && (
<FormHelperText sx={{ color: 'red' }}>{requiredText}</FormHelperText>
)}
</Box>
<BaseIconButton
size="small"
colorVariant="icon-error"
sx={{ flexShrink: 0, height: 'auto', marginTop: 1 }}
onClick={() => removePaket(index)}
>
<Close />
</BaseIconButton>
</Box>
))} ))}
<BaseButton <BaseButton variant='outlined' onClick={appendPacket} startIcon={<AddCircleRounded />} sx={{ mt: 2 }}>
variant="outlined"
onClick={appendPaket}
startIcon={<AddCircleRounded />}
>
{t('add_packet')} {t('add_packet')}
</BaseButton> </BaseButton>
</div>
</Stack> </Stack>
</Grid> </Grid>
</Grid> </Grid>
<BaseButton <BaseButton variant='contained' type='submit' disabled={loading} fullWidth sx={{ py: 1.5 }}>
variant="contained" {loading ? <CircularProgress size={24} color='inherit' /> : editMode ? t('update_box') : t('create_box')}
type="submit"
disabled={loading}
fullWidth
>
{editMode ? t('update_box') : t('create_box')}
</BaseButton> </BaseButton>
</Box> </Box>
</StyledCreateBox> </StyledCreateBox>

View File

@@ -1,11 +1,11 @@
'use client'; 'use client';
import Loader from '@/components/common/Loader'; import Loader from '@/components/common/Loader';
import { real_box_requests } from '@/data/real-box/real-box.requests';
import useRequest from '@/hooks/useRequest'; import useRequest from '@/hooks/useRequest';
import { useParams } from 'next/navigation'; import { useParams } from 'next/navigation';
import React, { useEffect } from 'react'; import { useEffect } from 'react';
import DashboardCreateRealBoxPage from './DashboardCreateRealBox'; import DashboardCreateRealBoxPage from './DashboardCreateRealBox';
import { real_box_requests } from '@/data/real-box/real-box.requests';
type Props = {}; type Props = {};
@@ -19,27 +19,28 @@ const DashboardEditRealBoxPage = (props: Props) => {
}, },
{ {
selectData(data) { selectData(data) {
const boxData = data.data; // ⬅️ Faqat data.data, data.data.data emas const boxData = data.data;
if (!boxData) return null; if (!boxData) return null;
console.log(boxData.data, 'boxdata');
return { return {
id: box_id, id: box_id,
boxId: box_id!, boxId: box_id!,
partyId: boxData.data.party.id, // ⬅️ Togri yol partyId: boxData.data.party.id,
partyName: boxData.data.party.name!, partyName: boxData.data.party.name!,
paketIds: boxData.data.packets! paketIds: boxData.data.packetItemDtos!,
items: boxData.data.packetItemDtos.items,
// id?: number;
// boxId?: string;
// partyId?: number;
// partyName?: string;
// paketIds?: Array<{ id: number; packetName: string }>;
}; };
}, },
} }
); );
useEffect(() => { useEffect(() => {
getOneBox.refetch() getOneBox.refetch();
}, [box_id]) }, [box_id]);
console.log(getOneBox);
if (getOneBox.loading || !getOneBox.data) { if (getOneBox.loading || !getOneBox.data) {
return <Loader p={8} size={96} />; return <Loader p={8} size={96} />;

View File

@@ -1,31 +1,25 @@
'use client'; 'use client';
import ActionPopMenu from '@/components/common/ActionPopMenu'; import ActionPopMenu from '@/components/common/ActionPopMenu';
import { MyTable, ColumnData } from '@/components/common/MyTable'; import { ColumnData, MyTable } from '@/components/common/MyTable';
import StatusChangePopup from '@/components/common/StatusChangePopup';
import BaseButton from '@/components/ui-kit/BaseButton'; import BaseButton from '@/components/ui-kit/BaseButton';
import BaseIconButton from '@/components/ui-kit/BaseIconButton';
import BaseInput from '@/components/ui-kit/BaseInput'; import BaseInput from '@/components/ui-kit/BaseInput';
import BasePagination from '@/components/ui-kit/BasePagination'; 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 } from '@/data/box/box.model';
import { box_requests } from '@/data/box/box.requests'; import { box_requests } from '@/data/box/box.requests';
import { IRealBox } from '@/data/real-box/real-box.model'; import { IRealBox } from '@/data/real-box/real-box.model';
import { real_box_requests } from '@/data/real-box/real-box.requests'; import { real_box_requests } from '@/data/real-box/real-box.requests';
import { DEFAULT_PAGE_SIZE, pageLinks } from '@/helpers/constants'; import { DEFAULT_PAGE_SIZE, pageLinks } from '@/helpers/constants';
import useDebouncedInput from '@/hooks/useDebouncedInput';
import useInput from '@/hooks/useInput'; import useInput from '@/hooks/useInput';
import { useMyNavigation } from '@/hooks/useMyNavigation'; import { useMyNavigation } from '@/hooks/useMyNavigation';
import { useMyTranslation } from '@/hooks/useMyTranslation'; import { useMyTranslation } from '@/hooks/useMyTranslation';
import useRequest from '@/hooks/useRequest'; 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 { getBoxStatusStyles, getStatusColor } from '@/theme/getStatusBoxStyles'; import { Add, Delete, Download, Edit, FilterListOff, QrCode, Search } from '@mui/icons-material';
import { Add, QrCode, AddCircleOutline, Circle, Delete, Download, Edit, FilterList, FilterListOff, Search, PlusOne } from '@mui/icons-material'; import { Box, Button, Stack, Typography } from '@mui/material';
import { Box, Button, Stack, Tooltip, Typography } from '@mui/material'; import { useEffect, useMemo, useState } from 'react';
import { useRouter } from 'next/navigation';
import { log } from 'node:console';
import React, { useEffect, useMemo, useState } from 'react';
type Props = {}; type Props = {};
@@ -135,7 +129,7 @@ const DashboardRealBoxesPage = (props: Props) => {
try { try {
setDownloadIds(p => [...p, id]); setDownloadIds(p => [...p, id]);
const response = await real_box_requests.downloadQrCode({ boxId: id }); const response = await real_box_requests.downloadQrCode({ boxId: id });
console.log(response, "rres"); console.log(response, 'rres');
const file = new File([response.data], 'qr.png', { type: response.data.type }); const file = new File([response.data], 'qr.png', { type: response.data.type });
file_service.download(file); file_service.download(file);
} catch (error) { } catch (error) {
@@ -178,25 +172,26 @@ const DashboardRealBoxesPage = (props: Props) => {
}, },
}, },
{ {
label: t("qr_code"), label: t('qr_code'),
width: 120, width: 120,
renderCell: data => { renderCell: data => {
return <Button return (
onClick={() => <Button onClick={() => onDownloadQrCode(data.id)}>
onDownloadQrCode(data.id)
}>
<QrCode /> <QrCode />
</Button >; </Button>
);
}, },
}, },
{ {
dataKey: 'id', dataKey: 'id',
label: t("add_more"), label: t('add_more'),
width: 120, width: 120,
renderCell: data => { renderCell: data => {
return <Button onClick={() => navigation.push(pageLinks.dashboard.real_boxes.edit(data.id))}> return (
<Button onClick={() => navigation.push(pageLinks.dashboard.real_boxes.edit(data.id))}>
<Add /> <Add />
</Button>; </Button>
);
}, },
}, },
{ {
@@ -205,7 +200,7 @@ const DashboardRealBoxesPage = (props: Props) => {
width: 120, width: 120,
}, },
{ {
dataKey: "boxName", dataKey: 'boxName',
label: t('party_name'), label: t('party_name'),
width: 120, width: 120,
}, },
@@ -220,7 +215,7 @@ const DashboardRealBoxesPage = (props: Props) => {
// width: 120, // width: 120,
// }, // },
{ {
dataKey: 'packetsCount', dataKey: 'itemCount',
label: t('count_of_items'), label: t('count_of_items'),
width: 120, width: 120,
}, },