import { ChangeEventHandler, useMemo, useState } from 'react'; import { CellContext } from '@tanstack/react-table'; import { useSearchParams } from 'next/navigation'; import useSWR from 'swr'; import Button from '@/components/Button'; import Card from '@/components/Card'; import DateInput from '@/components/input/DateInput'; import DebouncedTextInput from '@/components/input/DebouncedTextInput'; import SelectInput, { OptionType, useSelect, } from '@/components/input/SelectInput'; import Table from '@/components/Table'; import { formatCurrency, formatDate, formatTitleCase } from '@/lib/helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; import { Finance } from '@/types/api/finance/finance'; import { FINANCE_INITIAL_BALANCE_STATUS, FINANCE_INJECTION_STATUS, FINANCE_TRANSACTION_STATUS, } from '@/config/constant'; import { FinanceApi } from '@/services/api/finance'; import { isResponseSuccess } from '@/lib/api-helper'; import { BankApi } from '@/services/api/master-data'; import { Bank } from '@/types/api/master-data/bank'; import { useModal } from '@/components/Modal'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; import toast from 'react-hot-toast'; import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper'; import RequirePermission from '@/components/helper/RequirePermission'; import { Icon } from '@iconify/react'; import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions'; const RowOptionsMenu = ({ type = 'dropdown', props, deleteClickHandler, }: { type: 'dropdown' | 'collapse'; props: CellContext; deleteClickHandler: () => void; }) => { return ( {FINANCE_TRANSACTION_STATUS.includes( props.row.original.transaction_type ) && ( )} {FINANCE_INITIAL_BALANCE_STATUS.includes( props.row.original.transaction_type ) && ( )} {FINANCE_INJECTION_STATUS.includes( props.row.original.transaction_type ) && ( )} ); }; const FinanceTable = () => { const { state: tableFilterState, updateFilter, setPage, setPageSize, toQueryString: getTableFilterQueryString, } = useTableFilter({ initial: { search: '', transactionType: '', bankId: '', partyType: '', sortBy: '', startDate: '', endDate: '', }, paramMap: { page: 'page', pageSize: 'limit', transactionType: 'transaction_type', bankId: 'bank_id', partyType: 'party_type', sortBy: 'sort_date', startDate: 'start_date', endDate: 'end_date', }, }); // ===== State ===== const [searchParams, setSearchParams] = useSearchParams(); const deleteModal = useModal(); const [pendingFilters, setPendingFilters] = useState({ search: '', transactionType: '', bankId: '', partyType: '', sortBy: '', startDate: '', endDate: '', }); const [selectedTransactionType, setSelectedTransactionType] = useState(null); const [selectedBank, setSelectedBank] = useState(null); const [selectedPartyType, setSelectedPartyType] = useState( null ); const [selectedSortBy, setSelectedSortBy] = useState(null); const [selectedFinance, setSelectedFinance] = useState(null); const [isDeleteLoading, setIsDeleteLoading] = useState(false); // ===== Fetch Data ===== const { data: finances, isLoading, mutate: refreshFinances, } = useSWR( `${FinanceApi.basePath}/transactions${getTableFilterQueryString()}`, FinanceApi.getAllFetcher ); // ===== Options ===== const transactionTypeOptions = useMemo(() => { return [ { label: 'Transfer', value: 'TRANSFER' }, { label: 'Cash', value: 'CASH' }, { label: 'Card', value: 'CARD' }, { label: 'Cheque', value: 'CHEQUE' }, { label: 'Saldo', value: 'SALDO' }, ]; }, []); const partyTypeOptions = useMemo(() => { return [ { label: 'Customer', value: 'CUSTOMER' }, { label: 'Supplier', value: 'SUPPLIER' }, ]; }, []); const sortByOptions = useMemo(() => { return [ { label: 'Tanggal Pembayaran', value: 'payment_date' }, { label: 'Tanggal Dibuat', value: 'created_at' }, ]; }, []); const { options: bankOptions, rawData: bankRawData, setInputValue: bankInputValue, loadMore: bankLoadMore, } = useSelect(BankApi.basePath, 'id', 'alias'); // ===== Handler ===== const searchChangeHandler: ChangeEventHandler = (e) => { setPendingFilters((prev) => ({ ...prev, search: e.target.value })); }; const transactionTypeChangeHandler = ( val: OptionType | OptionType[] | null ) => { setSelectedTransactionType(val as OptionType); setPendingFilters((prev) => ({ ...prev, transactionType: val ? ((val as OptionType).value as string) : '', })); }; const bankChangeHandler = (val: OptionType | OptionType[] | null) => { setSelectedBank(val as OptionType); setPendingFilters((prev) => ({ ...prev, bankId: val ? ((val as OptionType).value as string) : '', })); }; const partyTypeChangeHandler = (val: OptionType | OptionType[] | null) => { setSelectedPartyType(val as OptionType); setPendingFilters((prev) => ({ ...prev, partyType: val ? ((val as OptionType).value as string) : '', })); }; const sortByChangeHandler = (val: OptionType | OptionType[] | null) => { setSelectedSortBy(val as OptionType); setPendingFilters((prev) => ({ ...prev, sortBy: val ? ((val as OptionType).value as string) : '', })); }; const startDateChangeHandler: ChangeEventHandler = (e) => { setPendingFilters((prev) => ({ ...prev, startDate: e.target.value })); }; const endDateChangeHandler: ChangeEventHandler = (e) => { setPendingFilters((prev) => ({ ...prev, endDate: e.target.value })); }; const pageSizeChangeHandler = (val: OptionType | OptionType[] | null) => { const newVal = val as OptionType; setPageSize(newVal.value as number); }; const submitFilterHandler = () => { updateFilter('search', pendingFilters.search); updateFilter('transactionType', pendingFilters.transactionType); updateFilter('bankId', pendingFilters.bankId); updateFilter('partyType', pendingFilters.partyType); updateFilter('sortBy', pendingFilters.sortBy); updateFilter('startDate', pendingFilters.startDate); updateFilter('endDate', pendingFilters.endDate); }; const resetFilterHandler = () => { setSelectedTransactionType(null); setSelectedBank(null); setSelectedPartyType(null); setSelectedSortBy(null); const emptyFilters = { search: '', transactionType: '', bankId: '', partyType: '', sortBy: '', startDate: '', endDate: '', }; setPendingFilters(emptyFilters); updateFilter('search', ''); updateFilter('transactionType', ''); updateFilter('bankId', ''); updateFilter('partyType', ''); updateFilter('sortBy', ''); updateFilter('startDate', ''); updateFilter('endDate', ''); }; const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); await FinanceApi.delete(selectedFinance?.id as number); refreshFinances(); deleteModal.closeModal(); toast.success('Successfully delete Finance!'); setIsDeleteLoading(false); }; const columns = useMemo(() => { return [ { header: 'ID', accessorKey: 'payment_code', }, { header: 'References Number', accessorKey: 'reference_number', cell: (props: CellContext) => { const value = props.row.original.reference_number; return {value ?? '-'}; }, }, { header: 'Jenis Transaksi', accessorKey: 'transaction_type', cell: (props: CellContext) => { const value = props.row.original.transaction_type .split('_') .join(' '); return {formatTitleCase(value)}; }, }, { header: 'Pihak', accessorFn: (finance: Finance) => finance.party.name, cell: (props: CellContext) => { if (props.row.original.party.id) { return {props.row.original.party.name}; } return {'-'}; }, }, { header: 'Tanggal', accessorFn: (finance: Finance) => formatDate(finance.payment_date, 'DD MMM YYYY'), }, { header: 'Metode Pembayaran', accessorKey: 'payment_method', cell: (props: CellContext) => { const value = props.row.original.payment_method.split('_').join(' '); return {formatTitleCase(value)}; }, }, { header: 'Bank', accessorFn: (finance: Finance) => `${finance.bank.alias} - ${finance.bank.account_number} - ${finance.bank.owner}`, }, { header: 'Pengeluaran (Rp)', accessorFn: (finance: Finance) => formatCurrency(finance.expense_amount), }, { header: 'Pemasukan (Rp)', accessorFn: (finance: Finance) => formatCurrency(finance.income_amount), }, { header: 'Aksi', cell: (props: CellContext) => { const currentPageSize = props.table.getPaginationRowModel().rows.length; const currentPageRows = props.table.getPaginationRowModel().flatRows; const currentRowRelativeIndex = currentPageRows.findIndex((r) => r.id === props.row.id) + 1; const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; const deleteClickHandler = () => { setSelectedFinance(props.row.original); deleteModal.openModal(); }; return ( <> {currentPageSize > 2 && ( )} {currentPageSize <= 2 && ( )} ); }, }, ]; }, []); return (
} >
({ label: bankRawData.data.find((data) => data.id === bank.value) ?.alias + ' - ' + bankRawData.data.find((data) => data.id === bank.value) ?.account_number + ' - ' + bankRawData.data.find((data) => data.id === bank.value) ?.owner, value: bank.value, })) : [] } label='Bank' value={selectedBank} onChange={bankChangeHandler} onInputChange={bankInputValue} onMenuScrollToBottom={bankLoadMore} isClearable />
data={isResponseSuccess(finances) ? finances.data : []} columns={columns} pageSize={tableFilterState.pageSize} page={tableFilterState.page} onPageChange={setPage} onPageSizeChange={setPageSize} totalItems={ isResponseSuccess(finances) ? finances.meta?.total_results : 0 } isLoading={isLoading} />
); }; export default FinanceTable;