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 { useMemo, useState, useEffect } from 'react';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import useSWR from 'swr';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import Button from '@/components/Button'; import Button from '@/components/Button';
import TextInput from '@/components/input/TextInput'; import TextInput from '@/components/input/TextInput';
import SelectInput, { OptionType } from '@/components/input/SelectInput';
import CheckboxInput from '@/components/input/CheckboxInput'; import CheckboxInput from '@/components/input/CheckboxInput';
import ConfirmationModal from '@/components/modal/ConfirmationModal'; import ConfirmationModal from '@/components/modal/ConfirmationModal';
import { FormHeader } from '@/components/helper/form/FormHeader'; import { FormHeader } from '@/components/helper/form/FormHeader';
@@ -21,6 +23,9 @@ import {
UpdateRecordingFormSchema, UpdateRecordingFormSchema,
} from './RecordingForm.schema'; } from './RecordingForm.schema';
import { useRecordingFormHandlers } from './useRecordingFormHandlers'; 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'; import Card from '@/components/Card';
@@ -34,6 +39,62 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
const [selectedStocks, setSelectedStocks] = useState<number[]>([]); const [selectedStocks, setSelectedStocks] = useState<number[]>([]);
const [selectedDepletions, setSelectedDepletions] = 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 { const {
deleteModal, deleteModal,
recordingFormErrorMessage, recordingFormErrorMessage,
@@ -114,9 +175,19 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
}); });
// EVENT HANDLERS - Select Inputs // EVENT HANDLERS - Select Inputs
const projectFlockChangeHandler = (value: string) => { const locationChangeHandler = (val: OptionType | OptionType[] | null) => {
const projectFlockId = parseInt(value) || 0; setSelectedLocation(val as OptionType);
formik.setFieldValue('project_flock_kandang_id', projectFlockId, false);
// 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 // 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'> <div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
<TextInput <SelectInput
required required
label='Project Flock Kandang ID' label='Lokasi'
type='number' value={selectedLocation}
name='project_flock_kandang_id' onChange={locationChangeHandler}
value={formik.values.project_flock_kandang_id?.toString() || ''} options={locationOptions}
onChange={(e) => { onInputChange={setLocationSearchValue}
const value = e.target.value; isLoading={isLoadingLocations}
formik.setFieldValue( isDisabled={type === 'detail'}
'project_flock_kandang_id', placeholder='Pilih Lokasi'
parseInt(value) || 0 isClearable
); isSearchable
projectFlockChangeHandler(value); />
}}
onBlur={formik.handleBlur} <SelectInput
required
label='Project Flock'
value={formik.values.project_flock_kandang ?? undefined}
onChange={projectFlockKandangChangeHandler}
options={projectFlockKandangOptions}
onInputChange={setProjectFlockSearchValue}
isLoading={isLoadingProjectFlocks}
isError={ isError={
formik.touched.project_flock_kandang_id && formik.touched.project_flock_kandang_id &&
Boolean(formik.errors.project_flock_kandang_id) Boolean(formik.errors.project_flock_kandang_id)
} }
errorMessage={formik.errors.project_flock_kandang_id as string} errorMessage={formik.errors.project_flock_kandang_id as string}
readOnly={type === 'detail'} isDisabled={type === 'detail' || !selectedLocation}
placeholder='Masukkan project flock kandang ID' placeholder={
selectedLocation
? 'Pilih Project Flock - Kandang'
: 'Pilih Lokasi terlebih dahulu'
}
isClearable
isSearchable
/> />
<TextInput <TextInput
@@ -369,21 +453,13 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
readOnly={type === 'detail'} readOnly={type === 'detail'}
placeholder='Masukkan status (0-3)' 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> </div>
</Card> </Card>
{/* Body Weights Table */} {/* Body Weights Table */}
<Card <Card
title='Data Bobot Badan' title='Bobot Badan'
className={{ className={{
wrapper: 'w-full mb-4 shadow', wrapper: 'w-full mb-4 shadow',
title: 'mb-4', title: 'mb-4',
@@ -583,7 +659,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
{/* Stocks Table */} {/* Stocks Table */}
<Card <Card
title='Data Stok Pakan' title='Stok Persediaan'
className={{ className={{
wrapper: 'w-full mb-4 shadow', wrapper: 'w-full mb-4 shadow',
title: 'mb-4', title: 'mb-4',
@@ -620,7 +696,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
</th> </th>
)} )}
<th> <th>
Product Warehouse ID Persediaan
<span <span
className='tooltip tooltip-error tooltip-bottom z-[9999]' className='tooltip tooltip-error tooltip-bottom z-[9999]'
data-tip='required' data-tip='required'
@@ -826,7 +902,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
{/* Depletions Table */} {/* Depletions Table */}
<Card <Card
title='Data Depletions (Hilang/Mati)' title='Deplesi'
className={{ className={{
wrapper: 'w-full mb-4 shadow', wrapper: 'w-full mb-4 shadow',
title: 'mb-4', title: 'mb-4',