From 517e8c758c8500ef6155bac3653cd68f46932091 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Fri, 26 Dec 2025 17:12:40 +0700 Subject: [PATCH] refactor(FE-438): Add Project Flock selection and lookup --- .../uniformity/form/UniformityForm.schema.ts | 25 +- .../pages/uniformity/form/UniformityForm.tsx | 231 +++++++++++++++--- 2 files changed, 215 insertions(+), 41 deletions(-) diff --git a/src/components/pages/uniformity/form/UniformityForm.schema.ts b/src/components/pages/uniformity/form/UniformityForm.schema.ts index 87e79aa3..819c6b7c 100644 --- a/src/components/pages/uniformity/form/UniformityForm.schema.ts +++ b/src/components/pages/uniformity/form/UniformityForm.schema.ts @@ -8,7 +8,12 @@ type UniformityFormSchemaType = { label: string; } | null; location_id: number; - project_flock_kandang_id: number; + project_flock?: { + value: number; + label: string; + } | null; + project_flock_id: number; + project_flock_kandang_id: number | null; kandang?: { value: number; label: string; @@ -46,9 +51,14 @@ export const UniformityFormSchema: Yup.ObjectSchema = location_id: Yup.number() .required('Location wajib diisi!') .typeError('Location wajib diisi!'), - project_flock_kandang_id: Yup.number() - .required('Project flock kandang wajib diisi!') - .typeError('Project flock kandang wajib diisi!'), + project_flock: Yup.object({ + value: Yup.number().min(1).required(), + label: Yup.string().required(), + }).nullable(), + project_flock_id: Yup.number() + .required('Project flock wajib diisi!') + .typeError('Project flock wajib diisi!'), + project_flock_kandang_id: Yup.number().optional().nullable().default(null), kandang: Yup.object({ value: Yup.number().min(1).required(), label: Yup.string().required(), @@ -73,6 +83,13 @@ export const getUniformityFormInitialValues = ( } : null, location_id: initialValues?.location?.id ?? 0, + project_flock: initialValues?.project_flock + ? { + value: initialValues.project_flock.id, + label: initialValues.project_flock.flock_name, + } + : null, + project_flock_id: initialValues?.project_flock?.id ?? 0, project_flock_kandang_id: initialValues?.project_flock_kandang_id ?? 0, kandang: initialValues?.kandang ? { diff --git a/src/components/pages/uniformity/form/UniformityForm.tsx b/src/components/pages/uniformity/form/UniformityForm.tsx index da4d7e4f..7c8ba5d9 100644 --- a/src/components/pages/uniformity/form/UniformityForm.tsx +++ b/src/components/pages/uniformity/form/UniformityForm.tsx @@ -21,14 +21,22 @@ import { UniformityFormValues, getUniformityFormInitialValues, } from '@/components/pages/uniformity/form/UniformityForm.schema'; -import { LocationApi, KandangApi } from '@/services/api/master-data'; +import { LocationApi } from '@/services/api/master-data'; +import { + ProjectFlockApi, + ProjectFlockKandangApi, +} from '@/services/api/production'; import { UniformityApi } from '@/services/api/uniformity'; -import { isResponseError } from '@/lib/api-helper'; +import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { Uniformity, CreateUniformityPayload, } from '@/types/api/uniformity/uniformity'; +import { type BaseApiResponse } from '@/types/api/api-general'; +import { ProjectFlockKandangLookup } from '@/types/api/production/project-flock'; +import { Kandang } from '@/types/api/master-data/kandang'; import ExpandedDrawerForm from '@/components/pages/uniformity/form/ExpandedDrawerForm'; +import useSWR from 'swr'; interface UniformityFormProps { formType?: 'add' | 'edit'; @@ -50,17 +58,142 @@ const UniformityForm = ({ useState(''); // ===== SELECT INPUT DATA ===== + const [selectedLocation, setSelectedLocation] = useState( + null + ); + + const [projectFlockSearchValue, setProjectFlockSearchValue] = useState(''); + const [selectedProjectFlock, setSelectedProjectFlock] = + useState(null); + + const [selectedKandang, setSelectedKandang] = useState( + null + ); + const { setInputValue: setLocationSelectInputValue, options: locationOptions, isLoadingOptions: isLoadingLocations, } = useSelect(LocationApi.basePath, 'id', 'name', 'search'); - const { - setInputValue: setKandangSelectInputValue, - options: kandangOptions, - isLoadingOptions: isLoadingKandangs, - } = useSelect(KandangApi.basePath, 'id', 'name', 'search'); + // ===== FETCH PROJECT FLOCKS DATA ===== + const projectFlocksUrl = useMemo(() => { + const params = new URLSearchParams({ + search: projectFlockSearchValue || '', + limit: '100', + }); + if (selectedLocation) { + params.append('location_id', selectedLocation.value.toString()); + } + return `${ProjectFlockApi.basePath}?${params.toString()}`; + }, [projectFlockSearchValue, selectedLocation]); + + const { data: projectFlocksData, isLoading: isLoadingProjectFlocks } = useSWR( + projectFlocksUrl, + ProjectFlockApi.getAllFetcher + ); + + const projectFlocksDataList = + projectFlocksData?.status === 'success' + ? projectFlocksData.data + : undefined; + + // ===== PROJECT FLOCK OPTIONS ===== + const projectFlockOptions = useMemo(() => { + let options: OptionType[] = []; + + if (isResponseSuccess(projectFlocksData)) { + const flockOptions = + projectFlocksData?.data.map((projectFlock) => ({ + value: projectFlock.id, + label: projectFlock.flock_name || '', + })) || []; + options = options.concat(flockOptions); + } + + return options; + }, [projectFlocksData]); + + // ===== APPROVED PROJECT FLOCK KANDANGS ===== + const approvedProjectFlockKandangsUrl = useMemo(() => { + const params = new URLSearchParams({ + step_name: 'Disetujui', + limit: '100', + }); + return `${ProjectFlockKandangApi.basePath}?${params.toString()}`; + }, []); + + const { data: approvedProjectFlockKandangsData } = useSWR( + approvedProjectFlockKandangsUrl, + ProjectFlockKandangApi.getAllFetcher + ); + + const approvedProjectFlockKandangs = useMemo(() => { + if (!isResponseSuccess(approvedProjectFlockKandangsData)) return []; + return approvedProjectFlockKandangsData.data; + }, [approvedProjectFlockKandangsData]); + + // ===== KANDANG OPTIONS (FILTERED BY SELECTED PROJECT FLOCK) ===== + const kandangOptions = useMemo(() => { + let options: OptionType[] = []; + + if (selectedProjectFlock && projectFlocksDataList) { + const selectedProjectFlockData = projectFlocksDataList.find( + (pf) => pf.id === selectedProjectFlock.value + ); + + if (selectedProjectFlockData?.kandangs) { + const approvedKandangIds = approvedProjectFlockKandangs + .filter((pfk) => pfk.project_flock_id === selectedProjectFlock.value) + .map((pfk) => pfk.kandang_id); + + const kandangOpts = selectedProjectFlockData.kandangs + .filter((kandang: Kandang) => { + if (formType === 'add') { + return approvedKandangIds.includes(kandang.id); + } + return true; + }) + .map((kandang: Kandang) => ({ + value: kandang.id, + label: kandang.name || '', + })); + options = options.concat(kandangOpts); + } + } + + return options; + }, [ + selectedProjectFlock, + projectFlocksDataList, + approvedProjectFlockKandangs, + formType, + ]); + + // ===== PROJECT FLOCK KANDANG LOOKUP ===== + const projectFlockKandangLookupUrl = useMemo(() => { + if (!selectedProjectFlock || !selectedKandang) return null; + const params = new URLSearchParams({ + project_flock_id: selectedProjectFlock.value.toString(), + kandang_id: selectedKandang.value.toString(), + }); + return `${ProjectFlockApi.basePath}/kandangs/lookup?${params.toString()}`; + }, [selectedProjectFlock, selectedKandang]); + + const { data: projectFlockKandangLookupData } = useSWR( + projectFlockKandangLookupUrl, + projectFlockKandangLookupUrl + ? () => + ProjectFlockApi.getAllFetcher( + projectFlockKandangLookupUrl + ) as Promise> + : null + ); + + const projectFlockKandangLookup = + projectFlockKandangLookupData?.status === 'success' + ? projectFlockKandangLookupData.data + : undefined; // ===== FORM CONFIGURATION ===== const formikInitialValues = useMemo( @@ -76,14 +209,22 @@ const UniformityForm = ({ validateOnMount: false, enableReinitialize: true, onSubmit: async (values) => { + const projectFlockKandangId = projectFlockKandangLookup?.id; + + if (!projectFlockKandangId) { + setUniformityFormErrorMessage( + 'Project Flock Kandang tidak ditemukan. Silakan pilih Project Flock dan Kandang yang valid.' + ); + return; + } + const formData = new FormData(); formData.append('date', values.date); formData.append('location_id', values.location_id.toString()); formData.append( 'project_flock_kandang_id', - values.project_flock_kandang_id.toString() + projectFlockKandangId.toString() ); - formData.append('kandang_id', values.kandang_id.toString()); if (values.files) { formData.append('files[]', values.files); @@ -111,6 +252,35 @@ const UniformityForm = ({ formik.setFieldValue('location', location); formik.setFieldTouched('location_id', true); formik.setFieldValue('location_id', (location as OptionType)?.value || 0); + + setSelectedLocation(location); + setSelectedProjectFlock(null); + setSelectedKandang(null); + + formik.setFieldValue('project_flock', null); + formik.setFieldValue('project_flock_id', 0); + formik.setFieldValue('kandang', null); + formik.setFieldValue('kandang_id', 0); + }, + [formik] + ); + + const handleProjectFlockChange = useCallback( + (val: OptionType | OptionType[] | null) => { + const projectFlock = val as OptionType | null; + formik.setFieldTouched('project_flock', true); + formik.setFieldValue('project_flock', projectFlock); + formik.setFieldTouched('project_flock_id', true); + formik.setFieldValue( + 'project_flock_id', + (projectFlock as OptionType)?.value || 0 + ); + + setSelectedProjectFlock(projectFlock); + setSelectedKandang(null); + + formik.setFieldValue('kandang', null); + formik.setFieldValue('kandang_id', 0); }, [formik] ); @@ -122,6 +292,8 @@ const UniformityForm = ({ formik.setFieldValue('kandang', kandang); formik.setFieldTouched('kandang_id', true); formik.setFieldValue('kandang_id', (kandang as OptionType)?.value || 0); + + setSelectedKandang(kandang); }, [formik] ); @@ -225,7 +397,7 @@ const UniformityForm = ({ { - const option = val as OptionType | null; - formik.setFieldValue( - 'project_flock_kandang_id', - option?.value || 0 - ); - }} - options={[ - { value: 1, label: '1' }, - { value: 2, label: '2' }, - { value: 3, label: '3' }, - ]} + label='Project Flock' + placeholder='Pilih Project Flock...' + value={formik.values.project_flock} + onChange={handleProjectFlockChange} + options={projectFlockOptions} + onInputChange={setProjectFlockSearchValue} + isLoading={isLoadingProjectFlocks} + isDisabled={!formik.values.location_id} isError={ - formik.touched.project_flock_kandang_id && - Boolean(formik.errors.project_flock_kandang_id) + formik.touched.project_flock_id && + Boolean(formik.errors.project_flock_id) } - errorMessage={formik.errors.project_flock_kandang_id as string} + errorMessage={formik.errors.project_flock_id as string} isClearable className={{ wrapper: 'w-full' }} /> @@ -280,8 +438,7 @@ const UniformityForm = ({ value={formik.values.kandang} onChange={handleKandangChange} options={kandangOptions} - onInputChange={setKandangSelectInputValue} - isLoading={isLoadingKandangs} + isDisabled={!formik.values.project_flock_id} isError={ formik.touched.kandang_id && Boolean(formik.errors.kandang_id) }