diff --git a/src/components/pages/flock/recording/form/RecordingForm.tsx b/src/components/pages/flock/recording/form/RecordingForm.tsx index 95600120..d29e7f72 100644 --- a/src/components/pages/flock/recording/form/RecordingForm.tsx +++ b/src/components/pages/flock/recording/form/RecordingForm.tsx @@ -23,8 +23,10 @@ import { ProjectFlockApi } from '@/services/api/production'; import { isResponseSuccess } from '@/lib/api-helper'; import { RECORDING_FLAG_OPTIONS } from '@/config/constant'; import useSWR from 'swr'; -import { KandangApi, LocationApi } from '@/services/api/master-data'; import { ProductWarehouseApi } from '@/services/api/inventory'; +import { ProjectFlock } from '@/types/api/production/project-flock'; +import { Warehouse } from '@/types/api/master-data/warehouse'; +import { LocationApi } from '@/services/api/master-data'; interface RecordingFormProps { type?: 'add' | 'edit' | 'detail'; @@ -32,9 +34,10 @@ interface RecordingFormProps { } const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { - const [flockSelectInputValue, setFlockSelectInputValue] = useState(''); const [locationSelectInputValue, setLocationSelectInputValue] = useState(''); - const [coopSelectInputValue, setCoopSelectInputValue] = useState(''); + const [flockSelectInputValue, setFlockSelectInputValue] = useState(''); + const [selectedProjectFlock, setSelectedProjectFlock] = + useState(null); const [selectedFeed, setSelectedFeed] = useState([]); const [selectedWeight, setSelectedWeight] = useState([]); const [selectedVaccine, setSelectedVaccine] = useState([]); @@ -104,87 +107,159 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { }, }); - // Flock selection - const projectFlocksUrl = `${ProjectFlockApi.basePath}?${new URLSearchParams({ search: flockSelectInputValue }).toString()}`; - const { data: projectFlocks, isLoading: isLoadingFlocks } = useSWR( - projectFlocksUrl, - ProjectFlockApi.getAllFetcher - ); - const flockOptions = isResponseSuccess(projectFlocks) - ? projectFlocks?.data.map((flock) => ({ - value: flock.id, - label: flock.flock.name, - })) - : []; - - // Pakan selection - const pakanUrl = `${ProductWarehouseApi.basePath}?${new URLSearchParams({ flag: 'PAKAN', search: '' }).toString()}`; - const { data: pakanProducts } = useSWR( - pakanUrl, - ProductWarehouseApi.getAllFetcher - ); - const pakanOptions = isResponseSuccess(pakanProducts) - ? pakanProducts?.data.map((product) => ({ - value: product.id, - label: `${product.product.name} - ${product.warehouse.name} (Stock: ${product.quantity.toLocaleString('id-ID')})`, - })) - : []; - - // Create stock mapping for pakan (Feed) - const pakanStockMap = useMemo(() => { - if (!isResponseSuccess(pakanProducts)) - return new Map(); - const map = new Map(); - pakanProducts.data.forEach((product) => { - map.set(product.id, product.quantity); - }); - return map; - }, [pakanProducts]); - - // OVK selection - const ovkUrl = `${ProductWarehouseApi.basePath}?${new URLSearchParams({ flag: 'OVK', search: '' }).toString()}`; - const { data: ovkProducts } = useSWR( - ovkUrl, - ProductWarehouseApi.getAllFetcher - ); - const ovkOptions = isResponseSuccess(ovkProducts) - ? ovkProducts?.data.map((product) => ({ - value: product.id, - label: `${product.product.name} - ${product.warehouse.name} (Stock: ${product.quantity.toLocaleString('id-ID')})`, - })) - : []; - - // Create stock mapping for OVK (Vaccination) - const ovkStockMap = useMemo(() => { - if (!isResponseSuccess(ovkProducts)) return new Map(); - const map = new Map(); - ovkProducts.data.forEach((product) => { - map.set(product.id, product.quantity); - }); - return map; - }, [ovkProducts]); - - const locationsUrl = `${LocationApi.basePath}?${new URLSearchParams({ search: locationSelectInputValue ?? '' }).toString()}`; + const locationsUrl = `${LocationApi.basePath}?${new URLSearchParams({ search: locationSelectInputValue }).toString()}`; const { data: locations, isLoading: isLoadingLocations } = useSWR( locationsUrl, LocationApi.getAllFetcher ); const locationOptions = isResponseSuccess(locations) - ? locations?.data.map((loc) => ({ value: loc.id, label: loc.name })) + ? locations.data.map((loc) => ({ value: loc.id, label: loc.name })) : []; - const coopsUrl = `${KandangApi.basePath}?${new URLSearchParams({ search: coopSelectInputValue ?? '' }).toString()}`; - const { data: coops, isLoading: isLoadingCoops } = useSWR( - coopsUrl, - KandangApi.getAllFetcher + const projectFlocksUrl = useMemo(() => { + if (!formik.values.location_id) return null; + const params = new URLSearchParams({ + search: flockSelectInputValue, + location_id: formik.values.location_id.toString(), + }); + return `${ProjectFlockApi.basePath}?${params.toString()}`; + }, [formik.values.location_id, flockSelectInputValue]); + + const { data: projectFlocks, isLoading: isLoadingFlocks } = useSWR( + projectFlocksUrl, + ProjectFlockApi.getAllFetcher ); + const flockOptions = isResponseSuccess(projectFlocks) + ? projectFlocks.data.map((flock) => ({ + value: flock.id, + label: flock.flock.name, + })) + : []; + + const buildWarehouseLabel = useCallback((warehouse: Warehouse) => { + const parts: string[] = [warehouse.name]; + + if ('kandang' in warehouse && warehouse.kandang) { + parts.push(warehouse.kandang.name); + } + + if ('location' in warehouse && warehouse.location) { + parts.push(warehouse.location.name); + } + + if (warehouse.area) { + parts.push(warehouse.area.name); + } + + return parts.join(' - '); + }, []); + + const pakanUrl = useMemo(() => { + if (!formik.values.location_id) return null; + const params = new URLSearchParams({ + flag: 'PAKAN', + search: '', + location_id: formik.values.location_id.toString(), + }); + return `${ProductWarehouseApi.basePath}?${params.toString()}`; + }, [formik.values.location_id]); + + const { data: pakanProducts, isLoading: isLoadingPakan } = useSWR( + pakanUrl, + ProductWarehouseApi.getAllFetcher + ); + + const filteredPakanProducts = useMemo(() => { + if (!isResponseSuccess(pakanProducts) || !formik.values.location_id) + return []; + + return pakanProducts.data.filter((product) => { + const warehouse = product.warehouse; + + if ('location' in warehouse && warehouse.location) { + return warehouse.location.id === formik.values.location_id; + } + + // If warehouse only has area, include it if area matches the location's area + // Note: This might need adjustment based on your business logic + return false; + }); + }, [pakanProducts, formik.values.location_id]); + + const pakanOptions = useMemo( + () => + filteredPakanProducts.map((product) => ({ + value: product.id, + label: `${product.product.name} - ${buildWarehouseLabel(product.warehouse)} (Stock: ${product.quantity.toLocaleString('id-ID')})`, + })), + [filteredPakanProducts, buildWarehouseLabel] + ); + + const pakanStockMap = useMemo(() => { + const map = new Map(); + filteredPakanProducts.forEach((product) => { + map.set(product.id, product.quantity); + }); + return map; + }, [filteredPakanProducts]); + + const ovkUrl = useMemo(() => { + if (!formik.values.location_id) return null; + const params = new URLSearchParams({ + flag: 'OVK', + search: '', + location_id: formik.values.location_id.toString(), + }); + return `${ProductWarehouseApi.basePath}?${params.toString()}`; + }, [formik.values.location_id]); + + const { data: ovkProducts, isLoading: isLoadingOvk } = useSWR( + ovkUrl, + ProductWarehouseApi.getAllFetcher + ); + + const filteredOvkProducts = useMemo(() => { + if (!isResponseSuccess(ovkProducts) || !formik.values.location_id) + return []; + + return ovkProducts.data.filter((product) => { + const warehouse = product.warehouse; + + if ('location' in warehouse && warehouse.location) { + return warehouse.location.id === formik.values.location_id; + } + + // If warehouse only has area, include it if area matches the location's area + // Note: This might need adjustment based on your business logic + return false; + }); + }, [ovkProducts, formik.values.location_id]); + + const ovkOptions = useMemo( + () => + filteredOvkProducts.map((product) => ({ + value: product.id, + label: `${product.product.name} - ${buildWarehouseLabel(product.warehouse)} (Stock: ${product.quantity.toLocaleString('id-ID')})`, + })), + [filteredOvkProducts, buildWarehouseLabel] + ); + + const ovkStockMap = useMemo(() => { + const map = new Map(); + filteredOvkProducts.forEach((product) => { + map.set(product.id, product.quantity); + }); + return map; + }, [filteredOvkProducts]); + const coopOptions = useMemo(() => { - if (!isResponseSuccess(coops) || !formik.values.location_id) return []; - return coops.data - .filter((coop) => coop.location.id === formik.values.location_id) - .map((coop) => ({ value: coop.id, label: coop.name })); - }, [coops, formik.values.location_id]); + if (!selectedProjectFlock || !selectedProjectFlock.kandangs) return []; + return selectedProjectFlock.kandangs.map((kandang) => ({ + value: kandang.id, + label: kandang.name, + })); + }, [selectedProjectFlock]); const locationChangeHandler = (val: OptionType | OptionType[] | null) => { const locationValue = (val as OptionType)?.value; @@ -192,9 +267,28 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { formik.setFieldValue('location', val, false); formik.setFieldValue('location_id', locationValue || 0, false); + formik.setFieldValue('flock', null, false); + formik.setFieldValue('flock_id', 0, false); + formik.setFieldValue('coop', null, false); + formik.setFieldValue('coop_id', 0, false); + setSelectedProjectFlock(null); + setFlockSelectInputValue(''); + }; + + const flockChangeHandler = (val: OptionType | OptionType[] | null) => { + const flockValue = (val as OptionType)?.value; + + const selected = isResponseSuccess(projectFlocks) + ? projectFlocks.data.find((flock) => flock.id === flockValue) + : null; + + setSelectedProjectFlock(selected || null); + + formik.setFieldValue('flock', val, false); + formik.setFieldValue('flock_id', flockValue || 0, false); + formik.setFieldValue('coop', null, false); formik.setFieldValue('coop_id', 0, false); - setCoopSelectInputValue(''); }; const coopChangeHandler = (val: OptionType | OptionType[] | null) => { @@ -204,6 +298,17 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { formik.setFieldValue('coop_id', coopValue || 0, false); }; + useEffect(() => { + if (initialValues?.flock && isResponseSuccess(projectFlocks)) { + const flock = projectFlocks.data.find( + (f) => f.id === initialValues.flock.id + ); + if (flock) { + setSelectedProjectFlock(flock); + } + } + }, [initialValues, projectFlocks]); + const isRepeaterInputError = ( arrayName: T, field: T extends 'feed_data' @@ -245,13 +350,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { }; }; - const flockChangeHandler = (val: OptionType | OptionType[] | null) => { - const flockValue = (val as OptionType)?.value; - - formik.setFieldValue('flock', val, false); - formik.setFieldValue('flock_id', flockValue || 0, false); - }; - const addFeedData = () => { const newFeedData = [ ...(formik.values.feed_data || []), @@ -372,25 +470,28 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { {/* Basic Info Card */}
-

