diff --git a/public/media/file-templates/upload-batch-template.xlsx b/public/media/file-templates/upload-batch-template.xlsx index 2af339b..e31ef5a 100644 Binary files a/public/media/file-templates/upload-batch-template.xlsx and b/public/media/file-templates/upload-batch-template.xlsx differ diff --git a/src/pages/dashboards/home/DashboardHomePage.tsx b/src/pages/dashboards/home/DashboardHomePage.tsx index a42faee..71ba39a 100644 --- a/src/pages/dashboards/home/DashboardHomePage.tsx +++ b/src/pages/dashboards/home/DashboardHomePage.tsx @@ -268,13 +268,16 @@ const DashboardHomePage = () => { ) : null}
- {bankaccount?.data && bankaccount?.data.length > 0 && getAuth()?.statusbalance=='Y' ? ( - bankaccount?.data.map((bankaccountdatas: { amount: string, creditlimit: string, monthlylimit: string; wallet: string; }, index: number) => ( + {bankaccount?.data && bankaccount?.data.length > 0 + && getAuth()?.statusbalance=='Y' + ? ( + bankaccount?.data.map((bankaccountdatas: { id_balance:string,amount: string, credit_limit: string, monthly_limit: string; wallet: string; }, index: number) => ( )) ) : ( diff --git a/src/pages/dashboards/home/blocks/BankSaldo.tsx b/src/pages/dashboards/home/blocks/BankSaldo.tsx index 928b576..93302a6 100644 --- a/src/pages/dashboards/home/blocks/BankSaldo.tsx +++ b/src/pages/dashboards/home/blocks/BankSaldo.tsx @@ -1,62 +1,72 @@ -// BankSaldo.tsx -import { Wallet } from "lucide-react"; +import React, { useState } from 'react'; +import { Wallet } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import ShowDetailWalletDialog from './ShowDetailWalletDialog'; interface AccountCardProps { title: string; balance: string; creditLimit: string; monthlyLimit: string; + idbalance: string; } -export const BankSaldo = ({ +export const BankSaldo: React.FC = ({ title, balance, creditLimit, monthlyLimit, -}: AccountCardProps) => { + idbalance, +}) => { + const [showDialog, setShowDialog] = useState(false); + return ( -
-
-
-
-

{title}

- -
-
- {new Intl.NumberFormat("en-US", { - style: "currency", - currency: "USD", - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }).format(parseFloat(balance))} -
-
-
- Credit Limit: - {creditLimit} + <> +
+
+
+
+

{title}

+
-
- Monthly Limit: - {monthlyLimit} +
+ {new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(parseFloat(balance))} +
+
+
+ Credit Limit: + {creditLimit} +
+
+ Monthly Limit: + {monthlyLimit} +
+
+ +
-
+ + {showDialog && ( + setShowDialog(false)} + idbalance={idbalance} + /> + )} + ); }; - -interface AccountCardsProps { - accounts: AccountCardProps[]; -} - -export const AccountCards = ({ accounts }: AccountCardsProps) => { - return ( -
- {accounts.map((account, index) => ( - - ))} -
- ); -}; - -export default BankSaldo +export default BankSaldo; \ No newline at end of file diff --git a/src/pages/dashboards/home/blocks/ShowDetailWalletDialog.tsx b/src/pages/dashboards/home/blocks/ShowDetailWalletDialog.tsx new file mode 100644 index 0000000..c4ec9ad --- /dev/null +++ b/src/pages/dashboards/home/blocks/ShowDetailWalletDialog.tsx @@ -0,0 +1,336 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { + Dialog, + DialogBody, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle +} from '@/components/ui/dialog'; +import { useCallApi } from '@/hooks'; +import { apiConfig } from '@/config/api.config'; +import { DataGridColumnHeader, DataGridProvider } from '@/components'; +import { Button } from '@/components/ui/button'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { DefaultTooltip, KeenIcon } from '@/components'; + +interface ShowDetailWalletDialogProps { + open: boolean; + onClose: () => void; + idbalance: string; +} + +interface TransferType { + id: string; + name: string; +} + +const formatDate = (date: Date) => date.toLocaleDateString('sv-SE'); +const getDefaultDateRange = () => { + const today = new Date(); + const sevenDaysAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000); + return { + from: formatDate(sevenDaysAgo), + to: formatDate(today), + }; +}; + +const ShowDetailWalletDialog: React.FC = ({ + open, + onClose, + idbalance, +}) => { + const { GetData } = useCallApi(); + + const [dateRange, setDateRange] = useState(getDefaultDateRange()); + const [selectedTransferType, setSelectedTransferType] = useState(null); + const [transferType, setTransferType] = useState([]); + const [selectedCategory, setSelectedCategory] = useState(null); + const [searchValue, setSearchValue] = useState(''); + const [category, setCategory] = useState([]); + const [transaction, setTransaction] = useState([]); + const [filters, setFilters] = useState({}); + + const fetchTransferType = useCallback(async () => { + try { + const response = await GetData(`${apiConfig.service_transaction}/transactiontype/list`, { + limit: 100, + page: 1, + with_deleted: false, + order_field: 'created_at', + order_direction: 'ASC', + }); + setTransferType(response?.data?.list || []); + } catch (error) { + console.error('Error fetching transfer types', error); + } + }, [GetData]); + + useEffect(() => { + fetchTransferType(); + }, [fetchTransferType]); + + useEffect(() => { + const uniqueCategories = Array.from( + new Set(transaction.map((tx) => tx.category).filter((cat) => !!cat)) + ); + setCategory(uniqueCategories); + }, [transaction]); + + const handleTransferTypeChange = (value: string) => { + setSelectedTransferType(value === 'all' ? null : value); + }; + + const handleCategoryChange = (value: string) => { + setSelectedCategory(value === '' ? null : value); + }; + + const handleClearAllFilters = () => { + setDateRange(getDefaultDateRange()); + setSelectedTransferType(null); + setSelectedCategory(null); + setSearchValue(''); + setFilters({}); + }; + + const handleApplyFilters = () => { + const appliedFilters: any = {}; + + if (dateRange.from) appliedFilters.date_from = dateRange.from+" 00:00:00"; + if (dateRange.to) appliedFilters.date_to = dateRange.to+" 23:59:59"; + if (selectedTransferType) appliedFilters.transaction_type_id = selectedTransferType; + if (selectedCategory) appliedFilters.category = selectedCategory; + if (searchValue) appliedFilters.transaction_code = searchValue; + + setFilters(appliedFilters); + }; + + + const columns = [ + { + accessorKey: 'transaction_code', + header: ({ column }: any) => , + enableSorting: false, + meta: { headerClassName: 'min-w-[120px]' }, + }, + { + accessorKey: 'msisdn_reff', + header: ({ column }: any) => , + enableSorting: false, + meta: { headerClassName: 'min-w-[120px]' }, + }, + { + accessorKey: 'transaction_type.name', + header: ({ column }: any) => , + enableSorting: false, + meta: { headerClassName: 'min-w-[180px]' }, + }, + { + accessorKey: 'type', + header: ({ column }: any) => , + enableSorting: false, + meta: { headerClassName: 'min-w-[80px]' }, + }, + { + accessorKey: 'amount', + header: ({ column }: any) => , + enableSorting: false, + cell: (info: any) => info.getValue()?.toFixed(2), + meta: { headerClassName: 'min-w-[100px]' }, + }, + { + accessorKey: 'pre_amount', + header: ({ column }: any) => , + enableSorting: false, + cell: (info: any) => info.getValue()?.toFixed(2), + meta: { headerClassName: 'min-w-[100px]' }, + }, + { + accessorKey: 'post_amount', + header: ({ column }: any) => , + enableSorting: false, + cell: (info: any) => info.getValue()?.toFixed(2), + meta: { headerClassName: 'min-w-[100px]' }, + }, + { + accessorKey: 'category', + header: ({ column }: any) => , + enableSorting: false, + meta: { headerClassName: 'min-w-[80px]' }, + }, + { + accessorKey: 'date', + header: ({ column }: any) => , + enableSorting: false, + cell: (info: any) => { + const val = info.getValue(); + if (!val) return '-'; + return new Date(val).toLocaleString('id-ID', { + day: '2-digit', + month: 'short', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + }); + }, + meta: { headerClassName: 'min-w-[150px]' }, + }, + { + accessorKey: 'purpose', + header: ({ column }: any) => , + enableSorting: false, + cell: (info: any) => info.getValue() || '-', + meta: { headerClassName: 'min-w-[200px]' }, + }, + { + accessorKey: 'notes', + header: ({ column }: any) => , + enableSorting: false, + cell: (info: any) => info.getValue() || '-', + meta: { headerClassName: 'min-w-[200px]' }, + }, + ]; + + const getTransactionLists = useCallback( + async (page: number, limit: number, sorting: any, _columnFilters: any) => { + // console.log(idbalance); + try { + const response = await GetData(`${apiConfig.service_wallet}/dashboard/balance/list-balance-detail/${idbalance}`, { + limit, + page: page + 1, + with_deleted: false, + order_field: "created_at", + order_direction: 'DESC', + filter: filters + }); + + // console.log(filters); + + if (!response || !response.data) { + console.warn('No data received:', response); + return { data: [], totalCount: 0 }; + } + + setTransaction(response.data.list); + return { data: response.data.list, totalCount: response.data.total_count }; + } catch (error) { + console.error('Error fetching transaction', error); + return { data: [], totalCount: 0 }; + } + }, + [GetData, idbalance, filters] + ); + + return ( + + + + Details Wallet Statement + + + + +
+
+ + + + +
+ +
+ +
+ +
+ + + + + + + + +
+
+ +
+ + getTransactionLists(pageIndex, pageSize, sorting, columnFilters) + } + /> +
+
+
+
+ ); +}; + +export default ShowDetailWalletDialog; diff --git a/src/pages/dashboards/home/blocks/TransactionPieChart.tsx b/src/pages/dashboards/home/blocks/TransactionPieChart.tsx index d5b0aae..0d40772 100644 --- a/src/pages/dashboards/home/blocks/TransactionPieChart.tsx +++ b/src/pages/dashboards/home/blocks/TransactionPieChart.tsx @@ -48,6 +48,14 @@ const TransactionPieChart = ({ startdate, enddate }: Props) => { { name: 'Top Up P24', value: parseFloat((responseTransactionValue?.data?.B ?? 0).toFixed(2)), color: '#FADA7A' }, { name: 'Transfer Agent', value: parseFloat((responseTransactionValue?.data?.A ?? 0).toFixed(2)), color: '#B1C29E' }, { name: 'Withdrawal Agent', value: parseFloat((responseTransactionValue?.data?.M ?? 0).toFixed(2)), color: '#FCE7C8' }, + { name: 'Top Up Agent', value: parseFloat((responseTransactionValue?.data?.O ?? 0).toFixed(2)), color: '#F0A04B' }, + { name: 'Transfer P24', value: parseFloat((responseTransactionValue?.data?.S ?? 0).toFixed(2)), color: '#FADA7A' }, + { name: 'Withdraw Merchant', value: parseFloat((responseTransactionValue?.data?.I ?? 0).toFixed(2)), color: '#B1C29E' }, + { name: 'Donation', value: parseFloat((responseTransactionValue?.data?.D ?? 0).toFixed(2)), color: '#FCE7C8' }, + { name: 'Fee', value: parseFloat((responseTransactionValue?.data?.F ?? 0).toFixed(2)), color: '#F0A04B' }, + { name: 'Reversal', value: parseFloat((responseTransactionValue?.data?.V ?? 0).toFixed(2)), color: '#FADA7A' }, + { name: 'Cashback Cash', value: parseFloat((responseTransactionValue?.data?.C ?? 0).toFixed(2)), color: '#B1C29E' }, + { name: 'Cashback Point', value: parseFloat((responseTransactionValue?.data?.H ?? 0).toFixed(2)), color: '#FCE7C8' }, ]; return ( diff --git a/src/pages/dashboards/home/blocks/TransactionValue.tsx b/src/pages/dashboards/home/blocks/TransactionValue.tsx index 3892b15..97f1fc8 100644 --- a/src/pages/dashboards/home/blocks/TransactionValue.tsx +++ b/src/pages/dashboards/home/blocks/TransactionValue.tsx @@ -71,7 +71,7 @@ const TransactionValue = ({ startdate, enddate }: Props) => { type = "Purchase Loja" break; case "B": - type ="Top Up P24"; + type = "Top Up P24"; break; case "A": type = " Transfer Agent"; @@ -79,6 +79,30 @@ const TransactionValue = ({ startdate, enddate }: Props) => { case "M": type = "Withdrawal Agent"; break; + case 'O': + type = 'TOP UP AGENT'; + break; + case 'S': + type = 'TRANSFER P24'; + break; + case 'I': + type = 'WIJTDRAW MERCHANT'; + break; + case 'D': + type = 'DONATION'; + break; + case 'F': + type = 'FEE'; + break; + case 'V': + type = 'REVERSAL'; + break; + case 'C': + type = 'CASHBACK CASH'; + break; + case 'H': + type = 'CASHBACK POINT'; + break; default: type = "Unknown"; break; diff --git a/src/pages/disbursement/history-transaction/HistoryTransaction.tsx b/src/pages/disbursement/history-transaction/HistoryTransaction.tsx index 61b9d2d..792a69f 100644 --- a/src/pages/disbursement/history-transaction/HistoryTransaction.tsx +++ b/src/pages/disbursement/history-transaction/HistoryTransaction.tsx @@ -23,7 +23,7 @@ const HistoryTransactionDisbursement = () => { - History Disbursement + Disbursement
diff --git a/src/pages/disbursement/history-transaction/hooks/TransactionContext.tsx b/src/pages/disbursement/history-transaction/hooks/TransactionContext.tsx index 50829d6..861dcea 100644 --- a/src/pages/disbursement/history-transaction/hooks/TransactionContext.tsx +++ b/src/pages/disbursement/history-transaction/hooks/TransactionContext.tsx @@ -285,7 +285,7 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => { pagination={{ size: 10 }} toolbar={} layout={{ card: true }} - sorting={[{ id: 'execution_date', desc: false }]} + sorting={[{ id: 'execution_date', desc: true }]} serverSide={true} onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => getTransactionLists(pageIndex, pageSize, sorting, columnFilters) diff --git a/src/pages/members/manage-members/blocks/ListToolBar.tsx b/src/pages/members/manage-members/blocks/ListToolBar.tsx index b17a730..951cfd7 100644 --- a/src/pages/members/manage-members/blocks/ListToolBar.tsx +++ b/src/pages/members/manage-members/blocks/ListToolBar.tsx @@ -3,6 +3,7 @@ import { UserPlus } from 'lucide-react'; import { KeenIcon, useDataGrid } from '@/components'; import { DefaultTooltip } from '@/components'; import { useState } from 'react'; +import { toast } from 'sonner'; import { Select, SelectContent, @@ -10,6 +11,9 @@ import { SelectTrigger, SelectValue } from '@/components/ui/select'; +import { getAuth } from '@/auth'; +import { apiConfig } from '@/config/api.config'; + const API_URL = apiConfig.service_customer; interface ListToolbarProps { createMember: () => void; @@ -19,62 +23,131 @@ interface ListToolbarProps { } const ListToolbar = ({ createMember, onReload, isReloading, groups }: ListToolbarProps) => { - const [groupFilter, setGroupFilter] = useState(''); - const [usernameFilter, setUsernameFilter] = useState(''); - const [msisdnFilter, setMsisdnFilter] = useState(''); - const {table}=useDataGrid(); + const { table, reload } = useDataGrid(); + const [searchValue, setSearchValue] = useState(); + const [typeSearchValue, setSearchTypeValue] = useState(); - const handleUsernameChange = (e: React.ChangeEvent) => { - setUsernameFilter(e.target.value); - table.getColumn('username')?.setFilterValue(e.target.value); - }; - const handleMsisdnChange = (e: React.ChangeEvent) => { - setMsisdnFilter(e.target.value); - table.getColumn('msisdn')?.setFilterValue(e.target.value); + const handleSearch = (e: React.ChangeEvent) => { + e.preventDefault() + // setSearchValue(e.target.value) + if (typeSearchValue === 'username') { + table.getColumn('username')?.setFilterValue(searchValue); + table.getColumn('msisdn')?.setFilterValue(''); + } + if (typeSearchValue === 'msisdn') { + table.getColumn('msisdn')?.setFilterValue(searchValue); + table.getColumn('username')?.setFilterValue(''); + } + table.getColumn('group_name')?.setFilterValue(groupFilter); }; const handleGroupChange = (e: any) => { let value = e.target.value; if (value === '__all__') value = ''; setGroupFilter(value); - table.getColumn('group_name')?.setFilterValue(value); + // if (typeSearchValue === 'username') table.getColumn('username')?.setFilterValue(searchValue); + // if (typeSearchValue === 'msisdn') table.getColumn('msisdn')?.setFilterValue(searchValue); + // table.getColumn('group_name')?.setFilterValue(value); + }; + + const generateExportFilters = (groupFilter: string, typeSearchValue: any, searchValue: any): { id: string; value: string }[] => { + const filters: { id: string; value: string }[] = []; + if (groupFilter) filters.push({ id: 'group_name', value: groupFilter }); + if (typeSearchValue && searchValue) filters.push({ id: typeSearchValue, value: searchValue }); + return filters; + }; + + const exporDataToExcel = async (filters: { id: string; value: string }[]) => { + try { + const filterParam = encodeURIComponent(JSON.stringify(filters)); + const response = await fetch(`${API_URL}/customer/export-excel?filter=${filterParam}`, { + method: 'GET', + headers: { + Authorization: `Bearer ${getAuth()?.access_token}`, + } + }); + if (!response.ok) throw new Error('Failed to fetch file'); + + const blob = await response.blob(); + const contentDisposition = response.headers.get('content-disposition'); + const currentYear = new Date().getFullYear(); + const filename = `TPAY_members_${currentYear}.xlsx`; + return { blob, filename }; + } catch (error:any) { + console.error('Error exporting data:', error); + toast.error(error) + } }; return (
+ +
handleSearch(e)}> +
+ + + + + + +
+
+
- - - -
-
+ diff --git a/src/pages/transaction/approval-transaction/blocks/ListToolbar.tsx b/src/pages/transaction/approval-transaction/blocks/ListToolbar.tsx index a81aea3..559857f 100644 --- a/src/pages/transaction/approval-transaction/blocks/ListToolbar.tsx +++ b/src/pages/transaction/approval-transaction/blocks/ListToolbar.tsx @@ -14,76 +14,61 @@ import { const ListToolbar = () => { const { table, reload } = useDataGrid(); - // Set the initial state for trxDate + // State untuk filter const [trxDate, settrxDate] = useState({ from: '', to: '' }); - const [statusApproval, setStatusApproval] = useState( - (table.getColumn('status_approve')?.getFilterValue() as string) ?? '' - ); + const [statusApproval, setStatusApproval] = useState(''); + const [searchValue, setSearchValue] = useState(''); + const [typeSearchValue, setSearchTypeValue] = useState(''); - useEffect(() => { - const timer = setTimeout(() => { - table.getColumn('status_approve')?.setFilterValue(statusApproval); - table.setPageIndex(0); - }, 200); - return () => clearTimeout(timer); - }, [statusApproval, table]); - - // Function to format date to YYYY-MM-DD - const formatDate = (date: Date): string => { - return date.toISOString().split('T')[0]; - }; - - const [searchValue, setSearchValue] = useState(); - const [typeSearchValue, setSearchTypeValue] = useState(); - - - - // useEffect to set the default date values + // Set tanggal default saat pertama mount useEffect(() => { const today = new Date(); - // const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1); + const formatDate = (date: Date) => date.toISOString().split('T')[0]; settrxDate({ from: formatDate(today), to: formatDate(today), }); }, []); + // Fungsi untuk apply semua filter sekaligus saat tombol Filter ditekan const handleFilterData = useCallback(() => { try { + if (!trxDate.from || !trxDate.to) { + toast.error('Please select From and To dates'); + return; + } + + // Set filter tanggal table.getColumn('transaction_date')?.setFilterValue(trxDate); + + // Set filter status approval + table.getColumn('status_approve')?.setFilterValue(statusApproval || ''); + + // Set filter search type sebagai column filter id 'searchtype' + table.setColumnFilters((prevFilters) => { + // Hapus dulu filter dengan id 'searchtype' dan 'code' agar gak duplikat + const filtered = prevFilters.filter( + (f) => f.id !== 'searchtype' && f.id !== 'code' + ); + + // Tambahkan filter baru jika ada + if (typeSearchValue) { + filtered.push({ id: 'searchtype', value: typeSearchValue }); + } + if (searchValue) { + filtered.push({ id: 'code', value: searchValue }); + } + + return filtered; + }); + + // Reset ke halaman pertama + table.setPageIndex(0); } catch (error) { toast.error('Error applying filter'); - console.error('Error applying filter:', error); + console.error(error); } - }, [trxDate, table]); - - useEffect(() => { - const timer = setTimeout(() => { - // Add search type filter to column filters - table.setColumnFilters((prev) => [ - ...prev.filter((f) => f.id !== 'searchtype'), - { id: 'searchtype', value: typeSearchValue }, - ]); - table.setPageIndex(0); - }, 200); - return () => clearTimeout(timer); - }, [typeSearchValue, table]); - - - useEffect(() => { - const timer = setTimeout(() => { - table.getColumn('code')?.setFilterValue(searchValue); - table.setPageIndex(0); - }, 200); - - return () => clearTimeout(timer); - }, [searchValue, table]); - - useEffect(() => { - if (trxDate.from && trxDate.to) { - handleFilterData(); - } - }, [trxDate]); + }, [trxDate, statusApproval, searchValue, typeSearchValue, table]); return (
@@ -93,11 +78,8 @@ const ListToolbar = () => { From - settrxDate({ ...trxDate, from: event.target.value }) - } + onChange={(e) => settrxDate({ ...trxDate, from: e.target.value })} name="from" /> @@ -106,20 +88,15 @@ const ListToolbar = () => { To - settrxDate({ ...trxDate, to: event.target.value }) - } + onChange={(e) => settrxDate({ ...trxDate, to: e.target.value })} name="to" /> { - setSearchTypeValue(value); - }} + onValueChange={setSearchTypeValue} > @@ -151,12 +126,16 @@ const ListToolbar = () => { setSearchValue(event.target.value)} + onChange={(e) => setSearchValue(e.target.value)} /> + {/* Tombol Filter */} +
@@ -166,23 +145,22 @@ const ListToolbar = () => { className="h-7.5" onClick={() => { const today = new Date(); - // const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1); + const formatDate = (date: Date) => date.toISOString().split('T')[0]; - // Resetting all filters + // Reset semua filter dan tanggal ke hari ini setSearchValue(''); setStatusApproval(''); - settrxDate({ - from: formatDate(today), - to: formatDate(today), - }); - + settrxDate({ from: formatDate(today), to: formatDate(today) }); setSearchTypeValue(''); + // Reset filter di tabel table.getColumn('code')?.setFilterValue(''); table.getColumn('status_approve')?.setFilterValue(''); + table.getColumn('transaction_date')?.setFilterValue(''); + table.setColumnFilters([]); table.setPageIndex(0); - reload(); // Reload table data + reload(); }} > diff --git a/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx b/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx index e9b1186..f9133d4 100644 --- a/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx +++ b/src/pages/transaction/history-transaction/blocks/DetailTransaction.tsx @@ -456,7 +456,24 @@ const DetailTransaction = () => { kind = 'TRANSFER AGENT'; } else if (transactionDetails?.kind === 'M') { kind = 'WITHDRAWAL AGENT'; + }else if( transactionDetails?.kind === 'O') { + kind = 'TOPUP AGENT'; + }else if (transactionDetails?.kind === 'S') { + kind = 'TOPUP P24'; + }else if (transactionDetails?.kind === 'I') { + kind = 'WITHDRAW MERCHANT'; + }else if (transactionDetails?.kind === 'D') { + kind = 'DONATION'; + }else if (transactionDetails?.kind === 'F') { + kind = 'FEE'; + }else if (transactionDetails?.kind === 'V') { + kind = 'REVERSAL'; + }else if( transactionDetails?.kind === 'C') { + kind = 'CASHBACK CASH'; + }else if( transactionDetails?.kind === 'H') { + kind = 'CASHBACK POINT'; } + return kind; })()}

diff --git a/src/pages/transaction/history-transaction/blocks/ListToolbar.tsx b/src/pages/transaction/history-transaction/blocks/ListToolbar.tsx index 413b7ef..72987dc 100644 --- a/src/pages/transaction/history-transaction/blocks/ListToolbar.tsx +++ b/src/pages/transaction/history-transaction/blocks/ListToolbar.tsx @@ -1,7 +1,7 @@ import { DefaultTooltip, KeenIcon, useDataGrid } from '@/components'; import { useTransactionContext } from '../hooks/useTransactionContext'; import { Button } from '@/components/ui/button'; -import { useCallback, useState, useEffect } from 'react'; +import { useCallback, useState, useEffect, useRef } from 'react'; import { toast } from 'sonner'; import { Select, @@ -17,8 +17,8 @@ import { getAuth } from '@/auth'; const ListToolbar = () => { const { table, reload } = useDataGrid(); const [trxDate, settrxDate] = useState({ from: '', to: '' }); - const [searchValue, setSearchValue] = useState(''); // default: empty string - const [typeSearchValue, setSearchTypeValue] = useState(''); // default: empty string + const [searchValue, setSearchValue] = useState(''); + const [typeSearchValue, setSearchTypeValue] = useState(''); const [typeValue, setTypeValue] = useState( (table.getState().columnFilters.find(f => f.id === 'kind')?.value as string) ?? '' ); @@ -26,62 +26,44 @@ const ListToolbar = () => { const { GetData } = useCallApi(); const API_URL = apiConfig.transaction; - const formatDate = (date: Date): string => { - return date.toISOString().split('T')[0]; - }; + const formatDate = (date: Date): string => date.toISOString().split('T')[0]; + // Set tanggal default hari ini saat mount useEffect(() => { const today = new Date(); - settrxDate({ - from: formatDate(today), - to: formatDate(today), - }); + const formatted = formatDate(today); + settrxDate({ from: formatted, to: formatted }); }, []); - useEffect(() => { - const timer = setTimeout(() => { - table.setColumnFilters((prev) => [ - ...prev.filter((f) => f.id !== 'kind'), - { id: 'kind', value: typeValue }, - ]); - table.setPageIndex(0); - }, 200); - return () => clearTimeout(timer); - }, [typeValue, table]); - - useEffect(() => { - const timer = setTimeout(() => { - table.setColumnFilters((prev) => [ - ...prev.filter((f) => f.id !== 'searchtype'), - { id: 'searchtype', value: typeSearchValue }, - ]); - table.setPageIndex(0); - }, 200); - return () => clearTimeout(timer); - }, [typeSearchValue, table]); - - useEffect(() => { - const timer = setTimeout(() => { - table.getColumn('code')?.setFilterValue(searchValue); - table.setPageIndex(0); - }, 200); - return () => clearTimeout(timer); - }, [searchValue, table]); + // **Hilangkan semua useEffect yang auto apply filter saat input berubah** + // Karena kamu mau filter apply hanya lewat tombol Filter + // Fungsi untuk apply SEMUA filter sekaligus saat tombol Filter diklik const handleFilterData = useCallback(() => { - try { - table.getColumn('transaction_date')?.setFilterValue(trxDate); - } catch (error) { - toast.error('Error applying filter'); - console.error('Error applying filter:', error); + if (!trxDate.from || !trxDate.to) { + toast.error('Please select both From and To dates'); + return; } - }, [trxDate, table]); - useEffect(() => { - if (trxDate.from && trxDate.to) { - handleFilterData(); - } - }, [trxDate]); + // Terapkan filter tanggal + table.getColumn('transaction_date')?.setFilterValue(trxDate); + + // Terapkan filter jenis transaksi (kind) + table.setColumnFilters((prev) => { + // Hapus filter 'kind' dan 'searchtype' agar bisa set ulang + const others = prev.filter(f => f.id !== 'kind' && f.id !== 'searchtype' && f.id !== 'code'); + // Bangun array baru dengan filter yang diinginkan + const filters: any[] = [...others]; + + if (typeValue) filters.push({ id: 'kind', value: typeValue }); + if (typeSearchValue) filters.push({ id: 'searchtype', value: typeSearchValue }); + if (searchValue) filters.push({ id: 'code', value: searchValue }); + + return filters; + }); + + table.setPageIndex(0); + }, [trxDate, typeValue, typeSearchValue, searchValue, table]); const exporDataToExcel = async ( typeSearchValue: string, @@ -93,8 +75,8 @@ const ListToolbar = () => { const formattedFilter: any = { "Transactions.transaction_date": { from: `${trxDate.from} 00:00:00`, - to: `${trxDate.to} 23:59:59` - } + to: `${trxDate.to} 23:59:59`, + }, }; if (typeSearchValue === 'msisdn') { @@ -106,17 +88,20 @@ const ListToolbar = () => { } if (typeValue) { - formattedFilter["Transactions.kind"] = typeValue; + formattedFilter['Transactions.kind'] = typeValue; } const filterParam = encodeURIComponent(JSON.stringify(formattedFilter)); - const response = await fetch(`${API_URL}/transaction/export?filter=${filterParam}`, { - method: 'GET', - headers: { - Authorization: `Bearer ${getAuth()?.access_token}`, // ganti dengan token kamu + const response = await fetch( + `${API_URL}/transaction/export?filter=${filterParam}`, + { + method: 'GET', + headers: { + Authorization: `Bearer ${getAuth()?.access_token}`, + }, } - }); + ); if (!response.ok) { throw new Error('Failed to fetch file'); @@ -134,7 +119,6 @@ const ListToolbar = () => { } }; - return (
@@ -162,11 +146,7 @@ const ListToolbar = () => { name="to" /> - - setTypeValue(value)}> @@ -182,13 +162,18 @@ const ListToolbar = () => { TOP UP P24 TRANSFER AGENT WITHDRAWAL AGENT + TOP UP AGENT + TRANSFER P24 + WITHDRAW MERCHANT + DONATION + FEE + REVERSAL + CASHBACK CASH + CASHBACK POINT - setSearchTypeValue(value)}> @@ -208,6 +193,11 @@ const ListToolbar = () => { onChange={(event) => setSearchValue(event.target.value)} /> + + +
@@ -237,28 +227,17 @@ const ListToolbar = () => { Export Data - -
diff --git a/src/pages/transfer/transfertype/hooks/ManageTransferTypeContext.tsx b/src/pages/transfer/transfertype/hooks/ManageTransferTypeContext.tsx index 736d83b..569156e 100644 --- a/src/pages/transfer/transfertype/hooks/ManageTransferTypeContext.tsx +++ b/src/pages/transfer/transfertype/hooks/ManageTransferTypeContext.tsx @@ -34,8 +34,8 @@ const formatInteger = (num: number): string => { return num.toLocaleString('en-US', { style: 'decimal', maximumFractionDigits: 0 - }) -} + }); +}; interface AccountProps { id: string; @@ -224,9 +224,9 @@ const ManageTransferTypeContextProvider = ({ children }: { children: React.React TM: 'Top Up Master Agent', TA: 'Top Up Agent', PL: 'Purchase Loja', - DE: 'Disbursment Escrow', - DM: 'Disbursment Master Agent', - DA: 'Disbursment Agent', + DE: 'Disbursement Escrow', + DM: 'Disbursement Master Agent', + DA: 'Disbursement Agent', WI: 'Withdraw Merchant', IC: 'Income Merchant', DN: 'Donation' @@ -287,7 +287,16 @@ const ManageTransferTypeContextProvider = ({ children }: { children: React.React L: { label: 'Purchase Loja', className: 'bg-rose-100 text-rose-600' }, B: { label: 'Top Up P24', className: 'bg-rose-100 text-rose-600' }, A: { label: 'Transfer Agent', className: 'bg-rose-100 text-rose-600' }, - M: { label: 'Withdraw Agent', className: 'bg-rose-100 text-rose-600' } + M: { label: 'Withdraw Agent', className: 'bg-rose-100 text-rose-600' }, + O: { label: 'Top Up Agent', className: 'bg-rose-100 text-rose-600' }, + S: { label: 'Transfer P24', className: 'bg-rose-100 text-rose-600' }, + I: { label: 'Withdraw Merchant', className: 'bg-rose-100 text-rose-600' }, + D: { label: 'Donation', className: 'bg-rose-100 text-rose-600' }, + F: { label: 'Fee', className: 'bg-rose-100 text-rose-600' }, + V: { label: 'Raversal', className: 'bg-rose-100 text-rose-600' }, + C: { label: 'Cashback Cash', className: 'bg-rose-100 text-rose-600' }, + H: { label: 'Cashback Point', className: 'bg-rose-100 text-rose-600' }, + J: { label: 'Withdraw Admin', className: 'bg-rose-100 text-rose-600' }, }; const kindInfo = mapping[kind] || { @@ -366,7 +375,7 @@ const ManageTransferTypeContextProvider = ({ children }: { children: React.React let filterObject: Record = {}; if (debouncedSearchTerm) { - filterObject['any'] = debouncedSearchTerm.toLowerCase(); + filterObject['name'] = `%${debouncedSearchTerm.toLowerCase()}%`; } if (columnFilters.length > 0) {