Merge branch 'dev/hotfix/restu' into 'development'

[HOTFIX/FE] Adjustment Param State on Customer Payment Tab

See merge request mbugroup/lti-web-client!313
This commit is contained in:
Rivaldi A N S
2026-02-05 08:54:05 +00:00
3 changed files with 202 additions and 78 deletions
@@ -96,8 +96,7 @@ interface CustomerPaymentExportPDFParams {
// sales?: string;
start_date?: string;
end_date?: string;
// TODO: Uncomment when BE is ready
// filter_by?: string;
filter_by?: string;
};
}
@@ -3,18 +3,18 @@ import useSWR from 'swr';
import { Icon } from '@iconify/react';
import Card from '@/components/Card';
import Badge from '@/components/Badge';
import SelectInput, { useSelect } from '@/components/input/SelectInput';
import { useSelect } from '@/components/input/SelectInput';
import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
import SelectInputRadio from '@/components/input/SelectInputRadio';
import DateInput from '@/components/input/DateInput';
import { CustomerApi } from '@/services/api/master-data';
import { FinanceApi } from '@/services/api/report/finance-report';
import { UserApi } from '@/services/api/user';
// import { UserApi } from '@/services/api/user';
import Table from '@/components/Table';
import { ColumnDef } from '@tanstack/react-table';
import { formatCurrency, formatDate, formatNumber, cn } from '@/lib/helper';
import {
CustomerPaymentReport,
CustomerPaymentRow,
CustomerPaymentSummary,
} from '@/types/api/report/customer-payment';
import { isResponseSuccess } from '@/lib/api-helper';
@@ -48,38 +48,58 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
const [isSubmitted, setIsSubmitted] = useState(false);
// ===== FILTER STATE =====
const [appliedFilterCustomer, setAppliedFilterCustomer] = useState<
typeof customerOptions
>([]);
// TODO: Uncomment when BE is ready
// const [appliedFilterSales, setAppliedFilterSales] = useState<
// typeof salesOptions
// >([]);
const [appliedFilterByType, setAppliedFilterByType] = useState<
(typeof dataTypeOptions)[0] | null
>(null);
const [appliedFilterStartDate, setAppliedFilterStartDate] = useState('');
const [appliedFilterEndDate, setAppliedFilterEndDate] = useState('');
const [dateErrorShown, setDateErrorShown] = useState(false);
const [hasDateError, setHasDateError] = useState(false);
const [filterCustomer, setFilterCustomer] = useState<typeof customerOptions>(
[]
);
// TODO: Uncomment when BE is ready
// const [filterSales, setFilterSales] = useState<typeof salesOptions>([]);
const [filterSales, setFilterSales] = useState<typeof salesOptions>([]);
const [filterStartDate, setFilterStartDate] = useState('');
const [filterEndDate, setFilterEndDate] = useState('');
const filterModal = useModal();
const dataTypeOptions = useMemo(
() => [
{ value: 'trans_date', label: 'Tanggal Jual/Bayar' },
{ value: 'realization_date', label: 'Tanggal Realisasi' },
],
[]
);
const [filterByType, setFilterByType] = useState<
(typeof dataTypeOptions)[0] | null
>(null);
const {
options: customerOptions,
setInputValue: setCustomerInputValue,
isLoadingOptions: isLoadingCustomers,
loadMore: loadMoreCustomers,
hasMore: hasMoreCustomers,
} = useSelect(CustomerApi.basePath, 'id', 'name', 'search');
// TODO: Uncomment when BE is ready
const {
options: salesOptions,
setInputValue: setSalesInputValue,
isLoadingOptions: isLoadingSales,
loadMore: loadMoreSales,
hasMore: hasMoreSales,
} = useSelect(UserApi.basePath, 'id', 'name', 'search');
const dataTypeOptions = useMemo(
() => [{ value: 'do_date', label: 'Tanggal Jual' }],
[]
);
// const {
// options: salesOptions,
// setInputValue: setSalesInputValue,
// isLoadingOptions: isLoadingSales,
// loadMore: loadMoreSales,
// hasMore: hasMoreSales,
// } = useSelect(UserApi.basePath, 'id', 'name', 'search');
const getPaymentStatusColor = (notes: string) => {
const normalizedValue = notes.toLowerCase();
@@ -119,49 +139,145 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
// ===== FILTER HANDLERS =====
const handleFilterModalOpen = useCallback(() => {
setFilterCustomer(appliedFilterCustomer);
// setFilterSales(appliedFilterSales);
setFilterByType(appliedFilterByType);
setFilterStartDate(appliedFilterStartDate);
setFilterEndDate(appliedFilterEndDate);
filterModal.openModal();
}, [filterModal]);
}, [
filterModal,
appliedFilterCustomer,
appliedFilterByType,
appliedFilterStartDate,
appliedFilterEndDate,
]);
const handleResetFilters = useCallback(() => {
setIsSubmitted(false);
setFilterCustomer([]);
setFilterSales([]);
setFilterByType(null);
setFilterStartDate('');
setFilterEndDate('');
}, []);
setAppliedFilterCustomer([]);
setAppliedFilterByType(null);
setAppliedFilterStartDate('');
setAppliedFilterEndDate('');
setHasDateError(false);
if (dateErrorShown) {
toast.dismiss();
setDateErrorShown(false);
}
}, [dateErrorShown]);
const handleApplyFilters = useCallback(() => {
setAppliedFilterCustomer(filterCustomer);
setAppliedFilterByType(filterByType);
setAppliedFilterStartDate(filterStartDate);
setAppliedFilterEndDate(filterEndDate);
setIsSubmitted(true);
setCurrentPage(1);
filterModal.closeModal();
}, [filterModal]);
}, [
filterModal,
filterCustomer,
filterByType,
filterStartDate,
filterEndDate,
]);
const handleStartDateChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setFilterStartDate(value);
if (value && filterEndDate) {
const startDate = new Date(value);
const endDateObj = new Date(filterEndDate);
if (endDateObj < startDate) {
setHasDateError(true);
if (!dateErrorShown) {
toast.error('Tanggal akhir tidak boleh masa lampau', {
duration: Infinity,
});
setDateErrorShown(true);
}
} else {
setHasDateError(false);
if (dateErrorShown) {
toast.dismiss();
setDateErrorShown(false);
}
}
} else {
setHasDateError(false);
}
},
[filterEndDate, dateErrorShown]
);
const handleEndDateChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setFilterEndDate(value);
if (value && filterStartDate) {
const startDateObj = new Date(filterStartDate);
const endDate = new Date(value);
if (endDate < startDateObj) {
setHasDateError(true);
if (!dateErrorShown) {
toast.error('Tanggal akhir tidak boleh masa lampau', {
duration: Infinity,
});
setDateErrorShown(true);
}
return;
}
}
setHasDateError(false);
if (dateErrorShown) {
toast.dismiss();
setDateErrorShown(false);
}
},
[filterStartDate, dateErrorShown]
);
// ===== ACTIVE FILTERS COUNT =====
const activeFiltersCount = useMemo(() => {
let count = 0;
// Date filter (start_date + end_date = 1 filter)
if (filterStartDate || filterEndDate) {
if (appliedFilterStartDate || appliedFilterEndDate) {
count += 1;
}
// Customer filter
if (filterCustomer.length > 0) {
if (appliedFilterCustomer.length > 0) {
count += 1;
}
// Filter by type filter (hanya dihitung jika ada nilai yang dipilih)
if (appliedFilterByType) {
count += 1;
}
// TODO: Uncomment when BE is ready
// // Sales filter
// if (filterSales.length > 0) {
// if (appliedFilterSales.length > 0) {
// count += 1;
// }
return count;
}, [
filterStartDate,
filterEndDate,
filterCustomer,
// filterSales,
appliedFilterStartDate,
appliedFilterEndDate,
appliedFilterCustomer,
appliedFilterByType,
]);
const hasFilters = activeFiltersCount > 0;
@@ -172,17 +288,20 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
? () => {
const params = {
customer_ids:
filterCustomer.length > 0
? filterCustomer.map((v) => String(v.value)).join(',')
appliedFilterCustomer.length > 0
? appliedFilterCustomer.map((v) => String(v.value)).join(',')
: undefined,
// TODO: Uncomment when BE is ready
// sales_id:
// filterSales.length > 0
// ? filterSales.map((v) => String(v.value)).join(',')
// appliedFilterSales.length > 0
// ? appliedFilterSales.map((v) => String(v.value)).join(',')
// : undefined,
// filter_by: 'do_date' as const,
start_date: filterStartDate || undefined,
end_date: filterEndDate || undefined,
filter_by: appliedFilterByType?.value as
| 'trans_date'
| 'realization_date'
| undefined,
start_date: appliedFilterStartDate || undefined,
end_date: appliedFilterEndDate || undefined,
page: currentPage,
limit: pageSize,
};
@@ -193,8 +312,7 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
([, params]) =>
FinanceApi.getCustomerPaymentReport(
params.customer_ids,
undefined, // TODO: Change to params.sales_id when BE is ready
undefined, // TODO: Change to params.filter_by when BE is ready
params.filter_by,
params.start_date,
params.end_date,
params.page,
@@ -216,24 +334,27 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
> => {
const params = {
customer_ids:
filterCustomer.length > 0
? filterCustomer.map((v) => String(v.value)).join(',')
appliedFilterCustomer.length > 0
? appliedFilterCustomer.map((v) => String(v.value)).join(',')
: undefined,
// TODO: Uncomment when BE is ready
// sales_id:
// filterSales.length > 0
// ? filterSales.map((v) => String(v.value)).join(',')
// appliedFilterSales.length > 0
// ? appliedFilterSales.map((v) => String(v.value)).join(',')
// : undefined,
start_date: filterStartDate || undefined,
end_date: filterEndDate || undefined,
filter_by: appliedFilterByType?.value as
| 'trans_date'
| 'realization_date'
| undefined,
start_date: appliedFilterStartDate || undefined,
end_date: appliedFilterEndDate || undefined,
limit: 100,
page: 1,
};
const response = await FinanceApi.getCustomerPaymentReport(
params.customer_ids,
undefined, // TODO: Change to params.sales_id when BE is ready
undefined, // TODO: Change to params.filter_by when BE is ready
params.filter_by,
params.start_date,
params.end_date,
params.page,
@@ -243,7 +364,13 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
return isResponseSuccess(response)
? (response.data as unknown as CustomerPaymentReport[])
: null;
}, [filterCustomer, filterSales, filterStartDate, filterEndDate]);
}, [
appliedFilterCustomer,
// appliedFilterSales,
appliedFilterStartDate,
appliedFilterEndDate,
appliedFilterByType,
]);
// ===== EXPORT HANDLERS =====
const handleExportExcel = useCallback(async () => {
@@ -287,18 +414,20 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
data: allDataForExport,
params: {
customer_name:
filterCustomer.length > 0
? filterCustomer.map((c) => c.label).join(', ')
appliedFilterCustomer.length > 0
? appliedFilterCustomer.map((c) => c.label).join(', ')
: undefined,
// TODO: Uncomment when BE is ready
// sales:
// filterSales.length > 0
// ? filterSales.map((s) => s.label).join(', ')
// appliedFilterSales.length > 0
// ? appliedFilterSales.map((s) => s.label).join(', ')
// : undefined,
start_date: filterStartDate || undefined,
end_date: filterEndDate || undefined,
// TODO: Uncomment when BE is ready
// filter_by: 'do_date' as const,
start_date: appliedFilterStartDate || undefined,
end_date: appliedFilterEndDate || undefined,
filter_by: appliedFilterByType?.value as
| 'trans_date'
| 'realization_date'
| undefined,
},
});
toast.success('PDF berhasil dibuat dan diunduh.');
@@ -406,7 +535,7 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
footer: () => <div className='font-semibold text-gray-900'>Total</div>,
},
{
id: 'do_date_or_payment_date',
id: 'trans_date',
header: 'Tanggal Jual/Bayar',
accessorKey: 'trans_date',
enableSorting: false,
@@ -811,9 +940,7 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
<DateInput
name='start_date'
value={filterStartDate}
onChange={(e) => {
setFilterStartDate(e.target.value);
}}
onChange={handleStartDateChange}
className={{ wrapper: 'w-full' }}
isNestedModal
/>
@@ -822,9 +949,7 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
<DateInput
name='end_date'
value={filterEndDate}
onChange={(e) => {
setFilterEndDate(e.target.value);
}}
onChange={handleEndDateChange}
className={{ wrapper: 'w-full' }}
isNestedModal
/>
@@ -864,17 +989,18 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
/>
</div> */}
{/* TODO: Uncomment when BE is ready */}
{/* <div>
<SelectInput
label='Filter Berdasarkan'
placeholder='Pilih Filter Berdasarkan'
options={dataTypeOptions}
value={dataTypeOptions[0]}
isDisabled={true}
className={{ wrapper: 'w-full' }}
/>
</div> */}
<SelectInputRadio
label='Filter Berdasarkan'
placeholder='Pilih Filter Berdasarkan'
options={dataTypeOptions}
value={filterByType}
onChange={(val) => {
if (val && !Array.isArray(val)) {
setFilterByType(val);
}
}}
className={{ wrapper: 'w-full' }}
/>
{/* Action Buttons */}
</div>
@@ -889,6 +1015,7 @@ const CustomerPaymentTab = ({ tabId }: CustomerPaymentTabProps) => {
<Button
className='min-w-40 text-sm rounded-lg py-3 text-white font-semibold'
onClick={handleApplyFilters}
disabled={hasDateError}
>
Apply Filter
</Button>
+2 -4
View File
@@ -15,9 +15,7 @@ export class FinanceApiService extends BaseApiService<
customer_ids?: string,
// TODO: Uncomment when BE is ready
// sales_id?: string,
// filter_by?: 'do_date',
sales_id?: string,
filter_by?: 'do_date' | undefined,
filter_by?: 'trans_date' | 'realization_date',
start_date?: string,
end_date?: string,
page?: number,
@@ -31,7 +29,7 @@ export class FinanceApiService extends BaseApiService<
customer_ids: customer_ids,
// TODO: Uncomment when BE is ready
// sales_id: sales_id,
// filter_by: filter_by,
filter_by: filter_by,
start_date: start_date,
end_date: end_date,
page: page,