364 lines
15 KiB
TypeScript
364 lines
15 KiB
TypeScript
'use client';
|
|
|
|
import BaseButton from '@/components/ui-kit/BaseButton';
|
|
import { party_requests } from '@/data/party/party.requests';
|
|
import { pageLinks } from '@/helpers/constants';
|
|
import { notifyUnknownError } from '@/services/notification';
|
|
import { Box, FormHelperText, Grid, Stack, Typography, styled } from '@mui/material';
|
|
import { useSearchParams } from 'next/navigation';
|
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
import { Controller, useFieldArray, useForm } from 'react-hook-form';
|
|
import BaseIconButton from '@/components/ui-kit/BaseIconButton';
|
|
import { AddCircleRounded, Close } from '@mui/icons-material';
|
|
import useRequest from '@/hooks/useRequest';
|
|
import { useMyTranslation } from '@/hooks/useMyTranslation';
|
|
import { selectDefaultStyles } from '@/components/ui-kit/BaseReactSelect';
|
|
import { useAuthContext } from '@/context/auth-context';
|
|
import { useMyNavigation } from '@/hooks/useMyNavigation';
|
|
import AsyncSelect from 'react-select/async';
|
|
import { box_requests } from '@/data/box/box.requests';
|
|
import { real_box_requests } from '@/data/real-box/real-box.requests';
|
|
|
|
import get from 'lodash.get';
|
|
|
|
const StyledCreateBox = styled(Box)`
|
|
.item-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 16px;
|
|
}
|
|
|
|
& > * {
|
|
flex: 1 1 1;
|
|
}
|
|
`;
|
|
|
|
type Props = {
|
|
partiesData?: { value: number; label: string }[];
|
|
initialValues?: {
|
|
id: number;
|
|
boxId: string;
|
|
box_name: string;
|
|
partyId: number;
|
|
partyName: string;
|
|
packetId: string;
|
|
products_list: {
|
|
id: number;
|
|
price: number | string;
|
|
cargoId: string;
|
|
trekId: string;
|
|
name: string;
|
|
nameRu: string;
|
|
amount: number;
|
|
weight: number;
|
|
}[];
|
|
};
|
|
};
|
|
|
|
const DashboardCreateRealBoxPage = ({ initialValues, partiesData }: Props) => {
|
|
const { user, isAdmin: isAdminUser } = useAuthContext();
|
|
const editMode = !!initialValues && !!initialValues.id;
|
|
const isAdmin = isAdminUser && editMode;
|
|
const t = useMyTranslation();
|
|
const params = useSearchParams();
|
|
const navigation = useMyNavigation();
|
|
const [partyId, setPartyId] = useState<number | string>("");
|
|
const [loading, setLoading] = useState(false);
|
|
const helperRef = useRef<{
|
|
finished: boolean;
|
|
partyFinished: boolean;
|
|
clientFinished: boolean;
|
|
settedDefaultParty: any;
|
|
}>({
|
|
settedDefaultParty: null,
|
|
partyFinished: false,
|
|
clientFinished: false,
|
|
finished: false,
|
|
});
|
|
|
|
const {
|
|
register,
|
|
control,
|
|
handleSubmit,
|
|
setValue,
|
|
formState: { errors },
|
|
} = useForm<any>({
|
|
defaultValues: {
|
|
partyId: params.get('party_id') ? +params.get('party_id')! : '',
|
|
paketIds: editMode
|
|
? [{ id: initialValues?.partyId }]
|
|
: params.get('party_id')
|
|
? [{ id: +params.get('party_id')! }]
|
|
: [{ id: '' }],
|
|
...initialValues,
|
|
},
|
|
});
|
|
|
|
const pakets = useFieldArray({
|
|
control,
|
|
name: 'paketIds',
|
|
keyName: 'key',
|
|
});
|
|
|
|
const requiredText = t('required');
|
|
|
|
const getBoxesQuery = useRequest(
|
|
() =>
|
|
box_requests.getAll({
|
|
partyId: partyId,
|
|
}),
|
|
{
|
|
selectData(data) {
|
|
return data?.data?.data;
|
|
},
|
|
}
|
|
);
|
|
|
|
const { data: list } = useMemo(() => {
|
|
if (getBoxesQuery.data?.data) {
|
|
return {
|
|
data: getBoxesQuery.data.data.filter((box: any) => box.status === 'READY_TO_INVOICE'),
|
|
};
|
|
}
|
|
return { data: [] };
|
|
}, [getBoxesQuery, partyId]);
|
|
|
|
useEffect(() => {
|
|
if (partyId) {
|
|
getBoxesQuery.refetch();
|
|
}
|
|
}, [partyId]);
|
|
|
|
const { data: defaultParties } = useRequest(() => party_requests.getAll({ status: 'COLLECTING' }), {
|
|
enabled: true,
|
|
selectData(data) {
|
|
return data.data.data.data.map(p => ({ value: p.id, label: p.name }));
|
|
},
|
|
onSuccess(data) {
|
|
if (!editMode && data?.data?.data?.data?.[0]) {
|
|
helperRef.current.settedDefaultParty = data.data.data.data[0];
|
|
setValue('partyId', data.data.data.data[0].id);
|
|
setValue('paketIds[0].id', data.data.data.data[0].id);
|
|
}
|
|
helperRef.current.partyFinished = true;
|
|
if (helperRef.current.clientFinished) {
|
|
helperRef.current.finished = true;
|
|
}
|
|
},
|
|
placeholderData: [],
|
|
});
|
|
|
|
const onSubmit = handleSubmit(async values => {
|
|
try {
|
|
setLoading(true);
|
|
const packetDtos = values.paketIds.map((paket: any) => paket.id).filter((id: any) => id);
|
|
|
|
if (editMode) {
|
|
const updateBody: UpdateRealBoxBodyType = {
|
|
boxId: initialValues?.boxId,
|
|
partyName: values.partyName,
|
|
packetDtos,
|
|
};
|
|
await real_box_requests.update(updateBody);
|
|
} else {
|
|
const createBody: RealCreateBoxBodyType = {
|
|
partyName: values.partyName,
|
|
packetDtos,
|
|
};
|
|
await real_box_requests.create(createBody);
|
|
}
|
|
|
|
navigation.push(pageLinks.dashboard.boxes.index);
|
|
} catch (error) {
|
|
notifyUnknownError(error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
});
|
|
|
|
const partyOptions = (inputValue: string) => {
|
|
return party_requests.getAll({ status: 'COLLECTING', partyName: inputValue }).then(res => {
|
|
return res.data.data.data.map(p => ({ label: p.name, value: p.id }));
|
|
});
|
|
};
|
|
|
|
const appendPaket = () => {
|
|
pakets.append({ id: '' });
|
|
};
|
|
|
|
const removePaket = (index: number) => {
|
|
pakets.remove(index);
|
|
};
|
|
|
|
return (
|
|
<StyledCreateBox
|
|
width={1}
|
|
mb={3}
|
|
sx={{
|
|
padding: '28px',
|
|
borderRadius: '16px',
|
|
backgroundColor: '#fff',
|
|
}}
|
|
>
|
|
<Box component={'form'} onSubmit={onSubmit}>
|
|
<Typography variant='h5' mb={3.5}>
|
|
{editMode ? t('update_box') : t('create_box')}
|
|
</Typography>
|
|
|
|
<Grid container columnSpacing={2.5} rowSpacing={3} mb={3.5}>
|
|
<Grid item xs={12}>
|
|
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
|
{t('party_name')}
|
|
</Typography>
|
|
<Controller
|
|
name='partyId'
|
|
control={control}
|
|
rules={{ required: requiredText }}
|
|
render={({ field }) => (
|
|
<AsyncSelect
|
|
onChange={(newValue: any) => {
|
|
field.onChange(newValue.value);
|
|
setPartyId(newValue.value);
|
|
}}
|
|
defaultValue={
|
|
editMode
|
|
? {
|
|
value: initialValues.partyId,
|
|
label: initialValues.partyName,
|
|
}
|
|
: partiesData?.length
|
|
? {
|
|
value: partiesData[0].value,
|
|
label: partiesData[0].label,
|
|
}
|
|
: null
|
|
}
|
|
styles={selectDefaultStyles}
|
|
noOptionsMessage={() => t('not_found')}
|
|
loadingMessage={() => t('loading')}
|
|
onBlur={field.onBlur}
|
|
name={field.name}
|
|
defaultOptions={defaultParties!}
|
|
loadOptions={partyOptions}
|
|
placeholder={t('enter_party_name_to_find')}
|
|
/>
|
|
)}
|
|
/>
|
|
</Grid>
|
|
|
|
<Grid item xs={12}>
|
|
<Stack
|
|
sx={{
|
|
borderRadius: '8px',
|
|
border: '2px solid #3489E4',
|
|
background: '#FFF',
|
|
padding: '24px',
|
|
}}
|
|
>
|
|
<Typography fontSize={'18px'} fontWeight={500} color='#5D5850' mb={2}>
|
|
{t('packet')}
|
|
</Typography>
|
|
{pakets.fields.map((field, index) => (
|
|
<Box key={field.key} className="item-row" mb={2}>
|
|
<Box className="item-row-field" flex={1}>
|
|
<Controller
|
|
name={`paketIds.${index}.id`}
|
|
control={control}
|
|
rules={{ required: requiredText }}
|
|
render={({ field: paketField }) => (
|
|
<AsyncSelect
|
|
onChange={(newValue: any) => {
|
|
paketField.onChange(newValue?.value);
|
|
}}
|
|
defaultValue={
|
|
editMode && index === 0 && initialValues.packetId
|
|
? {
|
|
value: initialValues.packetId,
|
|
label: initialValues.box_name || `Box ${initialValues.packetId}`,
|
|
}
|
|
: null
|
|
}
|
|
styles={selectDefaultStyles}
|
|
noOptionsMessage={() => t('not_found')}
|
|
loadingMessage={() => t('loading')}
|
|
onBlur={paketField.onBlur}
|
|
name={paketField.name}
|
|
defaultOptions={
|
|
list.length > 0
|
|
? list.map((box: any) => ({
|
|
value: box.id,
|
|
label: box.box_name || box.name || `Box ${box.id}`,
|
|
}))
|
|
: []
|
|
}
|
|
loadOptions={async (inputValue: string) => {
|
|
if (!partyId || partyId === '') return [];
|
|
try {
|
|
const res = await box_requests.getAll({
|
|
partyId: partyId,
|
|
});
|
|
return res.data.data.data.map((box: any) => ({
|
|
label: box.box_name || box.name || `Box ${box.id}`,
|
|
value: box.id,
|
|
}));
|
|
} catch (error) {
|
|
notifyUnknownError(error);
|
|
return [];
|
|
}
|
|
}}
|
|
placeholder={t('enter_box_name_to_find')}
|
|
/>
|
|
)}
|
|
/>
|
|
{!!get(errors, `paketIds.${index}.id`) && (
|
|
<FormHelperText sx={{ color: 'red' }}>{requiredText}</FormHelperText>
|
|
)}
|
|
</Box>
|
|
{pakets.fields.length > 1 && (
|
|
<Box className="item-row-field">
|
|
<BaseIconButton
|
|
size="small"
|
|
colorVariant="icon-error"
|
|
sx={{ flexShrink: 0, height: 'auto', marginTop: 1 }}
|
|
onClick={() => removePaket(index)}
|
|
>
|
|
<Close />
|
|
</BaseIconButton>
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
))}
|
|
<Stack alignItems={'center'} mt={2}>
|
|
<BaseButton
|
|
sx={{ backgroundColor: '#239D5F' }}
|
|
startIcon={<AddCircleRounded />}
|
|
onClick={appendPaket}
|
|
>
|
|
{t('add_more')}
|
|
</BaseButton>
|
|
</Stack>
|
|
</Stack>
|
|
</Grid>
|
|
</Grid>
|
|
|
|
<BaseButton type='submit' colorVariant='blue' loading={loading}>
|
|
{editMode ? t('update') : t('create')}
|
|
</BaseButton>
|
|
</Box>
|
|
</StyledCreateBox>
|
|
);
|
|
};
|
|
|
|
export default DashboardCreateRealBoxPage;
|
|
|
|
export type RealCreateBoxBodyType = {
|
|
partyName: string;
|
|
packetDtos: number[];
|
|
};
|
|
|
|
export type UpdateRealBoxBodyType = {
|
|
boxId: string;
|
|
partyName: string;
|
|
packetDtos: number[];
|
|
}; |