refactor: optimize BalanceMonitoringTab with useTableFilter persistence pattern

Replace single-select customerFilter/salesFilter with OptionType[] multi-select
(customers, salesPersons, filterBy), switch SWR to httpClientFetcher with explicit
type, remove PDF export, enableReinitialize, useRef modal hack, useMemo on data/meta,
and useCallback on trivial handlers. Add formikResetHandler using resetFilter().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ValdiANS
2026-05-20 16:10:01 +07:00
parent 7437e2e584
commit c98a51326f
@@ -1,21 +1,22 @@
'use client'; 'use client';
import { useState, useMemo, useCallback, useEffect, useRef } from 'react'; import { useState, useMemo, useEffect } from 'react';
import useSWR from 'swr'; import useSWR from 'swr';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import { ColumnDef, SortingState, Updater } from '@tanstack/react-table'; import { ColumnDef } from '@tanstack/react-table';
import { AxiosError } from 'axios';
import { FinanceApi } from '@/services/api/report/finance-report'; import { FinanceApi } from '@/services/api/report/finance-report';
import { CustomerApi } from '@/services/api/master-data'; import { CustomerApi } from '@/services/api/master-data';
import { UserApi } from '@/services/api/user'; import { UserApi } from '@/services/api/user';
import SelectInput, { import { useSelect, OptionType } from '@/components/input/SelectInput';
useSelect, import { httpClientFetcher, SWRHttpKey } from '@/services/http/client';
OptionType, import { BaseApiResponse } from '@/types/api/api-general';
} from '@/components/input/SelectInput'; import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
import SelectInputRadio from '@/components/input/SelectInputRadio';
import { useTableFilter } from '@/services/hooks/useTableFilter'; import { useTableFilter } from '@/services/hooks/useTableFilter';
import { useTabActionsStore } from '@/stores/tab-actions/tab-actions.store'; import { useTabActionsStore } from '@/stores/tab-actions/tab-actions.store';
import Dropdown from '@/components/Dropdown';
import ButtonFilter from '@/components/helper/ButtonFilter'; import ButtonFilter from '@/components/helper/ButtonFilter';
import { formatCurrency, formatNumber } from '@/lib/helper'; import { formatCurrency, formatNumber } from '@/lib/helper';
import { isResponseSuccess } from '@/lib/api-helper'; import { isResponseSuccess } from '@/lib/api-helper';
@@ -24,7 +25,6 @@ import { CustomerPaymentRow } from '@/types/api/report/customer-payment';
import Modal, { useModal } from '@/components/Modal'; import Modal, { useModal } from '@/components/Modal';
import Button from '@/components/Button'; import Button from '@/components/Button';
import DateInput from '@/components/input/DateInput'; import DateInput from '@/components/input/DateInput';
import Pagination from '@/components/Pagination';
import Table from '@/components/Table'; import Table from '@/components/Table';
import CustomerSupplierSkeleton from '@/components/pages/report/finance/skeleton/CustomerSupplierSkeleton'; import CustomerSupplierSkeleton from '@/components/pages/report/finance/skeleton/CustomerSupplierSkeleton';
@@ -32,12 +32,15 @@ interface BalanceMonitoringTabProps {
tabId: string; tabId: string;
} }
const filterByOptions: OptionType<string>[] = [
{ label: 'Tanggal Penjualan (SO Date)', value: 'sold_at' },
{ label: 'Tanggal Realisasi (Delivery Date)', value: 'realized_at' },
];
const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => { const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
const [isPdfExportLoading, setIsPdfExportLoading] = useState(false);
const [hasDateError, setHasDateError] = useState(false); const [hasDateError, setHasDateError] = useState(false);
const [dateErrorShown, setDateErrorShown] = useState(false); const [dateErrorShown, setDateErrorShown] = useState(false);
const handleFilterModalOpenRef = useRef(() => {});
const filterModal = useModal(); const filterModal = useModal();
const setTabActions = useTabActionsStore((state) => state.setTabActions); const setTabActions = useTabActionsStore((state) => state.setTabActions);
@@ -48,20 +51,23 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
updateFilter, updateFilter,
setPage, setPage,
setPageSize, setPageSize,
toQueryString, toQueryString: getTableFilterQueryString,
reset: resetFilter,
} = useTableFilter<{ } = useTableFilter<{
start_date: string; start_date: string;
end_date: string; end_date: string;
customerFilter?: OptionType<number>; customers: OptionType<number>[];
salesFilter?: OptionType<number>; salesPersons: OptionType<number>[];
filterBy?: OptionType<string>;
sort_by: string; sort_by: string;
order_by: string; order_by: string;
}>({ }>({
initial: { initial: {
start_date: '', start_date: '',
end_date: '', end_date: '',
customerFilter: undefined, customers: [],
salesFilter: undefined, salesPersons: [],
filterBy: undefined,
sort_by: '', sort_by: '',
order_by: '', order_by: '',
}, },
@@ -70,8 +76,9 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
pageSize: 'limit', pageSize: 'limit',
start_date: 'start_date', start_date: 'start_date',
end_date: 'end_date', end_date: 'end_date',
customerFilter: 'customer_id', customers: 'customer_ids',
salesFilter: 'sales_id', salesPersons: 'sales_ids',
filterBy: 'filter_by',
sort_by: 'sort_by', sort_by: 'sort_by',
order_by: 'sort_order', order_by: 'sort_order',
}, },
@@ -79,31 +86,25 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
storeName: 'balance-monitoring-table', storeName: 'balance-monitoring-table',
}); });
// Keep a stable ref so handleExportPDF doesn't need toQueryString as a dep // const sorting: SortingState = tableFilterState.sort_by
const toQueryStringRef = useRef(toQueryString); // ? [
useEffect(() => { // {
toQueryStringRef.current = toQueryString; // id: tableFilterState.sort_by,
}); // desc: tableFilterState.order_by === 'desc',
// },
// ]
// : [];
const sorting: SortingState = tableFilterState.sort_by // const handleSortingChange = (updater: Updater<SortingState>) => {
? [ // const next = typeof updater === 'function' ? updater(sorting) : updater;
{ // if (next.length > 0) {
id: tableFilterState.sort_by, // updateFilter('sort_by', next[0].id, true);
desc: tableFilterState.order_by === 'desc', // updateFilter('order_by', next[0].desc ? 'desc' : 'asc', true);
}, // } else {
] // updateFilter('sort_by', '', true);
: []; // updateFilter('order_by', '', true);
// }
const handleSortingChange = (updater: Updater<SortingState>) => { // };
const next = typeof updater === 'function' ? updater(sorting) : updater;
if (next.length > 0) {
updateFilter('sort_by', next[0].id, true);
updateFilter('order_by', next[0].desc ? 'desc' : 'asc', true);
} else {
updateFilter('sort_by', '', true);
updateFilter('order_by', '', true);
}
};
const { const {
options: customerOptions, options: customerOptions,
@@ -123,33 +124,40 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
initialValues: { initialValues: {
start_date: tableFilterState.start_date, start_date: tableFilterState.start_date,
end_date: tableFilterState.end_date, end_date: tableFilterState.end_date,
customerFilter: tableFilterState.customerFilter, customers: tableFilterState.customers,
salesFilter: tableFilterState.salesFilter, salesPersons: tableFilterState.salesPersons,
filterBy: tableFilterState.filterBy,
}, },
enableReinitialize: true,
onSubmit: (values) => { onSubmit: (values) => {
updateFilter('start_date', values.start_date, true); updateFilter('start_date', values.start_date, true);
updateFilter('end_date', values.end_date, true); updateFilter('end_date', values.end_date, true);
updateFilter('customerFilter', values.customerFilter, true); updateFilter('customers', values.customers, true);
updateFilter('salesFilter', values.salesFilter, true); updateFilter('salesPersons', values.salesPersons, true);
filterModal.closeModal(); updateFilter('filterBy', values.filterBy, true);
},
onReset: () => {
updateFilter('start_date', '', true);
updateFilter('end_date', '', true);
updateFilter('customerFilter', undefined, true);
updateFilter('salesFilter', undefined, true);
setHasDateError(false);
if (dateErrorShown) {
toast.dismiss();
setDateErrorShown(false);
}
filterModal.closeModal(); filterModal.closeModal();
}, },
}); });
handleFilterModalOpenRef.current = () => { const formikResetHandler = () => {
filterModal.openModal(); resetFilter();
setHasDateError(false);
if (dateErrorShown) {
toast.dismiss();
setDateErrorShown(false);
}
formik.resetForm({
values: {
start_date: '',
end_date: '',
customers: [],
salesPersons: [],
filterBy: undefined,
},
});
filterModal.closeModal();
}; };
const handleStartDateChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleStartDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
@@ -201,41 +209,26 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
} }
}; };
const queryString = toQueryString(); const { data: balanceMonitoringsResponse, isLoading } = useSWR<
BaseApiResponse<BalanceMonitoringRow[]>,
const { data: response, isLoading } = useSWR(queryString, (qs) => AxiosError<BaseApiResponse>,
FinanceApi.getBalanceMonitoringReport( SWRHttpKey
Object.fromEntries(new URLSearchParams(qs)) as Parameters< >(
typeof FinanceApi.getBalanceMonitoringReport `${FinanceApi.basePath}/balance-monitoring${getTableFilterQueryString()}`,
>[0] httpClientFetcher
)
); );
const data: BalanceMonitoringRow[] = useMemo( const balanceMonitorings: BalanceMonitoringRow[] = isResponseSuccess(
() => balanceMonitoringsResponse
isResponseSuccess(response) )
? ((response.data as BalanceMonitoringRow[]) ?? []) ? ((balanceMonitoringsResponse.data as BalanceMonitoringRow[]) ?? [])
: [], : [];
[response]
);
const meta = useMemo( const meta =
() => (isResponseSuccess(response) && response.meta ? response.meta : null), isResponseSuccess(balanceMonitoringsResponse) &&
[response] balanceMonitoringsResponse.meta
); ? balanceMonitoringsResponse.meta
: null;
// Stable — uses ref so toQueryString is always current without being a dep
const handleExportPDF = useCallback(async () => {
setIsPdfExportLoading(true);
try {
await FinanceApi.exportBalanceMonitoringToPDF(toQueryStringRef.current());
toast.success('PDF berhasil dibuat dan diunduh.');
} catch {
toast.error('Gagal membuat PDF. Silakan coba lagi.');
} finally {
setIsPdfExportLoading(false);
}
}, []);
// Inject tab actions directly — no nested component, no remount cycle // Inject tab actions directly — no nested component, no remount cycle
useEffect(() => { useEffect(() => {
@@ -246,85 +239,39 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
values={{ values={{
start_date: tableFilterState.start_date, start_date: tableFilterState.start_date,
end_date: tableFilterState.end_date, end_date: tableFilterState.end_date,
customerFilter: tableFilterState.customerFilter, customers: tableFilterState.customers,
salesFilter: tableFilterState.salesFilter, salesPersons: tableFilterState.salesPersons,
filterBy: tableFilterState.filterBy,
}} }}
fieldGroups={[['start_date', 'end_date']]} fieldGroups={[['start_date', 'end_date']]}
onClick={() => handleFilterModalOpenRef.current()} onClick={filterModal.openModal}
variant='outline' variant='outline'
className='px-3 py-2.5' className='px-3 py-2.5'
/> />
<Dropdown
align='end'
direction='bottom'
className={{
content:
'mt-1 rounded-xl border border-base-content/5 shadow-sm overflow-hidden',
}}
trigger={
<Button
variant='outline'
color='none'
isLoading={isPdfExportLoading}
className='px-3 py-2.5 text-sm text-base-content/50 border border-base-content/10 rounded-xl shadow-button-soft'
>
<div className='flex flex-row items-center gap-1.5'>
<Icon
icon='heroicons:cloud-arrow-down'
width={20}
height={20}
/>
<span>Export</span>
<div className='w-px self-stretch bg-base-content/10' />
<Icon icon='heroicons:chevron-down' width={14} height={14} />
</div>
</Button>
}
>
<Button
variant='ghost'
color='none'
onClick={handleExportPDF}
isLoading={isPdfExportLoading}
className='w-full p-3 justify-start text-sm text-base-content/50 font-semibold text-nowrap'
>
<Icon icon='heroicons:document' width={20} height={20} />
Export to PDF
</Button>
</Dropdown>
</div> </div>
); );
}, [ }, [tabId, setTabActions, tableFilterState, filterModal.openModal]);
tabId,
setTabActions,
isPdfExportLoading,
handleExportPDF,
tableFilterState.start_date,
tableFilterState.end_date,
tableFilterState.customerFilter,
tableFilterState.salesFilter,
]);
useEffect(() => { useEffect(() => {
return () => clearTabActions(tabId); return () => clearTabActions(tabId);
}, [tabId, clearTabActions]); }, [tabId, clearTabActions]);
const page = meta?.page || tableFilterState.page;
const pageSize = meta?.limit || tableFilterState.pageSize;
const columns = useMemo( const columns = useMemo(
(): ColumnDef<BalanceMonitoringRow>[] => [ (): ColumnDef<BalanceMonitoringRow>[] => [
{ {
header: 'No', header: 'No',
enableSorting: false, enableSorting: false,
cell: (props) => (page - 1) * pageSize + props.row.index + 1, cell: (props) =>
(tableFilterState.page - 1) * tableFilterState.pageSize +
props.row.index +
1,
}, },
{ {
header: 'Customer', header: 'Customer',
accessorKey: 'customer_name', accessorKey: 'customer.name',
enableSorting: true, enableSorting: true,
id: 'customer_name', id: 'customer_name',
cell: ({ row }) => row.original.customer.name,
}, },
{ {
header: 'Saldo Awal', header: 'Saldo Awal',
@@ -342,34 +289,34 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
columns: [ columns: [
{ {
header: 'Ekor', header: 'Ekor',
accessorKey: 'penjualan_ayam_ekor', accessorKey: 'penjualan_ayam.ekor',
id: 'penjualan_ayam_ekor', id: 'penjualan_ayam_ekor',
enableSorting: true, enableSorting: true,
cell: ({ row }) => ( cell: ({ row }) => (
<div className='text-right'> <div className='text-right'>
{formatNumber(row.original.penjualan_ayam_ekor)} {formatNumber(row.original.penjualan_ayam.ekor)}
</div> </div>
), ),
}, },
{ {
header: 'Kg', header: 'Kg',
accessorKey: 'penjualan_ayam_kg', accessorKey: 'penjualan_ayam.kg',
id: 'penjualan_ayam_kg', id: 'penjualan_ayam_kg',
enableSorting: true, enableSorting: true,
cell: ({ row }) => ( cell: ({ row }) => (
<div className='text-right'> <div className='text-right'>
{formatNumber(row.original.penjualan_ayam_kg)} {formatNumber(row.original.penjualan_ayam.kg)}
</div> </div>
), ),
}, },
{ {
header: 'Nominal', header: 'Nominal',
accessorKey: 'penjualan_ayam_nominal', accessorKey: 'penjualan_ayam.nominal',
id: 'penjualan_ayam_nominal', id: 'penjualan_ayam_nominal',
enableSorting: true, enableSorting: true,
cell: ({ row }) => ( cell: ({ row }) => (
<div className='text-right'> <div className='text-right'>
{formatCurrency(row.original.penjualan_ayam_nominal)} {formatCurrency(row.original.penjualan_ayam.nominal)}
</div> </div>
), ),
}, },
@@ -379,35 +326,35 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
header: 'Penjualan Telur', header: 'Penjualan Telur',
columns: [ columns: [
{ {
header: 'Kuantitas', header: 'Butir',
accessorKey: 'penjualan_telur_kuantitas', accessorKey: 'penjualan_telur.butir',
id: 'penjualan_telur_kuantitas', id: 'penjualan_telur_butir',
enableSorting: true, enableSorting: true,
cell: ({ row }) => ( cell: ({ row }) => (
<div className='text-right'> <div className='text-right'>
{formatNumber(row.original.penjualan_telur_kuantitas)} {formatNumber(row.original.penjualan_telur.butir)}
</div> </div>
), ),
}, },
{ {
header: 'Kg', header: 'Kg',
accessorKey: 'penjualan_telur_kg', accessorKey: 'penjualan_telur.kg',
id: 'penjualan_telur_kg', id: 'penjualan_telur_kg',
enableSorting: true, enableSorting: true,
cell: ({ row }) => ( cell: ({ row }) => (
<div className='text-right'> <div className='text-right'>
{formatNumber(row.original.penjualan_telur_kg)} {formatNumber(row.original.penjualan_telur.kg)}
</div> </div>
), ),
}, },
{ {
header: 'Nominal', header: 'Nominal',
accessorKey: 'penjualan_telur_nominal', accessorKey: 'penjualan_telur.nominal',
id: 'penjualan_telur_nominal', id: 'penjualan_telur_nominal',
enableSorting: true, enableSorting: true,
cell: ({ row }) => ( cell: ({ row }) => (
<div className='text-right'> <div className='text-right'>
{formatCurrency(row.original.penjualan_telur_nominal)} {formatCurrency(row.original.penjualan_telur.nominal)}
</div> </div>
), ),
}, },
@@ -415,12 +362,12 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
}, },
{ {
header: 'Penjualan Trading', header: 'Penjualan Trading',
accessorKey: 'penjualan_trading', accessorKey: 'penjualan_trading.nominal',
id: 'penjualan_trading', id: 'penjualan_trading',
enableSorting: true, enableSorting: true,
cell: ({ row }) => ( cell: ({ row }) => (
<div className='text-right'> <div className='text-right'>
{formatCurrency(row.original.penjualan_trading)} {formatCurrency(row.original.penjualan_trading.nominal)}
</div> </div>
), ),
}, },
@@ -471,7 +418,7 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
), ),
}, },
], ],
[page, pageSize] [tableFilterState.page, tableFilterState.pageSize]
); );
return ( return (
@@ -483,7 +430,7 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
</div> </div>
)} )}
{!isLoading && data.length === 0 && ( {!isLoading && balanceMonitorings.length === 0 && (
<CustomerSupplierSkeleton <CustomerSupplierSkeleton
columns={columns as unknown as ColumnDef<CustomerPaymentRow>[]} columns={columns as unknown as ColumnDef<CustomerPaymentRow>[]}
icon={ icon={
@@ -499,20 +446,20 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
/> />
)} )}
{!isLoading && data.length > 0 && ( {!isLoading && balanceMonitorings.length > 0 && (
<> <>
<div className='w-full overflow-x-auto'> <div className='w-full overflow-x-auto'>
<Table <Table
data={data} data={balanceMonitorings}
columns={columns} columns={columns}
pageSize={pageSize} pageSize={tableFilterState.pageSize || 10}
page={meta?.page || 1} page={tableFilterState.page || 1}
totalItems={meta?.total_results || 0} totalItems={meta?.total_results || 0}
onPageChange={setPage} onPageChange={setPage}
onPageSizeChange={setPageSize} onPageSizeChange={setPageSize}
sorting={sorting} // sorting={sorting}
setSorting={handleSortingChange} // setSorting={handleSortingChange}
manualSorting // manualSorting
className={{ className={{
containerClassName: 'w-full mb-0!', containerClassName: 'w-full mb-0!',
tableWrapperClassName: 'overflow-x-auto', tableWrapperClassName: 'overflow-x-auto',
@@ -524,31 +471,9 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
'hover:bg-gray-50 transition-colors border-b border-l border-r border-b-gray-200 border-l-gray-200 border-r-gray-200', 'hover:bg-gray-50 transition-colors border-b border-l border-r border-b-gray-200 border-l-gray-200 border-r-gray-200',
bodyColumnClassName: bodyColumnClassName:
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap', 'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
paginationClassName: 'hidden',
}} }}
/> />
</div> </div>
{meta && (
<div className='mt-5 px-3'>
<Pagination
totalItems={meta.total_results || 0}
itemsPerPage={meta.limit || 0}
currentPage={meta.page || 0}
onPrevPage={() => setPage(Math.max(1, (meta.page || 1) - 1))}
onNextPage={() =>
setPage(
meta.total_pages && (meta.page || 1) < meta.total_pages
? (meta.page || 1) + 1
: meta.page || 1
)
}
onPageChange={setPage}
rowOptions={[10, 20, 50, 100]}
onRowChange={setPageSize}
/>
</div>
)}
</> </>
)} )}
</div> </div>
@@ -576,7 +501,7 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
</Button> </Button>
</div> </div>
<form onSubmit={formik.handleSubmit} onReset={formik.handleReset}> <form onSubmit={formik.handleSubmit} onReset={formikResetHandler}>
<div className='p-4 flex flex-col gap-3'> <div className='p-4 flex flex-col gap-3'>
<div> <div>
<label className='block text-xs font-semibold text-base-content py-2'> <label className='block text-xs font-semibold text-base-content py-2'>
@@ -602,16 +527,13 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
</div> </div>
</div> </div>
<SelectInput <SelectInputCheckbox
label='Customer' label='Customer'
placeholder='Pilih Customer' placeholder='Pilih Customer'
options={customerOptions} options={customerOptions}
value={formik.values.customerFilter ?? null} value={formik.values.customers}
onChange={(val) => onChange={(val) =>
formik.setFieldValue( formik.setFieldValue('customers', Array.isArray(val) ? val : [])
'customerFilter',
val as OptionType<number> | null
)
} }
onInputChange={setCustomerInput} onInputChange={setCustomerInput}
isLoading={isLoadingCustomers} isLoading={isLoadingCustomers}
@@ -620,15 +542,15 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
className={{ wrapper: 'w-full' }} className={{ wrapper: 'w-full' }}
/> />
<SelectInput <SelectInputCheckbox
label='Sales' label='Sales'
placeholder='Pilih Sales' placeholder='Pilih Sales'
options={salesOptions} options={salesOptions}
value={formik.values.salesFilter ?? null} value={formik.values.salesPersons}
onChange={(val) => onChange={(val) =>
formik.setFieldValue( formik.setFieldValue(
'salesFilter', 'salesPersons',
val as OptionType<number> | null Array.isArray(val) ? val : []
) )
} }
onInputChange={setSalesInput} onInputChange={setSalesInput}
@@ -637,6 +559,21 @@ const BalanceMonitoringTab = ({ tabId }: BalanceMonitoringTabProps) => {
onMenuScrollToBottom={loadMoreSales} onMenuScrollToBottom={loadMoreSales}
className={{ wrapper: 'w-full' }} className={{ wrapper: 'w-full' }}
/> />
<SelectInputRadio
label='Filter Berdasarkan'
placeholder='Pilih Filter Berdasarkan'
options={filterByOptions}
value={formik.values.filterBy ?? null}
onChange={(val) =>
formik.setFieldValue(
'filterBy',
!Array.isArray(val) ? (val ?? undefined) : undefined
)
}
isClearable
className={{ wrapper: 'w-full' }}
/>
</div> </div>
{/* Modal Footer */} {/* Modal Footer */}