feat: implement lazy loading in SelectInput

This commit is contained in:
ValdiANS
2026-01-15 09:48:02 +07:00
parent 3827204f13
commit c75563491f
9 changed files with 182 additions and 140 deletions
@@ -1,6 +1,6 @@
'use client';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
import { isResponseError } from '@/lib/api-helper';
import { InventoryAdjustmentApi } from '@/services/api/inventory';
import {
CreateInventoryAdjustmentPayload,
@@ -22,12 +22,18 @@ import {
} from '@/services/api/master-data';
import Button from '@/components/Button';
import { Icon } from '@iconify/react';
import SelectInput, { OptionType } from '@/components/input/SelectInput';
import SelectInput, {
OptionType,
useSelect,
} from '@/components/input/SelectInput';
import TextInput from '@/components/input/TextInput';
import { RadioGroup } from '@/components/input/RadioInput';
import TextArea from '@/components/input/TextArea';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
import AlertErrorList from '@/components/helper/form/FormErrors';
import { ProductCategory } from '@/types/api/master-data/product-category';
import { Product } from '@/types/api/master-data/product';
import { Warehouse } from '@/types/api/master-data/warehouse';
interface InventoryAdjustmentFormProps {
type?: 'add' | 'edit' | 'detail';
@@ -44,10 +50,7 @@ const InventoryAdjustmentForm = ({
InventoryAdjustmentFormErrorMessage,
setInventoryAdjustmentFormErrorMessage,
] = useState('');
const [selectedProductCategories, setSelectedProductCategories] =
useState('');
const [disabledProduct, setDisabledProduct] = useState(true);
const [optionsProduct, setOptionsProduct] = useState<OptionType[]>([]);
const [quantityLabel, setQuantityLabel] = useState('Tambah Stok');
// Submit Handler
@@ -108,45 +111,30 @@ const InventoryAdjustmentForm = ({
});
// Fetch Data
const productCategoriesUrl = `${
ProductCategoryApi.basePath
}?${new URLSearchParams({
search: '',
}).toString()}`;
const { data: productCategories, isLoading: isLoadingProductCategories } =
useSWR(productCategoriesUrl, ProductCategoryApi.getAllFetcher);
const {
setInputValue: setProductCategoryInputValue,
options: productCategoryOptions,
isLoadingOptions: isLoadingProductCategoryOptions,
loadMore: loadMoreProductCategories,
} = useSelect<ProductCategory>(ProductCategoryApi.basePath, 'id', 'name');
const productUrl = `${ProductApi.basePath}?${new URLSearchParams({
search: '',
product_category_id: selectedProductCategories,
}).toString()}`;
const { data: products, isLoading: isLoadingProducts } = useSWR(
productUrl,
ProductApi.getAllFetcher
);
const {
setInputValue: setProductInputValue,
options: productOptions,
isLoadingOptions: isLoadingProductOptions,
loadMore: loadMoreProducts,
} = useSelect<Product>(ProductApi.basePath, 'id', 'name', 'search', {
product_category_id: formik.values.product_category_id
? String(formik.values.product_category_id)
: '',
});
const warehouseUrl = `${WarehouseApi.basePath}?${new URLSearchParams({
search: '',
limit: '100',
}).toString()}`;
const { data: warehouses, isLoading: isLoadingWarehouses } = useSWR(
warehouseUrl,
WarehouseApi.getAllFetcher
);
// Map Data to Options
const optionsProductCategory = isResponseSuccess(productCategories)
? productCategories?.data.map((productCategory) => ({
value: productCategory.id,
label: productCategory.name,
}))
: [];
const optionsWarehouse = isResponseSuccess(warehouses)
? warehouses?.data.map((warehouse) => ({
value: warehouse.id,
label: warehouse.name,
}))
: [];
const {
setInputValue: setWarehouseInputValue,
options: warehouseOptions,
isLoadingOptions: isLoadingWarehouseOptions,
loadMore: loadMoreWarehouses,
} = useSelect<Warehouse>(WarehouseApi.basePath, 'id', 'name');
// Options Handler
const productCategoryChangeHandler = (
@@ -157,7 +145,6 @@ const InventoryAdjustmentForm = ({
formik.setFieldValue('product_category', val);
setSelectedProductCategories((val as OptionType)?.value as string);
const disabled = (val as OptionType)?.value == null;
setDisabledProduct(disabled);
formik.setFieldValue('product_id', 0);
@@ -193,9 +180,6 @@ const InventoryAdjustmentForm = ({
// Effect
useEffect(() => {
if (initialValues?.product_warehouse?.product?.id) {
setSelectedProductCategories(
String(initialValues.product_warehouse.product.id)
);
setDisabledProduct(false);
formik.setFieldValue(
'product_id',
@@ -219,25 +203,10 @@ const InventoryAdjustmentForm = ({
);
formik.setFieldValue('note', initialValues.note);
}
}, [
formik,
initialValues,
setQuantityLabel,
setDisabledProduct,
setSelectedProductCategories,
]);
}, [formik, initialValues, setQuantityLabel, setDisabledProduct]);
useEffect(() => {
formikSetValues(formikInitialValues as InventoryAdjustmentFormValues);
}, [formikSetValues, formikInitialValues]);
useEffect(() => {
if (isResponseSuccess(products)) {
const options = products.data.map((p) => ({
value: p.id,
label: p.name,
}));
setOptionsProduct(options);
}
}, [products]);
// Utils Function
const formatNumber = (value: string) => {
@@ -282,9 +251,10 @@ const InventoryAdjustmentForm = ({
label='Kategori Produk'
value={formik.values.product_category as OptionType}
onChange={productCategoryChangeHandler}
onInputChange={setSelectedProductCategories}
options={optionsProductCategory}
isLoading={isLoadingProductCategories}
onInputChange={setProductCategoryInputValue}
options={productCategoryOptions}
onMenuScrollToBottom={loadMoreProductCategories}
isLoading={isLoadingProductCategoryOptions}
isError={
formik.touched.product_category &&
Boolean(formik.errors.product_category)
@@ -300,8 +270,10 @@ const InventoryAdjustmentForm = ({
label='Produk'
value={formik.values.product as OptionType}
onChange={productChangeHandler}
options={optionsProduct}
isLoading={isLoadingProducts}
onInputChange={setProductInputValue}
options={productOptions}
onMenuScrollToBottom={loadMoreProducts}
isLoading={isLoadingProductOptions}
isError={formik.touched.product && Boolean(formik.errors.product)}
errorMessage={formik.errors.product as string}
isDisabled={type === 'detail' || disabledProduct}
@@ -314,8 +286,10 @@ const InventoryAdjustmentForm = ({
label='Warehouse'
value={formik.values.warehouse as OptionType}
onChange={warehouseChangeHandler}
options={optionsWarehouse}
isLoading={isLoadingWarehouses}
onInputChange={setWarehouseInputValue}
options={warehouseOptions}
onMenuScrollToBottom={loadMoreWarehouses}
isLoading={isLoadingWarehouseOptions}
isError={
formik.touched.warehouse && Boolean(formik.errors.warehouse)
}