refactor: optimize CustomerPaymentTab with useTableFilter persistence pattern

Replace filterParams/currentPage/pageSize state with useTableFilter (persist:true),
switch SWR to httpClientFetcher with explicit type, store OptionType[] directly for
customers/filterBy, add formikResetHandler using resetFilter(), remove enableReinitialize
and handleFilterModalOpenRef, pass filterModal.openModal directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ValdiANS
2026-05-20 16:10:10 +07:00
parent c98a51326f
commit b3b60018bb
@@ -1,14 +1,17 @@
import { useState, useMemo, useCallback, useEffect, useRef } from 'react'; import { useState, useCallback, useEffect } from 'react';
import useSWR from 'swr'; import useSWR from 'swr';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import { AxiosError } from 'axios';
import Card from '@/components/Card'; import Card from '@/components/Card';
import StatusBadge from '@/components/helper/StatusBadge'; import StatusBadge from '@/components/helper/StatusBadge';
import { useSelect } from '@/components/input/SelectInput'; import { useSelect, OptionType } from '@/components/input/SelectInput';
import SelectInputCheckbox from '@/components/input/SelectInputCheckbox'; import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
import SelectInputRadio from '@/components/input/SelectInputRadio'; import SelectInputRadio from '@/components/input/SelectInputRadio';
import DateInput from '@/components/input/DateInput'; import DateInput from '@/components/input/DateInput';
import { CustomerApi } from '@/services/api/master-data'; import { CustomerApi } from '@/services/api/master-data';
import { FinanceApi } from '@/services/api/report/finance-report'; import { FinanceApi } from '@/services/api/report/finance-report';
import { httpClientFetcher, SWRHttpKey } from '@/services/http/client';
import { BaseApiResponse } from '@/types/api/api-general';
import Table from '@/components/Table'; import Table from '@/components/Table';
import { ColumnDef } from '@tanstack/react-table'; import { ColumnDef } from '@tanstack/react-table';
import { import {
@@ -27,28 +30,22 @@ import Dropdown from '@/components/Dropdown';
import Modal, { useModal } from '@/components/Modal'; import Modal, { useModal } from '@/components/Modal';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import {
CustomerPaymentFilterSchema,
CustomerPaymentFilterType,
} from '@/components/pages/report/finance/filter/CustomerPaymentFilter';
import { generateCustomerPaymentPDF } from '@/components/pages/report/finance/export/CustomerPaymentExportPDF'; import { generateCustomerPaymentPDF } from '@/components/pages/report/finance/export/CustomerPaymentExportPDF';
import { useTabActionsStore } from '@/stores/tab-actions/tab-actions.store'; import { useTabActionsStore } from '@/stores/tab-actions/tab-actions.store';
import CustomerSupplierSkeleton from '@/components/pages/report/finance/skeleton/CustomerSupplierSkeleton'; import CustomerSupplierSkeleton from '@/components/pages/report/finance/skeleton/CustomerSupplierSkeleton';
import { OptionType } from '@/components/table/TableRowSizeSelector';
import { Color } from '@/types/theme'; import { Color } from '@/types/theme';
import ButtonFilter from '@/components/helper/ButtonFilter'; import ButtonFilter from '@/components/helper/ButtonFilter';
import Pagination from '@/components/Pagination'; import Pagination from '@/components/Pagination';
import { useTableFilter } from '@/services/hooks/useTableFilter';
interface CustomerPaymentTabProps { interface CustomerPaymentTabProps {
tabId: string; tabId: string;
} }
interface FilterParams { const dataTypeOptions: OptionType<string>[] = [
customer_ids?: string; { value: 'trans_date', label: 'Tanggal Jual/Bayar' },
start_date?: string; { value: 'realization_date', label: 'Tanggal Realisasi' },
end_date?: string; ];
filter_by?: string;
}
const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => { const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
// ===== STATE MANAGEMENT ===== // ===== STATE MANAGEMENT =====
@@ -59,26 +56,44 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
const isAnyExportLoading = const isAnyExportLoading =
isPdfExportLoading || isExcelExportLoading || isExcelGeneralExportLoading; isPdfExportLoading || isExcelExportLoading || isExcelGeneralExportLoading;
// ===== PAGINATION STATE =====
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
// ===== SUBMISSION STATE =====
const [filterParams, setFilterParams] = useState<FilterParams>({});
const [dateErrorShown, setDateErrorShown] = useState(false); const [dateErrorShown, setDateErrorShown] = useState(false);
const [hasDateError, setHasDateError] = useState(false); const [hasDateError, setHasDateError] = useState(false);
const handleFilterModalOpenRef = useRef(() => {});
const filterModal = useModal(); const filterModal = useModal();
const dataTypeOptions = useMemo( const setTabActions = useTabActionsStore((state) => state.setTabActions);
() => [ const clearTabActions = useTabActionsStore((state) => state.clearTabActions);
{ value: 'trans_date', label: 'Tanggal Jual/Bayar' },
{ value: 'realization_date', label: 'Tanggal Realisasi' }, const {
], state: tableFilterState,
[] updateFilter,
); setPage,
setPageSize,
toQueryString: getTableFilterQueryString,
reset: resetFilter,
} = useTableFilter<{
start_date: string;
end_date: string;
customers: OptionType<number>[];
filterBy?: OptionType<string>;
}>({
initial: {
start_date: '',
end_date: '',
customers: [],
filterBy: undefined,
},
paramMap: {
page: 'page',
pageSize: 'limit',
start_date: 'start_date',
end_date: 'end_date',
customers: 'customer_ids',
filterBy: 'filter_by',
},
persist: true,
storeName: 'customer-payment-report-table',
});
const { const {
options: customerOptions, options: customerOptions,
@@ -88,72 +103,57 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
} = useSelect(CustomerApi.basePath, 'id', 'name', 'search'); } = useSelect(CustomerApi.basePath, 'id', 'name', 'search');
// ===== FORMIK SETUP ===== // ===== FORMIK SETUP =====
const formik = useFormik<CustomerPaymentFilterType>({ const formik = useFormik({
initialValues: { initialValues: {
start_date: null, start_date: tableFilterState.start_date,
end_date: null, end_date: tableFilterState.end_date,
customer_ids: null, customers: tableFilterState.customers,
filter_by: null, filterBy: tableFilterState.filterBy,
}, },
validationSchema: CustomerPaymentFilterSchema, onSubmit: (values) => {
onSubmit: (values, { setSubmitting }) => { updateFilter('start_date', values.start_date, true);
setFilterParams({ updateFilter('end_date', values.end_date, true);
start_date: values.start_date || undefined, updateFilter('customers', values.customers, true);
end_date: values.end_date || undefined, updateFilter('filterBy', values.filterBy, true);
customer_ids: values.customer_ids || undefined,
filter_by: values.filter_by || undefined,
});
filterModal.closeModal(); filterModal.closeModal();
setCurrentPage(1);
setSubmitting(false);
}, },
onReset: () => { });
setFilterParams({});
setCurrentPage(1); const formikResetHandler = () => {
resetFilter();
setHasDateError(false); setHasDateError(false);
if (dateErrorShown) { if (dateErrorShown) {
toast.dismiss(); toast.dismiss();
setDateErrorShown(false); setDateErrorShown(false);
} }
filterModal.closeModal();
formik.resetForm({
values: {
start_date: '',
end_date: '',
customers: [],
filterBy: undefined,
}, },
}); });
handleFilterModalOpenRef.current = () => { filterModal.closeModal();
formik.setValues({
start_date: filterParams.start_date || null,
end_date: filterParams.end_date || null,
customer_ids: filterParams.customer_ids || null,
filter_by: filterParams.filter_by || null,
});
filterModal.openModal();
}; };
const getPaymentStatusBadgeColor = (notes: string): Color => { const getPaymentStatusBadgeColor = (notes: string): Color => {
const normalizedValue = notes.toLowerCase(); const normalizedValue = notes.toLowerCase();
if (normalizedValue === 'lunas') return 'primary';
if (normalizedValue === 'lunas') { if (normalizedValue.includes('belum')) return 'warning';
return 'primary';
}
if (normalizedValue.includes('belum')) {
return 'warning';
}
return 'neutral'; return 'neutral';
}; };
// ===== DATE CHANGE HANDLERS ===== // ===== DATE CHANGE HANDLERS =====
const handleStartDateChange = useCallback( const handleStartDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
(e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value; const value = e.target.value;
formik.setFieldValue('start_date', value || null); formik.setFieldValue('start_date', value);
if (value && formik.values.end_date) { if (value && formik.values.end_date) {
const startDate = new Date(value); if (new Date(formik.values.end_date) < new Date(value)) {
const endDateObj = new Date(formik.values.end_date);
if (endDateObj < startDate) {
setHasDateError(true); setHasDateError(true);
if (!dateErrorShown) { if (!dateErrorShown) {
toast.error('Tanggal akhir tidak boleh masa lampau', { toast.error('Tanggal akhir tidak boleh masa lampau', {
@@ -171,20 +171,14 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
} else { } else {
setHasDateError(false); setHasDateError(false);
} }
}, };
[formik, dateErrorShown]
);
const handleEndDateChange = useCallback( const handleEndDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
(e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value; const value = e.target.value;
formik.setFieldValue('end_date', value || null); formik.setFieldValue('end_date', value);
if (value && formik.values.start_date) { if (value && formik.values.start_date) {
const startDateObj = new Date(formik.values.start_date); if (new Date(value) < new Date(formik.values.start_date)) {
const endDate = new Date(value);
if (endDate < startDateObj) {
setHasDateError(true); setHasDateError(true);
if (!dateErrorShown) { if (!dateErrorShown) {
toast.error('Tanggal akhir tidak boleh masa lampau', { toast.error('Tanggal akhir tidak boleh masa lampau', {
@@ -201,109 +195,67 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
toast.dismiss(); toast.dismiss();
setDateErrorShown(false); setDateErrorShown(false);
} }
},
[formik, dateErrorShown]
);
// ===== FILTER HELPERS =====
const customerIdsValue = useMemo(() => {
if (!formik.values.customer_ids) return [];
return customerOptions.filter((opt) =>
formik.values.customer_ids?.split(',').includes(String(opt.value))
);
}, [formik.values.customer_ids, customerOptions]);
const filterByValue = useMemo(() => {
if (!formik.values.filter_by) return null;
return (
dataTypeOptions.find((opt) => opt.value === formik.values.filter_by) ||
null
);
}, [formik.values.filter_by, dataTypeOptions]);
// ===== DATA FETCHING =====
const { data: customerPayment, isLoading } = useSWR(
() => {
const params = {
customer_ids: filterParams.customer_ids,
filter_by: filterParams.filter_by as
| 'trans_date'
| 'realization_date'
| undefined,
start_date: filterParams.start_date,
end_date: filterParams.end_date,
page: currentPage,
limit: pageSize,
}; };
return ['customer-payment-report', params]; // ===== DATA FETCHING =====
}, const { data: customerPayment, isLoading } = useSWR<
([, params]) => BaseApiResponse<CustomerPaymentReport>,
FinanceApi.getCustomerPaymentReport( AxiosError<BaseApiResponse>,
params.customer_ids, SWRHttpKey
params.filter_by, >(
params.start_date, `${FinanceApi.basePath}/customer-payment${getTableFilterQueryString()}`,
params.end_date, httpClientFetcher
params.page,
params.limit
)
); );
const data: CustomerPaymentReport[] = useMemo( const data: CustomerPaymentReport[] = isResponseSuccess(customerPayment)
() =>
isResponseSuccess(customerPayment)
? (customerPayment?.data as unknown as CustomerPaymentReport[]) || [] ? (customerPayment?.data as unknown as CustomerPaymentReport[]) || []
: [], : [];
[customerPayment]
);
const meta = useMemo( const meta =
() =>
isResponseSuccess(customerPayment) && customerPayment.meta isResponseSuccess(customerPayment) && customerPayment.meta
? customerPayment.meta ? customerPayment.meta
: null, : null;
[customerPayment]
);
// ===== EXPORT DATA FETCHER ===== // ===== EXPORT DATA FETCHER =====
const customerPaymentExport = useCallback(async (): Promise< const customerPaymentExport = useCallback(async (): Promise<
CustomerPaymentReport[] | null CustomerPaymentReport[] | null
> => { > => {
const params = { const customer_ids =
customer_ids: filterParams.customer_ids, tableFilterState.customers.length > 0
filter_by: filterParams.filter_by as ? tableFilterState.customers.map((o) => String(o.value)).join(',')
: undefined;
const filter_by = tableFilterState.filterBy?.value as
| 'trans_date' | 'trans_date'
| 'realization_date' | 'realization_date'
| undefined, | undefined;
start_date: filterParams.start_date,
end_date: filterParams.end_date,
limit: 100,
page: 1,
};
const response = await FinanceApi.getCustomerPaymentReport( const response = await FinanceApi.getCustomerPaymentReport(
params.customer_ids, customer_ids,
params.filter_by, filter_by,
params.start_date, tableFilterState.start_date || undefined,
params.end_date, tableFilterState.end_date || undefined,
params.page, 1,
params.limit 100
); );
return isResponseSuccess(response) return isResponseSuccess(response)
? (response.data as unknown as CustomerPaymentReport[]) ? (response.data as unknown as CustomerPaymentReport[])
: null; : null;
}, [filterParams]); }, [tableFilterState]);
// ===== EXPORT HANDLERS ===== // ===== EXPORT HANDLERS =====
const handleExportExcelGeneral = useCallback(async () => { const handleExportExcelGeneral = useCallback(async () => {
setIsExcelGeneralExportLoading(true); setIsExcelGeneralExportLoading(true);
try { try {
const customer_ids =
tableFilterState.customers.length > 0
? tableFilterState.customers.map((o) => String(o.value)).join(',')
: undefined;
await FinanceApi.exportCustomerPaymentToExcelGeneral( await FinanceApi.exportCustomerPaymentToExcelGeneral(
filterParams.customer_ids, customer_ids,
filterParams.filter_by, tableFilterState.filterBy?.value,
filterParams.start_date, tableFilterState.start_date || undefined,
filterParams.end_date tableFilterState.end_date || undefined
); );
toast.success('Excel General berhasil dibuat dan diunduh.'); toast.success('Excel General berhasil dibuat dan diunduh.');
} catch { } catch {
@@ -311,16 +263,20 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
} finally { } finally {
setIsExcelGeneralExportLoading(false); setIsExcelGeneralExportLoading(false);
} }
}, [filterParams]); }, [tableFilterState]);
const handleExportExcel = useCallback(async () => { const handleExportExcel = useCallback(async () => {
setIsExcelExportLoading(true); setIsExcelExportLoading(true);
try { try {
const customer_ids =
tableFilterState.customers.length > 0
? tableFilterState.customers.map((o) => String(o.value)).join(',')
: undefined;
await FinanceApi.exportCustomerPaymentToExcelCustomerPerSheet( await FinanceApi.exportCustomerPaymentToExcelCustomerPerSheet(
filterParams.customer_ids, customer_ids,
filterParams.filter_by, tableFilterState.filterBy?.value,
filterParams.start_date, tableFilterState.start_date || undefined,
filterParams.end_date tableFilterState.end_date || undefined
); );
toast.success('Excel berhasil dibuat dan diunduh.'); toast.success('Excel berhasil dibuat dan diunduh.');
} catch { } catch {
@@ -328,7 +284,7 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
} finally { } finally {
setIsExcelExportLoading(false); setIsExcelExportLoading(false);
} }
}, [filterParams]); }, [tableFilterState]);
const handleExportPdf = useCallback(async () => { const handleExportPdf = useCallback(async () => {
setIsPdfExportLoading(true); setIsPdfExportLoading(true);
@@ -344,22 +300,18 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
return; return;
} }
const customerName = filterParams.customer_ids const customerName =
? customerOptions tableFilterState.customers.length > 0
.filter((opt) => ? tableFilterState.customers.map((o) => o.label).join(', ')
filterParams.customer_ids?.split(',').includes(String(opt.value))
)
.map((opt) => opt.label)
.join(', ') || 'Semua Customer'
: 'Semua Customer'; : 'Semua Customer';
await generateCustomerPaymentPDF({ await generateCustomerPaymentPDF({
data: allDataForExport, data: allDataForExport,
params: { params: {
customer_name: customerName, customer_name: customerName,
start_date: filterParams.start_date, start_date: tableFilterState.start_date || undefined,
end_date: filterParams.end_date, end_date: tableFilterState.end_date || undefined,
filter_by: filterParams.filter_by as filter_by: tableFilterState.filterBy?.value as
| 'trans_date' | 'trans_date'
| 'realization_date' | 'realization_date'
| undefined, | undefined,
@@ -371,24 +323,22 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
} finally { } finally {
setIsPdfExportLoading(false); setIsPdfExportLoading(false);
} }
}, [customerPaymentExport, filterParams, customerOptions]); }, [customerPaymentExport, tableFilterState]);
// ===== TAB ACTIONS COMPONENT =====
const TabActions = useMemo(() => {
return function TabActionsComponent() {
const setTabActions = useTabActionsStore((state) => state.setTabActions);
const clearTabActions = useTabActionsStore(
(state) => state.clearTabActions
);
// ===== TAB ACTIONS =====
useEffect(() => { useEffect(() => {
setTabActions( setTabActions(
tabId, tabId,
<div className='flex flex-row gap-3'> <div className='flex flex-row gap-3'>
<ButtonFilter <ButtonFilter
values={filterParams} values={{
start_date: tableFilterState.start_date,
end_date: tableFilterState.end_date,
customers: tableFilterState.customers,
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'
/> />
@@ -413,16 +363,9 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
width={20} width={20}
height={20} height={20}
/> />
<span>Export</span> <span>Export</span>
<div className='w-px self-stretch bg-base-content/10' /> <div className='w-px self-stretch bg-base-content/10' />
<Icon icon='heroicons:chevron-down' width={14} height={14} />
<Icon
icon='heroicons:chevron-down'
width={14}
height={14}
/>
</div> </div>
</Button> </Button>
} }
@@ -447,7 +390,6 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
<Icon icon='heroicons:table-cells' width={20} height={20} /> <Icon icon='heroicons:table-cells' width={20} height={20} />
Export to Excel - General Export to Excel - General
</Button> </Button>
<Button <Button
variant='ghost' variant='ghost'
color='none' color='none'
@@ -461,29 +403,23 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
</Dropdown> </Dropdown>
</div> </div>
); );
}, [setTabActions]);
useEffect(() => {
return () => {
clearTabActions(tabId);
};
}, [clearTabActions]);
return null;
};
}, [ }, [
tabId, tabId,
setTabActions,
tableFilterState,
filterModal.openModal,
isAnyExportLoading, isAnyExportLoading,
handleExportExcelGeneral,
handleExportExcel, handleExportExcel,
handleExportExcelGeneral,
handleExportPdf, handleExportPdf,
isExcelGeneralExportLoading,
isExcelExportLoading, isExcelExportLoading,
isExcelGeneralExportLoading,
isPdfExportLoading, isPdfExportLoading,
filterParams,
]); ]);
const TabActionsElement = useMemo(() => <TabActions />, [TabActions]); useEffect(() => {
return () => clearTabActions(tabId);
}, [tabId, clearTabActions]);
const getTableColumns = ( const getTableColumns = (
summary: CustomerPaymentSummary summary: CustomerPaymentSummary
@@ -690,11 +626,7 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
enableSorting: false, enableSorting: false,
cell: (props) => { cell: (props) => {
const value = props.row.original.status; const value = props.row.original.status;
if (!value) return '-';
if (!value) {
return '-';
}
return ( return (
<StatusBadge <StatusBadge
color={getPaymentStatusBadgeColor(value)} color={getPaymentStatusBadgeColor(value)}
@@ -733,7 +665,6 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
return ( return (
<> <>
{TabActionsElement}
<div className='w-full p-0 sm:p-3 flex flex-col gap-3'> <div className='w-full p-0 sm:p-3 flex flex-col gap-3'>
{isLoading && ( {isLoading && (
<div className='w-full flex flex-row justify-center items-center p-4'> <div className='w-full flex flex-row justify-center items-center p-4'>
@@ -762,16 +693,16 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
<Pagination <Pagination
totalItems={meta.total_results || 0} totalItems={meta.total_results || 0}
itemsPerPage={meta.limit || 0} itemsPerPage={meta.limit || 0}
currentPage={meta.page || 0} currentPage={tableFilterState.page}
onPrevPage={() => onPrevPage={() => setPage(Math.max(1, tableFilterState.page - 1))}
setCurrentPage((curr) => (curr > 1 ? curr - 1 : curr))
}
onNextPage={() => onNextPage={() =>
setCurrentPage((curr) => setPage(
meta.total_pages && curr < meta.total_pages ? curr + 1 : curr meta.total_pages && tableFilterState.page < meta.total_pages
? tableFilterState.page + 1
: tableFilterState.page
) )
} }
onPageChange={(pageNumber) => setCurrentPage(pageNumber)} onPageChange={setPage}
rowOptions={[10, 20, 50, 100]} rowOptions={[10, 20, 50, 100]}
onRowChange={setPageSize} onRowChange={setPageSize}
/> />
@@ -878,16 +809,16 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
<Pagination <Pagination
totalItems={meta.total_results || 0} totalItems={meta.total_results || 0}
itemsPerPage={meta.limit || 0} itemsPerPage={meta.limit || 0}
currentPage={meta.page || 0} currentPage={tableFilterState.page}
onPrevPage={() => onPrevPage={() => setPage(Math.max(1, tableFilterState.page - 1))}
setCurrentPage((curr) => (curr > 1 ? curr - 1 : curr))
}
onNextPage={() => onNextPage={() =>
setCurrentPage((curr) => setPage(
meta.total_pages && curr < meta.total_pages ? curr + 1 : curr meta.total_pages && tableFilterState.page < meta.total_pages
? tableFilterState.page + 1
: tableFilterState.page
) )
} }
onPageChange={(pageNumber) => setCurrentPage(pageNumber)} onPageChange={setPage}
rowOptions={[10, 20, 50, 100]} rowOptions={[10, 20, 50, 100]}
onRowChange={setPageSize} onRowChange={setPageSize}
/> />
@@ -917,7 +848,7 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
<Icon icon='heroicons:x-mark' width={20} height={20} /> <Icon icon='heroicons:x-mark' width={20} height={20} />
</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-1.5'> <div className='p-4 flex flex-col gap-1.5'>
<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'>
@@ -927,29 +858,18 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
<DateInput <DateInput
name='start_date' name='start_date'
value={formik.values.start_date || ''} value={formik.values.start_date || ''}
errorMessage={formik.errors.start_date}
onChange={handleStartDateChange} onChange={handleStartDateChange}
className={{ wrapper: 'w-full' }} className={{ wrapper: 'w-full' }}
isNestedModal isNestedModal
isError={
formik.touched.start_date &&
Boolean(formik.errors.start_date)
}
/> />
<hr className='w-full max-w-3 h-px border-base-content/10' /> <hr className='w-full max-w-3 h-px border-base-content/10' />
<DateInput <DateInput
name='end_date' name='end_date'
value={formik.values.end_date || ''} value={formik.values.end_date || ''}
errorMessage={formik.errors.end_date}
onChange={handleEndDateChange} onChange={handleEndDateChange}
className={{ wrapper: 'w-full' }} className={{ wrapper: 'w-full' }}
isNestedModal isNestedModal
isError={ isError={hasDateError}
(formik.touched.end_date &&
Boolean(formik.errors.end_date)) ||
hasDateError
}
/> />
</div> </div>
</div> </div>
@@ -958,15 +878,10 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
label='Customer' label='Customer'
placeholder='Pilih Customer' placeholder='Pilih Customer'
options={customerOptions} options={customerOptions}
value={customerIdsValue} value={formik.values.customers}
onChange={(val) => { onChange={(val) =>
formik.setFieldValue( formik.setFieldValue('customers', Array.isArray(val) ? val : [])
'customer_ids', }
Array.isArray(val) && val.length > 0
? val.map((v: OptionType) => String(v.value)).join(',')
: null
);
}}
onInputChange={setCustomerInputValue} onInputChange={setCustomerInputValue}
isLoading={isLoadingCustomers} isLoading={isLoadingCustomers}
isClearable isClearable
@@ -978,14 +893,15 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
label='Filter Berdasarkan' label='Filter Berdasarkan'
placeholder='Pilih Filter Berdasarkan' placeholder='Pilih Filter Berdasarkan'
options={dataTypeOptions} options={dataTypeOptions}
value={filterByValue} value={formik.values.filterBy ?? null}
onChange={(val) => { onChange={(val) =>
if (!Array.isArray(val)) { formik.setFieldValue(
formik.setFieldValue('filter_by', val?.value || null); 'filterBy',
!Array.isArray(val) ? (val ?? undefined) : undefined
)
} }
}}
className={{ wrapper: 'w-full' }} className={{ wrapper: 'w-full' }}
isClearable={true} isClearable
/> />
</div> </div>
@@ -1001,7 +917,7 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
<Button <Button
type='submit' type='submit'
className='min-w-40 text-sm rounded-lg py-3 text-white font-semibold' className='min-w-40 text-sm rounded-lg py-3 text-white font-semibold'
disabled={hasDateError || !formik.isValid || formik.isSubmitting} disabled={hasDateError}
> >
Apply Filter Apply Filter
</Button> </Button>