'use client'; import { FormEventHandler, useCallback, useEffect, useMemo, useState, } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; 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 TransferToLayingConfirmationModal from '@/components/pages/production/transfer-to-laying/TransferToLayingConfirmationModal'; 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 isModalActionForForm = modalAction === 'add' || modalAction === 'edit'; const { mutate } = useSWRConfig(); const refreshTransferToLayings = () => { mutate( (key) => typeof key === 'string' && key.includes(TransferToLayingApi.basePath) ); }; const { data: transferToLaying, isLoading: isLoadingTransferToLaying } = useSWR( isModalActionForForm && transferToLayingId ? ['detail-transfer-to-laying', transferToLayingId] : undefined, ([, id]) => TransferToLayingApi.getSingle(Number(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 successModal = useModal(); const [formikLastValues, setFormikLastValues] = useState< TransferToLayingFormValues | undefined >(undefined); const [formErrorMessage, setFormErrorMessage] = useState(null); // Flock Source const { setInputValue: setFlockSourceInputValue, options: flockSourceOptions, isLoadingOptions: isLoadingFlockSourceOptions, loadMore: loadMoreFlockSource, rawData: flockSourceRawData, } = useSelect( ProjectFlockApi.basePath, 'id', 'flock_name', 'search', { category: 'GROWING', transfer_context: 'transfer_to_laying', } ); // Flock Destination const { setInputValue: setFlockDestinationInputValue, options: flockDestinationOptions, isLoadingOptions: isLoadingFlockDestinationOptions, loadMore: loadMoreFlockDestination, rawData: flockDestinationRawData, } = useSelect( 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); successModal.openModal(); }, [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); successModal.openModal(); }, [router] ); const [formikInitialValues, setFormikInitialValues] = useState( getTransferToLayingFormInitialValues() ); const formik = useFormik({ 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, }; setFormikLastValues(values); 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 { data: flockDestinationKandangsMaxTargetQty, isLoading: isLoadingFlockDestinationKandangsMaxTargetQty, } = useSWR( formik.values.flockDestination ? [ 'transfer-to-laying', 'max-target-qty', String(formik.values.flockDestination.value), ] : undefined, ([, , id]: string[]) => TransferToLayingApi.getMappedFlockKandangsMaxTargetQty(Number(id)) ); const mappedFlockDestinationKandangsMaxTargetQty: { kandang_name: string; max_target_qty: number; project_flock_kandang_id: number; }[] = useMemo(() => { if ( !flockDestinationKandangsMaxTargetQty || !selectedFlockDestinationRawData ) return []; return selectedFlockDestinationRawData ? selectedFlockDestinationRawData.kandangs.map((kandang) => { const maxQty = flockDestinationKandangsMaxTargetQty[ kandang.project_flock_kandang_id ]?.max_target_qty; return { kandang_name: kandang.name, max_target_qty: maxQty, project_flock_kandang_id: kandang.project_flock_kandang_id, }; }) : []; }, [flockDestinationKandangsMaxTargetQty, selectedFlockDestinationRawData]); const mappedFlockDestinationKandangsAvailabilityInfo: { available: number; unavailable: number; } = useMemo(() => { if (!selectedFlockDestinationRawData) return { available: 0, unavailable: 0 }; let countAvailable = 0; let countUnavailable = 0; mappedFlockDestinationKandangsMaxTargetQty.forEach((item) => { if (item.max_target_qty > 0) { countAvailable += 1; } else { countUnavailable += 1; } }); return { available: countAvailable, unavailable: countUnavailable }; }, [mappedFlockDestinationKandangsMaxTargetQty]); 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); } if (isResponseError(transferToLaying)) { router.push('/production/transfer-to-laying'); closeModalHandler(); toast.error(transferToLaying.message); } }; 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, formik.values.flockDestinationKandangs]); return ( <>
{/* 1st Section */}

{modalAction === 'add' ? 'Add' : 'Edit'} Transfer to Laying

Informasi Umum

2} /> 2} /> 2} />
{step >= 2 && ( <>

Pilih Kandang Asal

{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 (
); } )}

Pilih Kandang Tujuan

{formik.touched.flockDestinationKandangs && formik.errors.flockDestinationKandangs && typeof formik.errors.flockDestinationKandangs === 'string' && ( {formik.errors.flockDestinationKandangs} )}
{mappedFlockDestinationKandangsMaxTargetQty.map( (item, itemIdx) => { const isAvailable = item.max_target_qty > 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.kandang_name, }, quantity: '', maxQuantity: item.max_target_qty, }, ]); } else { formik.setFieldValue( 'flockDestinationKandangs', formik.values.flockDestinationKandangs.filter( (k) => k.kandang.value !== item.project_flock_kandang_id ) ); } }; return (
); } )}
)}
{step < 3 && ( )}
{/* 2nd Section */} {step === 3 && (

Tambah Kuantitas

Informasi Kandang

{/* Source Kandang */}
Kandang Asal{' '} * {formik.values.flockSourceKandangs.length === 0 && ( Belum ada kandang asal yang dipilih )} {formik.values.flockSourceKandangs.length > 0 && (
{formik.values.flockSourceKandangs.map((item, index) => { const isInvalid = !Boolean( getIn( formik.touched, `flockSourceKandangs[${index}].quantity` ) ) && item.quantity === '' ? false : Boolean( getIn( formik.errors, `flockSourceKandangs[${index}].quantity` ) ); const errorMessage = getIn( formik.errors, `flockSourceKandangs[${index}].quantity` ); return ( {item.kandang.label}
} 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', }} /> ); })}
)}
{/* Destination Kandang */}
Kandang Tujuan{' '} *
{formik.values.flockDestinationKandangs.length === 0 && ( Belum ada kandang tujuan yang dipilih )} {formik.values.flockDestinationKandangs.length > 0 && (
{formik.values.flockDestinationKandangs.map( (item, index) => { const isInvalid = !Boolean( getIn( formik.touched, `flockDestinationKandangs.${index}.quantity` ) ) && item.quantity === '' ? false : Boolean( getIn( formik.errors, `flockDestinationKandangs[${index}].quantity` ) ); const errorMessage = getIn( formik.errors, `flockDestinationKandangs[${index}].quantity` ); return ( {item.kandang.label}
} 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', }} /> ); } )}
)}

Informasi Umum