From ba679865c4c896737dfbc12b446feac07cf7ba82 Mon Sep 17 00:00:00 2001 From: randy-ar Date: Wed, 14 Jan 2026 10:40:54 +0700 Subject: [PATCH 1/2] fix(FE): fixing laporan ledger debt supplier --- .../pages/report/finance/FinanceTabs.tsx | 12 +- .../finance/export/DebtSupllierExportPDF.tsx | 73 +++++++++- .../finance/export/DebtSupplierExportXLSX.tsx | 57 +++++--- .../report/finance/tab/DebtSupplierTab.tsx | 132 ++++++++---------- src/services/api/report/debt-supplier.ts | 6 +- 5 files changed, 172 insertions(+), 108 deletions(-) diff --git a/src/components/pages/report/finance/FinanceTabs.tsx b/src/components/pages/report/finance/FinanceTabs.tsx index aaaae985..58d1e78b 100644 --- a/src/components/pages/report/finance/FinanceTabs.tsx +++ b/src/components/pages/report/finance/FinanceTabs.tsx @@ -8,16 +8,16 @@ const FinanceTabs = () => { const tabs = [ { id: '1', - label: 'Kontrol Pembayaran Customer', - - content: , - }, - { - id: '2', label: 'Rekapitulasi Hutang Ke Supplier', content: , }, + { + id: '2', + label: 'Kontrol Pembayaran Customer', + + content: , + }, ]; return ( diff --git a/src/components/pages/report/finance/export/DebtSupllierExportPDF.tsx b/src/components/pages/report/finance/export/DebtSupllierExportPDF.tsx index 7f6fa45b..083904a5 100644 --- a/src/components/pages/report/finance/export/DebtSupllierExportPDF.tsx +++ b/src/components/pages/report/finance/export/DebtSupllierExportPDF.tsx @@ -176,7 +176,7 @@ const createPDFDocument = (params: DebtSupplierExportPDFParams) => { No. PO - Tgl Terima + Tgl Terima/Bayar Tgl PO @@ -191,19 +191,19 @@ const createPDFDocument = (params: DebtSupplierExportPDFParams) => { Gudang - Tgl Jatuh Tempo + Jatuh Tempo Status Jatuh Tempo - Total Harga + Nominal Pembelian (Rp) - Pembayaran + Pembayaran (Rp) - Hutang + Sisa Saldo Hutang (Rp) Status @@ -213,6 +213,65 @@ const createPDFDocument = (params: DebtSupplierExportPDFParams) => { + {/* Initial Balance Row */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {formatCurrency(supplierReport.initial_balance || 0)} + + + + + + + + + + {/* Table Body */} {supplierReport.rows.map((item, index) => ( { - {formatCurrency(item.debt_price)} + {formatCurrency(item.balance)} {item.status || '-'} diff --git a/src/components/pages/report/finance/export/DebtSupplierExportXLSX.tsx b/src/components/pages/report/finance/export/DebtSupplierExportXLSX.tsx index 58b07e30..3ba96a22 100644 --- a/src/components/pages/report/finance/export/DebtSupplierExportXLSX.tsx +++ b/src/components/pages/report/finance/export/DebtSupplierExportXLSX.tsx @@ -2,7 +2,7 @@ import * as XLSX from 'xlsx'; import { formatDate } from '@/lib/helper'; -import { DebtSupplier } from '@/types/api/report/debt-supplier'; +import { DebtRow, DebtSupplier } from '@/types/api/report/debt-supplier'; interface DebtSupplierExportExcelParams { data: DebtSupplier[]; @@ -21,12 +21,29 @@ export const generateDebtSupplierExcel = ( const supplierData = supplierReport.rows; const supplierName = supplierReport.supplier.name || 'Unknown Supplier'; - const excelData: { [key: string]: string | number }[] = supplierData.map( - (item, index) => ({ + const excelData: { [key: string]: string | number }[] = [ + { + No: '', + 'Nomor PR': '', + 'Nomor PO': '', + 'Tanggal Terima/Bayar': '', + 'Tanggal PO': '', + 'Aging (Hari)': '', + Area: '', + Gudang: '', + 'Jatuh Tempo': '', + 'Status Jatuh Tempo': '', + 'Nominal Pembelian (Rp)': '', + 'Pembayaran (Rp)': '', + 'Sisa Saldo Hutang (Rp)': supplierReport.initial_balance || 0, + Status: '', + 'Nomor Perjalanan': '', + }, + ...supplierData.map((item, index) => ({ No: index + 1, 'Nomor PR': item.pr_number || '', 'Nomor PO': item.po_number || '', - 'Tanggal Terima': item.received_date + 'Tanggal Terima/Bayar': item.received_date ? item.received_date != '-' ? formatDate(item.received_date, 'MM/DD/YYYY') : '-' @@ -39,35 +56,35 @@ export const generateDebtSupplierExcel = ( 'Aging (Hari)': item.aging || 0, Area: item.area?.name || '', Gudang: item.warehouse?.name || '', - 'Tanggal Jatuh Tempo': item.due_date + 'Jatuh Tempo': item.due_date ? item.due_date != '-' ? formatDate(item.due_date, 'MM/DD/YYYY') : '-' : '-', 'Status Jatuh Tempo': item.due_status || '', - 'Total Harga': item.total_price || 0, - 'Harga Pembayaran': item.payment_price || 0, - 'Harga Hutang': item.debt_price || 0, + 'Nominal Pembelian (Rp)': item.total_price || 0, + 'Pembayaran (Rp)': item.payment_price || 0, + 'Sisa Saldo Hutang (Rp)': item.debt_price || 0, Status: item.status || '', 'Nomor Perjalanan': item.travel_number || '', - }) - ); + })), + ]; if (supplierReport.total) { excelData.push({ No: 'Total', 'Nomor PR': '', 'Nomor PO': '', - 'Tanggal Terima': '', + 'Tanggal Terima/Bayar': '', 'Tanggal PO': '', 'Aging (Hari)': supplierReport.total.aging || 0, Area: '', Gudang: '', - 'Tanggal Jatuh Tempo': '', + 'Jatuh Tempo': '', 'Status Jatuh Tempo': '', - 'Total Harga': supplierReport.total.total_price || 0, - 'Harga Pembayaran': supplierReport.total.payment_price || 0, - 'Harga Hutang': supplierReport.total.debt_price || 0, + 'Nominal Pembelian (Rp)': supplierReport.total.total_price || 0, + 'Pembayaran (Rp)': supplierReport.total.payment_price || 0, + 'Sisa Saldo Hutang (Rp)': supplierReport.total.debt_price || 0, Status: '', 'Nomor Perjalanan': '', }); @@ -79,16 +96,16 @@ export const generateDebtSupplierExcel = ( { wch: 5 }, // No { wch: 15 }, // Nomor PR { wch: 15 }, // Nomor PO - { wch: 15 }, // Tanggal PR + { wch: 15 }, // Tanggal Terima/Bayar { wch: 15 }, // Tanggal PO { wch: 12 }, // Aging { wch: 15 }, // Area { wch: 15 }, // Gudang - { wch: 18 }, // Tanggal Jatuh Tempo + { wch: 18 }, // Jatuh Tempo { wch: 18 }, // Status Jatuh Tempo - { wch: 15 }, // Total Harga - { wch: 15 }, // Harga Pembayaran - { wch: 15 }, // Harga Hutang + { wch: 15 }, // Nominal Pembelian (Rp) + { wch: 15 }, // Pembayaran (Rp) + { wch: 15 }, // Sisa Saldo Hutang (Rp) { wch: 12 }, // Status { wch: 15 }, // Nomor Perjalanan ]; diff --git a/src/components/pages/report/finance/tab/DebtSupplierTab.tsx b/src/components/pages/report/finance/tab/DebtSupplierTab.tsx index 5a72ea3c..14bd7e70 100644 --- a/src/components/pages/report/finance/tab/DebtSupplierTab.tsx +++ b/src/components/pages/report/finance/tab/DebtSupplierTab.tsx @@ -21,7 +21,6 @@ import { ColumnDef } from '@tanstack/react-table'; import { useCallback, useMemo, useState } from 'react'; import toast from 'react-hot-toast'; import useSWR from 'swr'; -import Pagination from '@/components/Pagination'; import { DebtSupplierApi } from '@/services/api/report/debt-supplier'; const DebtSupplierTab = () => { @@ -30,10 +29,6 @@ const DebtSupplierTab = () => { const [isExcelExportLoading, setIsExcelExportLoading] = useState(false); const isAnyExportLoading = isPdfExportLoading || isExcelExportLoading; - // ===== PAGINATION STATE ===== - const [currentPage, setCurrentPage] = useState(1); - const [pageSize, setPageSize] = useState(10); - // ===== SUBMISSION STATE ===== const [isSubmitted, setIsSubmitted] = useState(false); @@ -71,18 +66,10 @@ const DebtSupplierTab = () => { const handleApplyFilters = useCallback(() => { const errors: Record = {}; - if (!filterStartDate) { - errors.start_date = 'Tanggal mulai wajib diisi'; - } - if (!filterEndDate) { - errors.end_date = 'Tanggal akhir wajib diisi'; - } - setFilterErrors(errors); if (Object.keys(errors).length === 0) { setIsSubmitted(true); - setCurrentPage(1); filterModal.closeModal(); } }, [filterModal, filterStartDate, filterEndDate]); @@ -96,11 +83,9 @@ const DebtSupplierTab = () => { filterSupplier.length > 0 ? filterSupplier.map((v) => String(v.value)).join(',') : undefined, - filter_by: filterDateType?.value, + filter_by: filterDateType?.value?.toString() || undefined, start_date: filterStartDate || undefined, end_date: filterEndDate || undefined, - page: currentPage, - limit: pageSize, }; return ['debt-supplier-report', params]; @@ -109,11 +94,9 @@ const DebtSupplierTab = () => { ([, params]) => DebtSupplierApi.getDebtSupplierReport( params.supplier_ids, - params.filter_by?.toString(), + params.filter_by?.toString() || undefined, params.start_date, - params.end_date, - params.page, - params.limit + params.end_date ) ); @@ -138,7 +121,7 @@ const DebtSupplierTab = () => { filterSupplier.length > 0 ? filterSupplier.map((v) => String(v.value)).join(',') : undefined, - filter_by: filterDateType?.value?.toString(), + filter_by: filterDateType?.value?.toString() || undefined, start_date: filterStartDate || undefined, end_date: filterEndDate || undefined, date_type: filterDateType ? filterDateType.value : undefined, @@ -150,9 +133,7 @@ const DebtSupplierTab = () => { params.supplier_ids, params.filter_by, params.start_date, - params.end_date, - params.page, - params.limit + params.end_date ); return isResponseSuccess(response) @@ -207,37 +188,18 @@ const DebtSupplierTab = () => { } }, [debtSupplierExport]); - // ===== PAGINATION HANDLERS ===== - const handlePageChange = (page: number) => { - setCurrentPage(page); - }; - - const handleRowChange = (pageSize: number) => { - setPageSize(pageSize); - }; - - const handleNextPage = () => { - if (meta && currentPage < meta.total_pages) { - setCurrentPage(currentPage + 1); - } - }; - - const handlePrevPage = () => { - if (currentPage > 1) { - setCurrentPage(currentPage - 1); - } - }; - const getTableColumns = (supplier: DebtSupplier): ColumnDef[] => [ { id: 'no', header: 'No', - cell: (props) => props.row.index + 1, + enableSorting: false, + cell: (props) => props.row.index, }, { id: 'pr_number', header: 'Nomor PR', accessorKey: 'pr_number', + enableSorting: false, cell: (props) => { const value = props.row.original.pr_number; return value || '-'; @@ -247,6 +209,7 @@ const DebtSupplierTab = () => { id: 'po_number', header: 'Nomor PO', accessorKey: 'po_number', + enableSorting: false, cell: (props) => { const value = props.row.original.po_number; return value || '-'; @@ -254,8 +217,9 @@ const DebtSupplierTab = () => { }, { id: 'received_date', - header: 'Tanggal Terima', + header: 'Tanggal Terima/Bayar', accessorKey: 'received_date', + enableSorting: false, cell: (props) => { const value = props.row.original.received_date; return value @@ -269,6 +233,7 @@ const DebtSupplierTab = () => { id: 'po_date', header: 'Tanggal PO', accessorKey: 'po_date', + enableSorting: false, cell: (props) => { const value = props.row.original.po_date; return value @@ -282,6 +247,7 @@ const DebtSupplierTab = () => { id: 'aging', header: 'Aging', accessorKey: 'aging', + enableSorting: false, cell: (props) => { const value = props.row.original.aging; return
{formatNumber(value)} Hari
; @@ -295,6 +261,7 @@ const DebtSupplierTab = () => { id: 'area', header: 'Area', accessorKey: 'area', + enableSorting: false, cell: (props) => { const value = props.row.original.area?.name; return value || '-'; @@ -304,6 +271,7 @@ const DebtSupplierTab = () => { id: 'warehouse', header: 'Gudang', accessorKey: 'warehouse', + enableSorting: false, cell: (props) => { const value = props.row.original.warehouse?.name; return value || '-'; @@ -311,8 +279,9 @@ const DebtSupplierTab = () => { }, { id: 'due_date', - header: 'Tanggal Jatuh Tempo', + header: 'Jatuh Tempo', accessorKey: 'due_date', + enableSorting: false, cell: (props) => { const value = props.row.original.due_date; return value @@ -326,6 +295,7 @@ const DebtSupplierTab = () => { id: 'due_status', header: 'Status Jatuh Tempo', accessorKey: 'due_status', + enableSorting: false, cell: (props) => { const value = props.row.original.due_status; return value || '-'; @@ -333,8 +303,9 @@ const DebtSupplierTab = () => { }, { id: 'total_price', - header: 'Total Harga', + header: 'Nominal Pembelian', accessorKey: 'total_price', + enableSorting: false, cell: (props) => { const value = props.row.original.total_price; return ( @@ -354,8 +325,9 @@ const DebtSupplierTab = () => { }, { id: 'payment_price', - header: 'Harga Pembayaran', + header: 'Pembayaran', accessorKey: 'payment_price', + enableSorting: false, cell: (props) => { const value = props.row.original.payment_price; return ( @@ -374,11 +346,12 @@ const DebtSupplierTab = () => { }, }, { - id: 'debt_price', - header: 'Harga Hutang', - accessorKey: 'debt_price', + id: 'balance', + header: 'Sisa Saldo Hutang', + accessorKey: 'balance', + enableSorting: false, cell: (props) => { - const value = props.row.original.debt_price; + const value = props.row.original.balance; return (
{formatCurrency(value)} @@ -398,6 +371,7 @@ const DebtSupplierTab = () => { id: 'status', header: 'Status', accessorKey: 'status', + enableSorting: false, cell: (props) => { const value = props.row.original.status; return value || '-'; @@ -407,6 +381,7 @@ const DebtSupplierTab = () => { id: 'travel_number', header: 'Nomor Perjalanan', accessorKey: 'travel_number', + enableSorting: false, cell: (props) => { const value = props.row.original.travel_number; return value || '-'; @@ -471,9 +446,14 @@ const DebtSupplierTab = () => { collapsible={true} > 0} className={{ containerClassName: 'w-full', @@ -493,26 +473,38 @@ const DebtSupplierTab = () => { 'px-4 py-3 text-xs text-gray-900 whitespace-nowrap', paginationClassName: 'hidden', }} + renderCustomRow={(row) => { + if (row.index == 0) { + return ( + + + + + + ); + } + }} /> ); }) )} - {meta && data.length > 0 && ( -
- -
- )} {/* Filter Modal */} | undefined> { return await this.customRequest>( `debt-supplier`, @@ -28,8 +26,6 @@ export class DebtSupplierApiService extends BaseApiService< filter_by: filter_by, start_date: start_date, end_date: end_date, - page: page, - limit: limit, }, } ); From fc71defa08f9ae0d1e762c6695bd86531e00e543 Mon Sep 17 00:00:00 2001 From: randy-ar Date: Wed, 14 Jan 2026 11:43:10 +0700 Subject: [PATCH 2/2] feat(FE): adding button filter component --- src/components/helper/ButtonFilter.tsx | 40 ++++ .../finance/filter/DebtSupplierFilter.ts | 36 ++++ .../report/finance/tab/DebtSupplierTab.tsx | 184 +++++++++++------- src/lib/formik-helper.ts | 65 ++++++- src/types/api/report/debt-supplier.d.ts | 8 + 5 files changed, 258 insertions(+), 75 deletions(-) create mode 100644 src/components/helper/ButtonFilter.tsx create mode 100644 src/components/pages/report/finance/filter/DebtSupplierFilter.ts diff --git a/src/components/helper/ButtonFilter.tsx b/src/components/helper/ButtonFilter.tsx new file mode 100644 index 00000000..81f70b92 --- /dev/null +++ b/src/components/helper/ButtonFilter.tsx @@ -0,0 +1,40 @@ +import Button, { ButtonProps } from '@/components/Button'; +import { getFilledFormikValuesCount } from '@/lib/formik-helper'; +import { Icon } from '@iconify/react'; +import { FormikValues } from 'formik'; + +export type ButtonFilterProps = ButtonProps & { + values: FormikValues; + onClick: () => void; +}; + +const ButtonFilter = ({ values, onClick, ...props }: ButtonFilterProps) => { + return ( + + ); +}; + +export default ButtonFilter; diff --git a/src/components/pages/report/finance/filter/DebtSupplierFilter.ts b/src/components/pages/report/finance/filter/DebtSupplierFilter.ts new file mode 100644 index 00000000..1c1c2fac --- /dev/null +++ b/src/components/pages/report/finance/filter/DebtSupplierFilter.ts @@ -0,0 +1,36 @@ +import { OptionType } from '@/components/input/SelectInput'; +import * as yup from 'yup'; + +export type DebtSupplierFilterType = { + startDate: string | null | undefined; + endDate: string | null | undefined; + supplierIds: OptionType[] | null | undefined; + filterBy: OptionType | null | undefined; +}; + +export const DebtSupplierFilterSchema: yup.ObjectSchema = + yup.object({ + startDate: yup.string().optional().notRequired(), + endDate: yup.string().optional().notRequired(), + supplierIds: yup + .array() + .of( + yup.object({ + value: yup.mixed().required(), + label: yup.string().required(), + }) + ) + .optional() + .notRequired(), + filterBy: yup + .object({ + value: yup.mixed().required(), + label: yup.string().required(), + }) + .optional() + .notRequired(), + }); + +export type DebtSupplierFilterValues = yup.InferType< + typeof DebtSupplierFilterSchema +>; diff --git a/src/components/pages/report/finance/tab/DebtSupplierTab.tsx b/src/components/pages/report/finance/tab/DebtSupplierTab.tsx index 14bd7e70..2214ecd6 100644 --- a/src/components/pages/report/finance/tab/DebtSupplierTab.tsx +++ b/src/components/pages/report/finance/tab/DebtSupplierTab.tsx @@ -13,7 +13,11 @@ import Table from '@/components/Table'; import { isResponseSuccess } from '@/lib/api-helper'; import { formatCurrency, formatDate, formatNumber } from '@/lib/helper'; import { SupplierApi } from '@/services/api/master-data'; -import { DebtRow, DebtSupplier } from '@/types/api/report/debt-supplier'; +import { + DebtRow, + DebtSupplier, + DebtSupplierFilter, +} from '@/types/api/report/debt-supplier'; import { generateDebtSupplierExcel } from '@/components/pages/report/finance/export/DebtSupplierExportXLSX'; import { generateDebtSupplierPDF } from '@/components/pages/report/finance/export/DebtSupllierExportPDF'; import { Icon } from '@iconify/react'; @@ -22,6 +26,13 @@ import { useCallback, useMemo, useState } from 'react'; import toast from 'react-hot-toast'; import useSWR from 'swr'; import { DebtSupplierApi } from '@/services/api/report/debt-supplier'; +import { useFormik } from 'formik'; +import { + DebtSupplierFilterSchema, + DebtSupplierFilterType, +} from '@/components/pages/report/finance/filter/DebtSupplierFilter'; +import { getFilledFormikValuesCount } from '@/lib/formik-helper'; +import ButtonFilter from '@/components/helper/ButtonFilter'; const DebtSupplierTab = () => { // ===== STATE MANAGEMENT ===== @@ -30,15 +41,14 @@ const DebtSupplierTab = () => { const isAnyExportLoading = isPdfExportLoading || isExcelExportLoading; // ===== SUBMISSION STATE ===== + const [filterParams, setFilterParams] = useState({ + start_date: undefined, + end_date: undefined, + supplier_ids: undefined, + filter_by: undefined, + }); const [isSubmitted, setIsSubmitted] = useState(false); - // ===== FILTER STATE ===== - const [filterSupplier, setFilterSupplier] = useState([]); - const [filterStartDate, setFilterStartDate] = useState(''); - const [filterEndDate, setFilterEndDate] = useState(''); - const [filterDateType, setFilterDateType] = useState(); - const [filterErrors, setFilterErrors] = useState>({}); - const filterModal = useModal(); const { options: supplierOptions, isLoadingOptions: isLoadingSuppliers } = @@ -54,38 +64,51 @@ const DebtSupplierTab = () => { [] ); - // ===== FILTER HANDLERS ===== - const handleResetFilters = useCallback(() => { - setIsSubmitted(false); - setFilterSupplier([]); - setFilterStartDate(''); - setFilterEndDate(''); - setFilterErrors({}); - }, []); + const handleFilterModalOpen = () => { + filterModal.openModal(); + }; - const handleApplyFilters = useCallback(() => { - const errors: Record = {}; - - setFilterErrors(errors); - - if (Object.keys(errors).length === 0) { - setIsSubmitted(true); + // ===== FORMIK SETUP ===== + const formik = useFormik({ + initialValues: { + startDate: null, + endDate: null, + supplierIds: null, + filterBy: null, + }, + validationSchema: DebtSupplierFilterSchema, + onSubmit: (values) => { + setFilterParams({ + start_date: values.startDate?.toString() || undefined, + end_date: values.endDate?.toString() || undefined, + supplier_ids: + values.supplierIds?.map((v) => String(v.value)).join(',') || + undefined, + filter_by: values.filterBy?.value?.toString() || undefined, + }); filterModal.closeModal(); - } - }, [filterModal, filterStartDate, filterEndDate]); + setIsSubmitted(true); + }, + onReset: (values) => { + setFilterParams({ + start_date: undefined, + end_date: undefined, + supplier_ids: undefined, + filter_by: undefined, + }); + setIsSubmitted(false); + }, + }); // ===== DATA FETCHING ===== const { data: debtSupplier, isLoading } = useSWR( isSubmitted ? () => { const params = { - supplier_ids: - filterSupplier.length > 0 - ? filterSupplier.map((v) => String(v.value)).join(',') - : undefined, - filter_by: filterDateType?.value?.toString() || undefined, - start_date: filterStartDate || undefined, - end_date: filterEndDate || undefined, + supplier_ids: filterParams.supplier_ids, + filter_by: filterParams.filter_by, + start_date: filterParams.start_date, + end_date: filterParams.end_date, }; return ['debt-supplier-report', params]; @@ -94,7 +117,7 @@ const DebtSupplierTab = () => { ([, params]) => DebtSupplierApi.getDebtSupplierReport( params.supplier_ids, - params.filter_by?.toString() || undefined, + params.filter_by, params.start_date, params.end_date ) @@ -118,13 +141,15 @@ const DebtSupplierTab = () => { > => { const params = { supplier_ids: - filterSupplier.length > 0 - ? filterSupplier.map((v) => String(v.value)).join(',') + formik.values.supplierIds && formik.values.supplierIds.length > 0 + ? formik.values.supplierIds.map((v) => String(v.value)).join(',') : undefined, - filter_by: filterDateType?.value?.toString() || undefined, - start_date: filterStartDate || undefined, - end_date: filterEndDate || undefined, - date_type: filterDateType ? filterDateType.value : undefined, + filter_by: formik.values.filterBy?.value?.toString() || undefined, + start_date: formik.values.startDate || undefined, + end_date: formik.values.endDate || undefined, + date_type: formik.values.filterBy + ? formik.values.filterBy.value + : undefined, limit: 100, page: 1, }; @@ -139,7 +164,12 @@ const DebtSupplierTab = () => { return isResponseSuccess(response) ? (response.data as unknown as DebtSupplier[]) : null; - }, [filterSupplier, filterStartDate, filterEndDate]); + }, [ + formik.values.supplierIds, + formik.values.startDate, + formik.values.endDate, + formik.values.filterBy, + ]); // ===== EXPORT HANDLERS ===== const handleExportExcel = useCallback(async () => { @@ -396,10 +426,11 @@ const DebtSupplierTab = () => { className={{ wrapper: 'w-full', body: 'p-1!' }} >
- + { modalBox: 'p-0 rounded-2xl xl:max-w-4/12 max-w-sm', }} > -
+
{/* Modal Header */}
@@ -534,37 +569,31 @@ const DebtSupplierTab = () => {
{ - setFilterStartDate(e.target.value); - setFilterErrors((prev) => ({ ...prev, start_date: '' })); + formik.setFieldValue('startDate', e.target.value || null); }} className={{ wrapper: 'w-full' }} + isError={ + formik.touched.startDate && !!formik.errors.startDate + } + errorMessage={formik.errors.startDate} /> - {filterErrors.start_date && ( -

- {filterErrors.start_date} -

- )}
{ - setFilterEndDate(e.target.value); - setFilterErrors((prev) => ({ ...prev, end_date: '' })); + formik.setFieldValue('endDate', e.target.value || null); }} className={{ wrapper: 'w-full' }} + isError={formik.touched.endDate && !!formik.errors.endDate} + errorMessage={formik.errors.endDate} /> - {filterErrors.end_date && ( -

- {filterErrors.end_date} -

- )}
@@ -574,15 +603,20 @@ const DebtSupplierTab = () => { placeholder='Pilih Supplier' isMulti options={supplierOptions} - value={filterSupplier} + value={formik.values.supplierIds || []} onChange={(val) => { - setFilterSupplier( - Array.isArray(val) ? val : val ? [val] : [] + formik.setFieldValue( + 'supplierIds', + Array.isArray(val) ? val : val ? [val] : null ); }} isLoading={isLoadingSuppliers} isClearable className={{ wrapper: 'w-full' }} + isError={ + formik.touched.supplierIds && !!formik.errors.supplierIds + } + errorMessage={formik.errors.supplierIds as string} />
@@ -591,12 +625,17 @@ const DebtSupplierTab = () => { label='Filter Berdasarkan' placeholder='Pilih Filter Berdasarkan' options={dataTypeOptions} - value={filterDateType} + value={formik.values.filterBy || null} onChange={(val) => { - setFilterDateType(val ? (val as OptionType) : undefined); + formik.setFieldValue( + 'filterBy', + val ? (val as OptionType) : null + ); }} className={{ wrapper: 'w-full' }} isClearable + isError={formik.touched.filterBy && !!formik.errors.filterBy} + errorMessage={formik.errors.filterBy as string} />
@@ -606,18 +645,15 @@ const DebtSupplierTab = () => { - - +
); diff --git a/src/lib/formik-helper.ts b/src/lib/formik-helper.ts index 9c457e86..17c6bc7d 100644 --- a/src/lib/formik-helper.ts +++ b/src/lib/formik-helper.ts @@ -1,4 +1,4 @@ -import { FormikErrors } from 'formik'; +import { FormikErrors, FormikValues } from 'formik'; export type ErrorMessage = { key: string; @@ -69,3 +69,66 @@ export function getUniqueFormikErrors(errors: FormikErrors): string[] { export function getAllFormikErrors(errors: FormikErrors): ErrorMessage[] { return parseFormikErrors(errors); } + +/** + * Check if a value is considered "filled" (not empty) + * @param value - Value to check + * @returns True if value is filled, false otherwise + */ +function isValueFilled(value: unknown): boolean { + // Check for null or undefined + if (value === null || value === undefined) { + return false; + } + + // Check for empty string + if (typeof value === 'string' && value.trim() === '') { + return false; + } + + // Check for empty array + if (Array.isArray(value) && value.length === 0) { + return false; + } + + // Check for empty object (but not Date or other special objects) + if ( + typeof value === 'object' && + !Array.isArray(value) && + !(value instanceof Date) && + Object.keys(value).length === 0 + ) { + return false; + } + + return true; +} + +/** + * Count the number of filled (non-empty) values in Formik values object + * @param values - Formik values object + * @returns Number of filled values + * @example + * const values = { + * name: 'John', + * email: '', + * age: null, + * tags: ['tag1', 'tag2'], + * emptyArray: [], + * }; + * getFilledFormikValuesCount(values); // Returns 2 (name and tags) + */ +export function getFilledFormikValuesCount( + values: T +): number { + let count = 0; + + Object.keys(values).forEach((key) => { + const value = values[key]; + if (isValueFilled(value)) { + count++; + } + }); + + return count; +} diff --git a/src/types/api/report/debt-supplier.d.ts b/src/types/api/report/debt-supplier.d.ts index 46849599..c00db7df 100644 --- a/src/types/api/report/debt-supplier.d.ts +++ b/src/types/api/report/debt-supplier.d.ts @@ -33,3 +33,11 @@ export interface DebtRow { travel_number: string; balance: number; } + +// Filter Param +export interface DebtSupplierFilter { + start_date?: string; + end_date?: string; + supplier_ids?: string; + filter_by?: string; +}
+
+ {formatCurrency(row.original.balance)} +
+