diff --git a/src/pages/wallet/wallet-history/blocks/ShowDetailDialog.tsx b/src/pages/wallet/wallet-history/blocks/ShowDetailDialog.tsx deleted file mode 100644 index c86d88b..0000000 --- a/src/pages/wallet/wallet-history/blocks/ShowDetailDialog.tsx +++ /dev/null @@ -1,363 +0,0 @@ -import { useEffect, useState } from 'react'; -import { - Dialog, - DialogBody, - DialogContent, - DialogHeader, - DialogTitle -} from '@/components/ui/dialog'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue -} from '@/components/ui/select'; -import { useManageWalletContext } from '../hooks/useManageWalletHistoryContext'; -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'; - -const formatDate = (date: Date) => date.toLocaleDateString('sv-SE'); - -const API_URL_WALLET = apiConfig.service_wallet; -const API_URL = apiConfig.service_transaction; - -interface TransferType { - id: string; - name: string; -} - -interface WalletTransaction { - ID: string; - id_balance: 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; -} - -const ShowDialog = () => { - const { showDetailDialog, setShowDetailDialog, selectedWallet } = useManageWalletContext(); - const [isLoading, setIsLoading] = useState(false); - const { GetData } = useCallApi(); - - const [transferType, setTransferType] = useState([]); - const [transactions, setTransactions] = useState([]); - const [filteredTransactions, setFilteredTransactions] = useState([]); - const [category, setCategory] = useState([]); - - const getDefaultDateRange = () => { - const today = new Date(); - const sevenDaysAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000); - return { - from: formatDate(sevenDaysAgo), - to: formatDate(today) - }; - }; - - useEffect(() => { - const uniqueCategories = Array.from( - new Set(transactions.map((transaction) => transaction.category)) - ); - setCategory(uniqueCategories); - }, [transactions]); - - const [dateRange, setDateRange] = useState(getDefaultDateRange()); - const [selectedCategory, setSelectedCategory] = useState(null); - const [selectedTransferType, setSelectedTransferType] = useState(null); - const [searchValue, setSearchValue] = useState(''); - - const fetchTransferType = async () => { - try { - const response = await GetData(`${API_URL}/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 type', error); - } - }; - - useEffect(() => { - fetchTransferType(); - }, []); - - useEffect(() => { - const fetchWalletTransactions = async () => { - if (!selectedWallet?.ID || !showDetailDialog) return; - - setIsLoading(true); - try { - const response = await GetData( - `${API_URL_WALLET}/dashboard/balance/list-balance-detail/${selectedWallet.ID}`, - { - limit: 100, - page: 1, - order_field: 'created_at', - order_direction: 'DESC' - } - ); - - const transactionData = - response?.data?.list || (Array.isArray(response?.data) ? response.data : []); - - // console.log('Loaded Transactions:', transactionData); - setTransactions(transactionData); - setFilteredTransactions(transactionData); - } catch (error) { - console.error('Error fetching balance:', error); - } finally { - setIsLoading(false); - } - }; - - fetchWalletTransactions(); - }, [showDetailDialog, selectedWallet, GetData]); - - useEffect(() => { - if (transactions.length === 0) return; - - setIsLoading(true); - const timer = setTimeout(() => { - // console.log('Debugging Filter - Selected Transfer Type:', selectedTransferType); - - const filtered = transactions.filter((transaction) => { - // console.log('Transaction Type:', { - // id: transaction.transaction_type?.id, - // expected: selectedTransferType, - // match: transaction.transaction_type?.id === selectedTransferType - // }); - - const transactionDate = new Date(transaction.date); - const fromDate = new Date(dateRange.from); - const toDate = new Date(dateRange.to); - - fromDate.setHours(0, 0, 0, 0); - toDate.setHours(23, 59, 59, 999); - - const dateMatch = transactionDate >= fromDate && transactionDate <= toDate; - const typeMatch = selectedTransferType - ? transaction.transaction_type?.id === selectedTransferType - : true; - const codeMatch = searchValue - ? transaction.transaction_code.toLowerCase().includes(searchValue.toLowerCase()) - : true; - const categoryMatch = selectedCategory ? transaction.category === selectedCategory : true; - - return dateMatch && typeMatch && codeMatch && categoryMatch; - }); - - // console.log('Filtered Results Count:', filtered.length); - setFilteredTransactions(filtered); - setIsLoading(false); - }, 300); - - return () => clearTimeout(timer); - }, [dateRange, transactions, selectedTransferType, searchValue, selectedCategory]); - - const handleCategoryChange = (value: string) => { - setSelectedCategory(value); - }; - - const handleTransferTypeChange = (value: string) => { - // console.log('Transfer Type Changed:', value); - setSelectedTransferType(value === 'all' ? null : value); - }; - - const handleClearAllFilters = () => { - setDateRange(getDefaultDateRange()); - setSelectedTransferType(null); - setSearchValue(''); - setSelectedCategory(null); - }; - - 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'); - } - }; - - checkNewDay(); - - const interval = setInterval(checkNewDay, 60 * 60 * 1000); - return () => clearInterval(interval); - }, [showDetailDialog, dateRange]); - - return ( - - - - Details Wallet Statement - - -
-
- - - - -
- -
- -
- -
- - - - - - -
-
- -
- {isLoading ? ( -
Loading details...
- ) : filteredTransactions.length > 0 ? ( -
-
- - - - - - - - - - - - - - - - {filteredTransactions.map((transaction) => ( - - - - - - - - - - - - ))} - -
Transaction CodeTransaction TypeTypeAmountPre AmountPost AmountCategoryDateNotes
{transaction.transaction_code} - {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 || '-'}
-
-
- ) : ( -
No transaction data available
- )} -
-
-
-
- ); -}; - -export default ShowDialog; diff --git a/src/pages/wallet/wallet-history/hooks/useManageWalletHistoryContext.tsx b/src/pages/wallet/wallet-history/hooks/useManageWalletHistoryContext.tsx deleted file mode 100644 index 9e5f912..0000000 --- a/src/pages/wallet/wallet-history/hooks/useManageWalletHistoryContext.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { useContext } from 'react'; -import { ManageWalletContext } from './ManageWalletHistoryContext'; - -const useManageWalletContext = () => { - const context = useContext(ManageWalletContext); - if (!context) { - throw new Error('useManageWalletContext must be used within a ManageWalletContextProvider'); - } - return context; -}; - -export { useManageWalletContext }; diff --git a/src/pages/wallet/wallet-history/WalletHistory.tsx b/src/pages/wallet/wallet-statement/WalletStatement.tsx similarity index 93% rename from src/pages/wallet/wallet-history/WalletHistory.tsx rename to src/pages/wallet/wallet-statement/WalletStatement.tsx index 641ba29..ab6a685 100644 --- a/src/pages/wallet/wallet-history/WalletHistory.tsx +++ b/src/pages/wallet/wallet-statement/WalletStatement.tsx @@ -1,9 +1,9 @@ import { Container, DataGridInner } from '@/components'; -import { ManageWalletContextProvider } from './hooks/ManageWalletHistoryContext'; import { Breadcrumbs, Link } from '@mui/material'; import { Helmet } from 'react-helmet'; +import { ManageWalletContextProvider } from './hooks/ManageWalletStatementContext'; -const WalletHistory = () => { +const WalletStatement = () => { return ( <> @@ -35,4 +35,4 @@ const WalletHistory = () => { ); }; -export default WalletHistory; +export default WalletStatement; diff --git a/src/pages/wallet/wallet-history/blocks/ListToolbar.tsx b/src/pages/wallet/wallet-statement/blocks/ListToolbar.tsx similarity index 100% rename from src/pages/wallet/wallet-history/blocks/ListToolbar.tsx rename to src/pages/wallet/wallet-statement/blocks/ListToolbar.tsx diff --git a/src/pages/wallet/wallet-statement/blocks/ShowDetailDialog.tsx b/src/pages/wallet/wallet-statement/blocks/ShowDetailDialog.tsx new file mode 100644 index 0000000..33d7ede --- /dev/null +++ b/src/pages/wallet/wallet-statement/blocks/ShowDetailDialog.tsx @@ -0,0 +1,503 @@ +import { useEffect, useState } from 'react'; +import { + Dialog, + DialogBody, + DialogContent, + DialogHeader, + DialogTitle +} from '@/components/ui/dialog'; +import { + Select, + SelectContent, + SelectItem, + 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'; + +const API_URL_WALLET = apiConfig.service_wallet; +const API_URL = apiConfig.service_transaction; + +interface TransactionType { + id: string; + name: string; +} + +interface WalletTransaction { + ID: string; + id_balance: 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; +} + +const ShowDialog = () => { + const { showDetailDialog, setShowDetailDialog, selectedWallet } = useManageStatementContext(); + const [isLoading, setIsLoading] = useState(false); + 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 [dateRange, setDateRange] = useState(getDefaultDateRange()); + const [category, setCategory] = useState(null); + const [selectedCategory, setSelectedCategory] = useState(''); + const [selectedTransactionType, setSelectedTransactionType] = useState(''); + const [searchValue, setSearchValue] = useState(''); + + 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 () => { + try { + setIsLoading(true); + const response = await GetData(`${API_URL}/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)) + ); + } catch (error) { + console.error('Error fetching Transaction type', error); + toast.error('Failed to load transaction types'); + } finally { + setIsLoading(false); + } + }; + + useEffect(() => { + fetchTransactionType(); + }, []); + + const fetchFilteredTransactions = async () => { + if (!selectedWallet?.ID) return; + + setIsLoading(true); + try { + let fromDateTime = dateRange.from; + if (!fromDateTime.includes('T')) { + const fromDate = new Date(fromDateTime); + fromDateTime = formatDate(fromDate, true); + } + + 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); + } + }; + + 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 handleClearAllFilters = () => { + const defaultDates = getDefaultDateRange(); + console.log('Resetting filters to default:', defaultDates); + setDateRange(defaultDates); + setSelectedTransactionType(''); + setSearchValue(''); + setSelectedCategory(''); + setCurrentPage(1); + }; + + const handlePageChange = (page: number) => { + setCurrentPage(page); + }; + + 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'); + } + }; + + checkNewDay(); + + const interval = setInterval(checkNewDay, 60 * 60 * 1000); + return () => clearInterval(interval); + }, [showDetailDialog, dateRange]); + + const getDisplayDate = (dateTimeString: string) => { + if (!dateTimeString) return ''; + return dateTimeString.split('T')[0]; + }; + + return ( + + + + Details Wallet Statement + + +
+
+ + + + +
+ +
+ +
+ +
+ + + + + + +
+ + {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 ( + + ); + })} +
+ + +
+
+
+
+
+
+ ); +}; + +export default ShowDialog; diff --git a/src/pages/wallet/wallet-history/hooks/ManageWalletHistoryContext.tsx b/src/pages/wallet/wallet-statement/hooks/ManageWalletStatementContext.tsx similarity index 97% rename from src/pages/wallet/wallet-history/hooks/ManageWalletHistoryContext.tsx rename to src/pages/wallet/wallet-statement/hooks/ManageWalletStatementContext.tsx index 629762a..ac3cc27 100644 --- a/src/pages/wallet/wallet-history/hooks/ManageWalletHistoryContext.tsx +++ b/src/pages/wallet/wallet-statement/hooks/ManageWalletStatementContext.tsx @@ -250,14 +250,7 @@ const ManageWalletContextProvider = ({ children }: { children: React.ReactNode } if (Array.isArray(filter)) { filter.forEach((f: any) => { if (f.id === 'msisdn' && f.value) { - filterParams.msisdn = { like: `%${f.value.toLowerCase()}%` }; - } - - if (f.id === 'CreatedAt' && f.value?.from && f.value?.to) { - filterParams.created_at = { - from: `${f.value.from} 00:00:00`, - to: `${f.value.to} 23:59:59` - }; + filterParams.msisdn = f.value; } if (f.id === 'id_wallet' && f.value) { diff --git a/src/pages/wallet/wallet-statement/hooks/useManageWalletStatementContext.tsx b/src/pages/wallet/wallet-statement/hooks/useManageWalletStatementContext.tsx new file mode 100644 index 0000000..dd7c6df --- /dev/null +++ b/src/pages/wallet/wallet-statement/hooks/useManageWalletStatementContext.tsx @@ -0,0 +1,13 @@ +import { useContext } from 'react'; +import { ManageWalletContext } from './ManageWalletStatementContext'; + + +const useManageStatementContext = () => { + const context = useContext(ManageWalletContext); + if (!context) { + throw new Error('useManageStatementContext must be used within a ManageStatementContextProvider'); + } + return context; +}; + +export { useManageStatementContext }; diff --git a/src/routing/AppRoutingSetup.tsx b/src/routing/AppRoutingSetup.tsx index f053db3..1da4e34 100644 --- a/src/routing/AppRoutingSetup.tsx +++ b/src/routing/AppRoutingSetup.tsx @@ -41,7 +41,7 @@ import ProviderMaster from '@/pages/master/provider/ProviderMaster'; import ConversionMaster from '@/pages/master/conversion/ConversionMaster'; import RewardMaster from '@/pages/master/reward/RewardMaster'; import WalletRuleMaster from '@/pages/master/walletRule/WalletRuleMaster'; -import WalletHistory from '@/pages/wallet/wallet-history/WalletHistory'; +import WalletStatement from '@/pages/wallet/wallet-statement/WalletStatement'; import WalletMaster from '@/pages/master/wallet/WalletMaster'; import CurrencyMaster from '@/pages/master/currency/CurrencyMaster'; import FeedbackMemberMaster from '@/pages/members/feedback-member/FeedbackMember'; @@ -101,7 +101,7 @@ const AppRoutingSetup = (): ReactElement => { } /> - } /> + } /> } />