Flock

+

Recording Information

+ { readOnly={type === 'detail'} />
+
- - + + @@ -551,14 +655,13 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { `feed_data.${idx}.feed_qty`, stock ); - // Reset feed_stock when changing feed formik.setFieldValue( `feed_data.${idx}.feed_stock`, 0 ); }} options={pakanOptions} - isLoading={false} + isLoading={isLoadingPakan} isError={ isRepeaterInputError('feed_data', 'feed_id', idx) .isError @@ -968,14 +1071,13 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { `vaccination.${idx}.total_stock`, stock ); - // Reset used_stock when changing vaccine formik.setFieldValue( `vaccination.${idx}.used_stock`, 0 ); }} options={ovkOptions} - isLoading={false} + isLoading={isLoadingOvk} isError={ isRepeaterInputError( 'vaccination', diff --git a/src/types/api/production/project-flock.d.ts b/src/types/api/production/project-flock.d.ts index cd68df40..7a251d38 100644 --- a/src/types/api/production/project-flock.d.ts +++ b/src/types/api/production/project-flock.d.ts @@ -3,6 +3,7 @@ import { Area } from '@/types/api/master-data/area'; import { ProductCategory } from '@/types/api/master-data/product-category'; import { Fcr } from '@/types/api/master-data/fcr'; import { Kandang } from '@/types/api/master-data/kandang'; +import { Location } from '@/types/api/master-data/location'; import { BaseMetadata } from '@/types/api/api-general'; export type BaseProjectFlock = {