Merge branch 'feat/select-input-lazy-loading' into 'development'

[FEAT/FE] SelectInput Lazy Loading Implementation

See merge request mbugroup/lti-web-client!179
This commit is contained in:
Rivaldi A N S
2026-01-15 02:56:09 +00:00
10 changed files with 184 additions and 142 deletions
+2 -2
View File
@@ -325,7 +325,7 @@ const SelectInput = <T extends OptionType>(props: SelectInputProps<T>) => {
}; };
const useSelect = <T,>( const useSelect = <T,>(
basePath: string, basePath: string | null,
valueKey: keyof T | string, valueKey: keyof T | string,
labelKey: keyof T | string, labelKey: keyof T | string,
searchKey: string = 'search', searchKey: string = 'search',
@@ -354,7 +354,7 @@ const useSelect = <T,>(
[limitKey]: String(limit), [limitKey]: String(limit),
}).toString(); }).toString();
return `${basePath}?${qs}`; return basePath ? `${basePath}?${qs}` : null;
}; };
const { const {
@@ -163,6 +163,7 @@ const ClosingsTable = () => {
setInputValue: setLocationInputValue, setInputValue: setLocationInputValue,
options: locationOptions, options: locationOptions,
isLoadingOptions: isLoadingLocationOptions, isLoadingOptions: isLoadingLocationOptions,
loadMore: loadMoreLocations,
} = useSelect<Location>(LocationApi.basePath, 'id', 'name'); } = useSelect<Location>(LocationApi.basePath, 'id', 'name');
const [selectedLocation, setSelectedLocation] = useState<OptionType | null>( const [selectedLocation, setSelectedLocation] = useState<OptionType | null>(
@@ -228,6 +229,7 @@ const ClosingsTable = () => {
value={selectedLocation} value={selectedLocation}
onChange={locationChangeHandler} onChange={locationChangeHandler}
onInputChange={setLocationInputValue} onInputChange={setLocationInputValue}
onMenuScrollToBottom={loadMoreLocations}
isClearable isClearable
className={{ className={{
wrapper: 'col-span-12 sm:col-span-6', wrapper: 'col-span-12 sm:col-span-6',
@@ -1,6 +1,6 @@
'use client'; 'use client';
import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { isResponseError } from '@/lib/api-helper';
import { InventoryAdjustmentApi } from '@/services/api/inventory'; import { InventoryAdjustmentApi } from '@/services/api/inventory';
import { import {
CreateInventoryAdjustmentPayload, CreateInventoryAdjustmentPayload,
@@ -22,12 +22,18 @@ import {
} from '@/services/api/master-data'; } from '@/services/api/master-data';
import Button from '@/components/Button'; import Button from '@/components/Button';
import { Icon } from '@iconify/react'; 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 TextInput from '@/components/input/TextInput';
import { RadioGroup } from '@/components/input/RadioInput'; import { RadioGroup } from '@/components/input/RadioInput';
import TextArea from '@/components/input/TextArea'; import TextArea from '@/components/input/TextArea';
import { useFormikErrorList } from '@/services/hooks/useFormikErrorList'; import { useFormikErrorList } from '@/services/hooks/useFormikErrorList';
import AlertErrorList from '@/components/helper/form/FormErrors'; 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 { interface InventoryAdjustmentFormProps {
type?: 'add' | 'edit' | 'detail'; type?: 'add' | 'edit' | 'detail';
@@ -44,10 +50,7 @@ const InventoryAdjustmentForm = ({
InventoryAdjustmentFormErrorMessage, InventoryAdjustmentFormErrorMessage,
setInventoryAdjustmentFormErrorMessage, setInventoryAdjustmentFormErrorMessage,
] = useState(''); ] = useState('');
const [selectedProductCategories, setSelectedProductCategories] =
useState('');
const [disabledProduct, setDisabledProduct] = useState(true); const [disabledProduct, setDisabledProduct] = useState(true);
const [optionsProduct, setOptionsProduct] = useState<OptionType[]>([]);
const [quantityLabel, setQuantityLabel] = useState('Tambah Stok'); const [quantityLabel, setQuantityLabel] = useState('Tambah Stok');
// Submit Handler // Submit Handler
@@ -108,45 +111,30 @@ const InventoryAdjustmentForm = ({
}); });
// Fetch Data // Fetch Data
const productCategoriesUrl = `${ const {
ProductCategoryApi.basePath setInputValue: setProductCategoryInputValue,
}?${new URLSearchParams({ options: productCategoryOptions,
search: '', isLoadingOptions: isLoadingProductCategoryOptions,
}).toString()}`; loadMore: loadMoreProductCategories,
const { data: productCategories, isLoading: isLoadingProductCategories } = } = useSelect<ProductCategory>(ProductCategoryApi.basePath, 'id', 'name');
useSWR(productCategoriesUrl, ProductCategoryApi.getAllFetcher);
const productUrl = `${ProductApi.basePath}?${new URLSearchParams({ const {
search: '', setInputValue: setProductInputValue,
product_category_id: selectedProductCategories, options: productOptions,
}).toString()}`; isLoadingOptions: isLoadingProductOptions,
const { data: products, isLoading: isLoadingProducts } = useSWR( loadMore: loadMoreProducts,
productUrl, } = useSelect<Product>(ProductApi.basePath, 'id', 'name', 'search', {
ProductApi.getAllFetcher product_category_id: formik.values.product_category_id
); ? String(formik.values.product_category_id)
: '',
});
const warehouseUrl = `${WarehouseApi.basePath}?${new URLSearchParams({ const {
search: '', setInputValue: setWarehouseInputValue,
limit: '100', options: warehouseOptions,
}).toString()}`; isLoadingOptions: isLoadingWarehouseOptions,
const { data: warehouses, isLoading: isLoadingWarehouses } = useSWR( loadMore: loadMoreWarehouses,
warehouseUrl, } = useSelect<Warehouse>(WarehouseApi.basePath, 'id', 'name');
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,
}))
: [];
// Options Handler // Options Handler
const productCategoryChangeHandler = ( const productCategoryChangeHandler = (
@@ -157,7 +145,6 @@ const InventoryAdjustmentForm = ({
formik.setFieldValue('product_category', val); formik.setFieldValue('product_category', val);
setSelectedProductCategories((val as OptionType)?.value as string);
const disabled = (val as OptionType)?.value == null; const disabled = (val as OptionType)?.value == null;
setDisabledProduct(disabled); setDisabledProduct(disabled);
formik.setFieldValue('product_id', 0); formik.setFieldValue('product_id', 0);
@@ -193,9 +180,6 @@ const InventoryAdjustmentForm = ({
// Effect // Effect
useEffect(() => { useEffect(() => {
if (initialValues?.product_warehouse?.product?.id) { if (initialValues?.product_warehouse?.product?.id) {
setSelectedProductCategories(
String(initialValues.product_warehouse.product.id)
);
setDisabledProduct(false); setDisabledProduct(false);
formik.setFieldValue( formik.setFieldValue(
'product_id', 'product_id',
@@ -219,25 +203,10 @@ const InventoryAdjustmentForm = ({
); );
formik.setFieldValue('note', initialValues.note); formik.setFieldValue('note', initialValues.note);
} }
}, [ }, [formik, initialValues, setQuantityLabel, setDisabledProduct]);
formik,
initialValues,
setQuantityLabel,
setDisabledProduct,
setSelectedProductCategories,
]);
useEffect(() => { useEffect(() => {
formikSetValues(formikInitialValues as InventoryAdjustmentFormValues); formikSetValues(formikInitialValues as InventoryAdjustmentFormValues);
}, [formikSetValues, formikInitialValues]); }, [formikSetValues, formikInitialValues]);
useEffect(() => {
if (isResponseSuccess(products)) {
const options = products.data.map((p) => ({
value: p.id,
label: p.name,
}));
setOptionsProduct(options);
}
}, [products]);
// Utils Function // Utils Function
const formatNumber = (value: string) => { const formatNumber = (value: string) => {
@@ -282,9 +251,10 @@ const InventoryAdjustmentForm = ({
label='Kategori Produk' label='Kategori Produk'
value={formik.values.product_category as OptionType} value={formik.values.product_category as OptionType}
onChange={productCategoryChangeHandler} onChange={productCategoryChangeHandler}
onInputChange={setSelectedProductCategories} onInputChange={setProductCategoryInputValue}
options={optionsProductCategory} options={productCategoryOptions}
isLoading={isLoadingProductCategories} onMenuScrollToBottom={loadMoreProductCategories}
isLoading={isLoadingProductCategoryOptions}
isError={ isError={
formik.touched.product_category && formik.touched.product_category &&
Boolean(formik.errors.product_category) Boolean(formik.errors.product_category)
@@ -300,8 +270,10 @@ const InventoryAdjustmentForm = ({
label='Produk' label='Produk'
value={formik.values.product as OptionType} value={formik.values.product as OptionType}
onChange={productChangeHandler} onChange={productChangeHandler}
options={optionsProduct} onInputChange={setProductInputValue}
isLoading={isLoadingProducts} options={productOptions}
onMenuScrollToBottom={loadMoreProducts}
isLoading={isLoadingProductOptions}
isError={formik.touched.product && Boolean(formik.errors.product)} isError={formik.touched.product && Boolean(formik.errors.product)}
errorMessage={formik.errors.product as string} errorMessage={formik.errors.product as string}
isDisabled={type === 'detail' || disabledProduct} isDisabled={type === 'detail' || disabledProduct}
@@ -314,8 +286,10 @@ const InventoryAdjustmentForm = ({
label='Warehouse' label='Warehouse'
value={formik.values.warehouse as OptionType} value={formik.values.warehouse as OptionType}
onChange={warehouseChangeHandler} onChange={warehouseChangeHandler}
options={optionsWarehouse} onInputChange={setWarehouseInputValue}
isLoading={isLoadingWarehouses} options={warehouseOptions}
onMenuScrollToBottom={loadMoreWarehouses}
isLoading={isLoadingWarehouseOptions}
isError={ isError={
formik.touched.warehouse && Boolean(formik.errors.warehouse) formik.touched.warehouse && Boolean(formik.errors.warehouse)
} }
@@ -38,6 +38,8 @@ import Card from '@/components/Card';
import { S3_PUBLIC_BASE_URL } from '@/config/constant'; import { S3_PUBLIC_BASE_URL } from '@/config/constant';
import { getUniqueFormikErrors } from '@/lib/formik-helper'; import { getUniqueFormikErrors } from '@/lib/formik-helper';
import AlertErrorList from '@/components/helper/form/FormErrors'; import AlertErrorList from '@/components/helper/form/FormErrors';
import { Warehouse } from '@/types/api/master-data/warehouse';
import { ProductWarehouse } from '@/types/api/inventory/product-warehouse';
interface MovementFormProps { interface MovementFormProps {
type?: 'add' | 'edit' | 'detail'; type?: 'add' | 'edit' | 'detail';
@@ -49,10 +51,6 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
// ===== STATE MANAGEMENT ===== // ===== STATE MANAGEMENT =====
const [movementFormErrorMessage, setMovementFormErrorMessage] = useState(''); const [movementFormErrorMessage, setMovementFormErrorMessage] = useState('');
const [
productWarehouseSelectInputValue,
setProductWarehouseSelectInputValue,
] = useState('');
const [selectedProducts, setSelectedProducts] = useState<number[]>([]); const [selectedProducts, setSelectedProducts] = useState<number[]>([]);
const [selectedDeliveries, setSelectedDeliveries] = useState<number[]>([]); const [selectedDeliveries, setSelectedDeliveries] = useState<number[]>([]);
const [formErrorList, setFormErrorList] = useState<string[]>([]); const [formErrorList, setFormErrorList] = useState<string[]>([]);
@@ -93,10 +91,11 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
// ===== USE SELECT HOOKS ===== // ===== USE SELECT HOOKS =====
const { const {
inputValue: warehouseSelectInputValue,
setInputValue: setWarehouseSelectInputValue, setInputValue: setWarehouseSelectInputValue,
isLoadingOptions: isLoadingWarehouses, isLoadingOptions: isLoadingWarehouses,
} = useSelect(WarehouseApi.basePath, 'id', 'name', 'search'); loadMore: loadMoreWarehouses,
rawData: warehouses,
} = useSelect<Warehouse>(WarehouseApi.basePath, 'id', 'name', 'search');
// ===== SELECT INPUT DATA ===== // ===== SELECT INPUT DATA =====
const { const {
@@ -107,12 +106,6 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
category: 'BOP', category: 'BOP',
}); });
const warehousesUrl = `${WarehouseApi.basePath}?${new URLSearchParams({ search: warehouseSelectInputValue }).toString()}`;
const { data: warehouses } = useSWR(
warehousesUrl,
WarehouseApi.getAllFetcher
);
// ===== DATA PROCESSING ===== // ===== DATA PROCESSING =====
const warehouseStockMap = useMemo(() => { const warehouseStockMap = useMemo(() => {
if (!isResponseSuccess(allProductWarehouses)) return new Map(); if (!isResponseSuccess(allProductWarehouses)) return new Map();
@@ -269,24 +262,21 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
}); });
// ===== PRODUCT WAREHOUSE FETCHING (after form initialization) ===== // ===== PRODUCT WAREHOUSE FETCHING (after form initialization) =====
const getProductWarehousesUrl = useCallback(() => { const {
const productWarehouseParams = new URLSearchParams({ setInputValue: setProductWarehouseSelectInputValue,
search: productWarehouseSelectInputValue, isLoadingOptions: isLoadingProductWarehouses,
}); loadMore: loadMoreProductWarehouses,
if (formik.values.source_warehouse_id) { rawData: productWarehouses,
productWarehouseParams.append( } = useSelect<ProductWarehouse>(
'warehouse_id', formik.values.source_warehouse_id ? ProductWarehouseApi.basePath : null,
formik.values.source_warehouse_id.toString() 'id',
); 'name',
'search',
{
warehouse_id: formik.values.source_warehouse_id
? formik.values.source_warehouse_id.toString()
: '',
} }
return `${ProductWarehouseApi.basePath}?${productWarehouseParams.toString()}`;
}, [formik.values.source_warehouse_id, productWarehouseSelectInputValue]);
const productWarehousesUrl = getProductWarehousesUrl();
const { data: productWarehouses, isLoading: isLoadingProductWarehouses } =
useSWR(
formik.values.source_warehouse_id ? productWarehousesUrl : null,
ProductWarehouseApi.getAllFetcher
); );
const productWarehouseOptions = isResponseSuccess(productWarehouses) const productWarehouseOptions = isResponseSuccess(productWarehouses)
@@ -1006,6 +996,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
}} }}
options={warehouseOptions} options={warehouseOptions}
onInputChange={setWarehouseSelectInputValue} onInputChange={setWarehouseSelectInputValue}
onMenuScrollToBottom={loadMoreWarehouses}
isLoading={isLoadingWarehouses} isLoading={isLoadingWarehouses}
isError={ isError={
formik.touched.source_warehouse_id && formik.touched.source_warehouse_id &&
@@ -1104,6 +1095,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
options={warehouseOptions} options={warehouseOptions}
onInputChange={setWarehouseSelectInputValue} onInputChange={setWarehouseSelectInputValue}
isLoading={isLoadingWarehouses} isLoading={isLoadingWarehouses}
onMenuScrollToBottom={loadMoreWarehouses}
isError={ isError={
formik.touched.destination_warehouse_id && formik.touched.destination_warehouse_id &&
Boolean(formik.errors.destination_warehouse_id) Boolean(formik.errors.destination_warehouse_id)
@@ -1263,6 +1255,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
}} }}
options={productWarehouseOptions} options={productWarehouseOptions}
onInputChange={setProductWarehouseSelectInputValue} onInputChange={setProductWarehouseSelectInputValue}
onMenuScrollToBottom={loadMoreProductWarehouses}
isLoading={isLoadingProductWarehouses} isLoading={isLoadingProductWarehouses}
isDisabled={ isDisabled={
type === 'detail' || type === 'detail' ||
@@ -87,6 +87,7 @@ const DailyMarketingReportContent = () => {
setInputValue: setAreaInputValue, setInputValue: setAreaInputValue,
options: areaOptions, options: areaOptions,
isLoadingOptions: isLoadingAreaOptions, isLoadingOptions: isLoadingAreaOptions,
loadMore: loadMoreAreas,
} = useSelect<Area>(AreaApi.basePath, 'id', 'name'); } = useSelect<Area>(AreaApi.basePath, 'id', 'name');
const areaChangeHandler = (val: OptionType | OptionType[] | null) => { const areaChangeHandler = (val: OptionType | OptionType[] | null) => {
@@ -101,6 +102,7 @@ const DailyMarketingReportContent = () => {
setInputValue: setLocationInputValue, setInputValue: setLocationInputValue,
options: locationOptions, options: locationOptions,
isLoadingOptions: isLoadingLocationOptions, isLoadingOptions: isLoadingLocationOptions,
loadMore: loadMoreLocations,
} = useSelect<Location>(LocationApi.basePath, 'id', 'name'); } = useSelect<Location>(LocationApi.basePath, 'id', 'name');
const locationChangeHandler = (val: OptionType | OptionType[] | null) => { const locationChangeHandler = (val: OptionType | OptionType[] | null) => {
@@ -118,6 +120,7 @@ const DailyMarketingReportContent = () => {
setInputValue: setWarehouseInputValue, setInputValue: setWarehouseInputValue,
options: warehouseOptions, options: warehouseOptions,
isLoadingOptions: isLoadingWarehouseOptions, isLoadingOptions: isLoadingWarehouseOptions,
loadMore: loadMoreWarehouses,
} = useSelect<Warehouse>(WarehouseApi.basePath, 'id', 'name'); } = useSelect<Warehouse>(WarehouseApi.basePath, 'id', 'name');
const warehouseChangeHandler = (val: OptionType | OptionType[] | null) => { const warehouseChangeHandler = (val: OptionType | OptionType[] | null) => {
@@ -135,6 +138,7 @@ const DailyMarketingReportContent = () => {
setInputValue: setCustomerInputValue, setInputValue: setCustomerInputValue,
options: customerOptions, options: customerOptions,
isLoadingOptions: isLoadingCustomerOptions, isLoadingOptions: isLoadingCustomerOptions,
loadMore: loadMoreCustomers,
} = useSelect<Customer>(CustomerApi.basePath, 'id', 'name'); } = useSelect<Customer>(CustomerApi.basePath, 'id', 'name');
const customerChangeHandler = (val: OptionType | OptionType[] | null) => { const customerChangeHandler = (val: OptionType | OptionType[] | null) => {
@@ -298,6 +302,7 @@ const DailyMarketingReportContent = () => {
value={selectedArea} value={selectedArea}
onChange={areaChangeHandler} onChange={areaChangeHandler}
onInputChange={setAreaInputValue} onInputChange={setAreaInputValue}
onMenuScrollToBottom={loadMoreAreas}
isClearable isClearable
className={{ className={{
wrapper: 'col-span-12 sm:col-span-6 lg:col-span-4', wrapper: 'col-span-12 sm:col-span-6 lg:col-span-4',
@@ -312,6 +317,7 @@ const DailyMarketingReportContent = () => {
value={selectedLocation} value={selectedLocation}
onChange={locationChangeHandler} onChange={locationChangeHandler}
onInputChange={setLocationInputValue} onInputChange={setLocationInputValue}
onMenuScrollToBottom={loadMoreLocations}
isClearable isClearable
className={{ className={{
wrapper: 'col-span-12 sm:col-span-6 lg:col-span-4', wrapper: 'col-span-12 sm:col-span-6 lg:col-span-4',
@@ -326,6 +332,7 @@ const DailyMarketingReportContent = () => {
value={selectedWarehouse} value={selectedWarehouse}
onChange={warehouseChangeHandler} onChange={warehouseChangeHandler}
onInputChange={setWarehouseInputValue} onInputChange={setWarehouseInputValue}
onMenuScrollToBottom={loadMoreWarehouses}
isClearable isClearable
className={{ className={{
wrapper: 'col-span-12 sm:col-span-6 lg:col-span-4', wrapper: 'col-span-12 sm:col-span-6 lg:col-span-4',
@@ -340,6 +347,7 @@ const DailyMarketingReportContent = () => {
value={selectedCustomer} value={selectedCustomer}
onChange={customerChangeHandler} onChange={customerChangeHandler}
onInputChange={setCustomerInputValue} onInputChange={setCustomerInputValue}
onMenuScrollToBottom={loadMoreCustomers}
isClearable isClearable
className={{ className={{
wrapper: 'col-span-12 sm:col-span-6 lg:col-span-4', wrapper: 'col-span-12 sm:col-span-6 lg:col-span-4',
@@ -26,6 +26,15 @@ import MenuItem from '@/components/menu/MenuItem';
import * as XLSX from 'xlsx'; import * as XLSX from 'xlsx';
import { generateReportExpensePDF } from './pdf/ReportExpenseExport'; import { generateReportExpensePDF } from './pdf/ReportExpenseExport';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import {
KandangApi,
LocationApi,
NonstockApi,
SupplierApi,
} from '@/services/api/master-data';
import { Supplier } from '@/types/api/master-data/supplier';
import { Kandang } from '@/types/api/master-data/kandang';
import { Nonstock } from '@/types/api/master-data/nonstock';
const ReportExpenseTable = () => { const ReportExpenseTable = () => {
// ===== STATE MANAGEMENT ===== // ===== STATE MANAGEMENT =====
@@ -64,16 +73,33 @@ const ReportExpenseTable = () => {
}); });
// ===== SELECT OPTIONS ===== // ===== SELECT OPTIONS =====
const { options: optionsLocation, isLoadingOptions: isLoadingLocation } = const {
useSelect(`/master-data/locations`, 'id', 'name'); setInputValue: setLocationInputValue,
const { options: optionsSupplier, isLoadingOptions: isLoadingSupplier } = options: locationOptions,
useSelect(`/master-data/suppliers`, 'id', 'name'); isLoadingOptions: isLoadingLocationOptions,
const { options: optionsKandang, isLoadingOptions: isLoadingKandang } = loadMore: loadMoreLocations,
useSelect(`/master-data/kandangs`, 'id', 'name', '', { } = useSelect<Location>(LocationApi.basePath, 'id', 'name');
location_id: filterState.location_id,
}); const {
const { options: optionsNonstock, isLoadingOptions: isLoadingNonstock } = setInputValue: setSupplierInputValue,
useSelect(`/master-data/nonstocks`, 'id', 'name'); options: supplierOptions,
isLoadingOptions: isLoadingSupplierOptions,
loadMore: loadMoreSuppliers,
} = useSelect<Supplier>(SupplierApi.basePath, 'id', 'name');
const {
setInputValue: setKandangInputValue,
options: kandangOptions,
isLoadingOptions: isLoadingKandangOptions,
loadMore: loadMoreKandangs,
} = useSelect<Kandang>(KandangApi.basePath, 'id', 'name');
const {
setInputValue: setNonstockInputValue,
options: nonstockOptions,
isLoadingOptions: isLoadingNonstockOptions,
loadMore: loadMoreNonstocks,
} = useSelect<Nonstock>(NonstockApi.basePath, 'id', 'name');
const categoryOptions = useMemo( const categoryOptions = useMemo(
() => [ () => [
@@ -86,31 +112,31 @@ const ReportExpenseTable = () => {
// Mendapatkan value option select dari filter state // Mendapatkan value option select dari filter state
const selectedLocation = useMemo( const selectedLocation = useMemo(
() => () =>
optionsLocation.find( locationOptions.find(
(opt) => String(opt.value) === filterState.location_id (opt) => String(opt.value) === filterState.location_id
) || null, ) || null,
[optionsLocation, filterState.location_id] [locationOptions, filterState.location_id]
); );
const selectedSupplier = useMemo( const selectedSupplier = useMemo(
() => () =>
optionsSupplier.find( supplierOptions.find(
(opt) => String(opt.value) === filterState.supplier_id (opt) => String(opt.value) === filterState.supplier_id
) || null, ) || null,
[optionsSupplier, filterState.supplier_id] [supplierOptions, filterState.supplier_id]
); );
const selectedKandang = useMemo( const selectedKandang = useMemo(
() => () =>
optionsKandang.find( kandangOptions.find(
(opt) => String(opt.value) === filterState.kandang_id (opt) => String(opt.value) === filterState.kandang_id
) || null, ) || null,
[optionsKandang, filterState.kandang_id] [kandangOptions, filterState.kandang_id]
); );
const selectedNonstock = useMemo( const selectedNonstock = useMemo(
() => () =>
optionsNonstock.find( nonstockOptions.find(
(opt) => String(opt.value) === filterState.nonstock_id (opt) => String(opt.value) === filterState.nonstock_id
) || null, ) || null,
[optionsNonstock, filterState.nonstock_id] [nonstockOptions, filterState.nonstock_id]
); );
const selectedCategory = useMemo( const selectedCategory = useMemo(
() => () =>
@@ -756,38 +782,46 @@ const ReportExpenseTable = () => {
<SelectInput <SelectInput
isClearable isClearable
label='Lokasi' label='Lokasi'
options={optionsLocation} options={locationOptions}
isLoading={isLoadingLocation} isLoading={isLoadingLocationOptions}
placeholder='Lokasi' placeholder='Lokasi'
value={selectedLocation} value={selectedLocation}
onChange={locationChangeHandler} onChange={locationChangeHandler}
onInputChange={setLocationInputValue}
onMenuScrollToBottom={loadMoreLocations}
/> />
<SelectInput <SelectInput
isClearable isClearable
label='Kandang' label='Kandang'
options={optionsKandang} options={kandangOptions}
isLoading={isLoadingKandang} isLoading={isLoadingKandangOptions}
placeholder='Kandang' placeholder='Kandang'
value={selectedKandang} value={selectedKandang}
onChange={kandangChangeHandler} onChange={kandangChangeHandler}
onInputChange={setKandangInputValue}
onMenuScrollToBottom={loadMoreKandangs}
/> />
<SelectInput <SelectInput
isClearable isClearable
label='Supplier' label='Supplier'
options={optionsSupplier} options={supplierOptions}
isLoading={isLoadingSupplier} isLoading={isLoadingSupplierOptions}
placeholder='Supplier' placeholder='Supplier'
value={selectedSupplier} value={selectedSupplier}
onChange={supplierChangeHandler} onChange={supplierChangeHandler}
onInputChange={setSupplierInputValue}
onMenuScrollToBottom={loadMoreSuppliers}
/> />
<SelectInput <SelectInput
isClearable isClearable
label='Produk' label='Produk'
options={optionsNonstock} options={nonstockOptions}
isLoading={isLoadingNonstock} isLoading={isLoadingNonstockOptions}
placeholder='Produk' placeholder='Produk'
value={selectedNonstock} value={selectedNonstock}
onChange={nonstockChangeHandler} onChange={nonstockChangeHandler}
onInputChange={setNonstockInputValue}
onMenuScrollToBottom={loadMoreNonstocks}
/> />
<SelectInput <SelectInput
isClearable isClearable
@@ -55,6 +55,7 @@ const CustomerPaymentTab = () => {
const { const {
options: customerOptions, options: customerOptions,
setInputValue: setCustomerInputValue,
isLoadingOptions: isLoadingCustomers, isLoadingOptions: isLoadingCustomers,
loadMore: loadMoreCustomers, loadMore: loadMoreCustomers,
hasMore: hasMoreCustomers, hasMore: hasMoreCustomers,
@@ -62,6 +63,7 @@ const CustomerPaymentTab = () => {
const { const {
options: salesOptions, options: salesOptions,
setInputValue: setSalesInputValue,
isLoadingOptions: isLoadingSales, isLoadingOptions: isLoadingSales,
loadMore: loadMoreSales, loadMore: loadMoreSales,
hasMore: hasMoreSales, hasMore: hasMoreSales,
@@ -654,6 +656,7 @@ const CustomerPaymentTab = () => {
Array.isArray(val) ? val : val ? [val] : [] Array.isArray(val) ? val : val ? [val] : []
); );
}} }}
onInputChange={setCustomerInputValue}
isLoading={isLoadingCustomers} isLoading={isLoadingCustomers}
isClearable isClearable
onMenuScrollToBottom={loadMoreCustomers} onMenuScrollToBottom={loadMoreCustomers}
@@ -670,6 +673,7 @@ const CustomerPaymentTab = () => {
onChange={(val) => { onChange={(val) => {
setFilterSales(Array.isArray(val) ? val : val ? [val] : []); setFilterSales(Array.isArray(val) ? val : val ? [val] : []);
}} }}
onInputChange={setSalesInputValue}
isLoading={isLoadingSales} isLoading={isLoadingSales}
isClearable isClearable
onMenuScrollToBottom={loadMoreSales} onMenuScrollToBottom={loadMoreSales}
@@ -33,6 +33,7 @@ import {
} from '@/components/pages/report/finance/filter/DebtSupplierFilter'; } from '@/components/pages/report/finance/filter/DebtSupplierFilter';
import { getFilledFormikValuesCount } from '@/lib/formik-helper'; import { getFilledFormikValuesCount } from '@/lib/formik-helper';
import ButtonFilter from '@/components/helper/ButtonFilter'; import ButtonFilter from '@/components/helper/ButtonFilter';
import { Supplier } from '@/types/api/master-data/supplier';
const DebtSupplierTab = () => { const DebtSupplierTab = () => {
// ===== STATE MANAGEMENT ===== // ===== STATE MANAGEMENT =====
@@ -51,10 +52,12 @@ const DebtSupplierTab = () => {
const filterModal = useModal(); const filterModal = useModal();
const { options: supplierOptions, isLoadingOptions: isLoadingSuppliers } = const {
useSelect(SupplierApi.basePath, 'id', 'name', '', { setInputValue: setSupplierInputValue,
limit: 'limit', options: supplierOptions,
}); isLoadingOptions: isLoadingSupplierOptions,
loadMore: loadMoreSuppliers,
} = useSelect<Supplier>(SupplierApi.basePath, 'id', 'name');
const dataTypeOptions = useMemo( const dataTypeOptions = useMemo(
() => [ () => [
@@ -610,7 +613,9 @@ const DebtSupplierTab = () => {
Array.isArray(val) ? val : val ? [val] : null Array.isArray(val) ? val : val ? [val] : null
); );
}} }}
isLoading={isLoadingSuppliers} onInputChange={setSupplierInputValue}
onMenuScrollToBottom={loadMoreSuppliers}
isLoading={isLoadingSupplierOptions}
isClearable isClearable
className={{ wrapper: 'w-full' }} className={{ wrapper: 'w-full' }}
isError={ isError={
@@ -62,6 +62,7 @@ const ProductionResultContent = () => {
setInputValue: setAreaInputValue, setInputValue: setAreaInputValue,
options: areaOptions, options: areaOptions,
isLoadingOptions: isLoadingAreaOptions, isLoadingOptions: isLoadingAreaOptions,
loadMore: loadMoreAreas,
} = useSelect<BaseKandang>(AreaApi.basePath, 'id', 'name'); } = useSelect<BaseKandang>(AreaApi.basePath, 'id', 'name');
const areaChangeHandler = (val: OptionType | OptionType[] | null) => { const areaChangeHandler = (val: OptionType | OptionType[] | null) => {
@@ -78,6 +79,7 @@ const ProductionResultContent = () => {
setInputValue: setLocationInputValue, setInputValue: setLocationInputValue,
options: locationOptions, options: locationOptions,
isLoadingOptions: isLoadingLocationOptions, isLoadingOptions: isLoadingLocationOptions,
loadMore: loadMoreLocations,
} = useSelect<BaseKandang>(LocationApi.basePath, 'id', 'name', 'search', { } = useSelect<BaseKandang>(LocationApi.basePath, 'id', 'name', 'search', {
area_id: selectedArea ? ((selectedArea as OptionType).value as string) : '', area_id: selectedArea ? ((selectedArea as OptionType).value as string) : '',
}); });
@@ -94,6 +96,7 @@ const ProductionResultContent = () => {
setInputValue: setProjectFlockInputValue, setInputValue: setProjectFlockInputValue,
options: projectFlockOptions, options: projectFlockOptions,
isLoadingOptions: isLoadingProjectFlockOptions, isLoadingOptions: isLoadingProjectFlockOptions,
loadMore: loadMoreProjectFlocks,
} = useSelect<BaseKandang>( } = useSelect<BaseKandang>(
ProjectFlockApi.basePath, ProjectFlockApi.basePath,
'id', 'id',
@@ -120,6 +123,7 @@ const ProductionResultContent = () => {
setInputValue: setProjectFlockKandangInputValue, setInputValue: setProjectFlockKandangInputValue,
options: projectFlockKandangOptions, options: projectFlockKandangOptions,
isLoadingOptions: isLoadingProjectFlockKandangOptions, isLoadingOptions: isLoadingProjectFlockKandangOptions,
loadMore: loadMoreProjectFlockKandangs,
} = useSelect<BaseKandang>( } = useSelect<BaseKandang>(
ProjectFlockKandangApi.basePath, ProjectFlockKandangApi.basePath,
'id', 'id',
@@ -235,6 +239,7 @@ const ProductionResultContent = () => {
value={selectedArea} value={selectedArea}
onChange={areaChangeHandler} onChange={areaChangeHandler}
onInputChange={setAreaInputValue} onInputChange={setAreaInputValue}
onMenuScrollToBottom={loadMoreAreas}
isClearable isClearable
className={{ className={{
wrapper: 'col-span-12 sm:col-span-6 lg:col-span-4', wrapper: 'col-span-12 sm:col-span-6 lg:col-span-4',
@@ -251,6 +256,7 @@ const ProductionResultContent = () => {
value={selectedLocation} value={selectedLocation}
onChange={locationChangeHandler} onChange={locationChangeHandler}
onInputChange={setLocationInputValue} onInputChange={setLocationInputValue}
onMenuScrollToBottom={loadMoreLocations}
isClearable isClearable
isDisabled={!selectedArea} isDisabled={!selectedArea}
className={{ className={{
@@ -270,6 +276,7 @@ const ProductionResultContent = () => {
value={selectedProjectFlock} value={selectedProjectFlock}
onChange={projectFlockChangeHandler} onChange={projectFlockChangeHandler}
onInputChange={setProjectFlockInputValue} onInputChange={setProjectFlockInputValue}
onMenuScrollToBottom={loadMoreProjectFlocks}
isClearable isClearable
isDisabled={!selectedArea || !selectedLocation} isDisabled={!selectedArea || !selectedLocation}
className={{ className={{
@@ -289,6 +296,7 @@ const ProductionResultContent = () => {
value={selectedProjectFlockKandang} value={selectedProjectFlockKandang}
onChange={projectFlockKandangChangeHandler} onChange={projectFlockKandangChangeHandler}
onInputChange={setProjectFlockKandangInputValue} onInputChange={setProjectFlockKandangInputValue}
onMenuScrollToBottom={loadMoreProjectFlockKandangs}
isClearable isClearable
isDisabled={!selectedProjectFlock} isDisabled={!selectedProjectFlock}
className={{ className={{
@@ -58,18 +58,26 @@ const HppPerKandangTab = () => {
}, },
}); });
const { options: areaOptions, isLoadingOptions: isLoadingAreas } = useSelect( const {
AreaApi.basePath, setInputValue: setAreaInputValue,
'id', options: areaOptions,
'name', isLoadingOptions: isLoadingAreas,
'search' loadMore: loadMoreAreas,
); } = useSelect(AreaApi.basePath, 'id', 'name', 'search');
const { options: locationOptions, isLoadingOptions: isLoadingLocations } = const {
useSelect(LocationApi.basePath, 'id', 'name', 'search'); setInputValue: setLocationInputValue,
options: locationOptions,
isLoadingOptions: isLoadingLocations,
loadMore: loadMoreLocations,
} = useSelect(LocationApi.basePath, 'id', 'name', 'search');
const { options: kandangOptions, isLoadingOptions: isLoadingKandangs } = const {
useSelect(KandangApi.basePath, 'id', 'name', 'search'); setInputValue: setKandangInputValue,
options: kandangOptions,
isLoadingOptions: isLoadingKandangs,
loadMore: loadMoreKandangs,
} = useSelect(KandangApi.basePath, 'id', 'name', 'search');
const showUnrecordedOptions: OptionType[] = [ const showUnrecordedOptions: OptionType[] = [
{ value: 'false', label: 'Sembunyikan' }, { value: 'false', label: 'Sembunyikan' },
@@ -810,6 +818,8 @@ const HppPerKandangTab = () => {
.includes(String(opt.value)) .includes(String(opt.value))
)} )}
onChange={areaChangeHandler} onChange={areaChangeHandler}
onInputChange={setAreaInputValue}
onMenuScrollToBottom={loadMoreAreas}
isLoading={isLoadingAreas} isLoading={isLoadingAreas}
isClearable isClearable
/> />
@@ -824,6 +834,8 @@ const HppPerKandangTab = () => {
.includes(String(opt.value)) .includes(String(opt.value))
)} )}
onChange={locationChangeHandler} onChange={locationChangeHandler}
onInputChange={setLocationInputValue}
onMenuScrollToBottom={loadMoreLocations}
isLoading={isLoadingLocations} isLoading={isLoadingLocations}
isClearable isClearable
/> />
@@ -838,6 +850,8 @@ const HppPerKandangTab = () => {
.includes(String(opt.value)) .includes(String(opt.value))
)} )}
onChange={kandangChangeHandler} onChange={kandangChangeHandler}
onInputChange={setKandangInputValue}
onMenuScrollToBottom={loadMoreKandangs}
isLoading={isLoadingKandangs} isLoading={isLoadingKandangs}
isClearable isClearable
/> />