diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index dec106b1..f492c987 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -12,7 +12,10 @@ import RequirePermission from '@/components/helper/RequirePermission'; import Card from '@/components/Card'; import Badge from '@/components/Badge'; import NumberInput from '@/components/input/NumberInput'; -import SelectInput, { OptionType } from '@/components/input/SelectInput'; +import SelectInput, { + OptionType, + useSelect, +} from '@/components/input/SelectInput'; import CheckboxInput from '@/components/input/CheckboxInput'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes'; @@ -26,6 +29,7 @@ import { } from '@/services/api/production'; import { LocationApi } from '@/services/api/master-data'; import { ProductWarehouseApi } from '@/services/api/inventory'; +import { ProductWarehouse } from '@/types/api/inventory/product-warehouse'; import { CreateGrowingRecordingPayload, @@ -36,7 +40,10 @@ import { NextDayRecording, } from '@/types/api/production/recording'; import { type BaseApiResponse } from '@/types/api/api-general'; -import { ProjectFlockKandangLookup } from '@/types/api/production/project-flock'; +import { + ProjectFlockKandangLookup, + ProjectFlock, +} from '@/types/api/production/project-flock'; import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang'; import { Kandang } from '@/types/api/master-data/kandang'; @@ -77,16 +84,27 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { const [selectedDepletions, setSelectedDepletions] = useState([]); const [selectedEggs, setSelectedEggs] = useState([]); - const [locationSearchValue, setLocationSearchValue] = useState(''); const [selectedLocation, setSelectedLocation] = useState( null ); - const [projectFlockSearchValue, setProjectFlockSearchValue] = useState(''); const [selectedProjectFlock, setSelectedProjectFlock] = useState(null); const [selectedKandang, setSelectedKandang] = useState( null ); + const [selectedProjectFlockLocationId, setSelectedProjectFlockLocationId] = + useState(''); + const [stockProductsLocationId, setStockProductsLocationId] = + useState(''); + const [stockProductsKandangId, setStockProductsKandangId] = + useState(''); + const [depletionProductsLocationId, setDepletionProductsLocationId] = + useState(''); + const [depletionProductsKandangId, setDepletionProductsKandangId] = + useState(''); + const [eggProductsLocationId, setEggProductsLocationId] = + useState(''); + const [eggProductsKandangId, setEggProductsKandangId] = useState(''); const [isApproveLoading, setIsApproveLoading] = useState(false); const [isRejectLoading, setIsRejectLoading] = useState(false); @@ -210,26 +228,24 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { }, [deleteModal, initialValues?.id, router]); // ===== API DATA FETCHING ===== - const locationsUrl = `${LocationApi.basePath}?${new URLSearchParams({ - search: locationSearchValue || '', - limit: '100', - }).toString()}`; - const { data: locations, isLoading: isLoadingLocations } = useSWR( - locationsUrl, - LocationApi.getAllFetcher - ); + const { + setInputValue: setLocationSearchValue, + options: locationOptions, + isLoadingOptions: isLoadingLocations, + loadMore: loadMoreLocations, + hasMore: hasMoreLocations, + } = useSelect(LocationApi.basePath, 'id', 'name', 'search'); - const projectFlocksUrl = `${ProjectFlockApi.basePath}?${new URLSearchParams({ - search: projectFlockSearchValue || '', - limit: '100', - ...(selectedLocation - ? { location_id: selectedLocation.value.toString() } - : {}), - }).toString()}`; - const { data: projectFlocks, isLoading: isLoadingProjectFlocks } = useSWR( - projectFlocksUrl, - ProjectFlockApi.getAllFetcher - ); + const { + setInputValue: setProjectFlockSearchValue, + options: projectFlockOptions, + rawData: projectFlocksRawData, + isLoadingOptions: isLoadingProjectFlocks, + loadMore: loadMoreProjectFlocks, + hasMore: hasMoreProjectFlocks, + } = useSelect(ProjectFlockApi.basePath, 'id', 'flock_name', 'search', { + location_id: selectedProjectFlockLocationId, + }); const projectFlockKandangLookupUrl = useMemo(() => { if (!selectedProjectFlock || !selectedKandang) return null; @@ -279,46 +295,28 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { ? projectFlockKandangDetailData.data : undefined; - const stockProductsUrl = useMemo(() => { - if (!selectedLocation || !selectedKandang) return null; - const params = new URLSearchParams({ - flags: 'PAKAN,OVK', - search: '', - limit: '100', - location_id: selectedLocation.value.toString(), - }); + const { + options: stockProductOptions, + rawData: stockProducts, + isLoadingOptions: isLoadingStockProducts, + loadMore: loadMoreStockProducts, + hasMore: hasMoreStockProducts, + } = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', '', { + flags: 'PAKAN,OVK', + location_id: stockProductsLocationId, + kandang_id: stockProductsKandangId, + }); - if (projectFlockKandangLookup?.kandang?.id) { - params.append( - 'kandang_id', - projectFlockKandangLookup.kandang.id.toString() - ); - } else if (selectedKandang) { - params.append('kandang_id', selectedKandang.value.toString()); - } - - return `${ProductWarehouseApi.basePath}?${params.toString()}`; - }, [selectedLocation, selectedKandang, projectFlockKandangLookup]); - - const depletionProductsUrl = useMemo(() => { - if (!selectedLocation || !selectedKandang) return null; - const params = new URLSearchParams({ - search: '', - limit: '100', - location_id: selectedLocation.value.toString(), - }); - - if (projectFlockKandangLookup?.kandang?.id) { - params.append( - 'kandang_id', - projectFlockKandangLookup.kandang.id.toString() - ); - } else if (selectedKandang) { - params.append('kandang_id', selectedKandang.value.toString()); - } - - return `${ProductWarehouseApi.basePath}?${params.toString()}`; - }, [selectedLocation, selectedKandang, projectFlockKandangLookup]); + const { + options: depletionProductOptions, + rawData: depletionProductsData, + isLoadingOptions: isLoadingDepletionProducts, + loadMore: loadMoreDepletionProducts, + hasMore: hasMoreDepletionProducts, + } = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', '', { + location_id: depletionProductsLocationId, + kandang_id: depletionProductsKandangId, + }); const today = new Date().toISOString().split('T')[0]; const existingRecordingsUrl = `${RecordingApi.basePath}`; @@ -360,38 +358,17 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { } }, [nextDayRecordingData]); - const { data: stockProducts, isLoading: isLoadingStockProducts } = useSWR( - stockProductsUrl, - ProductWarehouseApi.getAllFetcher - ); - - const { data: depletionProductsData, isLoading: isLoadingDepletionProducts } = - useSWR(depletionProductsUrl, ProductWarehouseApi.getAllFetcher); - - const eggProductsUrl = useMemo(() => { - if (!selectedLocation || !selectedKandang) return null; - const params = new URLSearchParams({ - search: 'telur', - limit: '100', - location_id: selectedLocation.value.toString(), - }); - - if (projectFlockKandangLookup?.kandang?.id) { - params.append( - 'kandang_id', - projectFlockKandangLookup.kandang.id.toString() - ); - } else if (selectedKandang) { - params.append('kandang_id', selectedKandang.value.toString()); - } - - return `${ProductWarehouseApi.basePath}?${params.toString()}`; - }, [selectedLocation, selectedKandang, projectFlockKandangLookup]); - - const { data: eggProductsData, isLoading: isLoadingEggProducts } = useSWR( - eggProductsUrl, - ProductWarehouseApi.getAllFetcher - ); + const { + options: eggProductOptions, + rawData: eggProductsData, + isLoadingOptions: isLoadingEggProducts, + loadMore: loadMoreEggProducts, + hasMore: hasMoreEggProducts, + } = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', 'search', { + search: 'telur', + location_id: eggProductsLocationId, + kandang_id: eggProductsKandangId, + }); const approvedProjectFlockKandangsUrl = useMemo(() => { const params = new URLSearchParams({ @@ -448,17 +425,8 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { }); // ===== DATA PROCESSING ===== - const locationOptions = useMemo(() => { - let options: OptionType[] = []; - - if (isResponseSuccess(locations)) { - const locationOptionsList = - locations?.data.map((location) => ({ - value: location.id, - label: location.name || '', - })) || []; - options = options.concat(locationOptionsList); - } + const enhancedLocationOptions = useMemo(() => { + const options = [...locationOptions]; if (projectFlockKandangDetail && (type === 'edit' || type === 'detail')) { const currentLocation = projectFlockKandangDetail.project_flock.location; @@ -474,19 +442,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { } return options; - }, [locations, projectFlockKandangDetail, type]); + }, [locationOptions, projectFlockKandangDetail, type]); - const projectFlockOptions = useMemo(() => { - let options: OptionType[] = []; - - if (isResponseSuccess(projectFlocks)) { - const flockOptions = - projectFlocks?.data.map((projectFlock) => ({ - value: projectFlock.id, - label: projectFlock.flock_name || '', - })) || []; - options = options.concat(flockOptions); - } + const enhancedProjectFlockOptions = useMemo(() => { + const options = [...projectFlockOptions]; if (projectFlockKandangDetail && (type === 'edit' || type === 'detail')) { const currentProjectFlock = projectFlockKandangDetail.project_flock; @@ -502,13 +461,14 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { } return options; - }, [projectFlocks, projectFlockKandangDetail, type]); + }, [projectFlockOptions, projectFlockKandangDetail, type]); const kandangOptions = useMemo(() => { let options: OptionType[] = []; - if (selectedProjectFlock && isResponseSuccess(projectFlocks)) { - const selectedProjectFlockData = projectFlocks.data.find( + if (selectedProjectFlock && isResponseSuccess(projectFlocksRawData)) { + const data = projectFlocksRawData.data as ProjectFlock[]; + const selectedProjectFlockData = data.find( (pf) => pf.id === selectedProjectFlock.value ); @@ -548,7 +508,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { return options; }, [ selectedProjectFlock, - projectFlocks, + projectFlocksRawData, projectFlockKandangDetail, type, approvedProjectFlockKandangs, @@ -598,20 +558,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { ]); const unifiedStockProducts = useMemo(() => { - const options: OptionType[] = []; - if (isResponseSuccess(stockProducts) && selectedKandang) { - stockProducts.data.forEach((product) => { - const hasPakanFlag = product.product.flags?.includes('PAKAN'); - const hasOvkFlag = product.product.flags?.includes('OVK'); - - if (hasPakanFlag || hasOvkFlag) { - options.push({ - value: product.id, - label: product.product.name, - }); - } - }); - } + const options = [...stockProductOptions]; if ( initialValues && @@ -635,12 +582,14 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { } return options; - }, [stockProducts, initialValues, type, selectedKandang]); + }, [stockProductOptions, initialValues, type]); const depletionProducts = useMemo(() => { const options: OptionType[] = []; + if (isResponseSuccess(depletionProductsData) && selectedKandang) { - depletionProductsData.data.forEach((product) => { + const data = depletionProductsData.data as unknown as ProductWarehouse[]; + data.forEach((product) => { const productName = product.product.name; if ( @@ -680,8 +629,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { const eggProducts = useMemo(() => { const options: OptionType[] = []; + if (isResponseSuccess(eggProductsData) && selectedKandang) { - eggProductsData.data.forEach((product) => { + const data = eggProductsData.data as unknown as ProductWarehouse[]; + data.forEach((product) => { const productName = product.product.name; if ( @@ -812,33 +763,12 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { }; // ===== HELPER FUNCTIONS ===== - useCallback((): OptionType | null => { - if ( - !formik.values.project_flock_kandang || - !isResponseSuccess(projectFlocks) - ) { - return selectedLocation; - } - const projectFlockId = formik.values.project_flock_kandang.value; - const projectFlock = projectFlocks.data.find( - (pf) => pf.id === projectFlockId - ); - if (projectFlock && projectFlock.location) { - return { - value: projectFlock.location.id, - label: projectFlock.location.name, - }; - } - return selectedLocation; - }, [formik.values.project_flock_kandang, projectFlocks, selectedLocation]); - const getAvailableStock = useCallback( (productWarehouseId: number) => { if ((type as 'add' | 'edit' | 'detail') === 'detail') return 0; if (!isResponseSuccess(stockProducts)) return 0; - const productWarehouse = stockProducts.data.find( - (pw) => pw.id === productWarehouseId - ); + const data = stockProducts.data as unknown as ProductWarehouse[]; + const productWarehouse = data.find((pw) => pw.id === productWarehouseId); return productWarehouse?.quantity ?? 0; }, [stockProducts, type] @@ -915,9 +845,8 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { (productWarehouseId: number) => { if (!isResponseSuccess(stockProducts)) return null; - const productWarehouse = stockProducts.data.find( - (pw) => pw.id === productWarehouseId - ); + const data = stockProducts.data as unknown as ProductWarehouse[]; + const productWarehouse = data.find((pw) => pw.id === productWarehouseId); if (!productWarehouse) return null; const hasPakanFlag = productWarehouse.product.flags?.includes('PAKAN'); @@ -1002,9 +931,13 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { // ===== EVENT HANDLERS ===== const locationChangeHandler = (val: OptionType | OptionType[] | null) => { - setSelectedLocation(val as OptionType); + const location = val as OptionType; + setSelectedLocation(location); setSelectedProjectFlock(null); setSelectedKandang(null); + setSelectedProjectFlockLocationId( + location ? location.value.toString() : '' + ); formik.setFieldValue('project_flock_kandang', null); formik.setFieldValue('project_flock_kandang_id', 0); }; @@ -1017,7 +950,23 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { }; const kandangChangeHandler = (val: OptionType | OptionType[] | null) => { - setSelectedKandang(val as OptionType); + const kandang = val as OptionType; + setSelectedKandang(kandang); + if (selectedLocation && kandang) { + setStockProductsLocationId(selectedLocation.value.toString()); + setStockProductsKandangId(kandang.value.toString()); + setDepletionProductsLocationId(selectedLocation.value.toString()); + setDepletionProductsKandangId(kandang.value.toString()); + setEggProductsLocationId(selectedLocation.value.toString()); + setEggProductsKandangId(kandang.value.toString()); + } else { + setStockProductsLocationId(''); + setStockProductsKandangId(''); + setDepletionProductsLocationId(''); + setDepletionProductsKandangId(''); + setEggProductsLocationId(''); + setEggProductsKandangId(''); + } formik.setFieldTouched('project_flock_kandang', true); formik.setFieldTouched('project_flock_kandang_id', true); }; @@ -1091,6 +1040,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { label: location.name || '', }; setSelectedLocation(locationOption); + setSelectedProjectFlockLocationId(location.id.toString()); if (projectFlock) { const projectFlockOption = { @@ -1106,6 +1056,13 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { }; setSelectedKandang(kandangOption); + setStockProductsLocationId(location.id.toString()); + setStockProductsKandangId(kandang.id.toString()); + setDepletionProductsLocationId(location.id.toString()); + setDepletionProductsKandangId(kandang.id.toString()); + setEggProductsLocationId(location.id.toString()); + setEggProductsKandangId(kandang.id.toString()); + if ( formik.values.project_flock_kandang_id !== projectFlockKandangDetail.id @@ -1126,7 +1083,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { }, [ projectFlockKandangDetail, type, - projectFlockOptions, + enhancedProjectFlockOptions, formik.values.project_flock_kandang_id, ]); @@ -1415,23 +1372,25 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { label='Lokasi' value={selectedLocation} onChange={locationChangeHandler} - options={locationOptions} + options={enhancedLocationOptions} onInputChange={setLocationSearchValue} isLoading={isLoadingLocations} + onMenuScrollToBottom={loadMoreLocations} placeholder='Pilih Lokasi' isClearable isSearchable />