From deebba530ab104183776cfca24dccc4daa1b4833 Mon Sep 17 00:00:00 2001 From: wayanrivan Date: Wed, 21 May 2025 09:50:21 +0700 Subject: [PATCH 01/10] update transaction kind --- .../home/blocks/TransactionPieChart.tsx | 8 ++++++ .../home/blocks/TransactionValue.tsx | 26 ++++++++++++++++++- .../blocks/DetailTransaction.tsx | 17 ++++++++++++ .../blocks/ListToolbar.tsx | 9 +++++++ .../hooks/TransactionContext.tsx | 8 ++++++ 5 files changed, 67 insertions(+), 1 deletion(-) 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/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..696dfe6 100644 --- a/src/pages/transaction/history-transaction/blocks/ListToolbar.tsx +++ b/src/pages/transaction/history-transaction/blocks/ListToolbar.tsx @@ -171,6 +171,7 @@ const ListToolbar = () => { + TRANSFER PURCHASE WITHDRAW @@ -182,6 +183,14 @@ const ListToolbar = () => { TOP UP P24 TRANSFER AGENT WITHDRAWAL AGENT + TOP UP AGENT + TRANSFER P24 + WITHDRAW MERCHANT + DONATION + FEE + REVERSAL + CASHBACK CASH + CASHBACK POINT diff --git a/src/pages/transaction/history-transaction/hooks/TransactionContext.tsx b/src/pages/transaction/history-transaction/hooks/TransactionContext.tsx index 8f2a12c..6069da8 100644 --- a/src/pages/transaction/history-transaction/hooks/TransactionContext.tsx +++ b/src/pages/transaction/history-transaction/hooks/TransactionContext.tsx @@ -82,6 +82,14 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => { case 'B': return 'TOP UP P24'; case 'A': return 'TRANSFER AGENT'; case 'M': return 'WITHDRAWAL AGENT'; + case 'O': return 'TOP UP AGENT'; + case 'S': return 'TRANSFER P24'; + case 'I': return 'WIJTDRAW MERCHANT'; + case 'D': return 'DONATION'; + case 'F': return 'FEE'; + case 'V': return 'REVERSAL'; + case 'C': return 'CASHBACK CASH'; + case 'H': return 'CASHBACK POINT'; default: return '_'; } }, From 4e12f9eb783772a8b47a35c44a5726fee2904f82 Mon Sep 17 00:00:00 2001 From: bagusajisaputroo Date: Wed, 21 May 2025 10:15:15 +0700 Subject: [PATCH 02/10] Add new value on status kind in transfer type --- .../transfertype/blocks/AddDialog.tsx | 9 ++++++++ .../transfertype/blocks/EditDialog.tsx | 11 ++++++++- .../transfertype/blocks/ListToolBar.tsx | 13 +++++++++-- .../hooks/ManageTransferTypeContext.tsx | 23 +++++++++++++------ 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/pages/transfer/transfertype/blocks/AddDialog.tsx b/src/pages/transfer/transfertype/blocks/AddDialog.tsx index 8977d5c..c6790ec 100644 --- a/src/pages/transfer/transfertype/blocks/AddDialog.tsx +++ b/src/pages/transfer/transfertype/blocks/AddDialog.tsx @@ -568,6 +568,15 @@ const AddDialog = () => { Top Up P24 Transfer Agent Withdraw Agent + Top Up Agent + Transfer P24 + Withdraw Merchant + Donation + Fee + Raversal + Cashback Cash + Cashback Point + Withdraw Admin {errors.status_kind && ( diff --git a/src/pages/transfer/transfertype/blocks/EditDialog.tsx b/src/pages/transfer/transfertype/blocks/EditDialog.tsx index a9c771c..8a15db6 100644 --- a/src/pages/transfer/transfertype/blocks/EditDialog.tsx +++ b/src/pages/transfer/transfertype/blocks/EditDialog.tsx @@ -766,7 +766,16 @@ const EditDialog = () => { Top Up P24 Transfer Agent Withdraw Agent - + Top Up Agent + Transfer P24 + Withdraw Merchant + Donation + Fee + Raversal + Cashback Cash + Cashback Point + Withdraw Admin + {' '} {errors.status_kind && ( {errors.status_kind} diff --git a/src/pages/transfer/transfertype/blocks/ListToolBar.tsx b/src/pages/transfer/transfertype/blocks/ListToolBar.tsx index b281c29..832c484 100644 --- a/src/pages/transfer/transfertype/blocks/ListToolBar.tsx +++ b/src/pages/transfer/transfertype/blocks/ListToolBar.tsx @@ -62,14 +62,14 @@ const ListToolbar = () => { const handleKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { - table.getColumn('name')?.setFilterValue(searchValue); +table.getColumn('name')?.setFilterValue(`%${searchValue}%`); table.setPageIndex(0); } }; useEffect(() => { const timer = setTimeout(() => { - table.getColumn('name')?.setFilterValue(searchValue); +table.getColumn('name')?.setFilterValue(`%${searchValue}%`); table.setPageIndex(0); }, 200); return () => clearTimeout(timer); @@ -210,6 +210,15 @@ const ListToolbar = () => { Top Up P24 Transfer Agent Withdraw Agent + Top Up Agent + Transfer P24 + Withdraw Merchant + Donation + Fee + Raversal + Cashback Cash + Cashback Point + Withdraw Admin 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) { From 6b1c498a5672b33737ec0dd7543adf8dbec56c95 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 21 May 2025 14:38:07 +0700 Subject: [PATCH 03/10] fix search and download --- .../manage-members/blocks/ListToolBar.tsx | 119 ++++++++++++++---- .../blocks/ListToolbar.tsx | 1 - 2 files changed, 93 insertions(+), 27 deletions(-) diff --git a/src/pages/members/manage-members/blocks/ListToolBar.tsx b/src/pages/members/manage-members/blocks/ListToolBar.tsx index b17a730..265d15e 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,42 +23,70 @@ 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) => { + setSearchValue(e.target.value) + if (typeSearchValue === 'username') { + table.getColumn('username')?.setFilterValue(e.target.value); + table.getColumn('msisdn')?.setFilterValue(''); + } + if (typeSearchValue === 'msisdn') { + table.getColumn('msisdn')?.setFilterValue(e.target.value); + table.getColumn('username')?.setFilterValue(''); + } + if (groupFilter) table.getColumn('group_name')?.setFilterValue(groupFilter); }; const handleGroupChange = (e: any) => { let value = e.target.value; if (value === '__all__') value = ''; setGroupFilter(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 (
- + - + + + + +
+ diff --git a/src/pages/transaction/history-transaction/blocks/ListToolbar.tsx b/src/pages/transaction/history-transaction/blocks/ListToolbar.tsx index 413b7ef..bfd82a7 100644 --- a/src/pages/transaction/history-transaction/blocks/ListToolbar.tsx +++ b/src/pages/transaction/history-transaction/blocks/ListToolbar.tsx @@ -134,7 +134,6 @@ const ListToolbar = () => { } }; - return (
From b440c14ee96e906ff407ee552428b0a958bc22c1 Mon Sep 17 00:00:00 2001 From: wayanrivan Date: Wed, 21 May 2025 15:20:03 +0700 Subject: [PATCH 04/10] update --- .../dashboards/home/DashboardHomePage.tsx | 11 +- .../dashboards/home/blocks/BankSaldo.tsx | 96 +++-- .../home/blocks/ShowDetailWalletDialog.tsx | 394 ++++++++++++++++++ 3 files changed, 454 insertions(+), 47 deletions(-) create mode 100644 src/pages/dashboards/home/blocks/ShowDetailWalletDialog.tsx 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..8f522db --- /dev/null +++ b/src/pages/dashboards/home/blocks/ShowDetailWalletDialog.tsx @@ -0,0 +1,394 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { + Dialog, + DialogBody, + DialogContent, + 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 WalletTransaction { + ID: string; + transaction_code: string; + transaction_type: { + id: string; + name: string; + }; + type: string; + amount: number; + pre_amount: number; + post_amount: number; + category: string; + notes: string; + date: string; + msisdn_reff?: string; + purpose?: string; +} + +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 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 ShowDetailWalletDialog: React.FC = ({ + open, + onClose, + idbalance, +}) => { + const { GetData } = useCallApi(); + const [data, setData] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [dateRange, setDateRange] = useState(getDefaultDateRange()); + const [selectedTransferType, setSelectedTransferType] = useState(null); + const [selectedCategory, setSelectedCategory] = useState(null); + const [searchValue, setSearchValue] = useState(''); + const [transferType, setTransferType] = useState([]); + const [category, setCategory] = useState([]); + + const fetchTransferType = 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); + } + }; + + const getDetailList = useCallback( + async ( + limit: number, + page: number, + with_deleted: boolean, + order_field: string, + order_direction: string, + filter: any + ): Promise => { + if (!idbalance) return []; + try { + const response = await GetData( + `${apiConfig.service_wallet}/dashboard/balance/list-balance-detail/${idbalance}`, + { + limit, + page, + with_deleted, + order_field, + order_direction, + filter: JSON.stringify(filter), + } + ); + return response?.data?.list || []; + } catch (error) { + console.error('Error fetching wallet detail:', error); + return []; + } + }, + [GetData, idbalance] + ); + + const fetchData = useCallback( + async (params: { + pageIndex: number; + pageSize: number; + sorting: Array<{ id: string; desc: boolean }>; + columnFilters: any; + }): Promise<{ data: WalletTransaction[] }> => { + // setIsLoading(true); + const order_field = params.sorting.length ? params.sorting[0].id : 'date'; + const order_direction = params.sorting.length && params.sorting[0].desc ? 'DESC' : 'ASC'; + const result = await getDetailList( + params.pageSize, + params.pageIndex + 1, + false, + order_field, + order_direction, + params.columnFilters + ); + setData(result); + setIsLoading(false); + return { data: result }; + }, + [getDetailList] + ); + + // Ambil unique category dari data transaksi setelah data di-fetch + useEffect(() => { + const uniqueCategories = Array.from( + new Set(data.map((tx) => tx.category).filter((cat) => !!cat)) + ); + setCategory(uniqueCategories); + }, [data]); + + 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(''); + }; + + useEffect(() => { + fetchTransferType(); + }, []); + + const handleApplyFilter = () => { + const filters = []; + + if (dateRange.from) filters.push({ id: 'date', value: { gte: dateRange.from } }); + if (dateRange.to) filters.push({ id: 'date', value: { lte: dateRange.to } }); + if (selectedTransferType) filters.push({ id: 'transaction_type.id', value: selectedTransferType }); + if (selectedCategory) filters.push({ id: 'category', value: selectedCategory }); + if (searchValue) filters.push({ id: 'transaction_code', value: searchValue }); + + console.log(filters); + + fetchData({ + pageIndex: 0, + pageSize: 10, + sorting: [{ id: 'date', desc: true }], + columnFilters: JSON.stringify(filters), + }); + }; + + + return ( + + + + Details Wallet Statement + + +
+ {/*
+
+ + + + +
+ +
+ +
+ +
+ + + + + + + + +
+
*/} + + { + return await fetchData({ + pageIndex: params.pageIndex, + pageSize: params.pageSize, + sorting: params.sorting ?? [], + columnFilters: params.columnFilters ?? [], + }); + }} + /> +
+
+
+
+ ); +}; + +export default ShowDetailWalletDialog; From 704617cf7301570bce549b1aec063f5c0ef33e58 Mon Sep 17 00:00:00 2001 From: wayanrivan Date: Wed, 21 May 2025 15:20:56 +0700 Subject: [PATCH 05/10] update --- .../home/blocks/ShowDetailWalletDialog.tsx | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/pages/dashboards/home/blocks/ShowDetailWalletDialog.tsx b/src/pages/dashboards/home/blocks/ShowDetailWalletDialog.tsx index 8f522db..2c64a5f 100644 --- a/src/pages/dashboards/home/blocks/ShowDetailWalletDialog.tsx +++ b/src/pages/dashboards/home/blocks/ShowDetailWalletDialog.tsx @@ -209,7 +209,7 @@ const ShowDetailWalletDialog: React.FC = ({ pageIndex: number; pageSize: number; sorting: Array<{ id: string; desc: boolean }>; - columnFilters: any; + columnFilters: any[]; }): Promise<{ data: WalletTransaction[] }> => { // setIsLoading(true); const order_field = params.sorting.length ? params.sorting[0].id : 'date'; @@ -256,7 +256,9 @@ const ShowDetailWalletDialog: React.FC = ({ fetchTransferType(); }, []); - const handleApplyFilter = () => { + useEffect(() => { + if (!open) return; + const filters = []; if (dateRange.from) filters.push({ id: 'date', value: { gte: dateRange.from } }); @@ -265,16 +267,13 @@ const ShowDetailWalletDialog: React.FC = ({ if (selectedCategory) filters.push({ id: 'category', value: selectedCategory }); if (searchValue) filters.push({ id: 'transaction_code', value: searchValue }); - console.log(filters); - fetchData({ pageIndex: 0, pageSize: 10, sorting: [{ id: 'date', desc: true }], - columnFilters: JSON.stringify(filters), + columnFilters: filters, }); - }; - + }, [dateRange, selectedTransferType, selectedCategory, searchValue, open]); return ( @@ -355,15 +354,6 @@ const ShowDetailWalletDialog: React.FC = ({ - -
*/} @@ -391,4 +381,4 @@ const ShowDetailWalletDialog: React.FC = ({ ); }; -export default ShowDetailWalletDialog; +export default ShowDetailWalletDialog; \ No newline at end of file From 4f620f781d350cc6d435c3d7b246469873962e9d Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 21 May 2025 16:13:42 +0700 Subject: [PATCH 06/10] fix member --- .../manage-members/blocks/ListToolBar.tsx | 86 ++++++++++--------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/src/pages/members/manage-members/blocks/ListToolBar.tsx b/src/pages/members/manage-members/blocks/ListToolBar.tsx index 265d15e..951cfd7 100644 --- a/src/pages/members/manage-members/blocks/ListToolBar.tsx +++ b/src/pages/members/manage-members/blocks/ListToolBar.tsx @@ -29,25 +29,26 @@ const ListToolbar = ({ createMember, onReload, isReloading, groups }: ListToolba const [typeSearchValue, setSearchTypeValue] = useState(); const handleSearch = (e: React.ChangeEvent) => { - setSearchValue(e.target.value) + e.preventDefault() + // setSearchValue(e.target.value) if (typeSearchValue === 'username') { - table.getColumn('username')?.setFilterValue(e.target.value); + table.getColumn('username')?.setFilterValue(searchValue); table.getColumn('msisdn')?.setFilterValue(''); } if (typeSearchValue === 'msisdn') { - table.getColumn('msisdn')?.setFilterValue(e.target.value); + table.getColumn('msisdn')?.setFilterValue(searchValue); table.getColumn('username')?.setFilterValue(''); } - if (groupFilter) table.getColumn('group_name')?.setFilterValue(groupFilter); + table.getColumn('group_name')?.setFilterValue(groupFilter); }; const handleGroupChange = (e: any) => { let value = e.target.value; if (value === '__all__') value = ''; setGroupFilter(value); - if (typeSearchValue === 'username') table.getColumn('username')?.setFilterValue(searchValue); - if (typeSearchValue === 'msisdn') table.getColumn('msisdn')?.setFilterValue(searchValue); - 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 }[] => { @@ -83,43 +84,48 @@ const ListToolbar = ({ createMember, onReload, isReloading, groups }: ListToolba
-
- +
handleSearch(e)}> +
+ - + - + + +
+
-
- +
+
-
*/} +
+ +
+ + + + + + + + +
+
+ +
{ - return await fetchData({ - pageIndex: params.pageIndex, - pageSize: params.pageSize, - sorting: params.sorting ?? [], - columnFilters: params.columnFilters ?? [], - }); - }} + sorting={[{ id: 'id', desc: false }]} + serverSide={true} + onFetchData={({ pageIndex, pageSize, sorting, columnFilters }) => + getTransactionLists(pageIndex, pageSize, sorting, columnFilters) + } />
@@ -381,4 +333,4 @@ const ShowDetailWalletDialog: React.FC = ({ ); }; -export default ShowDetailWalletDialog; \ No newline at end of file +export default ShowDetailWalletDialog; From 831627c371626498542aad546aa0d3cfbcf0c2b0 Mon Sep 17 00:00:00 2001 From: wayanrivan Date: Wed, 21 May 2025 16:59:04 +0700 Subject: [PATCH 10/10] update button filter --- .../dashboards/home/DashboardHomePage.tsx | 2 +- .../blocks/ListToolbar.tsx | 134 ++++++++---------- .../blocks/ListToolbar.tsx | 133 +++++++---------- 3 files changed, 109 insertions(+), 160 deletions(-) diff --git a/src/pages/dashboards/home/DashboardHomePage.tsx b/src/pages/dashboards/home/DashboardHomePage.tsx index 369149c..71ba39a 100644 --- a/src/pages/dashboards/home/DashboardHomePage.tsx +++ b/src/pages/dashboards/home/DashboardHomePage.tsx @@ -269,7 +269,7 @@ const DashboardHomePage = () => {
{bankaccount?.data && bankaccount?.data.length > 0 - // && getAuth()?.statusbalance=='Y' + && getAuth()?.statusbalance=='Y' ? ( bankaccount?.data.map((bankaccountdatas: { id_balance:string,amount: string, credit_limit: string, monthly_limit: string; wallet: string; }, index: number) => ( { 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/ListToolbar.tsx b/src/pages/transaction/history-transaction/blocks/ListToolbar.tsx index 2a56ce4..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'); @@ -161,16 +146,11 @@ const ListToolbar = () => { name="to" /> - - setTypeValue(value)}> - TRANSFER PURCHASE WITHDRAW @@ -193,10 +173,7 @@ const ListToolbar = () => { - setSearchTypeValue(value)}> @@ -216,6 +193,11 @@ const ListToolbar = () => { onChange={(event) => setSearchValue(event.target.value)} /> + + +
@@ -245,28 +227,17 @@ const ListToolbar = () => { Export Data - -