'use client' // React Imports import { useCallback, useEffect, useMemo, useState } from 'react' // Next Imports import Link from 'next/link' import { useParams } from 'next/navigation' // MUI Imports import Button from '@mui/material/Button' import Card from '@mui/material/Card' import CardHeader from '@mui/material/CardHeader' import Checkbox from '@mui/material/Checkbox' import Chip from '@mui/material/Chip' import IconButton from '@mui/material/IconButton' import MenuItem from '@mui/material/MenuItem' import { styled } from '@mui/material/styles' import type { TextFieldProps } from '@mui/material/TextField' import Typography from '@mui/material/Typography' // Third-party Imports import type { RankingInfo } from '@tanstack/match-sorter-utils' import { rankItem } from '@tanstack/match-sorter-utils' import type { ColumnDef, FilterFn } from '@tanstack/react-table' import { createColumnHelper, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table' import classnames from 'classnames' // Type Imports import type { Locale } from '@configs/i18n' // Component Imports import CustomTextField from '@core/components/mui/TextField' import OptionMenu from '@core/components/option-menu' // Style Imports import tableStyles from '@core/styles/table.module.css' import { Box, CircularProgress, TablePagination } from '@mui/material' import { useDispatch } from 'react-redux' import TablePaginationComponent from '@/components/TablePaginationComponent' import Loading from '@/components/layout/shared/Loading' import { getLocalizedUrl } from '@/utils/i18n' import StatusFilterTabs from '@/components/StatusFilterTab' import DateRangePicker from '@/components/RangeDatePicker' // Updated CashBankType export type CashBankType = { id: number date: string description: string reference: string accept: number send: number balance: number status: string } declare module '@tanstack/table-core' { interface FilterFns { fuzzy: FilterFn } interface FilterMeta { itemRank: RankingInfo } } type CashBankTypeWithAction = CashBankType & { actions?: string } // Styled Components const Icon = styled('i')({}) const fuzzyFilter: FilterFn = (row, columnId, value, addMeta) => { // Rank the item const itemRank = rankItem(row.getValue(columnId), value) // Store the itemRank info addMeta({ itemRank }) // Return if the item should be filtered in/out return itemRank.passed } const DebouncedInput = ({ value: initialValue, onChange, debounce = 500, ...props }: { value: string | number onChange: (value: string | number) => void debounce?: number } & Omit) => { // States const [value, setValue] = useState(initialValue) useEffect(() => { setValue(initialValue) }, [initialValue]) useEffect(() => { const timeout = setTimeout(() => { onChange(value) }, debounce) return () => clearTimeout(timeout) // eslint-disable-next-line react-hooks/exhaustive-deps }, [value]) return setValue(e.target.value)} /> } // Status color mapping for CashBank const getStatusColor = (status: string) => { switch (status) { case 'Transaksi di Apskel': return 'info' case 'Transaksi di Bank': return 'primary' case 'Rekonsiliasi': return 'success' case 'Transaksi Berulang': return 'secondary' case 'Void': return 'error' case 'Menunggu Persetujuan': return 'warning' case 'Di tolak': return 'error' default: return 'default' } } // Format currency const formatCurrency = (amount: number) => { return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(amount) } // Sample CashBank data const cashBankData: CashBankType[] = [ { id: 1, date: '2024-01-15', description: 'Transfer ke supplier ABC', reference: 'TRF-001', accept: 0, send: 5000000, balance: 45000000, status: 'Transaksi di Bank' }, { id: 2, date: '2024-01-16', description: 'Pembayaran dari customer XYZ', reference: 'PAY-002', accept: 10000000, send: 0, balance: 55000000, status: 'Rekonsiliasi' }, { id: 3, date: '2024-01-17', description: 'Pembayaran gaji karyawan', reference: 'SALARY-001', accept: 0, send: 25000000, balance: 30000000, status: 'Menunggu Persetujuan' }, { id: 4, date: '2024-01-18', description: 'Transfer antar rekening', reference: 'TRF-002', accept: 0, send: 2000000, balance: 28000000, status: 'Transaksi di Apskel' }, { id: 5, date: '2024-01-19', description: 'Pembayaran otomatis tagihan listrik', reference: 'AUTO-001', accept: 0, send: 1500000, balance: 26500000, status: 'Transaksi Berulang' }, { id: 6, date: '2024-01-20', description: 'Transfer yang dibatalkan', reference: 'TRF-003', accept: 0, send: 3000000, balance: 26500000, status: 'Void' }, { id: 7, date: '2024-01-21', description: 'Pembayaran yang ditolak sistem', reference: 'PAY-003', accept: 0, send: 5000000, balance: 26500000, status: 'Di tolak' } ] // Column Definitions const columnHelper = createColumnHelper() const CashBankDetailTable = () => { const dispatch = useDispatch() // States const [addTransactionOpen, setAddTransactionOpen] = useState(false) const [rowSelection, setRowSelection] = useState({}) const [currentPage, setCurrentPage] = useState(0) const [pageSize, setPageSize] = useState(10) const [openConfirm, setOpenConfirm] = useState(false) const [transactionId, setTransactionId] = useState('') const [search, setSearch] = useState('') const [statusFilter, setStatusFilter] = useState('Semua') const [filteredData, setFilteredData] = useState(cashBankData) const [startDate, setStartDate] = useState(new Date()) const [endDate, setEndDate] = useState(new Date()) // Hooks const { lang: locale } = useParams() // Filter data based on search and status useEffect(() => { let filtered = cashBankData // Filter by search if (search) { filtered = filtered.filter( transaction => transaction.description.toLowerCase().includes(search.toLowerCase()) || transaction.reference.toLowerCase().includes(search.toLowerCase()) || transaction.status.toLowerCase().includes(search.toLowerCase()) ) } // Filter by status if (statusFilter !== 'Semua') { filtered = filtered.filter(transaction => transaction.status === statusFilter) } setFilteredData(filtered) setCurrentPage(0) }, [search, statusFilter]) const totalCount = filteredData.length const paginatedData = useMemo(() => { const startIndex = currentPage * pageSize return filteredData.slice(startIndex, startIndex + pageSize) }, [filteredData, currentPage, pageSize]) // Calculate totals from filtered data const subtotalAccept = useMemo(() => { return filteredData.reduce((sum, transaction) => sum + transaction.accept, 0) }, [filteredData]) const subtotalSend = useMemo(() => { return filteredData.reduce((sum, transaction) => sum + transaction.send, 0) }, [filteredData]) const netBalance = subtotalAccept - subtotalSend const handlePageChange = useCallback((event: unknown, newPage: number) => { setCurrentPage(newPage) }, []) const handlePageSizeChange = useCallback((event: React.ChangeEvent) => { const newPageSize = parseInt(event.target.value, 10) setPageSize(newPageSize) setCurrentPage(0) }, []) const handleDelete = () => { setOpenConfirm(false) } const handleTransactionClick = (transactionId: string) => { console.log('Navigasi ke detail Transaksi:', transactionId) } const handleStatusFilter = (status: string) => { setStatusFilter(status) } const columns = useMemo[]>( () => [ { id: 'select', header: ({ table }) => ( ), cell: ({ row }) => ( ) }, columnHelper.accessor('date', { header: 'Tanggal', cell: ({ row }) => {row.original.date} }), columnHelper.accessor('description', { header: 'Keterangan', cell: ({ row }) => ( ) }), columnHelper.accessor('reference', { header: 'Referensi', cell: ({ row }) => {row.original.reference || '-'} }), columnHelper.accessor('status', { header: 'Status', cell: ({ row }) => (
) }), columnHelper.accessor('accept', { header: 'Penerimaan', cell: ({ row }) => ( 0 ? 'text-green-600' : 'text-gray-500'}`}> {row.original.accept > 0 ? formatCurrency(row.original.accept) : '-'} ) }), columnHelper.accessor('send', { header: 'Pengeluaran', cell: ({ row }) => ( 0 ? 'text-red-600' : 'text-gray-500'}`}> {row.original.send > 0 ? formatCurrency(row.original.send) : '-'} ) }), columnHelper.accessor('balance', { header: 'Saldo', cell: ({ row }) => ( = 0 ? 'text-green-600' : 'text-red-600'}`}> {formatCurrency(row.original.balance)} ) }) ], [] ) const table = useReactTable({ data: paginatedData as CashBankType[], columns, filterFns: { fuzzy: fuzzyFilter }, state: { rowSelection, pagination: { pageIndex: currentPage, pageSize } }, enableRowSelection: true, onRowSelectionChange: setRowSelection, getCoreRowModel: getCoreRowModel(), manualPagination: true, pageCount: Math.ceil(totalCount / pageSize) }) return ( <> {/* Filter Status Tabs and Range Date */}
setSearch(value as string)} placeholder='Cari Transaksi Cash/Bank' className='max-sm:is-full' />
10 25 50
{table.getHeaderGroups().map(headerGroup => ( {headerGroup.headers.map(header => ( ))} ))} {filteredData.length === 0 ? ( ) : ( {table.getRowModel().rows.map(row => { return ( {row.getVisibleCells().map(cell => ( ))} ) })} {/* Subtotal Row */} {/* Total Row */} )}
{header.isPlaceholder ? null : ( <>
{flexRender(header.column.columnDef.header, header.getContext())} {{ asc: , desc: }[header.column.getIsSorted() as 'asc' | 'desc'] ?? null}
)}
Tidak ada data tersedia
{flexRender(cell.column.columnDef.cell, cell.getContext())}
Subtotal {formatCurrency(subtotalAccept)} {formatCurrency(subtotalSend)} = 0 ? 'text-green-600' : 'text-red-600'}`} > {formatCurrency(netBalance)}
Total = 0 ? 'text-green-600' : 'text-red-600'}`} > {formatCurrency(netBalance)}
( )} count={totalCount} rowsPerPage={pageSize} page={currentPage} onPageChange={handlePageChange} onRowsPerPageChange={handlePageSizeChange} rowsPerPageOptions={[10, 25, 50]} />
) } export default CashBankDetailTable