mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-24 07:15:44 +00:00
752 lines
23 KiB
TypeScript
752 lines
23 KiB
TypeScript
import Button from '@/components/Button';
|
||
import Card from '@/components/Card';
|
||
import Dropdown from '@/components/Dropdown';
|
||
import DateInput from '@/components/input/DateInput';
|
||
import SelectInput, {
|
||
OptionType,
|
||
useSelect,
|
||
} from '@/components/input/SelectInput';
|
||
import Menu from '@/components/menu/Menu';
|
||
import MenuItem from '@/components/menu/MenuItem';
|
||
import Modal, { useModal } from '@/components/Modal';
|
||
import Table, { TABLE_DEFAULT_STYLING } from '@/components/Table';
|
||
import { isResponseSuccess } from '@/lib/api-helper';
|
||
import { cn, formatCurrency, formatDate, formatNumber } from '@/lib/helper';
|
||
import { SupplierApi } from '@/services/api/master-data';
|
||
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';
|
||
import { ColumnDef } from '@tanstack/react-table';
|
||
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 ButtonFilter from '@/components/helper/ButtonFilter';
|
||
import Badge from '@/components/Badge';
|
||
import { Color } from '@/types/theme';
|
||
import { Supplier } from '@/types/api/master-data/supplier';
|
||
import SelectInputCheckbox from '@/components/input/SelectInputCheckbox';
|
||
import SelectInputRadio from '@/components/input/SelectInputRadio';
|
||
|
||
const dueStatus: Record<string, Color> = {
|
||
'Sudah Jatuh Tempo': 'error',
|
||
'Belum Jatuh Tempo': 'success',
|
||
'Mendekati Jatuh Tempo': 'warning',
|
||
};
|
||
|
||
const paymentStatus: Record<string, Color> = {
|
||
'Belum Lunas': 'warning',
|
||
Lunas: 'primary',
|
||
Pembayaran: 'success',
|
||
};
|
||
|
||
const getPillBadge = (
|
||
statusText: string,
|
||
type: 'due' | 'payment' = 'payment'
|
||
) => {
|
||
// Get color based on type
|
||
const color =
|
||
type === 'due'
|
||
? dueStatus[statusText] || 'neutral'
|
||
: paymentStatus[statusText] || 'neutral';
|
||
|
||
return (
|
||
<Badge
|
||
color={color as Color}
|
||
size='sm'
|
||
variant='soft'
|
||
className={{
|
||
badge: `py-2.5 px-2 font-medium text-base-content rounded-full border border-${color}`,
|
||
}}
|
||
statusIndicator
|
||
>
|
||
{statusText}
|
||
</Badge>
|
||
);
|
||
};
|
||
|
||
const DebtSupplierTab = () => {
|
||
// ===== STATE MANAGEMENT =====
|
||
const [isPdfExportLoading, setIsPdfExportLoading] = useState(false);
|
||
const [isExcelExportLoading, setIsExcelExportLoading] = useState(false);
|
||
const isAnyExportLoading = isPdfExportLoading || isExcelExportLoading;
|
||
|
||
// ===== SUBMISSION STATE =====
|
||
const [filterParams, setFilterParams] = useState<DebtSupplierFilter>({
|
||
start_date: undefined,
|
||
end_date: undefined,
|
||
supplier_ids: undefined,
|
||
filter_by: undefined,
|
||
});
|
||
const [isSubmitted, setIsSubmitted] = useState(false);
|
||
|
||
const filterModal = useModal();
|
||
|
||
const {
|
||
setInputValue: setSupplierInputValue,
|
||
options: supplierOptions,
|
||
isLoadingOptions: isLoadingSupplierOptions,
|
||
loadMore: loadMoreSuppliers,
|
||
} = useSelect<Supplier>(SupplierApi.basePath, 'id', 'name');
|
||
|
||
const dataTypeOptions = useMemo(
|
||
() => [
|
||
{ value: 'received_date', label: 'Tanggal Terima' },
|
||
{ value: 'po_date', label: 'Tanggal PO' },
|
||
],
|
||
[]
|
||
);
|
||
|
||
const handleFilterModalOpen = () => {
|
||
filterModal.openModal();
|
||
};
|
||
|
||
// ===== FORMIK SETUP =====
|
||
const formik = useFormik<DebtSupplierFilterType>({
|
||
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();
|
||
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: filterParams.supplier_ids,
|
||
filter_by: filterParams.filter_by,
|
||
start_date: filterParams.start_date,
|
||
end_date: filterParams.end_date,
|
||
};
|
||
|
||
return ['debt-supplier-report', params];
|
||
}
|
||
: null,
|
||
([, params]) =>
|
||
DebtSupplierApi.getDebtSupplierReport(
|
||
params.supplier_ids,
|
||
params.filter_by,
|
||
params.start_date,
|
||
params.end_date
|
||
)
|
||
);
|
||
|
||
const data: DebtSupplier[] = useMemo(
|
||
() =>
|
||
isResponseSuccess(debtSupplier)
|
||
? (debtSupplier?.data as unknown as DebtSupplier[]) || []
|
||
: [],
|
||
[debtSupplier]
|
||
);
|
||
const meta =
|
||
isResponseSuccess(debtSupplier) && debtSupplier?.meta
|
||
? debtSupplier.meta
|
||
: null;
|
||
|
||
// ===== EXPORT DATA FETCHER =====
|
||
const debtSupplierExport = useCallback(async (): Promise<
|
||
DebtSupplier[] | null
|
||
> => {
|
||
const params = {
|
||
supplier_ids:
|
||
formik.values.supplierIds && formik.values.supplierIds.length > 0
|
||
? formik.values.supplierIds.map((v) => String(v.value)).join(',')
|
||
: 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,
|
||
};
|
||
|
||
const response = await DebtSupplierApi.getDebtSupplierReport(
|
||
params.supplier_ids,
|
||
params.filter_by,
|
||
params.start_date,
|
||
params.end_date
|
||
);
|
||
|
||
return isResponseSuccess(response)
|
||
? (response.data as unknown as DebtSupplier[])
|
||
: null;
|
||
}, [
|
||
formik.values.supplierIds,
|
||
formik.values.startDate,
|
||
formik.values.endDate,
|
||
formik.values.filterBy,
|
||
]);
|
||
|
||
// ===== EXPORT HANDLERS =====
|
||
const handleExportExcel = useCallback(async () => {
|
||
setIsExcelExportLoading(true);
|
||
try {
|
||
const allDataForExport = await debtSupplierExport();
|
||
|
||
if (
|
||
!allDataForExport ||
|
||
!Array.isArray(allDataForExport) ||
|
||
allDataForExport.length === 0
|
||
) {
|
||
toast.error('Tidak ada data untuk diekspor.');
|
||
return;
|
||
}
|
||
|
||
generateDebtSupplierExcel({ data: allDataForExport });
|
||
toast.success('Excel berhasil dibuat dan diunduh.');
|
||
} catch {
|
||
toast.error('Gagal membuat Excel. Silakan coba lagi.');
|
||
} finally {
|
||
setIsExcelExportLoading(false);
|
||
}
|
||
}, [debtSupplierExport]);
|
||
|
||
const handleExportPdf = useCallback(async () => {
|
||
setIsPdfExportLoading(true);
|
||
try {
|
||
const allDataForExport = await debtSupplierExport();
|
||
|
||
if (
|
||
!allDataForExport ||
|
||
!Array.isArray(allDataForExport) ||
|
||
allDataForExport.length === 0
|
||
) {
|
||
toast.error('Tidak ada data untuk diekspor.');
|
||
return;
|
||
}
|
||
|
||
await generateDebtSupplierPDF({
|
||
data: allDataForExport,
|
||
params: {
|
||
supplier_name: formik.values.supplierIds
|
||
?.map((v) => v.label)
|
||
.join(', '),
|
||
filter_by: formik.values.filterBy?.label,
|
||
start_date: formik.values.startDate || undefined,
|
||
end_date: formik.values.endDate || undefined,
|
||
},
|
||
});
|
||
toast.success('PDF berhasil dibuat dan diunduh.');
|
||
} catch {
|
||
toast.error('Gagal membuat PDF. Silakan coba lagi.');
|
||
} finally {
|
||
setIsPdfExportLoading(false);
|
||
}
|
||
}, [debtSupplierExport]);
|
||
|
||
const getTableColumns = (supplier: DebtSupplier): ColumnDef<DebtRow>[] => [
|
||
{
|
||
id: 'no',
|
||
header: 'No',
|
||
enableSorting: false,
|
||
cell: (props) => props.row.index,
|
||
footer: () => 'Total',
|
||
},
|
||
{
|
||
id: 'pr_number',
|
||
header: 'Nomor PR',
|
||
accessorKey: 'pr_number',
|
||
enableSorting: false,
|
||
cell: (props) => {
|
||
const value = props.row.original.pr_number;
|
||
return value || '-';
|
||
},
|
||
},
|
||
{
|
||
id: 'po_number',
|
||
header: 'Nomor PO',
|
||
accessorKey: 'po_number',
|
||
enableSorting: false,
|
||
cell: (props) => {
|
||
const value = props.row.original.po_number;
|
||
return value || '-';
|
||
},
|
||
},
|
||
{
|
||
id: 'received_date',
|
||
header: 'Tanggal Terima/Bayar',
|
||
accessorKey: 'received_date',
|
||
enableSorting: false,
|
||
cell: (props) => {
|
||
const value = props.row.original.received_date;
|
||
return value
|
||
? value != '-'
|
||
? formatDate(value, 'DD MMM YYYY')
|
||
: '-'
|
||
: '-';
|
||
},
|
||
},
|
||
{
|
||
id: 'po_date',
|
||
header: 'Tanggal PO',
|
||
accessorKey: 'po_date',
|
||
enableSorting: false,
|
||
cell: (props) => {
|
||
const value = props.row.original.po_date;
|
||
return value
|
||
? value != '-'
|
||
? formatDate(value, 'DD MMM YYYY')
|
||
: '-'
|
||
: '-';
|
||
},
|
||
},
|
||
{
|
||
id: 'aging',
|
||
header: 'Aging',
|
||
accessorKey: 'aging',
|
||
enableSorting: false,
|
||
cell: (props) => {
|
||
const value = props.row.original.aging;
|
||
return <div className='text-center'>{formatNumber(value)} Hari</div>;
|
||
},
|
||
footer: () => {
|
||
const value = supplier.total.aging;
|
||
return <div className='text-center'>{formatNumber(value)} Hari</div>;
|
||
},
|
||
},
|
||
{
|
||
id: 'area',
|
||
header: 'Area',
|
||
accessorKey: 'area',
|
||
enableSorting: false,
|
||
cell: (props) => {
|
||
const value = props.row.original.area?.name;
|
||
return value || '-';
|
||
},
|
||
},
|
||
{
|
||
id: 'warehouse',
|
||
header: 'Gudang',
|
||
accessorKey: 'warehouse',
|
||
enableSorting: false,
|
||
cell: (props) => {
|
||
const value = props.row.original.warehouse?.name;
|
||
return value || '-';
|
||
},
|
||
},
|
||
{
|
||
id: 'due_date',
|
||
header: 'Jatuh Tempo',
|
||
accessorKey: 'due_date',
|
||
enableSorting: false,
|
||
cell: (props) => {
|
||
const value = props.row.original.due_date;
|
||
return value
|
||
? value != '-'
|
||
? formatDate(value, 'DD MMM YYYY')
|
||
: '-'
|
||
: '-';
|
||
},
|
||
},
|
||
{
|
||
id: 'due_status',
|
||
header: 'Status Jatuh Tempo',
|
||
accessorKey: 'due_status',
|
||
enableSorting: false,
|
||
cell: (props) => {
|
||
const value = props.row.original.due_status;
|
||
return value ? (value != '-' ? getPillBadge(value, 'due') : '-') : '-';
|
||
},
|
||
},
|
||
{
|
||
id: 'total_price',
|
||
header: 'Nominal Pembelian',
|
||
accessorKey: 'total_price',
|
||
enableSorting: false,
|
||
cell: (props) => {
|
||
const value = props.row.original.total_price;
|
||
return (
|
||
<div className={`text-right ${value < 0 ? 'text-red-500' : ''}`}>
|
||
{formatCurrency(value)}
|
||
</div>
|
||
);
|
||
},
|
||
footer: () => {
|
||
const value = supplier.total.total_price;
|
||
return (
|
||
<div className={`text-right ${value < 0 ? 'text-red-500' : ''}`}>
|
||
{formatCurrency(value)}
|
||
</div>
|
||
);
|
||
},
|
||
},
|
||
{
|
||
id: 'payment_price',
|
||
header: 'Pembayaran',
|
||
accessorKey: 'payment_price',
|
||
enableSorting: false,
|
||
cell: (props) => {
|
||
const value = props.row.original.payment_price;
|
||
return (
|
||
<div className={`text-right ${value < 0 ? 'text-red-500' : ''}`}>
|
||
{formatCurrency(value)}
|
||
</div>
|
||
);
|
||
},
|
||
footer: () => {
|
||
const value = supplier.total.payment_price;
|
||
return (
|
||
<div className={`text-right ${value < 0 ? 'text-red-500' : ''}`}>
|
||
{formatCurrency(value)}
|
||
</div>
|
||
);
|
||
},
|
||
},
|
||
{
|
||
id: 'balance',
|
||
header: 'Sisa Saldo Hutang',
|
||
accessorKey: 'balance',
|
||
enableSorting: false,
|
||
cell: (props) => {
|
||
const value = props.row.original.balance;
|
||
return (
|
||
<div className={`text-right ${value < 0 ? 'text-red-500' : ''}`}>
|
||
{formatCurrency(value)}
|
||
</div>
|
||
);
|
||
},
|
||
footer: () => {
|
||
const value = supplier.total.debt_price;
|
||
return (
|
||
<div className={`text-right ${value < 0 ? 'text-red-500' : ''}`}>
|
||
{formatCurrency(value)}
|
||
</div>
|
||
);
|
||
},
|
||
},
|
||
{
|
||
id: 'status',
|
||
header: 'Status',
|
||
accessorKey: 'status',
|
||
enableSorting: false,
|
||
cell: (props) => {
|
||
const value = props.row.original.status;
|
||
return value
|
||
? value != '-'
|
||
? getPillBadge(value, 'payment')
|
||
: '-'
|
||
: '-';
|
||
},
|
||
},
|
||
{
|
||
id: 'travel_number',
|
||
header: 'Nomor Perjalanan',
|
||
accessorKey: 'travel_number',
|
||
enableSorting: false,
|
||
cell: (props) => {
|
||
const value = props.row.original.travel_number;
|
||
return value || '-';
|
||
},
|
||
},
|
||
];
|
||
return (
|
||
<>
|
||
<div className='w-full p-0 sm:p-4 flex flex-col gap-4'>
|
||
<Card
|
||
subtitle='Laporan > Rekapitulasi Hutang ke Supplier'
|
||
className={{ wrapper: 'w-full', body: 'p-1!' }}
|
||
>
|
||
<div className='mb-4 flex justify-end gap-2 [&_button]:px-4'>
|
||
<ButtonFilter
|
||
values={formik.values}
|
||
onClick={handleFilterModalOpen}
|
||
variant='outline'
|
||
/>
|
||
|
||
<Dropdown
|
||
trigger={
|
||
<Button variant='outline' isLoading={isAnyExportLoading}>
|
||
<Icon
|
||
icon='heroicons:cloud-arrow-down'
|
||
width={18}
|
||
height={18}
|
||
/>
|
||
Export
|
||
</Button>
|
||
}
|
||
align='end'
|
||
>
|
||
<Menu>
|
||
<MenuItem title='Excel' onClick={handleExportExcel} />
|
||
<MenuItem title='PDF' onClick={handleExportPdf} />
|
||
</Menu>
|
||
</Dropdown>
|
||
</div>
|
||
</Card>
|
||
|
||
{!isSubmitted ? (
|
||
<div className='mt-6 text-center text-gray-500'>
|
||
Silakan klik tombol Filter untuk mengatur filter dan menampilkan
|
||
data.
|
||
</div>
|
||
) : isLoading ? (
|
||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||
<span className='loading loading-spinner loading-xl' />
|
||
</div>
|
||
) : data.length === 0 ? (
|
||
<div className='mt-6 text-center text-gray-500'>
|
||
Tidak ada data yang dapat ditampilkan...
|
||
</div>
|
||
) : (
|
||
data.map((supplierReport) => {
|
||
return (
|
||
<Card
|
||
key={supplierReport.supplier.id}
|
||
title={supplierReport.supplier.name}
|
||
className={{
|
||
wrapper: 'w-full !rounded-lg',
|
||
body: 'p-0 rounded-lg',
|
||
title:
|
||
'ps-2 pt-1 pb-1 font-normal text-md bg-primary text-white',
|
||
}}
|
||
variant='bordered'
|
||
collapsible={true}
|
||
defaultCollapsed={true}
|
||
>
|
||
<Table
|
||
data={[
|
||
{
|
||
balance: supplierReport.initial_balance,
|
||
} as DebtRow,
|
||
...supplierReport.rows,
|
||
]}
|
||
columns={getTableColumns(supplierReport)}
|
||
pageSize={supplierReport.rows.length + 1}
|
||
renderFooter={supplierReport.rows.length > 0}
|
||
className={{
|
||
containerClassName: 'w-full',
|
||
tableWrapperClassName: 'overflow-x-auto',
|
||
headerColumnClassName: cn(
|
||
TABLE_DEFAULT_STYLING.headerColumnClassName,
|
||
'whitespace-nowrap'
|
||
),
|
||
bodyColumnClassName: cn(
|
||
TABLE_DEFAULT_STYLING.bodyColumnClassName,
|
||
'whitespace-nowrap'
|
||
),
|
||
footerRowClassName: cn(
|
||
TABLE_DEFAULT_STYLING.footerRowClassName,
|
||
'bg-white'
|
||
),
|
||
footerColumnClassName: cn(
|
||
TABLE_DEFAULT_STYLING.footerColumnClassName,
|
||
'whitespace-nowrap p-3'
|
||
),
|
||
paginationClassName: 'hidden',
|
||
}}
|
||
renderCustomRow={(row) => {
|
||
if (row.index == 0) {
|
||
return (
|
||
<tr
|
||
className={cn(TABLE_DEFAULT_STYLING.bodyRowClassName)}
|
||
key={row.index}
|
||
>
|
||
<td
|
||
className={cn(
|
||
TABLE_DEFAULT_STYLING.bodyColumnClassName
|
||
)}
|
||
colSpan={12}
|
||
></td>
|
||
<td
|
||
className={cn(
|
||
TABLE_DEFAULT_STYLING.bodyColumnClassName
|
||
)}
|
||
>
|
||
<div
|
||
className={`text-right ${row.original.balance < 0 ? 'text-red-500' : ''}`}
|
||
>
|
||
{formatCurrency(row.original.balance)}
|
||
</div>
|
||
</td>
|
||
<td
|
||
className={cn(
|
||
TABLE_DEFAULT_STYLING.bodyColumnClassName
|
||
)}
|
||
colSpan={2}
|
||
></td>
|
||
</tr>
|
||
);
|
||
}
|
||
}}
|
||
/>
|
||
</Card>
|
||
);
|
||
})
|
||
)}
|
||
</div>
|
||
|
||
{/* Filter Modal */}
|
||
<Modal
|
||
ref={filterModal.ref}
|
||
className={{
|
||
modal: 'p-0',
|
||
modalBox: 'p-0 rounded-2xl xl:max-w-4/12 max-w-sm',
|
||
}}
|
||
>
|
||
<form
|
||
className='space-y-6'
|
||
onSubmit={formik.handleSubmit}
|
||
onReset={formik.handleReset}
|
||
>
|
||
{/* Modal Header */}
|
||
<div className='flex items-center justify-between gap-2 py-3 border-b border-gray-300 px-4'>
|
||
<div className='flex items-center gap-2 text-primary'>
|
||
<Icon icon='heroicons:funnel' width={20} height={20} />
|
||
<h3 className='font-semibold'>Filter Data</h3>
|
||
</div>
|
||
<Button
|
||
variant='link'
|
||
onClick={filterModal.closeModal}
|
||
className='text-gray-500 hover:text-gray-700 transition-colors cursor-pointer'
|
||
>
|
||
<Icon icon='heroicons:x-mark' width={20} height={20} />
|
||
</Button>
|
||
</div>
|
||
<div className='space-y-4 px-4'>
|
||
<div className='grid grid-cols-1 sm:grid-cols-2 sm:gap-4'>
|
||
<div>
|
||
<DateInput
|
||
label='Tanggal'
|
||
name='startDate'
|
||
value={formik.values.startDate || ''}
|
||
onChange={(e) => {
|
||
formik.setFieldValue('startDate', e.target.value || null);
|
||
}}
|
||
className={{ wrapper: 'w-full' }}
|
||
isError={
|
||
formik.touched.startDate && !!formik.errors.startDate
|
||
}
|
||
errorMessage={formik.errors.startDate}
|
||
/>
|
||
</div>
|
||
|
||
<div className='mt-auto'>
|
||
<DateInput
|
||
label=' '
|
||
name='endDate'
|
||
value={formik.values.endDate || ''}
|
||
onChange={(e) => {
|
||
formik.setFieldValue('endDate', e.target.value || null);
|
||
}}
|
||
className={{ wrapper: 'w-full' }}
|
||
isError={formik.touched.endDate && !!formik.errors.endDate}
|
||
errorMessage={formik.errors.endDate}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<SelectInputCheckbox
|
||
label='Supplier'
|
||
placeholder='Pilih Supplier'
|
||
isMulti
|
||
options={supplierOptions}
|
||
value={
|
||
(formik.values.supplierIds as
|
||
| { value: number; label: string }
|
||
| { value: number; label: string }[]
|
||
| null
|
||
| undefined) || []
|
||
}
|
||
onChange={(val) => {
|
||
formik.setFieldValue(
|
||
'supplierIds',
|
||
Array.isArray(val) ? val : val ? [val] : null
|
||
);
|
||
}}
|
||
onInputChange={setSupplierInputValue}
|
||
onMenuScrollToBottom={loadMoreSuppliers}
|
||
isLoading={isLoadingSupplierOptions}
|
||
isClearable
|
||
className={{ wrapper: 'w-full' }}
|
||
isError={
|
||
formik.touched.supplierIds && !!formik.errors.supplierIds
|
||
}
|
||
errorMessage={formik.errors.supplierIds as string}
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<SelectInputRadio
|
||
label='Filter Berdasarkan'
|
||
placeholder='Pilih Filter Berdasarkan'
|
||
options={dataTypeOptions}
|
||
value={
|
||
(formik.values.filterBy as
|
||
| { value: string; label: string }
|
||
| { value: string; label: string }[]
|
||
| null
|
||
| undefined) || null
|
||
}
|
||
onChange={(val) => {
|
||
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}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Action Buttons */}
|
||
<div className='flex justify-between gap-4 py-4 mt-8 border-t border-gray-300 bg-gray-100'>
|
||
<Button
|
||
variant='soft'
|
||
className='ms-4 min-w-36 rounded-lg'
|
||
type='reset'
|
||
>
|
||
Reset Filter
|
||
</Button>
|
||
<Button className='me-4 min-w-36 rounded-lg' type='submit'>
|
||
Apply Filter
|
||
</Button>
|
||
</div>
|
||
</form>
|
||
</Modal>
|
||
</>
|
||
);
|
||
};
|
||
|
||
export default DebtSupplierTab;
|