mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
refactor(FE): Refactor selects to use useSelect hook
This commit is contained in:
@@ -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<number[]>([]);
|
||||
const [selectedEggs, setSelectedEggs] = useState<number[]>([]);
|
||||
|
||||
const [locationSearchValue, setLocationSearchValue] = useState('');
|
||||
const [selectedLocation, setSelectedLocation] = useState<OptionType | null>(
|
||||
null
|
||||
);
|
||||
const [projectFlockSearchValue, setProjectFlockSearchValue] = useState('');
|
||||
const [selectedProjectFlock, setSelectedProjectFlock] =
|
||||
useState<OptionType | null>(null);
|
||||
const [selectedKandang, setSelectedKandang] = useState<OptionType | null>(
|
||||
null
|
||||
);
|
||||
const [selectedProjectFlockLocationId, setSelectedProjectFlockLocationId] =
|
||||
useState<string>('');
|
||||
const [stockProductsLocationId, setStockProductsLocationId] =
|
||||
useState<string>('');
|
||||
const [stockProductsKandangId, setStockProductsKandangId] =
|
||||
useState<string>('');
|
||||
const [depletionProductsLocationId, setDepletionProductsLocationId] =
|
||||
useState<string>('');
|
||||
const [depletionProductsKandangId, setDepletionProductsKandangId] =
|
||||
useState<string>('');
|
||||
const [eggProductsLocationId, setEggProductsLocationId] =
|
||||
useState<string>('');
|
||||
const [eggProductsKandangId, setEggProductsKandangId] = useState<string>('');
|
||||
|
||||
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
|
||||
/>
|
||||
|
||||
<SelectInput
|
||||
key={`project-flock-select-${selectedProjectFlock?.value || 'default'}-${projectFlockOptions.length}`}
|
||||
key={`project-flock-select-${selectedProjectFlock?.value || 'default'}-${enhancedProjectFlockOptions.length}`}
|
||||
required
|
||||
label='Project Flock'
|
||||
value={selectedProjectFlock}
|
||||
onChange={projectFlockChangeHandler}
|
||||
options={projectFlockOptions}
|
||||
options={enhancedProjectFlockOptions}
|
||||
onInputChange={setProjectFlockSearchValue}
|
||||
isLoading={isLoadingProjectFlocks}
|
||||
onMenuScrollToBottom={loadMoreProjectFlocks}
|
||||
isDisabled={!selectedLocation}
|
||||
placeholder={
|
||||
selectedLocation
|
||||
|
||||
Reference in New Issue
Block a user