mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-22 14:25:47 +00:00
Merge branch 'development' of gitlab.com:mbugroup/lti-web-client into dev/hotfix/restu
This commit is contained in:
@@ -405,7 +405,9 @@ const FinanceTable = () => {
|
||||
{
|
||||
header: 'Bank',
|
||||
accessorFn: (finance: Finance) =>
|
||||
`${finance.bank?.alias} - ${finance.bank?.account_number} - ${finance.bank?.owner}`,
|
||||
finance.bank
|
||||
? `${finance.bank?.alias} - ${finance.bank?.account_number} - ${finance.bank?.owner}`
|
||||
: '-',
|
||||
},
|
||||
{
|
||||
header: 'Pengeluaran (Rp)',
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
'use client';
|
||||
|
||||
import { RefObject } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
|
||||
import { Icon } from '@iconify/react';
|
||||
import Modal from '@/components/Modal';
|
||||
import Button from '@/components/Button';
|
||||
import DateInput from '@/components/input/DateInput';
|
||||
import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
|
||||
|
||||
import { OptionType, useSelect } from '@/components/input/SelectInput';
|
||||
import { ProjectFlockApi } from '@/services/api/production';
|
||||
import { Flock } from '@/types/api/master-data/flock';
|
||||
import { TransferToLayingFilter } from '@/types/api/production/transfer-to-laying';
|
||||
|
||||
interface TransferToLayingFilterModal {
|
||||
ref: RefObject<HTMLDialogElement | null>;
|
||||
onSubmit?: (values: TransferToLayingFilter) => void;
|
||||
onReset?: () => void;
|
||||
}
|
||||
|
||||
const TransferToLayingFilterModal = ({
|
||||
ref,
|
||||
onSubmit,
|
||||
onReset,
|
||||
}: TransferToLayingFilterModal) => {
|
||||
const closeModalHandler = () => {
|
||||
ref.current?.close();
|
||||
};
|
||||
|
||||
// Flock Source
|
||||
const {
|
||||
setInputValue: setFlockSourceInputValue,
|
||||
options: flockSourceOptions,
|
||||
isLoadingOptions: isLoadingFlockSourceOptions,
|
||||
loadMore: loadMoreFlockSource,
|
||||
} = useSelect<Flock>(ProjectFlockApi.basePath, 'id', 'flock_name', 'search', {
|
||||
category: 'GROWING',
|
||||
});
|
||||
|
||||
// Flock Destination
|
||||
const {
|
||||
setInputValue: setFlockDestinationInputValue,
|
||||
options: flockDestinationOptions,
|
||||
isLoadingOptions: isLoadingFlockDestinationOptions,
|
||||
loadMore: loadMoreFlockDestination,
|
||||
} = useSelect<Flock>(ProjectFlockApi.basePath, 'id', 'flock_name', 'search', {
|
||||
category: 'LAYING',
|
||||
});
|
||||
|
||||
const formik = useFormik<{
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
flockSource: { value: number; label: string }[];
|
||||
flockDestination: { value: number; label: string }[];
|
||||
status: { value: number; label: string }[];
|
||||
}>({
|
||||
initialValues: {
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
flockSource: [],
|
||||
flockDestination: [],
|
||||
status: [],
|
||||
},
|
||||
onSubmit: async (values) => {
|
||||
const formattedValues = {
|
||||
...values,
|
||||
flockSource: values.flockSource.map((item) => item.value),
|
||||
flockDestination: values.flockDestination.map((item) => item.value),
|
||||
status: values.status.map((item) => item.value),
|
||||
};
|
||||
|
||||
onSubmit?.(formattedValues);
|
||||
closeModalHandler();
|
||||
},
|
||||
onReset: () => {
|
||||
onReset?.();
|
||||
closeModalHandler();
|
||||
},
|
||||
});
|
||||
|
||||
const flockSourceChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
formik.setFieldValue('flockSource', val as OptionType[]);
|
||||
};
|
||||
|
||||
const flockDestinationChangeHandler = (
|
||||
val: OptionType | OptionType[] | null
|
||||
) => {
|
||||
formik.setFieldValue('flockDestination', val as OptionType[]);
|
||||
};
|
||||
|
||||
const statusChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
formik.setFieldValue('status', val as OptionType[]);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
ref={ref}
|
||||
className={{
|
||||
modalBox: 'p-0 rounded-xl',
|
||||
}}
|
||||
>
|
||||
<form
|
||||
onSubmit={formik.handleSubmit}
|
||||
onReset={formik.handleReset}
|
||||
className='w-full flex flex-col'
|
||||
>
|
||||
{/* Modal Header */}
|
||||
<div className='p-4 flex items-center justify-between gap-2 border-b border-gray-300'>
|
||||
<div className='flex items-center gap-2 text-primary'>
|
||||
<Icon icon='heroicons:funnel' width={20} height={20} />
|
||||
<h3 className='text-sm font-medium'>Filter Data</h3>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type='button'
|
||||
variant='ghost'
|
||||
color='none'
|
||||
onClick={closeModalHandler}
|
||||
className='p-0 text-base-content/50 hover:text-base-content'
|
||||
>
|
||||
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Modal Body */}
|
||||
<div className='p-4 flex flex-col gap-1.5'>
|
||||
<div className='flex flex-col'>
|
||||
<span className='py-2 text-xs font-semibold'>Tanggal</span>
|
||||
<div className='flex flex-row items-center gap-1.5'>
|
||||
<DateInput
|
||||
name='startDate'
|
||||
placeholder='Tanggal Awal'
|
||||
value={formik.values.startDate}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
/>
|
||||
<hr className='w-full max-w-3 h-px border-base-content/10' />
|
||||
<DateInput
|
||||
name='endDate'
|
||||
placeholder='Tanggal Akhir'
|
||||
value={formik.values.endDate}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<SelectInputCheckbox
|
||||
label='Flock Asal'
|
||||
placeholder='Flock Asal'
|
||||
value={formik.values.flockSource}
|
||||
onChange={flockSourceChangeHandler}
|
||||
options={flockSourceOptions}
|
||||
isLoading={isLoadingFlockSourceOptions}
|
||||
onInputChange={setFlockSourceInputValue}
|
||||
onMenuScrollToBottom={loadMoreFlockSource}
|
||||
/>
|
||||
|
||||
<SelectInputCheckbox
|
||||
label='Flock Tujuan'
|
||||
placeholder='Flock Tujuan'
|
||||
value={formik.values.flockDestination}
|
||||
onChange={flockDestinationChangeHandler}
|
||||
options={flockDestinationOptions}
|
||||
isLoading={isLoadingFlockDestinationOptions}
|
||||
onInputChange={setFlockDestinationInputValue}
|
||||
onMenuScrollToBottom={loadMoreFlockDestination}
|
||||
/>
|
||||
|
||||
<SelectInputCheckbox
|
||||
label='Status'
|
||||
placeholder='Pilih Status'
|
||||
options={[
|
||||
{ value: 'PENDING', label: 'Pengajuan' },
|
||||
{ value: 'APPROVED', label: 'Disetujui' },
|
||||
{ value: 'REJECTED', label: 'Ditolak' },
|
||||
]}
|
||||
value={formik.values.status}
|
||||
onChange={statusChangeHandler}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Modal Footer */}
|
||||
<div className='p-4 flex justify-between gap-4 border-t border-gray-300 bg-gray-100'>
|
||||
<Button
|
||||
type='reset'
|
||||
variant='ghost'
|
||||
color='none'
|
||||
className='p-3 rounded-lg text-base-content/65'
|
||||
>
|
||||
Reset Filter
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type='submit'
|
||||
className='p-3 rounded-lg w-fit sm:w-full max-w-40 text-base-100 text-sm'
|
||||
>
|
||||
Apply Filter
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default TransferToLayingFilterModal;
|
||||
@@ -0,0 +1,975 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
FormEventHandler,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import useSWR, { useSWRConfig } from 'swr';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
import { Icon } from '@iconify/react';
|
||||
import Modal, { useModal } from '@/components/Modal';
|
||||
import Button from '@/components/Button';
|
||||
import DateInput from '@/components/input/DateInput';
|
||||
import SelectInputRadio from '@/components/input/SelectInputRadio';
|
||||
import { OptionType, useSelect } from '@/components/input/SelectInput';
|
||||
import NumberInput from '@/components/input/NumberInput';
|
||||
import TextArea from '@/components/input/TextArea';
|
||||
import AlertErrorList from '@/components/helper/form/FormErrors';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { ProjectFlockApi } from '@/services/api/production';
|
||||
import { getIn, useFormik } from 'formik';
|
||||
import {
|
||||
getFilledTransferToLayingFormInitialValues,
|
||||
getTransferToLayingFormInitialValues,
|
||||
TransferToLayingFormSchema,
|
||||
TransferToLayingFormValues,
|
||||
} from '@/components/pages/production/transfer-to-laying/form/TransferToLayingForm.schema';
|
||||
import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import StatusBadge from '@/components/helper/StatusBadge';
|
||||
import CheckboxInput from '@/components/input/CheckboxInput';
|
||||
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||
import { cn, formatNumber } from '@/lib/helper';
|
||||
import {
|
||||
CreateTransferToLayingPayload,
|
||||
UpdateTransferToLayingPayload,
|
||||
} from '@/types/api/production/transfer-to-laying';
|
||||
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
|
||||
|
||||
const TransferToLayingFormModal = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const modalAction = searchParams.get('action');
|
||||
const transferToLayingId = searchParams.get('id');
|
||||
|
||||
const { mutate } = useSWRConfig();
|
||||
|
||||
const refreshTransferToLayings = () => {
|
||||
mutate(
|
||||
(key) =>
|
||||
typeof key === 'string' && key.includes(TransferToLayingApi.basePath)
|
||||
);
|
||||
};
|
||||
|
||||
const { data: transferToLaying, isLoading: isLoadingTransferToLaying } =
|
||||
useSWR(transferToLayingId ? transferToLayingId : undefined, (id: number) =>
|
||||
TransferToLayingApi.getSingle(id)
|
||||
);
|
||||
|
||||
/**
|
||||
* Step 1: General Information
|
||||
* Step 2: Select source and destination kandang
|
||||
* Step 3: Enter transfered quantity
|
||||
* Step 4: Submit
|
||||
*/
|
||||
const [step, setStep] = useState(1);
|
||||
|
||||
const formModal = useModal();
|
||||
|
||||
const [formErrorMessage, setFormErrorMessage] = useState<string | null>(null);
|
||||
|
||||
// Flock Source
|
||||
const {
|
||||
setInputValue: setFlockSourceInputValue,
|
||||
options: flockSourceOptions,
|
||||
isLoadingOptions: isLoadingFlockSourceOptions,
|
||||
loadMore: loadMoreFlockSource,
|
||||
rawData: flockSourceRawData,
|
||||
} = useSelect<ProjectFlock>(
|
||||
ProjectFlockApi.basePath,
|
||||
'id',
|
||||
'flock_name',
|
||||
'search',
|
||||
{
|
||||
category: 'GROWING',
|
||||
}
|
||||
);
|
||||
|
||||
// Flock Destination
|
||||
const {
|
||||
setInputValue: setFlockDestinationInputValue,
|
||||
options: flockDestinationOptions,
|
||||
isLoadingOptions: isLoadingFlockDestinationOptions,
|
||||
loadMore: loadMoreFlockDestination,
|
||||
rawData: flockDestinationRawData,
|
||||
} = useSelect<ProjectFlock>(
|
||||
ProjectFlockApi.basePath,
|
||||
'id',
|
||||
'flock_name',
|
||||
'search',
|
||||
{
|
||||
category: 'LAYING',
|
||||
}
|
||||
);
|
||||
|
||||
const closeModalHandler = (shouldPushToRoute: boolean = true) => {
|
||||
if (shouldPushToRoute) {
|
||||
router.push('/production/transfer-to-laying');
|
||||
}
|
||||
|
||||
formik.resetForm();
|
||||
setStep(1);
|
||||
setFormErrorMessage('');
|
||||
formModal.closeModal();
|
||||
};
|
||||
|
||||
const createTransferToLayingHandler = useCallback(
|
||||
async (payload: CreateTransferToLayingPayload) => {
|
||||
const createTransferToLayingRes =
|
||||
await TransferToLayingApi.create(payload);
|
||||
|
||||
if (isResponseError(createTransferToLayingRes)) {
|
||||
setFormErrorMessage(createTransferToLayingRes.message);
|
||||
return;
|
||||
}
|
||||
|
||||
refreshTransferToLayings();
|
||||
toast.success(createTransferToLayingRes?.message as string);
|
||||
router.push('/production/transfer-to-laying');
|
||||
closeModalHandler(false);
|
||||
},
|
||||
[router]
|
||||
);
|
||||
|
||||
const updateTransferToLayingHandler = useCallback(
|
||||
async (
|
||||
transferToLayingId: number,
|
||||
payload: UpdateTransferToLayingPayload
|
||||
) => {
|
||||
const updateKandangRes = await TransferToLayingApi.update(
|
||||
transferToLayingId,
|
||||
payload
|
||||
);
|
||||
|
||||
if (updateKandangRes?.status === 'error') {
|
||||
setFormErrorMessage(updateKandangRes.message);
|
||||
return;
|
||||
}
|
||||
|
||||
refreshTransferToLayings();
|
||||
toast.success(updateKandangRes?.message as string);
|
||||
router.push('/production/transfer-to-laying');
|
||||
closeModalHandler(false);
|
||||
},
|
||||
[router]
|
||||
);
|
||||
|
||||
const [formikInitialValues, setFormikInitialValues] = useState(
|
||||
getTransferToLayingFormInitialValues()
|
||||
);
|
||||
|
||||
const formik = useFormik<TransferToLayingFormValues>({
|
||||
initialValues: formikInitialValues,
|
||||
validationSchema: TransferToLayingFormSchema,
|
||||
onSubmit: async (values) => {
|
||||
const transferToLayingPayload: CreateTransferToLayingPayload = {
|
||||
transfer_date: values.transfer_date as string,
|
||||
source_project_flock_id: values.flockSource?.value as number,
|
||||
target_project_flock_id: values.flockDestination?.value as number,
|
||||
totalQuantity: values.totalQuantity as number,
|
||||
|
||||
source_kandangs: values.flockSourceKandangs?.map((kandang) => ({
|
||||
project_flock_kandang_id: kandang.kandang.value,
|
||||
quantity: parseFloat(kandang.quantity as string),
|
||||
})) as CreateTransferToLayingPayload['source_kandangs'],
|
||||
|
||||
target_kandangs: values.flockDestinationKandangs?.map((kandang) => ({
|
||||
project_flock_kandang_id: kandang.kandang.value,
|
||||
quantity: parseFloat(kandang.quantity as string),
|
||||
})) as CreateTransferToLayingPayload['target_kandangs'],
|
||||
|
||||
reason: values.reason as string,
|
||||
};
|
||||
|
||||
switch (modalAction) {
|
||||
case 'add':
|
||||
await createTransferToLayingHandler(transferToLayingPayload);
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
await updateTransferToLayingHandler(
|
||||
Number(transferToLayingId),
|
||||
transferToLayingPayload
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
|
||||
|
||||
const [selectedFlockSourceRawData, setSelectedFlockSourceRawData] = useState<
|
||||
ProjectFlock | undefined
|
||||
>(undefined);
|
||||
|
||||
const selectedFlockDestinationRawData = isResponseSuccess(
|
||||
flockDestinationRawData
|
||||
)
|
||||
? flockDestinationRawData.data.find(
|
||||
(item) => item.id === formik.values.flockDestination?.value
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const {
|
||||
data: flockSourceKandangsAvailability,
|
||||
isLoading: isLoadingFlockSourceKandangsAvailability,
|
||||
} = useSWR(
|
||||
formik.values.flockSource
|
||||
? [
|
||||
'transfer-to-laying',
|
||||
'available-qty',
|
||||
String(formik.values.flockSource.value),
|
||||
]
|
||||
: undefined,
|
||||
([, , id]: string[]) =>
|
||||
TransferToLayingApi.getMappedFlockKandangsAvailability(Number(id))
|
||||
);
|
||||
|
||||
const mappedFlockSourceKandangsAvailability: {
|
||||
kandang_name: string;
|
||||
available_qty: number;
|
||||
project_flock_kandang_id: number;
|
||||
}[] = useMemo(() => {
|
||||
if (!flockSourceKandangsAvailability || !selectedFlockSourceRawData)
|
||||
return [];
|
||||
|
||||
return selectedFlockSourceRawData
|
||||
? selectedFlockSourceRawData.kandangs.map((kandang) => {
|
||||
const availability =
|
||||
flockSourceKandangsAvailability[kandang.project_flock_kandang_id]
|
||||
.available_qty;
|
||||
|
||||
return {
|
||||
kandang_name: kandang.name,
|
||||
available_qty: availability,
|
||||
project_flock_kandang_id: kandang.project_flock_kandang_id,
|
||||
};
|
||||
})
|
||||
: [];
|
||||
}, [flockSourceKandangsAvailability, selectedFlockSourceRawData]);
|
||||
|
||||
const mappedFlockSourceKandangsAvailabilityInfo: {
|
||||
available: number;
|
||||
unavailable: number;
|
||||
} = useMemo(() => {
|
||||
if (!mappedFlockSourceKandangsAvailability)
|
||||
return { available: 0, unavailable: 0 };
|
||||
|
||||
let countAvailable = 0;
|
||||
let countUnavailable = 0;
|
||||
|
||||
mappedFlockSourceKandangsAvailability.forEach((item) => {
|
||||
if (item.available_qty > 0) {
|
||||
countAvailable += 1;
|
||||
} else {
|
||||
countUnavailable += 1;
|
||||
}
|
||||
});
|
||||
|
||||
return { available: countAvailable, unavailable: countUnavailable };
|
||||
}, [mappedFlockSourceKandangsAvailability]);
|
||||
|
||||
const mappedFlockDestinationKandangsAvailabilityInfo: {
|
||||
available: number;
|
||||
unavailable: number;
|
||||
} = useMemo(() => {
|
||||
if (!selectedFlockDestinationRawData)
|
||||
return { available: 0, unavailable: 0 };
|
||||
|
||||
let countAvailable = 0;
|
||||
let countUnavailable = 0;
|
||||
|
||||
selectedFlockDestinationRawData?.kandangs.forEach((item) => {
|
||||
// TODO: change this to real available quota later
|
||||
if (item.capacity > 0) {
|
||||
countAvailable += 1;
|
||||
} else {
|
||||
countUnavailable += 1;
|
||||
}
|
||||
});
|
||||
|
||||
return { available: countAvailable, unavailable: countUnavailable };
|
||||
}, [selectedFlockDestinationRawData]);
|
||||
|
||||
const totalEnteredChickenForTransfer =
|
||||
formik.values.flockSourceKandangs.reduce(
|
||||
(acc, item) => acc + Number(item.quantity),
|
||||
0
|
||||
);
|
||||
|
||||
const totalTransferedChicken = formik.values.flockDestinationKandangs.reduce(
|
||||
(acc, item) => acc + Number(item.quantity),
|
||||
0
|
||||
);
|
||||
|
||||
const totalAvailableChickenForTransfer =
|
||||
totalEnteredChickenForTransfer - totalTransferedChicken;
|
||||
|
||||
const isNextButtonDisabled = useMemo(() => {
|
||||
if (step === 1) {
|
||||
return Boolean(
|
||||
!formik.values.transfer_date ||
|
||||
!formik.values.flockSource ||
|
||||
!formik.values.flockDestination
|
||||
);
|
||||
}
|
||||
|
||||
if (step === 2) {
|
||||
return Boolean(
|
||||
!formik.values.flockSourceKandangs.length ||
|
||||
!formik.values.flockDestinationKandangs.length
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}, [step, formik.values]);
|
||||
|
||||
const nextButtonHandler = () => {
|
||||
setStep(step + 1);
|
||||
};
|
||||
|
||||
const deleteEnteredKandangHandler = () => {
|
||||
formik.setFieldValue('flockSourceKandangs', []);
|
||||
formik.setFieldValue('flockDestinationKandangs', []);
|
||||
formik.setFieldValue('totalQuantity', '');
|
||||
formik.setFieldValue('maxTotalQuantity', '');
|
||||
formik.setFieldValue('reason', '');
|
||||
formik.setFieldTouched('reason', false);
|
||||
|
||||
setStep(2);
|
||||
};
|
||||
|
||||
const flockSourceChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
formik.setFieldValue('flockSource', val);
|
||||
formik.setFieldValue('flockSourceKandangs', []);
|
||||
};
|
||||
|
||||
const flockDestinationChangeHandler = (
|
||||
val: OptionType | OptionType[] | null
|
||||
) => {
|
||||
formik.setFieldValue('flockDestination', val);
|
||||
formik.setFieldValue('flockDestinationKandangs', []);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (modalAction === 'add' || modalAction === 'edit') {
|
||||
formModal.openModal();
|
||||
}
|
||||
}, [modalAction]);
|
||||
|
||||
useEffect(() => {
|
||||
const getFilledInitialValues = async () => {
|
||||
if (transferToLayingId && isResponseSuccess(transferToLaying)) {
|
||||
const filledInitialValues =
|
||||
await getFilledTransferToLayingFormInitialValues(
|
||||
transferToLaying.data
|
||||
);
|
||||
|
||||
formik.setValues(filledInitialValues);
|
||||
setStep(3);
|
||||
}
|
||||
};
|
||||
|
||||
const getFlockSourceData = async () => {
|
||||
if (transferToLayingId && isResponseSuccess(transferToLaying)) {
|
||||
const singleFlockSourceRawData = await ProjectFlockApi.getSingle(
|
||||
transferToLaying.data.from_project_flock.id
|
||||
);
|
||||
|
||||
if (isResponseSuccess(singleFlockSourceRawData)) {
|
||||
setSelectedFlockSourceRawData(singleFlockSourceRawData.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getFlockSourceData();
|
||||
getFilledInitialValues();
|
||||
}, [transferToLayingId, transferToLaying]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isResponseSuccess(flockSourceRawData)) {
|
||||
const selectedFlockSourceRawData = flockSourceRawData.data.find(
|
||||
(item) => item.id === formik.values.flockSource?.value
|
||||
);
|
||||
|
||||
setSelectedFlockSourceRawData(selectedFlockSourceRawData);
|
||||
}
|
||||
}, [flockSourceRawData]);
|
||||
|
||||
useEffect(() => {
|
||||
formik.setFieldValue('totalQuantity', totalTransferedChicken);
|
||||
formik.setFieldValue('maxTotalQuantity', totalTransferedChicken);
|
||||
}, [totalTransferedChicken]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
ref={formModal.ref}
|
||||
position='end'
|
||||
className={{
|
||||
modalBox: 'w-full sm:w-fit p-3 rounded-xl bg-transparent shadow-none',
|
||||
}}
|
||||
>
|
||||
<form
|
||||
onSubmit={handleFormSubmit}
|
||||
className='w-full min-h-full flex flex-col sm:flex-row items-stretch bg-base-100 rounded-xl overflow-y-auto'
|
||||
>
|
||||
{/* 1st Section */}
|
||||
<div className='w-full sm:w-[446px]'>
|
||||
<div className='w-full p-4 flex flex-row items-stretch gap-3 border-b border-base-content/10'>
|
||||
<Button
|
||||
type='button'
|
||||
variant='ghost'
|
||||
color='none'
|
||||
onClick={() => closeModalHandler()}
|
||||
className='p-0 text-black hover:text-base-content'
|
||||
>
|
||||
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
||||
</Button>
|
||||
|
||||
<div className='w-px border-none bg-base-content/10' />
|
||||
|
||||
<h4 className='text-sm font-medium text-base-content/50'>
|
||||
Add Transfer to Laying
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div className='w-full p-4 flex flex-col'>
|
||||
<h4 className='text-base font-medium text-base-content/50 font-roboto'>
|
||||
Informasi Umum
|
||||
</h4>
|
||||
|
||||
<DateInput
|
||||
name='transfer_date'
|
||||
label='Tanggal'
|
||||
placeholder='Tanggal'
|
||||
value={formik.values.transfer_date ?? ''}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
isError={
|
||||
formik.touched.transfer_date &&
|
||||
Boolean(formik.errors.transfer_date)
|
||||
}
|
||||
errorMessage={formik.errors.transfer_date}
|
||||
disabled={step > 2}
|
||||
/>
|
||||
|
||||
<SelectInputRadio
|
||||
label='Flock Asal'
|
||||
placeholder='Pilih Flock Asal'
|
||||
value={formik.values.flockSource}
|
||||
onChange={flockSourceChangeHandler}
|
||||
options={flockSourceOptions}
|
||||
isLoading={isLoadingFlockSourceOptions}
|
||||
onInputChange={setFlockSourceInputValue}
|
||||
onMenuScrollToBottom={loadMoreFlockSource}
|
||||
isDisabled={step > 2}
|
||||
/>
|
||||
|
||||
<SelectInputRadio
|
||||
label='Flock Tujuan'
|
||||
placeholder='Pilih Flock Tujuan'
|
||||
value={formik.values.flockDestination}
|
||||
onChange={flockDestinationChangeHandler}
|
||||
options={flockDestinationOptions}
|
||||
isLoading={isLoadingFlockDestinationOptions}
|
||||
onInputChange={setFlockDestinationInputValue}
|
||||
onMenuScrollToBottom={loadMoreFlockDestination}
|
||||
isDisabled={step > 2}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{step >= 2 && (
|
||||
<>
|
||||
<div className='w-full p-4 flex flex-col gap-3 border-y border-base-content/10'>
|
||||
<h4 className='text-base font-medium text-base-content/50 font-roboto'>
|
||||
Pilih Kandang Asal
|
||||
</h4>
|
||||
|
||||
<div className='w-fit flex flex-row items-stretch gap-3'>
|
||||
<StatusBadge
|
||||
color='info'
|
||||
text={`Tersedia (${mappedFlockSourceKandangsAvailabilityInfo.available})`}
|
||||
className={{ badge: 'text-nowrap' }}
|
||||
/>
|
||||
|
||||
<div className='w-px border-none bg-base-content/10' />
|
||||
|
||||
<StatusBadge
|
||||
color='neutral'
|
||||
text={`Tidak Tersedia (${mappedFlockSourceKandangsAvailabilityInfo.unavailable})`}
|
||||
className={{ badge: 'text-nowrap' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='w-full rounded-xl border border-base-content/10'>
|
||||
{mappedFlockSourceKandangsAvailability.map(
|
||||
(item, itemIdx) => {
|
||||
const isAvailable = item.available_qty > 0;
|
||||
const isChecked =
|
||||
formik.values.flockSourceKandangs.some(
|
||||
(k) =>
|
||||
k.kandang.value === item.project_flock_kandang_id
|
||||
);
|
||||
|
||||
const flockSourceKandangCheckboxChangeHandler: FormEventHandler<
|
||||
HTMLInputElement
|
||||
> = (e) => {
|
||||
const checked = (e.target as HTMLInputElement)
|
||||
.checked;
|
||||
if (checked) {
|
||||
formik.setFieldValue('flockSourceKandangs', [
|
||||
...formik.values.flockSourceKandangs,
|
||||
{
|
||||
kandang: {
|
||||
value: item.project_flock_kandang_id,
|
||||
label: item.kandang_name,
|
||||
},
|
||||
quantity: '',
|
||||
maxQuantity: item.available_qty,
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
formik.setFieldValue(
|
||||
'flockSourceKandangs',
|
||||
formik.values.flockSourceKandangs.filter(
|
||||
(k) =>
|
||||
k.kandang.value !==
|
||||
item.project_flock_kandang_id
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
key={itemIdx}
|
||||
className='w-full p-3 flex flex-row items-center justify-between'
|
||||
>
|
||||
<div className='flex flex-row items-center gap-3'>
|
||||
<CheckboxInput
|
||||
name={`flockSourceKandang.${itemIdx}.value`}
|
||||
value={item.project_flock_kandang_id}
|
||||
checked={isChecked}
|
||||
onChange={
|
||||
flockSourceKandangCheckboxChangeHandler
|
||||
}
|
||||
size='md'
|
||||
disabled={!isAvailable}
|
||||
classNames={{
|
||||
checkbox: cn({
|
||||
'bg-base-200 border border-base-content/10 opacity-100':
|
||||
!isAvailable,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
|
||||
<label
|
||||
htmlFor={`flockSourceKandang.${itemIdx}.value`}
|
||||
className={cn('text-sm text-base-content/50', {
|
||||
'cursor-pointer': isAvailable,
|
||||
'cursor-not-allowed': !isAvailable,
|
||||
})}
|
||||
>
|
||||
{item.kandang_name}{' '}
|
||||
<span className='text-base-content/20'>{`(Max: ${item.available_qty})`}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<StatusBadge
|
||||
color={isAvailable ? 'info' : 'neutral'}
|
||||
text={isAvailable ? 'Tersedia' : 'Tidak Tersedia'}
|
||||
className={{ badge: 'w-fit' }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full p-4 flex flex-col gap-3'>
|
||||
<div className='flex flex-row items-center justify-between'>
|
||||
<h4 className='text-base font-medium text-base-content/50 font-roboto'>
|
||||
Pilih Kandang Tujuan
|
||||
</h4>
|
||||
{formik.touched.flockDestinationKandangs &&
|
||||
formik.errors.flockDestinationKandangs &&
|
||||
typeof formik.errors.flockDestinationKandangs ===
|
||||
'string' && (
|
||||
<span className='text-xs text-error'>
|
||||
{formik.errors.flockDestinationKandangs}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='w-fit flex flex-row items-stretch gap-3'>
|
||||
<StatusBadge
|
||||
color='info'
|
||||
text={`Tersedia (${mappedFlockDestinationKandangsAvailabilityInfo.available})`}
|
||||
className={{ badge: 'text-nowrap' }}
|
||||
/>
|
||||
|
||||
<div className='w-px border-none bg-base-content/10' />
|
||||
|
||||
<StatusBadge
|
||||
color='neutral'
|
||||
text={`Tidak Tersedia (${mappedFlockDestinationKandangsAvailabilityInfo.unavailable})`}
|
||||
className={{ badge: 'text-nowrap' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='w-full rounded-xl border border-base-content/10'>
|
||||
{selectedFlockDestinationRawData?.kandangs.map(
|
||||
(item, itemIdx) => {
|
||||
// TODO: change this to real available quota later
|
||||
const isAvailable = item.capacity > 0;
|
||||
const isChecked =
|
||||
formik.values.flockDestinationKandangs.some(
|
||||
(k) =>
|
||||
k.kandang.value === item.project_flock_kandang_id
|
||||
);
|
||||
|
||||
const flockDestinationKandangCheckboxChangeHandler: FormEventHandler<
|
||||
HTMLInputElement
|
||||
> = (e) => {
|
||||
const checked = (e.target as HTMLInputElement)
|
||||
.checked;
|
||||
if (checked) {
|
||||
formik.setFieldValue('flockDestinationKandangs', [
|
||||
...formik.values.flockDestinationKandangs,
|
||||
{
|
||||
kandang: {
|
||||
value: item.project_flock_kandang_id,
|
||||
label: item.name,
|
||||
},
|
||||
quantity: '',
|
||||
// TODO: change this to real available quota later
|
||||
maxQuantity: item.capacity,
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
formik.setFieldValue(
|
||||
'flockDestinationKandangs',
|
||||
formik.values.flockDestinationKandangs.filter(
|
||||
(k) =>
|
||||
k.kandang.value !==
|
||||
item.project_flock_kandang_id
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
key={itemIdx}
|
||||
className='w-full p-3 flex flex-row items-center justify-between'
|
||||
>
|
||||
<div className='flex flex-row items-center gap-3'>
|
||||
<CheckboxInput
|
||||
name={`flockDestinationKandang.${itemIdx}.value`}
|
||||
value={item.project_flock_kandang_id}
|
||||
checked={isChecked}
|
||||
onChange={
|
||||
flockDestinationKandangCheckboxChangeHandler
|
||||
}
|
||||
size='md'
|
||||
disabled={!isAvailable}
|
||||
classNames={{
|
||||
checkbox: cn({
|
||||
'bg-base-200 border border-base-content/10 opacity-100':
|
||||
!isAvailable,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
|
||||
<label
|
||||
htmlFor={`flockDestinationKandang.${itemIdx}.value`}
|
||||
className={cn('text-sm text-base-content/50', {
|
||||
'cursor-pointer': isAvailable,
|
||||
'cursor-not-allowed': !isAvailable,
|
||||
})}
|
||||
>
|
||||
{item.name}{' '}
|
||||
{/* TODO: change this to real available quota later */}
|
||||
<span className='text-base-content/20'>{`(Max: ${item.capacity})`}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<StatusBadge
|
||||
color={isAvailable ? 'info' : 'neutral'}
|
||||
text={isAvailable ? 'Tersedia' : 'Tidak Tersedia'}
|
||||
className={{ badge: 'w-fit' }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className='w-full p-4 border-t border-base-content/10'>
|
||||
{step < 3 && (
|
||||
<Button
|
||||
type='button'
|
||||
onClick={nextButtonHandler}
|
||||
disabled={isNextButtonDisabled}
|
||||
className='w-full p-3 rounded-lg text-sm text-base-100'
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 2nd Section */}
|
||||
{step === 3 && (
|
||||
<div className='w-full sm:w-[446px] border-l border-base-content/10'>
|
||||
<div className='w-full p-4 flex flex-row items-center justify-between gap-3 border-b border-base-content/10'>
|
||||
<h4 className='text-sm font-medium text-base-content/50'>
|
||||
Tambah Kuantitas
|
||||
</h4>
|
||||
|
||||
<Button
|
||||
type='button'
|
||||
variant='ghost'
|
||||
color='none'
|
||||
onClick={deleteEnteredKandangHandler}
|
||||
className='p-0 text-error'
|
||||
>
|
||||
<Icon icon='heroicons:trash' width={20} height={20} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className='w-full p-4 flex flex-col'>
|
||||
<h4 className='text-base font-medium text-base-content/50 font-roboto'>
|
||||
Informasi Kandang
|
||||
</h4>
|
||||
|
||||
{/* Source Kandang */}
|
||||
<div className='flex flex-col'>
|
||||
<span className='w-full py-2 text-xs font-semibold'>
|
||||
Kandang Asal
|
||||
</span>
|
||||
|
||||
{formik.values.flockSourceKandangs.length === 0 && (
|
||||
<span className='text-sm text-base-content/50 italic'>
|
||||
Belum ada kandang asal yang dipilih
|
||||
</span>
|
||||
)}
|
||||
|
||||
{formik.values.flockSourceKandangs.length > 0 && (
|
||||
<div className='flex flex-col gap-3'>
|
||||
{formik.values.flockSourceKandangs.map((item, index) => {
|
||||
const isInvalid =
|
||||
item.quantity === ''
|
||||
? false
|
||||
: Boolean(
|
||||
getIn(
|
||||
formik.errors,
|
||||
`flockSourceKandangs[${index}].quantity`
|
||||
)
|
||||
);
|
||||
|
||||
const errorMessage = getIn(
|
||||
formik.errors,
|
||||
`flockSourceKandangs[${index}].quantity`
|
||||
);
|
||||
|
||||
return (
|
||||
<NumberInput
|
||||
key={`flockSourceKandangs-${item.kandang.value}-${index}`}
|
||||
name={`flockSourceKandangs.${index}.quantity`}
|
||||
placeholder='Masukkan Kuantitas'
|
||||
value={item.quantity}
|
||||
onChange={formik.handleChange}
|
||||
isError={isInvalid}
|
||||
errorMessage={errorMessage}
|
||||
inputPrefix={
|
||||
<div className='w-full h-full py-1 flex flex-row items-stretch justify-between gap-5'>
|
||||
<span
|
||||
title={item.kandang.label}
|
||||
className='text-sm text-base-content self-center text-nowrap truncate'
|
||||
>
|
||||
{item.kandang.label}
|
||||
</span>
|
||||
|
||||
<div className='w-px bg-base-content/10' />
|
||||
</div>
|
||||
}
|
||||
className={{
|
||||
inputPrefix:
|
||||
'py-0 px-0 pl-3 text-base-content/50 bg-transparent border-r-0',
|
||||
inputPrefixSuffixWrapper: 'grid grid-cols-2',
|
||||
inputWrapper: 'border-l-0 pl-5',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Destination Kandang */}
|
||||
<div className='mt-3 flex flex-col'>
|
||||
<span className='w-fit py-2 text-xs font-semibold flex flex-row items-center gap-3'>
|
||||
<span className='text-nowrap'>Kandang Tujuan</span>
|
||||
|
||||
<div className='w-px h-5 bg-base-content/10' />
|
||||
|
||||
<StatusBadge
|
||||
color={
|
||||
totalAvailableChickenForTransfer < 0
|
||||
? 'error'
|
||||
: 'neutral'
|
||||
}
|
||||
text={`Sisa transfer: ${formatNumber(
|
||||
totalAvailableChickenForTransfer
|
||||
)} ekor`}
|
||||
className={{
|
||||
badge: 'text-nowrap',
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
|
||||
{formik.values.flockDestinationKandangs.length === 0 && (
|
||||
<span className='text-sm text-base-content/50 italic'>
|
||||
Belum ada kandang tujuan yang dipilih
|
||||
</span>
|
||||
)}
|
||||
|
||||
{formik.values.flockDestinationKandangs.length > 0 && (
|
||||
<div className='flex flex-col gap-3'>
|
||||
{formik.values.flockDestinationKandangs.map(
|
||||
(item, index) => {
|
||||
const isInvalid =
|
||||
item.quantity === ''
|
||||
? false
|
||||
: Boolean(
|
||||
getIn(
|
||||
formik.errors,
|
||||
`flockDestinationKandangs[${index}].quantity`
|
||||
)
|
||||
);
|
||||
|
||||
const errorMessage = getIn(
|
||||
formik.errors,
|
||||
`flockDestinationKandangs[${index}].quantity`
|
||||
);
|
||||
|
||||
return (
|
||||
<NumberInput
|
||||
key={`flockDestinationKandangs-${item.kandang.value}-${index}`}
|
||||
name={`flockDestinationKandangs.${index}.quantity`}
|
||||
placeholder='Masukkan Kuantitas'
|
||||
value={item.quantity}
|
||||
onChange={formik.handleChange}
|
||||
isError={isInvalid}
|
||||
errorMessage={errorMessage}
|
||||
inputPrefix={
|
||||
<div className='w-full h-full py-1 flex flex-row items-stretch justify-between gap-5'>
|
||||
<span
|
||||
title={item.kandang.label}
|
||||
className='text-sm text-base-content self-center text-nowrap truncate'
|
||||
>
|
||||
{item.kandang.label}
|
||||
</span>
|
||||
|
||||
<div className='w-px bg-base-content/10' />
|
||||
</div>
|
||||
}
|
||||
className={{
|
||||
inputPrefix:
|
||||
'py-0 px-0 pl-3 text-base-content/50 bg-transparent border-r-0',
|
||||
inputPrefixSuffixWrapper: 'grid grid-cols-2',
|
||||
inputWrapper: 'border-l-0 pl-5',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full p-4 flex flex-col border-y border-base-content/10'>
|
||||
<h4 className='text-base font-medium text-base-content/50 font-roboto'>
|
||||
Informasi Umum
|
||||
</h4>
|
||||
|
||||
<NumberInput
|
||||
name='totalQuantity'
|
||||
label='Jumlah Transfer'
|
||||
placeholder='Total Kuantitas Transfer'
|
||||
value={formik.values.totalQuantity}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
isError={totalAvailableChickenForTransfer < 0}
|
||||
errorMessage={
|
||||
totalAvailableChickenForTransfer < 0
|
||||
? 'Jumlah transfer melebihi ketersediaan'
|
||||
: ''
|
||||
}
|
||||
disabled
|
||||
/>
|
||||
|
||||
<TextArea
|
||||
name='reason'
|
||||
label='Catatan'
|
||||
placeholder='Alasan Transfer'
|
||||
rows={4}
|
||||
value={formik.values.reason}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
isError={
|
||||
Boolean(formik.touched.reason) &&
|
||||
Boolean(formik.errors.reason)
|
||||
}
|
||||
errorMessage={formik.errors.reason}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='w-full p-4 self-end flex flex-col gap-3'>
|
||||
{formErrorMessage && (
|
||||
<div role='alert' className='alert alert-error w-full'>
|
||||
<Icon
|
||||
icon='material-symbols:error-outline'
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
<span>{formErrorMessage}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<AlertErrorList formErrorList={formErrorList} onClose={close} />
|
||||
|
||||
<Button
|
||||
type='submit'
|
||||
disabled={
|
||||
formik.isSubmitting || totalAvailableChickenForTransfer < 0
|
||||
}
|
||||
isLoading={formik.isSubmitting}
|
||||
className='w-full p-3 rounded-lg text-sm text-base-100'
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TransferToLayingFormModal;
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { ChangeEventHandler, useEffect, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import useSWR from 'swr';
|
||||
import {
|
||||
CellContext,
|
||||
@@ -12,30 +12,28 @@ import toast from 'react-hot-toast';
|
||||
|
||||
import { Icon } from '@iconify/react';
|
||||
import Table from '@/components/Table';
|
||||
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
||||
import Button from '@/components/Button';
|
||||
import { useModal } from '@/components/Modal';
|
||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||
import SelectInput, {
|
||||
OptionType,
|
||||
useSelect,
|
||||
} from '@/components/input/SelectInput';
|
||||
import CheckboxInput from '@/components/input/CheckboxInput';
|
||||
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
|
||||
import RequirePermission from '@/components/helper/RequirePermission';
|
||||
import DateInput from '@/components/input/DateInput';
|
||||
import PopoverButton from '@/components/popover/PopoverButton';
|
||||
import Badge from '@/components/Badge';
|
||||
import PopoverContent from '@/components/popover/PopoverContent';
|
||||
import Dropdown from '@/components/Dropdown';
|
||||
import StatusBadge from '@/components/helper/StatusBadge';
|
||||
import TransferToLayingFilterModal from '@/components/pages/production/transfer-to-laying/TransferToLayingFilterModal';
|
||||
|
||||
import { TransferToLaying } from '@/types/api/production/transfer-to-laying';
|
||||
import {
|
||||
TransferToLaying,
|
||||
TransferToLayingFilter,
|
||||
} from '@/types/api/production/transfer-to-laying';
|
||||
import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying';
|
||||
import { cn, formatDate } from '@/lib/helper';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||
import { Flock } from '@/types/api/master-data/flock';
|
||||
import { ProjectFlockApi } from '@/services/api/production';
|
||||
import Badge from '@/components/Badge';
|
||||
import { Color } from '@/types/theme';
|
||||
import PopoverContent from '@/components/popover/PopoverContent';
|
||||
|
||||
const RowOptionsMenu = ({
|
||||
props,
|
||||
@@ -56,8 +54,8 @@ const RowOptionsMenu = ({
|
||||
|
||||
const showDeleteButton = showEditButton;
|
||||
|
||||
const showApproveButton = showEditButton;
|
||||
const showRejectButton = showEditButton;
|
||||
// const showApproveButton = showEditButton;
|
||||
// const showRejectButton = showEditButton;
|
||||
|
||||
const popoverId = `transferToLaying#${props.row.original.id}`;
|
||||
const popoverAnchorName = `--anchor-transferToLaying#${props.row.original.id}`;
|
||||
@@ -78,7 +76,7 @@ const RowOptionsMenu = ({
|
||||
id={popoverId}
|
||||
anchorName={popoverAnchorName}
|
||||
position={popoverPosition === 'bottom' ? 'bottom-start' : 'left'}
|
||||
className='rounded-xl border border-base-content/5 shadow-sm'
|
||||
className='w-full max-w-40 rounded-xl border border-base-content/5 shadow-sm'
|
||||
>
|
||||
<div className='flex flex-col bg-base-100 rounded-xl'>
|
||||
<RequirePermission permissions='lti.production.transfer_to_laying.detail'>
|
||||
@@ -96,7 +94,7 @@ const RowOptionsMenu = ({
|
||||
{showEditButton && (
|
||||
<RequirePermission permissions='lti.production.transfer_to_laying.update'>
|
||||
<Button
|
||||
href={`/production/transfer-to-laying/detail/edit/?transferToLayingId=${props.row.original.id}`}
|
||||
href={`/production/transfer-to-laying/?action=edit&id=${props.row.original.id}`}
|
||||
variant='ghost'
|
||||
color='none'
|
||||
className='p-3 justify-start text-sm font-semibold w-full'
|
||||
@@ -107,34 +105,6 @@ const RowOptionsMenu = ({
|
||||
</RequirePermission>
|
||||
)}
|
||||
|
||||
{showApproveButton && (
|
||||
<RequirePermission permissions='lti.production.transfer_to_laying.approve'>
|
||||
<Button
|
||||
variant='ghost'
|
||||
color='success'
|
||||
onClick={approveClickHandler}
|
||||
className='p-3 justify-start text-sm font-semibold w-full'
|
||||
>
|
||||
<Icon icon='heroicons:check' width={20} height={20} />
|
||||
Approve
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
)}
|
||||
|
||||
{showRejectButton && (
|
||||
<RequirePermission permissions='lti.production.transfer_to_laying.approve'>
|
||||
<Button
|
||||
variant='ghost'
|
||||
color='error'
|
||||
onClick={rejectClickHandler}
|
||||
className='p-3 justify-start text-sm font-semibold w-full'
|
||||
>
|
||||
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
||||
Reject
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
)}
|
||||
|
||||
{showDeleteButton && (
|
||||
<RequirePermission permissions='lti.production.transfer_to_laying.delete'>
|
||||
<hr className='mx-3 border-base-content/10 h-px' />
|
||||
@@ -165,18 +135,22 @@ const TransferToLayingsTable = () => {
|
||||
} = useTableFilter({
|
||||
initial: {
|
||||
search: '',
|
||||
transferDate: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
flockSource: '',
|
||||
flockDestination: '',
|
||||
status: '',
|
||||
filter_by: '',
|
||||
sort_by: '',
|
||||
},
|
||||
paramMap: {
|
||||
page: 'page',
|
||||
pageSize: 'limit',
|
||||
transferDate: 'transfer_date',
|
||||
startDate: 'start_date',
|
||||
endDate: 'end_date',
|
||||
flockSource: 'flock_source',
|
||||
flockDestination: 'flock_destination',
|
||||
status: 'status',
|
||||
filter_by: 'filter_by',
|
||||
sort_by: 'sort_by',
|
||||
},
|
||||
@@ -191,34 +165,36 @@ const TransferToLayingsTable = () => {
|
||||
TransferToLayingApi.getAllFetcher
|
||||
);
|
||||
|
||||
const filterCount = useMemo(() => {
|
||||
let count = 0;
|
||||
|
||||
if (tableFilterState.startDate && tableFilterState.endDate) {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
if (tableFilterState.flockSource.length > 0) {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
if (tableFilterState.flockDestination.length > 0) {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
if (tableFilterState.status.length > 0) {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
return count;
|
||||
}, [tableFilterState]);
|
||||
|
||||
const isFilterActive = filterCount > 0;
|
||||
|
||||
// Modal hooks
|
||||
const filterModal = useModal();
|
||||
const deleteModal = useModal();
|
||||
const approveModal = useModal();
|
||||
const rejectModal = useModal();
|
||||
|
||||
// Flocks data
|
||||
const {
|
||||
setInputValue: setFlockSourceInputValue,
|
||||
options: flockSourceOptions,
|
||||
isLoadingOptions: isLoadingFlockSourceOptions,
|
||||
loadMore: loadMoreFlockSource,
|
||||
hasMore: hasMoreFlockSource,
|
||||
} = useSelect<Flock>(ProjectFlockApi.basePath, 'id', 'flock_name');
|
||||
|
||||
const {
|
||||
setInputValue: setFlockDestinationInputValue,
|
||||
options: flockDestinationOptions,
|
||||
isLoadingOptions: isLoadingFlockDestinationOptions,
|
||||
loadMore: loadMoreFlockDestination,
|
||||
hasMore: hasMoreFlockDestination,
|
||||
} = useSelect<Flock>(ProjectFlockApi.basePath, 'id', 'flock_name');
|
||||
|
||||
// Flocks value
|
||||
const [selectedFlockSource, setSelectedFlockSource] =
|
||||
useState<OptionType | null>(null);
|
||||
const [selectedFlockDestination, setSelectedFlockDestination] =
|
||||
useState<OptionType | null>(null);
|
||||
|
||||
const [selectedTransferToLaying, setSelectedTransferToLaying] = useState<
|
||||
TransferToLaying | undefined
|
||||
>(undefined);
|
||||
@@ -315,18 +291,7 @@ const TransferToLayingsTable = () => {
|
||||
latestApprovalStepName = 'Ditolak';
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge
|
||||
variant='soft'
|
||||
className={{
|
||||
badge: 'rounded-lg px-2 w-full flex flex-row justify-start',
|
||||
}}
|
||||
color={badgeColor}
|
||||
>
|
||||
<Icon icon='mdi:circle' width={12} height={12} color={badgeColor} />
|
||||
{latestApprovalStepName}
|
||||
</Badge>
|
||||
);
|
||||
return <StatusBadge color={badgeColor} text={latestApprovalStepName} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -337,7 +302,7 @@ const TransferToLayingsTable = () => {
|
||||
const currentRowRelativeIndex =
|
||||
currentPageRows.findIndex((r) => r.id === props.row.id) + 1;
|
||||
|
||||
const isLast2Rows = currentRowRelativeIndex > currentPageSize - 3;
|
||||
const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2;
|
||||
|
||||
const approveClickHandler = () => {
|
||||
setSelectedTransferToLaying(props.row.original);
|
||||
@@ -472,38 +437,25 @@ const TransferToLayingsTable = () => {
|
||||
setIsRejectLoading(false);
|
||||
};
|
||||
|
||||
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
updateFilter('search', e.target.value);
|
||||
const filterSubmitHandler = (values: TransferToLayingFilter) => {
|
||||
updateFilter('startDate', values.startDate);
|
||||
updateFilter('endDate', values.endDate);
|
||||
updateFilter('flockSource', values.flockSource.join(','));
|
||||
updateFilter('flockDestination', values.flockDestination.join(','));
|
||||
updateFilter('status', values.status.join(','));
|
||||
};
|
||||
|
||||
const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
const newVal = val as OptionType;
|
||||
|
||||
setPageSize(newVal.value as number);
|
||||
const filterResetHandler = () => {
|
||||
updateFilter('startDate', '');
|
||||
updateFilter('endDate', '');
|
||||
updateFilter('flockSource', '');
|
||||
updateFilter('flockDestination', '');
|
||||
updateFilter('status', '');
|
||||
};
|
||||
|
||||
const transferDateChangeHandler: ChangeEventHandler<HTMLInputElement> = (
|
||||
e
|
||||
) => {
|
||||
updateFilter('transferDate', e.target.value);
|
||||
};
|
||||
|
||||
const flockSourceChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
setSelectedFlockSource(val as OptionType);
|
||||
updateFilter(
|
||||
'flockSource',
|
||||
val ? ((val as OptionType).value as string) : ''
|
||||
);
|
||||
};
|
||||
|
||||
const flockDestinationChangeHandler = (
|
||||
val: OptionType | OptionType[] | null
|
||||
) => {
|
||||
setSelectedFlockDestination(val as OptionType);
|
||||
updateFilter(
|
||||
'flockDestination',
|
||||
val ? ((val as OptionType).value as string) : ''
|
||||
);
|
||||
// TODO: add export to excel functionality
|
||||
const exportToExcelHandler = () => {
|
||||
toast.error('Not implemented yet');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -518,111 +470,137 @@ const TransferToLayingsTable = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='w-full p-0'>
|
||||
<div className='flex flex-col gap-2 mb-4'>
|
||||
<div className='w-full flex flex-col xl:flex-row justify-between items-end xl:items-center gap-2'>
|
||||
<div className='w-full sm:w-fit flex flex-col sm:flex-row self-start gap-2'>
|
||||
<RequirePermission permissions='lti.production.transfer_to_laying.create'>
|
||||
<Button
|
||||
href='/production/transfer-to-laying/add'
|
||||
variant='outline'
|
||||
color='primary'
|
||||
className='w-full sm:w-fit'
|
||||
>
|
||||
<Icon icon='ic:round-plus' width={24} height={24} />
|
||||
Tambah
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
<div className='@container w-full'>
|
||||
<div className='w-full p-3 flex flex-row justify-between gap-3 flex-wrap border-b border-base-content/10'>
|
||||
<div className='w-fit flex flex-row gap-3 flex-wrap'>
|
||||
<RequirePermission permissions='lti.production.transfer_to_laying.create'>
|
||||
<Button
|
||||
href={{
|
||||
pathname: '/production/transfer-to-laying',
|
||||
query: {
|
||||
action: 'add',
|
||||
},
|
||||
}}
|
||||
color='primary'
|
||||
className='px-3 py-2.5 w-fit text-sm text-base-100 rounded-lg shadow-sm'
|
||||
>
|
||||
<Icon icon='heroicons:plus' width={20} height={20} />
|
||||
Add Transfer to Laying
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
|
||||
{selectedRowIds.length > 0 && (
|
||||
<>
|
||||
<RequirePermission permissions='lti.production.transfer_to_laying.approve'>
|
||||
<Button
|
||||
variant='outline'
|
||||
color='success'
|
||||
onClick={bulkApproveClickHandler}
|
||||
disabled={selectedRowIds.length === 0}
|
||||
className='w-full sm:w-fit'
|
||||
>
|
||||
<Icon
|
||||
icon='material-symbols:check'
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
Approve
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
{selectedRowIds.length > 0 && (
|
||||
<>
|
||||
<hr className='w-px h-full border-none bg-base-content/10 hidden @sm:block' />
|
||||
|
||||
<RequirePermission permissions='lti.production.transfer_to_laying.approve'>
|
||||
<Button
|
||||
variant='outline'
|
||||
color='error'
|
||||
onClick={bulkRejectClickHandler}
|
||||
disabled={selectedRowIds.length === 0}
|
||||
className='w-full sm:w-fit'
|
||||
>
|
||||
<Icon
|
||||
icon='material-symbols:close'
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
Reject
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<RequirePermission permissions='lti.production.transfer_to_laying.approve'>
|
||||
<Button
|
||||
variant='outline'
|
||||
color='none'
|
||||
onClick={bulkRejectClickHandler}
|
||||
disabled={selectedRowIds.length === 0}
|
||||
className='px-3 py-2.5 gap-1.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft'
|
||||
>
|
||||
<Icon
|
||||
icon='heroicons:x-mark'
|
||||
width={20}
|
||||
height={20}
|
||||
className='text-error'
|
||||
/>
|
||||
Reject
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
|
||||
<DebouncedTextInput
|
||||
name='search'
|
||||
placeholder='Cari TransferToLaying'
|
||||
value={tableFilterState.search}
|
||||
onChange={searchChangeHandler}
|
||||
className={{
|
||||
wrapper: 'sm:max-w-3xs',
|
||||
}}
|
||||
/>
|
||||
<RequirePermission permissions='lti.production.transfer_to_laying.approve'>
|
||||
<Button
|
||||
variant='outline'
|
||||
color='none'
|
||||
onClick={bulkApproveClickHandler}
|
||||
disabled={selectedRowIds.length === 0}
|
||||
className='px-3 py-2.5 gap-1.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft'
|
||||
>
|
||||
<Icon
|
||||
icon='heroicons:check'
|
||||
width={20}
|
||||
height={20}
|
||||
className='text-success'
|
||||
/>
|
||||
Approve
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='grid grid-cols-12 justify-end gap-4'>
|
||||
<DateInput
|
||||
name='transfer_date'
|
||||
label='Tanggal Transfer'
|
||||
placeholder='Tanggal Transfer'
|
||||
value={tableFilterState.transferDate}
|
||||
onChange={transferDateChangeHandler}
|
||||
className={{
|
||||
wrapper: 'col-span-12 sm:col-span-3',
|
||||
}}
|
||||
/>
|
||||
<div className='flex flex-row justify-center items-center gap-3'>
|
||||
<Button
|
||||
variant='outline'
|
||||
color='none'
|
||||
onClick={filterModal.openModal}
|
||||
className={cn(
|
||||
'px-3 py-2.5 gap-1.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft transition-all',
|
||||
{
|
||||
'border-primary-gradient text-primary': isFilterActive,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<Icon icon='heroicons:funnel' width={20} height={20} />
|
||||
Filter
|
||||
{isFilterActive && (
|
||||
<Badge
|
||||
className={{
|
||||
badge:
|
||||
'p-1.5 bg-[#FF3535] text-xs text-base-100 border border-base-300 rounded-lg',
|
||||
}}
|
||||
>
|
||||
{filterCount}
|
||||
</Badge>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<SelectInput
|
||||
label='Flock Asal'
|
||||
options={flockSourceOptions}
|
||||
isLoading={isLoadingFlockSourceOptions}
|
||||
value={selectedFlockSource}
|
||||
onChange={flockSourceChangeHandler}
|
||||
onInputChange={setFlockSourceInputValue}
|
||||
onMenuScrollToBottom={loadMoreFlockSource}
|
||||
isClearable
|
||||
<Dropdown
|
||||
align='end'
|
||||
direction='bottom'
|
||||
className={{
|
||||
wrapper: 'col-span-12 sm:col-span-3',
|
||||
content:
|
||||
'mt-1 rounded-xl border border-base-content/5 shadow-sm overflow-hidden',
|
||||
}}
|
||||
/>
|
||||
trigger={
|
||||
<Button
|
||||
variant='outline'
|
||||
color='none'
|
||||
className='px-3 py-2.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft'
|
||||
>
|
||||
<div className='flex flex-row items-center gap-1.5'>
|
||||
<Icon
|
||||
icon='heroicons:cloud-arrow-down'
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
label='Flock Tujuan'
|
||||
options={flockDestinationOptions}
|
||||
isLoading={isLoadingFlockDestinationOptions}
|
||||
value={selectedFlockDestination}
|
||||
onChange={flockDestinationChangeHandler}
|
||||
onInputChange={setFlockDestinationInputValue}
|
||||
onMenuScrollToBottom={loadMoreFlockDestination}
|
||||
isClearable
|
||||
className={{
|
||||
wrapper: 'col-span-12 sm:col-span-3',
|
||||
}}
|
||||
/>
|
||||
<span>Export</span>
|
||||
|
||||
<div className='w-px self-stretch bg-base-content/10' />
|
||||
|
||||
<Icon
|
||||
icon='heroicons:chevron-down'
|
||||
width={14}
|
||||
height={14}
|
||||
/>
|
||||
</div>
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
variant='ghost'
|
||||
color='none'
|
||||
onClick={exportToExcelHandler}
|
||||
className='w-full p-3 justify-start text-sm text-base-content/50 font-semibold text-nowrap'
|
||||
>
|
||||
<Icon icon='heroicons:table-cells' width={20} height={20} />
|
||||
Export to Excel
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -652,7 +630,7 @@ const TransferToLayingsTable = () => {
|
||||
enableRowSelection={tableEnableRowSelectionHandler}
|
||||
withCheckbox
|
||||
className={{
|
||||
containerClassName: cn({
|
||||
containerClassName: cn('p-3', {
|
||||
'w-full mb-20':
|
||||
isResponseSuccess(transferToLayings) &&
|
||||
transferToLayings?.data?.length === 0,
|
||||
@@ -662,15 +640,23 @@ const TransferToLayingsTable = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<TransferToLayingFilterModal
|
||||
ref={filterModal.ref}
|
||||
onSubmit={filterSubmitHandler}
|
||||
onReset={filterResetHandler}
|
||||
/>
|
||||
|
||||
<ConfirmationModal
|
||||
ref={deleteModal.ref}
|
||||
iconPosition='left'
|
||||
type='error'
|
||||
text={`Apakah anda yakin ingin menghapus data transfer ke laying ini?`}
|
||||
text='Delete This Data?'
|
||||
subtitleText='Are you sure you want to delete this data? '
|
||||
secondaryButton={{
|
||||
text: 'Tidak',
|
||||
text: 'Cancel',
|
||||
}}
|
||||
primaryButton={{
|
||||
text: 'Ya',
|
||||
text: 'Delete',
|
||||
color: 'error',
|
||||
isLoading: isDeleteLoading,
|
||||
onClick: confirmationModalDeleteClickHandler,
|
||||
@@ -680,12 +666,14 @@ const TransferToLayingsTable = () => {
|
||||
<ConfirmationModalWithNotes
|
||||
ref={approveModal.ref}
|
||||
type='success'
|
||||
text={`Apakah anda yakin ingin approve data transfer ke laying ini (${selectedRowIds.length} data)?`}
|
||||
iconPosition='left'
|
||||
text='Approve This Submission?'
|
||||
subtitleText='Are you sure you want to approve this submission?'
|
||||
secondaryButton={{
|
||||
text: 'Tidak',
|
||||
text: 'Cancel',
|
||||
}}
|
||||
primaryButton={{
|
||||
text: 'Ya',
|
||||
text: 'Approve',
|
||||
color: 'success',
|
||||
isLoading: isApproveLoading,
|
||||
onClick: confirmationModalApproveClickHandler,
|
||||
@@ -695,12 +683,14 @@ const TransferToLayingsTable = () => {
|
||||
<ConfirmationModalWithNotes
|
||||
ref={rejectModal.ref}
|
||||
type='error'
|
||||
text={`Apakah anda yakin ingin reject data transfer ke laying ini (${selectedRowIds.length} data)?`}
|
||||
iconPosition='left'
|
||||
text='Reject This Submission?'
|
||||
subtitleText='Are you sure you want to reject this submission?'
|
||||
secondaryButton={{
|
||||
text: 'Tidak',
|
||||
text: 'Cancel',
|
||||
}}
|
||||
primaryButton={{
|
||||
text: 'Ya',
|
||||
text: 'Reject',
|
||||
color: 'error',
|
||||
isLoading: isRejectLoading,
|
||||
onClick: confirmationModalRejectClickHandler,
|
||||
|
||||
+42
-27
@@ -1,7 +1,10 @@
|
||||
import * as Yup from 'yup';
|
||||
import { TransferToLaying } from '@/types/api/production/transfer-to-laying';
|
||||
import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying';
|
||||
import { formatDate } from '@/lib/helper';
|
||||
import { formatDate, formatNumber } from '@/lib/helper';
|
||||
import { ProjectFlock } from '@/types/api/production/project-flock';
|
||||
import { ProjectFlockApi } from '@/services/api/production/project-flock';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
type TransferToLayingFormSchemaType = {
|
||||
transfer_date?: string;
|
||||
@@ -14,7 +17,7 @@ type TransferToLayingFormSchemaType = {
|
||||
label: string;
|
||||
};
|
||||
|
||||
totalQuantity?: number;
|
||||
totalQuantity?: number | string;
|
||||
maxTotalQuantity?: number; // original cap (hidden), helper
|
||||
|
||||
flockSourceKandangs: {
|
||||
@@ -53,15 +56,15 @@ export const TransferToLayingFormSchema: Yup.ObjectSchema<TransferToLayingFormSc
|
||||
}).required('Flock tujuan wajib diisi!'),
|
||||
|
||||
totalQuantity: Yup.number()
|
||||
.min(1, 'Jumlah transfer minimal 1')
|
||||
.min(0, 'Jumlah transfer minimal 0')
|
||||
.max(
|
||||
Yup.ref('maxTotalQuantity'),
|
||||
({ max }) => `Kuantitas maksimal ${max}!`
|
||||
({ max }) => `Kuantitas maksimal ${formatNumber(max)}!`
|
||||
)
|
||||
.required('Jumlah transfer wajib diisi!'),
|
||||
|
||||
maxTotalQuantity: Yup.number()
|
||||
.min(1, 'Jumlah transfer minimal 1')
|
||||
.min(0, 'Jumlah transfer minimal 0')
|
||||
.required('Jumlah transfer wajib diisi!'),
|
||||
|
||||
flockSourceKandangs: Yup.array()
|
||||
@@ -76,7 +79,7 @@ export const TransferToLayingFormSchema: Yup.ObjectSchema<TransferToLayingFormSc
|
||||
.min(0, 'Kuantitas minimal 0!')
|
||||
.max(
|
||||
Yup.ref('maxQuantity'),
|
||||
({ max }) => `Kuantitas maksimal ${max}!`
|
||||
({ max }) => `Kuantitas maksimal ${formatNumber(max)}!`
|
||||
)
|
||||
.required('Kuantitas wajib diisi!'),
|
||||
|
||||
@@ -98,7 +101,7 @@ export const TransferToLayingFormSchema: Yup.ObjectSchema<TransferToLayingFormSc
|
||||
.min(0, 'Kuantitas minimal 0!')
|
||||
.max(
|
||||
Yup.ref('maxQuantity'),
|
||||
({ max }) => `Kuantitas maksimal ${max}!`
|
||||
({ max }) => `Kuantitas maksimal ${formatNumber(max)}!`
|
||||
)
|
||||
.required('Kuantitas wajib diisi!'),
|
||||
|
||||
@@ -137,12 +140,12 @@ export const getTransferToLayingFormInitialValues = (
|
||||
}
|
||||
: undefined,
|
||||
totalQuantity:
|
||||
initialValues?.usage_qty ?? initialValues?.pending_usage_qty ?? undefined,
|
||||
initialValues?.usage_qty ?? initialValues?.pending_usage_qty ?? '',
|
||||
|
||||
flockSourceKandangs: initialValues?.sources
|
||||
? initialValues.sources.map((sourceKandang) => ({
|
||||
kandang: {
|
||||
value: sourceKandang.source_project_flock_kandang.kandang.id,
|
||||
value: sourceKandang.source_project_flock_kandang.id,
|
||||
label: sourceKandang.source_project_flock_kandang.kandang.name,
|
||||
},
|
||||
quantity: sourceKandang.qty,
|
||||
@@ -152,7 +155,7 @@ export const getTransferToLayingFormInitialValues = (
|
||||
flockDestinationKandangs: initialValues?.targets
|
||||
? initialValues.targets.map((targetKandang) => ({
|
||||
kandang: {
|
||||
value: targetKandang.target_project_flock_kandang.kandang.id,
|
||||
value: targetKandang.target_project_flock_kandang.id,
|
||||
label: targetKandang.target_project_flock_kandang.kandang.name,
|
||||
},
|
||||
quantity: targetKandang.qty,
|
||||
@@ -174,7 +177,7 @@ export const getFilledTransferToLayingFormInitialValues = async (
|
||||
const formattedFlockSourceKandangs = initialValues?.sources
|
||||
? initialValues.sources.map((sourceKandang) => ({
|
||||
kandang: {
|
||||
value: sourceKandang.source_project_flock_kandang.kandang.id,
|
||||
value: sourceKandang.source_project_flock_kandang.id,
|
||||
label: sourceKandang.source_project_flock_kandang.kandang.name,
|
||||
},
|
||||
quantity: sourceKandang.qty,
|
||||
@@ -189,9 +192,35 @@ export const getFilledTransferToLayingFormInitialValues = async (
|
||||
|
||||
let maxTotalQuantity = 0;
|
||||
formattedFlockSourceKandangs.forEach((item) => {
|
||||
maxTotalQuantity += item.maxQuantity;
|
||||
maxTotalQuantity += item.quantity;
|
||||
});
|
||||
|
||||
const flockDestination = await ProjectFlockApi.getSingle(
|
||||
initialValues?.to_project_flock.id as number
|
||||
);
|
||||
|
||||
const formattedFlockDestinationKandangs = initialValues?.targets
|
||||
? initialValues.targets.map((targetKandang) => {
|
||||
const kandang = isResponseSuccess(flockDestination)
|
||||
? flockDestination?.data?.kandangs.find(
|
||||
(kandang) =>
|
||||
String(kandang.project_flock_kandang_id) ===
|
||||
String(targetKandang.target_project_flock_kandang.id)
|
||||
)
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
kandang: {
|
||||
value: targetKandang.target_project_flock_kandang.id,
|
||||
label: targetKandang.target_project_flock_kandang.kandang.name,
|
||||
},
|
||||
quantity: targetKandang.qty,
|
||||
|
||||
maxQuantity: kandang?.capacity ?? 0,
|
||||
};
|
||||
})
|
||||
: [];
|
||||
|
||||
return {
|
||||
transfer_date: initialValues?.transfer_date
|
||||
? formatDate(initialValues.transfer_date, 'YYYY-MM-DD')
|
||||
@@ -214,21 +243,7 @@ export const getFilledTransferToLayingFormInitialValues = async (
|
||||
|
||||
flockSourceKandangs: formattedFlockSourceKandangs,
|
||||
|
||||
flockDestinationKandangs: initialValues?.targets
|
||||
? initialValues.targets.map((targetKandang) => ({
|
||||
kandang: {
|
||||
value: targetKandang.target_project_flock_kandang.kandang.id,
|
||||
label: targetKandang.target_project_flock_kandang.kandang.name,
|
||||
},
|
||||
quantity: targetKandang.qty,
|
||||
|
||||
// maxQuantity:
|
||||
// targetKandang.target_project_flock_kandang.kandang.capacity,
|
||||
|
||||
// TODO: integrate this to real API kandang capacity
|
||||
maxQuantity: Infinity,
|
||||
}))
|
||||
: [],
|
||||
flockDestinationKandangs: formattedFlockDestinationKandangs,
|
||||
|
||||
reason: initialValues?.notes ?? undefined,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user