From 9b57aed2adef2959554a133ab1f6ceb2cdeba87e Mon Sep 17 00:00:00 2001 From: Raja Oktafrianto Date: Fri, 23 May 2025 06:29:21 +0700 Subject: [PATCH] update detail wallet statement --- .../blocks/ShowDetailDialog.tsx | 694 ++++++++---------- .../hooks/ManageWalletStatementContext.tsx | 1 - 2 files changed, 311 insertions(+), 384 deletions(-) diff --git a/src/pages/wallet/wallet-statement/blocks/ShowDetailDialog.tsx b/src/pages/wallet/wallet-statement/blocks/ShowDetailDialog.tsx index 33d7ede..b27f48f 100644 --- a/src/pages/wallet/wallet-statement/blocks/ShowDetailDialog.tsx +++ b/src/pages/wallet/wallet-statement/blocks/ShowDetailDialog.tsx @@ -1,11 +1,16 @@ -import { useEffect, useState } from 'react'; +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, @@ -13,277 +18,324 @@ import { SelectTrigger, SelectValue } from '@/components/ui/select'; -import { useManageStatementContext } from '../hooks/useManageWalletStatementContext'; import { DefaultTooltip, KeenIcon } from '@/components'; -import { toast } from 'sonner'; -import { Button } from '@/components/ui/button'; -import { useCallApi } from '@/hooks'; -import { apiConfig } from '@/config/api.config'; +import { useManageStatementContext } from '../hooks/useManageWalletStatementContext'; -const API_URL_WALLET = apiConfig.service_wallet; -const API_URL = apiConfig.service_transaction; - -interface TransactionType { +interface TransferType { id: string; name: string; } -interface WalletTransaction { - ID: string; - id_balance: string; - transaction_code: string; - transaction_type: { - 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) }; - type: string; - amount: number; - pre_amount: number; - post_amount: number; - category: string; - notes: string; - date: string; - msisdn_reff: string; - purpose: string; -} +}; -const ShowDialog = () => { - const { showDetailDialog, setShowDetailDialog, selectedWallet } = useManageStatementContext(); - const [isLoading, setIsLoading] = useState(false); +const ShowDetailWalletDialog = () => { const { GetData } = useCallApi(); - const [transactionType, setTransactionType] = useState([]); - const [filteredTransactions, setFilteredTransactions] = useState([]); - - const [currentPage, setCurrentPage] = useState(1); - const [totalItems, setTotalItems] = useState(0); - const [totalPages, setTotalPages] = useState(1); - const itemsPerPage = 10; - - const getDefaultDateRange = () => { - const today = new Date(); - today.setHours(23, 59, 59, 999); - - const sevenDaysAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000); - sevenDaysAgo.setHours(0, 0, 0, 0); - - return { - from: formatDate(sevenDaysAgo, true), - to: formatDate(today, true) - }; - }; - - const formatDate = (date: Date, includeTime = false) => { - const year = date.getFullYear(); - const month = String(date.getMonth() + 1).padStart(2, '0'); - const day = String(date.getDate()).padStart(2, '0'); - - if (includeTime) { - const hours = String(date.getHours()).padStart(2, '0'); - const minutes = String(date.getMinutes()).padStart(2, '0'); - const seconds = String(date.getSeconds()).padStart(2, '0'); - return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`; - } - - return `${year}-${month}-${day}`; - }; - + const { showDetailDialog, setShowDetailDialog, selectedWallet } = useManageStatementContext(); const [dateRange, setDateRange] = useState(getDefaultDateRange()); - const [category, setCategory] = useState(null); - const [selectedCategory, setSelectedCategory] = useState(''); - const [selectedTransactionType, setSelectedTransactionType] = useState(''); + 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({}); + console.log(selectedCategory); - const TransactionKind = { - return: 'R', - transfer: 'T', - purchase: 'P', - purchase_loja: 'L', - withdraw: 'W', - topup: 'U', - topup_p24: 'B', - topup_partner: 'N', - reward: 'E', - transfer_agent: 'A', - withdraw_agent: 'M', - topup_agent: 'O', - transfer_p24: 'S', - withdraw_merchant: 'I', - donation: 'D', - fee: 'F', - raversal: 'V', - cashback_cash: 'C', - cashback_point: 'H', - withdraw_admin: 'J' - }; - - type TransactionKindValue = (typeof TransactionKind)[keyof typeof TransactionKind]; - - const fetchTransactionType = async () => { + const fetchTransferType = useCallback(async () => { try { - setIsLoading(true); - const response = await GetData(`${API_URL}/transactiontype/list`, { + const response = await GetData(`${apiConfig.service_transaction}/transactiontype/list`, { limit: 100, page: 1, with_deleted: false, order_field: 'created_at', order_direction: 'ASC' }); - setTransactionType( - (response?.data?.list || []).sort((a: any, b: any) => a.name.localeCompare(b.name)) - ); + (response?.data?.list || []).sort((a: any, b: any) => a.name.localeCompare(b.name)); } catch (error) { - console.error('Error fetching Transaction type', error); - toast.error('Failed to load transaction types'); - } finally { - setIsLoading(false); + console.error('Error fetching transfer types', error); } + }, [GetData]); + + useEffect(() => { + fetchTransferType(); + }, [fetchTransferType]); + + const categoryMap: Record = { + T: 'TRANSFER', + P: 'PURCHASE', + W: 'WITHDRAW', + U: 'TOP UP', + R: 'RETURN', + N: 'TOP UP PARTNER', + E: 'REWARD', + L: 'PURCHASE LOJA', + B: 'TOP UP P24', + A: 'TRANSFER AGENT', + M: 'WITHDRAWAL AGENT', + O: 'TOP UP AGENT', + S: 'TRANSFER P24', + I: 'WIJTDRAW MERCHANT', + D: 'DONATION', + F: 'FEE', + V: 'REVERSAL', + C: 'CASHBACK CASH', + H: 'CASHBACK POINT' }; useEffect(() => { - fetchTransactionType(); - }, []); + const uniqueCategories = Array.from( + new Set( + transaction + .map((tx) => categoryMap[tx.category] || '_') + .filter((cat) => !!cat && cat !== '_') + ) + ); - const fetchFilteredTransactions = async () => { - if (!selectedWallet?.ID) return; + setCategory(uniqueCategories); + }, [transaction]); - setIsLoading(true); - try { - let fromDateTime = dateRange.from; - if (!fromDateTime.includes('T')) { - const fromDate = new Date(fromDateTime); - fromDateTime = formatDate(fromDate, true); - } + const reverseCategoryMap = Object.fromEntries( + Object.entries(categoryMap).map(([key, value]) => [value, key]) + ); - let toDateTime = dateRange.to; - if (!toDateTime.includes('T')) { - const toDate = new Date(toDateTime); - toDate.setHours(23, 59, 59, 999); - toDateTime = formatDate(toDate, true); - } - - const response = await GetData( - `${API_URL_WALLET}/dashboard/balance/list-balance-detail/${selectedWallet.ID}`, - { - limit: itemsPerPage, - page: currentPage, - order_field: 'created_at', - order_direction: 'DESC', - start_date: fromDateTime, - end_date: toDateTime, - transaction_type: selectedTransactionType || undefined, - category: selectedCategory || undefined, - search: searchValue || undefined - } - ); - console.log(response); - setFilteredTransactions(response?.data?.list || []); - setTotalItems(response?.data?.total || 0); - setTotalPages(Math.ceil((response?.data?.total || 0) / itemsPerPage)); - } catch (error) { - console.error('Error fetching filtered transactions:', error); - toast.error('Failed to load filtered transactions'); - } finally { - setIsLoading(false); - } + const handleTransferTypeChange = (value: string) => { + setSelectedTransferType(value === 'all' ? null : value); }; - useEffect(() => { - if (!showDetailDialog || !selectedWallet?.ID) return; - - const timer = setTimeout(() => { - fetchFilteredTransactions(); - }, 300); - - return () => clearTimeout(timer); - }, [ - showDetailDialog, - dateRange.from, - dateRange.to, - selectedTransactionType, - selectedCategory, - searchValue, - selectedWallet?.ID, - currentPage - ]); - - const handleSearchChange = (e: React.ChangeEvent) => { - const value = e.target.value; - setSearchValue(value); - }; - - const handleDateChange = (type: 'from' | 'to', value: string) => { - const date = new Date(value); - - if (type === 'from') { - date.setHours(0, 0, 0, 0); - } else { - date.setHours(23, 59, 59, 999); - } - - const formattedDate = formatDate(date, true); - console.log(`Setting ${type} date to:`, formattedDate); - setDateRange((prev) => ({ ...prev, [type]: formattedDate })); + const handleCategoryChange = (value: string) => { + setSelectedCategory(value === '' ? null : value); }; const handleClearAllFilters = () => { - const defaultDates = getDefaultDateRange(); - console.log('Resetting filters to default:', defaultDates); - setDateRange(defaultDates); - setSelectedTransactionType(''); + setDateRange(getDefaultDateRange()); + setSelectedTransferType(null); + setSelectedCategory(null); setSearchValue(''); - setSelectedCategory(''); - setCurrentPage(1); + setFilters({}); }; - const handlePageChange = (page: number) => { - setCurrentPage(page); + const handleApplyFilters = () => { + const appliedFilters: any = {}; + + if (dateRange.from && dateRange.to) { + const fromDate = new Date(`${dateRange.from}T00:00:00Z`); + const toDate = new Date(`${dateRange.to}T23:59:59Z`); + appliedFilters.date = { + from: fromDate.toISOString(), + to: toDate.toISOString() + }; + } + + if (selectedTransferType) { + appliedFilters.transaction_type_id = selectedTransferType; + } + + if (selectedCategory) { + appliedFilters.category = selectedCategory; + } + + if (searchValue) { + appliedFilters.transaction_code = searchValue; + } + + setFilters(appliedFilters); + console.log('applied filter :', appliedFilters.category); }; - useEffect(() => { - if (!showDetailDialog) return; - - const checkNewDay = () => { - const now = new Date(); - const currentDate = formatDate(now); - const fromDate = new Date(dateRange.from); - - if ( - currentDate !== formatDate(new Date(dateRange.to)) && - now.getTime() - fromDate.getTime() > 7 * 24 * 60 * 60 * 1000 - ) { - setDateRange(getDefaultDateRange()); - toast.info('Date range has been updated to the current period'); + 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]' } + }, + { + accessorFn: (row: any) => { + switch (row.category) { + case 'T': + return 'TRANSFER'; + case 'P': + return 'PURCHASE'; + case 'W': + return 'WITHDRAW'; + case 'U': + return 'TOP UP'; + case 'R': + return 'RETURN'; + case 'N': + return 'TOP UP PARTNER'; + case 'E': + return 'REWARD'; + case 'L': + return 'PURCHASE LOJA'; + 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 '_'; + } + }, + accessorKey: 'category', + header: ({ column }: any) => , + enableSorting: false, + enableHiding: false, + meta: { + headerClassName: 'w-[250px]' } - }; + }, + { + 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]' } + } + ]; - checkNewDay(); + const getTransactionLists = useCallback( + async (page: number, limit: number, sorting: any, _columnFilters: any) => { + try { + const response = await GetData( + `${apiConfig.service_wallet}/dashboard/balance/list-balance-detail/${selectedWallet?.ID}`, + { + limit, + page: page + 1, + with_deleted: false, + order_field: 'created_at', + order_direction: 'DESC', + filter: JSON.stringify(filters) + } + ); - const interval = setInterval(checkNewDay, 60 * 60 * 1000); - return () => clearInterval(interval); - }, [showDetailDialog, dateRange]); + // console.log(response); - const getDisplayDate = (dateTimeString: string) => { - if (!dateTimeString) return ''; - return dateTimeString.split('T')[0]; - }; + 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, filters, selectedWallet] + ); return ( - + Details Wallet Statement + + -
+
@@ -291,24 +343,24 @@ const ShowDialog = () => { To handleDateChange('to', e.target.value)} + value={dateRange.to} + onChange={(e) => setDateRange({ ...dateRange, to: e.target.value })} /> -
+
setSelectedCategory(value)} - > +
@@ -340,159 +393,34 @@ const ShowDialog = () => { placeholder="Transaction Code" className="overflow-hidden text-ellipsis w-full" value={searchValue} - onChange={handleSearchChange} + onChange={(e) => setSearchValue(e.target.value)} /> - - + +
+
- {isLoading ? ( -
Loading details...
- ) : filteredTransactions.length > 0 ? ( -
-
- - - - - - - - - - - - - - - - - - {filteredTransactions.map((transaction) => ( - - - - - - - - - - - - - - ))} - -
- Transaction Code - - MSISDN Reffer - - Transaction Type - TypeAmount - Pre Amount - - Post Amount - - Status Kind - - Date Time - Notes - Purpose -
- {transaction.transaction_code} - - {transaction.msisdn_reff} - - {transaction.transaction_type?.name || 'N/A'} - {transaction.type} - {transaction.amount.toFixed(2)} - - {transaction.pre_amount.toFixed(2)} - - {transaction.post_amount.toFixed(2)} - - {transaction.category} - - {new Date(transaction.date).toLocaleString('id-ID', { - day: '2-digit', - month: 'short', - year: 'numeric', - hour: '2-digit', - minute: '2-digit' - })} - - {transaction.notes || '-'} - - {transaction.purpose || '-'} -
-
-
- ) : ( -
No transaction data available
- )} -
-
- Showing {(currentPage - 1) * itemsPerPage + 1} to{' '} - {Math.min(currentPage * itemsPerPage, totalItems)} of {totalItems} entries -
-
- - -
- {Array.from({ length: Math.min(5, totalPages) }, (_, i) => { - let pageToShow; - if (totalPages <= 5) { - pageToShow = i + 1; - } else if (currentPage <= 3) { - pageToShow = i + 1; - } else if (currentPage >= totalPages - 2) { - pageToShow = totalPages - 4 + i; - } else { - pageToShow = currentPage - 2 + i; - } - - return ( - - ); - })} -
- - -
-
+
+ + getTransactionLists(pageIndex, pageSize, sorting, columnFilters) + } + />
@@ -500,4 +428,4 @@ const ShowDialog = () => { ); }; -export default ShowDialog; +export default ShowDetailWalletDialog; diff --git a/src/pages/wallet/wallet-statement/hooks/ManageWalletStatementContext.tsx b/src/pages/wallet/wallet-statement/hooks/ManageWalletStatementContext.tsx index ac3cc27..1cbac68 100644 --- a/src/pages/wallet/wallet-statement/hooks/ManageWalletStatementContext.tsx +++ b/src/pages/wallet/wallet-statement/hooks/ManageWalletStatementContext.tsx @@ -16,7 +16,6 @@ const formatNumber = (num: number): string => { interface WalletProps { ID: string; - id: string; id_wallet: string; name: string; id_currency: string;