feat(FE-114): integrate location and project flock selection in RecordingForm

This commit is contained in:
rstubryan
2025-10-23 21:34:40 +07:00
parent d61c0ab844
commit 71df86c8df
@@ -2,10 +2,12 @@
import { useMemo, useState, useEffect } from 'react';
import { useFormik } from 'formik';
import useSWR from 'swr';
import { Icon } from '@iconify/react';
import Button from '@/components/Button';
import TextInput from '@/components/input/TextInput';
import SelectInput, { OptionType } from '@/components/input/SelectInput';
import CheckboxInput from '@/components/input/CheckboxInput';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import { FormHeader } from '@/components/helper/form/FormHeader';
@@ -21,6 +23,9 @@ import {
UpdateRecordingFormSchema,
} from './RecordingForm.schema';
import { useRecordingFormHandlers } from './useRecordingFormHandlers';
import { ProjectFlockApi } from '@/services/api/production';
import { LocationApi } from '@/services/api/master-data';
import { isResponseSuccess } from '@/lib/api-helper';
import Card from '@/components/Card';
@@ -34,6 +39,62 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
const [selectedStocks, setSelectedStocks] = useState<number[]>([]);
const [selectedDepletions, setSelectedDepletions] = useState<number[]>([]);
// State for Location search and selection
const [locationSearchValue, setLocationSearchValue] = useState('');
const [selectedLocation, setSelectedLocation] = useState<OptionType | null>(null);
// State for Project Flock search
const [projectFlockSearchValue, setProjectFlockSearchValue] = useState('');
// Fetch Locations data
const locationsUrl = `${LocationApi.basePath}?${new URLSearchParams({
search: locationSearchValue || '',
}).toString()}`;
const { data: locations, isLoading: isLoadingLocations } = useSWR(
locationsUrl,
LocationApi.getAllFetcher
);
// Fetch Project Flocks data with location filter
const projectFlocksUrl = `${ProjectFlockApi.basePath}?${new URLSearchParams({
search: projectFlockSearchValue || '',
...(selectedLocation ? { location_id: selectedLocation.value.toString() } : {}),
}).toString()}`;
const { data: projectFlocks, isLoading: isLoadingProjectFlocks } = useSWR(
projectFlocksUrl,
ProjectFlockApi.getAllFetcher
);
// Extract location options from locations data
const locationOptions = useMemo(() => {
if (!isResponseSuccess(locations)) return [];
return locations?.data.map((location) => ({
value: location.id,
label: location.name,
})) || [];
}, [locations]);
// Extract kandang options from project_flocks data
const projectFlockKandangOptions = useMemo(() => {
if (!isResponseSuccess(projectFlocks)) return [];
const options: OptionType[] = [];
projectFlocks?.data.forEach((projectFlock) => {
projectFlock.kandangs.forEach((kandang) => {
options.push({
value: kandang.id,
label: `${projectFlock.flock.name} - ${projectFlock.area.name} - ${kandang.name}`,
});
});
});
return options;
}, [projectFlocks]);
const {
deleteModal,
recordingFormErrorMessage,
@@ -114,9 +175,19 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
});
// EVENT HANDLERS - Select Inputs
const projectFlockChangeHandler = (value: string) => {
const projectFlockId = parseInt(value) || 0;
formik.setFieldValue('project_flock_kandang_id', projectFlockId, false);
const locationChangeHandler = (val: OptionType | OptionType[] | null) => {
setSelectedLocation(val as OptionType);
// Reset project flock selection when location changes
formik.setFieldValue('project_flock_kandang', null);
formik.setFieldValue('project_flock_kandang_id', 0);
};
const projectFlockKandangChangeHandler = (val: OptionType | OptionType[] | null) => {
formik.setFieldTouched('project_flock_kandang', true);
formik.setFieldValue('project_flock_kandang', val);
formik.setFieldTouched('project_flock_kandang_id', true);
formik.setFieldValue('project_flock_kandang_id', (val as OptionType)?.value || 0);
};
// EVENT HANDLERS - Date Time
@@ -303,28 +374,41 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
}}
>
<div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
<TextInput
<SelectInput
required
label='Project Flock Kandang ID'
type='number'
name='project_flock_kandang_id'
value={formik.values.project_flock_kandang_id?.toString() || ''}
onChange={(e) => {
const value = e.target.value;
formik.setFieldValue(
'project_flock_kandang_id',
parseInt(value) || 0
);
projectFlockChangeHandler(value);
}}
onBlur={formik.handleBlur}
label='Lokasi'
value={selectedLocation}
onChange={locationChangeHandler}
options={locationOptions}
onInputChange={setLocationSearchValue}
isLoading={isLoadingLocations}
isDisabled={type === 'detail'}
placeholder='Pilih Lokasi'
isClearable
isSearchable
/>
<SelectInput
required
label='Project Flock'
value={formik.values.project_flock_kandang ?? undefined}
onChange={projectFlockKandangChangeHandler}
options={projectFlockKandangOptions}
onInputChange={setProjectFlockSearchValue}
isLoading={isLoadingProjectFlocks}
isError={
formik.touched.project_flock_kandang_id &&
Boolean(formik.errors.project_flock_kandang_id)
}
errorMessage={formik.errors.project_flock_kandang_id as string}
readOnly={type === 'detail'}
placeholder='Masukkan project flock kandang ID'
isDisabled={type === 'detail' || !selectedLocation}
placeholder={
selectedLocation
? 'Pilih Project Flock - Kandang'
: 'Pilih Lokasi terlebih dahulu'
}
isClearable
isSearchable
/>
<TextInput
@@ -369,21 +453,13 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
readOnly={type === 'detail'}
placeholder='Masukkan status (0-3)'
/>
<CheckboxInput
hidden={true}
name='ontime'
checked={formik.values.ontime || false}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
formik.setFieldValue('ontime', e.target.checked);
}}
disabled={type !== 'detail'}
/>
</div>
</Card>
{/* Body Weights Table */}
<Card
title='Data Bobot Badan'
title='Bobot Badan'
className={{
wrapper: 'w-full mb-4 shadow',
title: 'mb-4',
@@ -583,7 +659,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
{/* Stocks Table */}
<Card
title='Data Stok Pakan'
title='Stok Persediaan'
className={{
wrapper: 'w-full mb-4 shadow',
title: 'mb-4',
@@ -620,7 +696,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
</th>
)}
<th>
Product Warehouse ID
Persediaan
<span
className='tooltip tooltip-error tooltip-bottom z-[9999]'
data-tip='required'
@@ -826,7 +902,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
{/* Depletions Table */}
<Card
title='Data Depletions (Hilang/Mati)'
title='Deplesi'
className={{
wrapper: 'w-full mb-4 shadow',
title: 'mb-4',