Merge branch 'master' of https://git.shiblysolution.id/TPAY/dashboard
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Dialog, DialogContent } from '@/components/ui/dialog';
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
@ -59,16 +59,12 @@ const EditDialog = () => {
|
|||||||
if (response?.status) {
|
if (response?.status) {
|
||||||
toast.success('Feedback reviewed successfully');
|
toast.success('Feedback reviewed successfully');
|
||||||
|
|
||||||
// Close the edit dialog
|
|
||||||
handleEditDialog(false, null);
|
handleEditDialog(false, null);
|
||||||
|
|
||||||
// First close the detail dialog to reset its state
|
|
||||||
handleDetailDialog(false, null);
|
handleDetailDialog(false, null);
|
||||||
|
|
||||||
// Refresh the main data grid first
|
|
||||||
refreshData();
|
refreshData();
|
||||||
|
|
||||||
// Wait a tiny bit then reopen the detail with refreshed data
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
handleDetailDialog(true, selectedFeedback);
|
handleDetailDialog(true, selectedFeedback);
|
||||||
}, 300);
|
}, 300);
|
||||||
@ -97,7 +93,6 @@ const EditDialog = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data = response.data;
|
const data = response.data;
|
||||||
// console.log('Fetched data:', data);
|
|
||||||
|
|
||||||
setReviewNotes(data.review_notes || '');
|
setReviewNotes(data.review_notes || '');
|
||||||
|
|
||||||
@ -112,7 +107,11 @@ const EditDialog = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={showEditDialog} onOpenChange={(open) => handleEditDialog(open, null)}>
|
<Dialog open={showEditDialog} onOpenChange={(open) => handleEditDialog(open, null)}>
|
||||||
<DialogContent className="p-0 w-full sm:max-w-2xl bg-white rounded-lg overflow-hidden">
|
<DialogContent className="p-0 w-full sm:max-w-2xl bg-white rounded-lg overflow-hidden [&>button]:hidden">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle></DialogTitle>
|
||||||
|
<DialogDescription></DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="p-6 flex items-center justify-between border-b">
|
<div className="p-6 flex items-center justify-between border-b">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -10,8 +10,10 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle
|
DialogTitle
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
const API_URL = apiConfig.transaction;
|
const API_URL = apiConfig.transaction;
|
||||||
|
const API_URL_CURRENT_BALANCE = apiConfig.service_wallet;
|
||||||
|
|
||||||
const DetailApprovalTransaction = () => {
|
const DetailApprovalTransaction = () => {
|
||||||
const { GetData } = useCallApi();
|
const { GetData } = useCallApi();
|
||||||
@ -51,6 +53,60 @@ const DetailApprovalTransaction = () => {
|
|||||||
}
|
}
|
||||||
}, [showDetailDialog]);
|
}, [showDetailDialog]);
|
||||||
|
|
||||||
|
const [currentBalance, setCurrentBalance] = useState<number | null>(null);
|
||||||
|
const [currentBalanceTransfer, setCurrentBalanceTransfer] = useState<number | null>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrentBalance(null);
|
||||||
|
}, [transactionDetails]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrentBalanceTransfer(null);
|
||||||
|
}, [transactionDetails]);
|
||||||
|
|
||||||
|
const getCurrentBallanceWallet = async (customerId: string) => {
|
||||||
|
try {
|
||||||
|
const responsecurrentbalance = await GetData(
|
||||||
|
`${API_URL_CURRENT_BALANCE}/dashboard/balance/account/${customerId}`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
const match = responsecurrentbalance?.data?.find(
|
||||||
|
(item: any) => item.id_wallet === transactionDetails?.origin_wallet?.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
// console.log('Matched Wallet:', match); // Optional debug
|
||||||
|
return match.amount;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching current balance', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCurrentBallanceWalletTransfer = async (customerIdtransfer: string) => {
|
||||||
|
try {
|
||||||
|
const response = await GetData(
|
||||||
|
`${API_URL_CURRENT_BALANCE}/dashboard/balance/account/${customerIdtransfer}`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
const match = response?.data?.find(
|
||||||
|
(item: any) => item.id_wallet === transactionDetails?.transfer?.destination_wallet?.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
// console.log('Matched Wallet:', match);
|
||||||
|
return match.amount;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching current balance', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={showDetailDialog} onOpenChange={setShowDetailDialog}>
|
<Dialog open={showDetailDialog} onOpenChange={setShowDetailDialog}>
|
||||||
@ -471,7 +527,27 @@ const DetailApprovalTransaction = () => {
|
|||||||
<p className="text-sm text-gray-500">Description</p>
|
<p className="text-sm text-gray-500">Description</p>
|
||||||
<p className="font-medium">{transactionDetails?.origin_wallet.description}</p>
|
<p className="font-medium">{transactionDetails?.origin_wallet.description}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-gray-500">Current Balance</p>
|
||||||
|
<p className="font-medium">
|
||||||
|
{currentBalance !== null && currentBalance !== undefined
|
||||||
|
? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(currentBalance)
|
||||||
|
: '-'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Button
|
||||||
|
className="btn btn-primary"
|
||||||
|
type="button"
|
||||||
|
onClick={async () => {
|
||||||
|
const balance = await getCurrentBallanceWallet(transactionDetails?.origin_customer.id);
|
||||||
|
if (balance !== null) {
|
||||||
|
setCurrentBalance(balance);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Generate Current Balance
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -482,16 +558,42 @@ const DetailApprovalTransaction = () => {
|
|||||||
{!transactionDetails?.transfer ? (
|
{!transactionDetails?.transfer ? (
|
||||||
<div className="text-center text-sm text-gray-500">No Data available</div>
|
<div className="text-center text-sm text-gray-500">No Data available</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<>
|
||||||
<div>
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<p className="text-sm text-gray-500">Name</p>
|
<div>
|
||||||
<p className="font-medium">{transactionDetails.transfer.destination_wallet.name}</p>
|
<p className="text-sm text-gray-500">Name</p>
|
||||||
|
<p className="font-medium">{transactionDetails.transfer.destination_wallet.name}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-gray-500">Description</p>
|
||||||
|
<p className="font-medium">{transactionDetails.transfer.destination_wallet.description}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-gray-500">Current Balance</p>
|
||||||
|
<p className="font-medium">
|
||||||
|
{currentBalanceTransfer !== null
|
||||||
|
? new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'USD',
|
||||||
|
}).format(currentBalanceTransfer)
|
||||||
|
: '-'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<Button
|
||||||
<p className="text-sm text-gray-500">Description</p>
|
className="btn btn-primary"
|
||||||
<p className="font-medium">{transactionDetails.transfer.destination_wallet.description}</p>
|
type="button"
|
||||||
</div>
|
onClick={async () => {
|
||||||
</div>
|
const customerId = transactionDetails?.transfer?.destination_customer?.id;
|
||||||
|
const balance = await getCurrentBallanceWalletTransfer(customerId);
|
||||||
|
if (balance !== null) {
|
||||||
|
setCurrentBalanceTransfer(balance); // ✅ INI YANG BENAR
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Generate Current Balance
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -540,8 +642,8 @@ const DetailApprovalTransaction = () => {
|
|||||||
})
|
})
|
||||||
: ''}</td>
|
: ''}</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{log.request_body}</td>
|
<td className="px-4 py-2 text-sm text-gray-500">{log.request_body}</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{log.response_body}</td>
|
<td className="px-4 py-2 text-sm text-gray-500">{log.response_body}</td>
|
||||||
<td className="px-4 py-2 text-sm text-gray-500">{log.request_endpoint}</td>
|
<td className="px-4 py-2 text-sm text-gray-500">{log.request_endpoint}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -13,11 +13,15 @@ import {
|
|||||||
DialogTitle
|
DialogTitle
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
import { useDataGrid } from '@/components';
|
import { useDataGrid } from '@/components';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
const API_URL = apiConfig.transaction;
|
const API_URL = apiConfig.transaction;
|
||||||
|
const API_URL_CURRENT_BALANCE = apiConfig.service_wallet;
|
||||||
|
|
||||||
|
|
||||||
const DetailTransaction = () => {
|
const DetailTransaction = () => {
|
||||||
const { GetData } = useCallApi();
|
const { GetData } = useCallApi();
|
||||||
|
|
||||||
// const { reload } = useDataGrid();
|
// const { reload } = useDataGrid();
|
||||||
const {
|
const {
|
||||||
showDetailDialog,
|
showDetailDialog,
|
||||||
@ -45,7 +49,7 @@ const DetailTransaction = () => {
|
|||||||
id: selectedTransactionId
|
id: selectedTransactionId
|
||||||
});
|
});
|
||||||
setTransactionDetails(response?.data);
|
setTransactionDetails(response?.data);
|
||||||
// console.log(response?.data);
|
// console.log(response?.data.origin_customer.id);
|
||||||
// console.log(selectedTransactionId);
|
// console.log(selectedTransactionId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching transaction', error);
|
console.error('Error fetching transaction', error);
|
||||||
@ -67,6 +71,61 @@ const DetailTransaction = () => {
|
|||||||
}
|
}
|
||||||
}, [showDetailDialog]);
|
}, [showDetailDialog]);
|
||||||
|
|
||||||
|
const [currentBalance, setCurrentBalance] = useState<number | null>(null);
|
||||||
|
const [currentBalanceTransfer, setCurrentBalanceTransfer] = useState<number | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrentBalance(null);
|
||||||
|
}, [transactionDetails]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrentBalanceTransfer(null);
|
||||||
|
}, [transactionDetails]);
|
||||||
|
|
||||||
|
const getCurrentBallanceWallet = async (customerId: string) => {
|
||||||
|
try {
|
||||||
|
const responsecurrentbalance = await GetData(
|
||||||
|
`${API_URL_CURRENT_BALANCE}/dashboard/balance/account/${customerId}`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
const match = responsecurrentbalance?.data?.find(
|
||||||
|
(item: any) => item.id_wallet === transactionDetails?.origin_wallet?.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
// console.log('Matched Wallet:', match); // Optional debug
|
||||||
|
return match.amount;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching current balance', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const getCurrentBallanceWalletTransfer = async (customerIdtransfer: string) => {
|
||||||
|
try {
|
||||||
|
const response = await GetData(
|
||||||
|
`${API_URL_CURRENT_BALANCE}/dashboard/balance/account/${customerIdtransfer}`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
const match = response?.data?.find(
|
||||||
|
(item: any) => item.id_wallet === transactionDetails?.transfer?.destination_wallet?.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
// console.log('Matched Wallet:', match);
|
||||||
|
return match.amount;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching current balance', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={showDetailDialog} onOpenChange={setShowDetailDialog}>
|
<Dialog open={showDetailDialog} onOpenChange={setShowDetailDialog}>
|
||||||
@ -231,16 +290,6 @@ const DetailTransaction = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 className="font-semibold flex items-center">
|
|
||||||
Origin Wallet
|
|
||||||
<span className="ml-2 bg-blue-100 text-blue-800 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">
|
|
||||||
Wallet
|
|
||||||
</span>
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3 className="font-semibold flex items-center">
|
<h3 className="font-semibold flex items-center">
|
||||||
Purchase
|
Purchase
|
||||||
<span className="ml-2 bg-blue-100 text-blue-800 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">
|
<span className="ml-2 bg-blue-100 text-blue-800 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">
|
||||||
@ -482,7 +531,27 @@ const DetailTransaction = () => {
|
|||||||
<p className="text-sm text-gray-500">Description</p>
|
<p className="text-sm text-gray-500">Description</p>
|
||||||
<p className="font-medium">{transactionDetails?.origin_wallet.description}</p>
|
<p className="font-medium">{transactionDetails?.origin_wallet.description}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-gray-500">Current Balance</p>
|
||||||
|
<p className="font-medium">
|
||||||
|
{currentBalance !== null && currentBalance !== undefined
|
||||||
|
? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(currentBalance)
|
||||||
|
: '-'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Button
|
||||||
|
className="btn btn-primary"
|
||||||
|
type="button"
|
||||||
|
onClick={async () => {
|
||||||
|
const balance = await getCurrentBallanceWallet(transactionDetails?.origin_customer.id);
|
||||||
|
if (balance !== null) {
|
||||||
|
setCurrentBalance(balance);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Generate Current Balance
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -493,16 +562,43 @@ const DetailTransaction = () => {
|
|||||||
{!transactionDetails?.transfer ? (
|
{!transactionDetails?.transfer ? (
|
||||||
<div className="text-sm text-gray-500">No Data available</div>
|
<div className="text-sm text-gray-500">No Data available</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<>
|
||||||
<div>
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<p className="text-sm text-gray-500">Name</p>
|
<div>
|
||||||
<p className="font-medium">{transactionDetails.transfer.destination_wallet.name}</p>
|
<p className="text-sm text-gray-500">Name</p>
|
||||||
|
<p className="font-medium">{transactionDetails.transfer.destination_wallet.name}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-gray-500">Description</p>
|
||||||
|
<p className="font-medium">{transactionDetails.transfer.destination_wallet.description}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-gray-500">Current Balance</p>
|
||||||
|
<p className="font-medium">
|
||||||
|
{currentBalanceTransfer !== null
|
||||||
|
? new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'USD',
|
||||||
|
}).format(currentBalanceTransfer)
|
||||||
|
: '-'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<Button
|
||||||
<p className="text-sm text-gray-500">Description</p>
|
className="btn btn-primary"
|
||||||
<p className="font-medium">{transactionDetails.transfer.destination_wallet.description}</p>
|
type="button"
|
||||||
</div>
|
onClick={async () => {
|
||||||
</div>
|
const customerId = transactionDetails?.transfer?.destination_customer?.id;
|
||||||
|
const balance = await getCurrentBallanceWalletTransfer(customerId);
|
||||||
|
if (balance !== null) {
|
||||||
|
setCurrentBalanceTransfer(balance); // ✅ INI YANG BENAR
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Generate Current Balance
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -23,6 +23,29 @@ interface TransferType {
|
|||||||
|
|
||||||
const API_URL = apiConfig.service_transaction;
|
const API_URL = apiConfig.service_transaction;
|
||||||
|
|
||||||
|
const typeLabelMap: Record<string, string> = {
|
||||||
|
D: 'Disbursement',
|
||||||
|
O: 'Other',
|
||||||
|
CA: 'Change Group Emoney Customer to Agent',
|
||||||
|
AC: 'Change Group Emoney Agent to Customer',
|
||||||
|
PC: 'Change Group Point Agent to Customer',
|
||||||
|
PA: 'Change Group Point Customer to Agent',
|
||||||
|
CE: 'Return Customer Emoney',
|
||||||
|
AD: 'Return Agent Deposit',
|
||||||
|
AM: 'Return Agent Merchant',
|
||||||
|
AE: 'Return Agent Emoney',
|
||||||
|
R: 'Reward Point',
|
||||||
|
TE: 'Top Up Escrow',
|
||||||
|
TM: 'Top Up Master Agent',
|
||||||
|
TA: 'Top Up Agent',
|
||||||
|
PL: 'Purchase Loja',
|
||||||
|
DE: 'Disbursment Escrow',
|
||||||
|
DM: 'Disbursment Master Agent',
|
||||||
|
DA: 'Disbursment Agent',
|
||||||
|
WI: 'Withdraw Merchant',
|
||||||
|
IC: 'Income Merchant'
|
||||||
|
};
|
||||||
|
|
||||||
const ListToolbar = () => {
|
const ListToolbar = () => {
|
||||||
const { table, reload } = useDataGrid();
|
const { table, reload } = useDataGrid();
|
||||||
const { handleAddDialog } = useManageTransferTypeContext();
|
const { handleAddDialog } = useManageTransferTypeContext();
|
||||||
@ -31,40 +54,10 @@ const ListToolbar = () => {
|
|||||||
|
|
||||||
const [searchValue, setSearchValue] = useState('');
|
const [searchValue, setSearchValue] = useState('');
|
||||||
const [statusTypes, setStatusTypes] = useState('');
|
const [statusTypes, setStatusTypes] = useState('');
|
||||||
const [walletOrigins, setWalletOrigin] = useState('');
|
|
||||||
const [walletDestinations, setWalletDestination] = useState('');
|
|
||||||
const [approval, setApproval] = useState('');
|
const [approval, setApproval] = useState('');
|
||||||
|
|
||||||
const unique = (arr: string[]) => Array.from(new Set(arr));
|
const unique = (arr: string[]) => Array.from(new Set(arr));
|
||||||
const types = unique(transferTypes.map((t) => t.type));
|
const types = unique(transferTypes.map((t) => t.type));
|
||||||
const origins = unique(transferTypes.map((t) => t.wallet_origin.name));
|
|
||||||
const destinations = unique(transferTypes.map((t) => t.wallet_destination.name));
|
|
||||||
const typeLabelMap: Record<string, string> = {
|
|
||||||
D: 'Disbursement',
|
|
||||||
O: 'Other',
|
|
||||||
CA: 'Change Group Emoney Customer to Agent',
|
|
||||||
AC: 'Change Group Emoney Agent to Customer',
|
|
||||||
PC: 'Change Group Point Agent to Customer',
|
|
||||||
PA: 'Change Group Point Customer to Agent',
|
|
||||||
CE: 'Return Customer Emoney',
|
|
||||||
AD: 'Return Agent Deposit',
|
|
||||||
AM: 'Return Agent Merchant',
|
|
||||||
AE: 'Return Agent Emoney',
|
|
||||||
R: 'Reward Point',
|
|
||||||
TE: 'Top Up Escrow',
|
|
||||||
TM: 'Top Up Master Agent',
|
|
||||||
TA: 'Top Up Agent',
|
|
||||||
PL: 'Purchase Loja',
|
|
||||||
DE: 'Disbursment Escrow',
|
|
||||||
DM: 'Disbursment Master Agent',
|
|
||||||
DA: 'Disbursment Agent',
|
|
||||||
WI: 'Withdraw Merchant',
|
|
||||||
IC: 'Income Merchant'
|
|
||||||
};
|
|
||||||
const approvalLabelMap: Record<string, string> = {
|
|
||||||
Y: 'Yes',
|
|
||||||
N: 'No'
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
@ -78,18 +71,6 @@ const ListToolbar = () => {
|
|||||||
table.getColumn('type')?.setFilterValue(statusTypes);
|
table.getColumn('type')?.setFilterValue(statusTypes);
|
||||||
}, [statusTypes]);
|
}, [statusTypes]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
table
|
|
||||||
.getColumn('wallet_origin')
|
|
||||||
?.setFilterValue(walletOrigins === '__all__' ? '' : walletOrigins);
|
|
||||||
}, [walletOrigins]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
table
|
|
||||||
.getColumn('wallet_destination')
|
|
||||||
?.setFilterValue(walletDestinations === '__all__' ? '' : walletDestinations);
|
|
||||||
}, [walletDestinations]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
table.getColumn('status_approval')?.setFilterValue(approval);
|
table.getColumn('status_approval')?.setFilterValue(approval);
|
||||||
}, [approval]);
|
}, [approval]);
|
||||||
@ -115,62 +96,86 @@ const ListToolbar = () => {
|
|||||||
|
|
||||||
const handleClearFilter = () => {
|
const handleClearFilter = () => {
|
||||||
setSearchValue('');
|
setSearchValue('');
|
||||||
setStatusTypes('__all__');
|
setStatusTypes('');
|
||||||
setWalletOrigin('__all__');
|
setApproval('');
|
||||||
setWalletDestination('__all__');
|
|
||||||
setApproval('__all__');
|
|
||||||
table.getColumn('name')?.setFilterValue('');
|
table.getColumn('name')?.setFilterValue('');
|
||||||
table.getColumn('type')?.setFilterValue('');
|
table.getColumn('type')?.setFilterValue('');
|
||||||
table.getColumn('wallet_origin')?.setFilterValue('');
|
|
||||||
table.getColumn('wallet_destination')?.setFilterValue('');
|
|
||||||
table.getColumn('status_approval')?.setFilterValue('');
|
table.getColumn('status_approval')?.setFilterValue('');
|
||||||
table.setPageIndex(0);
|
|
||||||
|
setTimeout(() => {
|
||||||
|
table.setPageIndex(0);
|
||||||
|
reload();
|
||||||
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderSelect = (
|
|
||||||
placeholder: string,
|
|
||||||
value: string,
|
|
||||||
onChange: (val: string) => void,
|
|
||||||
options: string[],
|
|
||||||
labelMap?: Record<string, string>
|
|
||||||
) => (
|
|
||||||
<div className="min-w-[220px]">
|
|
||||||
<Select value={value === '' ? undefined : value} onValueChange={onChange}>
|
|
||||||
<SelectTrigger className="h-8 text-sm">
|
|
||||||
<SelectValue placeholder={placeholder} />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{options.map((opt) => (
|
|
||||||
<SelectItem key={opt} value={opt}>
|
|
||||||
{labelMap?.[opt] ?? opt}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card-header flex-wrap gap-2 border-b-0 px-5">
|
<div className="card-header flex-wrap border-b-0 px-5">
|
||||||
<div className="flex flex-wrap gap-2 lg:gap-5 w-full">
|
<div className="flex flex-wrap gap-1 w-full">
|
||||||
<div className="flex justify-between w-full items-center mb-3">
|
<div className="flex justify-between w-full items-center mb-3">
|
||||||
<div className="flex w-[34%] gap-2 items-center">
|
<div className="flex flex-nowrap items-end gap-3 w-full overflow-x-auto">
|
||||||
<label className="input input-sm w-full text-sm">
|
<div className="w-48 shrink-0">
|
||||||
<KeenIcon icon="magnifier" />
|
<label className="input input-sm w-full text-sm">
|
||||||
<input
|
<KeenIcon icon="magnifier" />
|
||||||
type="text"
|
<input
|
||||||
placeholder="Search Transaction Type Name"
|
type="text"
|
||||||
value={searchValue}
|
placeholder="Search Name"
|
||||||
onChange={(e) => setSearchValue(e.target.value)}
|
value={searchValue}
|
||||||
/>
|
onChange={(e) => setSearchValue(e.target.value)}
|
||||||
</label>
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="min-w-[220px] shrink-0">
|
||||||
|
<Select
|
||||||
|
key={`type-filter-${statusTypes}`}
|
||||||
|
value={statusTypes}
|
||||||
|
onValueChange={setStatusTypes}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-8 text-sm">
|
||||||
|
<SelectValue placeholder="Select Status Transaction Type" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent className="max-h-64 overflow-y-auto">
|
||||||
|
{types.map((opt) => (
|
||||||
|
<SelectItem key={opt} value={opt} className="truncate">
|
||||||
|
{typeLabelMap[opt] ?? opt}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="min-w-[220px] shrink-0">
|
||||||
|
<Select
|
||||||
|
key={`approval-filter-${approval}`}
|
||||||
|
value={approval}
|
||||||
|
onValueChange={setApproval}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-8 text-sm">
|
||||||
|
<SelectValue placeholder="Select Status Approval" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent className="max-h-64 overflow-y-auto">
|
||||||
|
<SelectItem value="Y" className="truncate">
|
||||||
|
Yes
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="N" className="truncate">
|
||||||
|
No
|
||||||
|
</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<DefaultTooltip title={'Reset Filter'} placement={'top'}>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="h-7.5 disabled:bg-gray-400"
|
||||||
|
onClick={handleClearFilter}
|
||||||
|
>
|
||||||
|
<KeenIcon icon="arrow-circle-left" />
|
||||||
|
</Button>
|
||||||
|
</DefaultTooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<Button variant="outline" className="h-8 px-3 text-xs" onClick={handleClearFilter}>
|
|
||||||
Clear
|
|
||||||
</Button>
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="h-8 px-3 text-xs"
|
className="h-8 px-3 text-xs"
|
||||||
@ -186,33 +191,6 @@ const ListToolbar = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Filter bar */}
|
|
||||||
<div className="flex flex-wrap gap-2 w-full">
|
|
||||||
{renderSelect(
|
|
||||||
'Select Status Transaction Type',
|
|
||||||
statusTypes,
|
|
||||||
setStatusTypes,
|
|
||||||
types,
|
|
||||||
typeLabelMap
|
|
||||||
)}
|
|
||||||
{renderSelect(
|
|
||||||
'Select Status Approval',
|
|
||||||
approval,
|
|
||||||
setApproval,
|
|
||||||
['Y', 'N'],
|
|
||||||
approvalLabelMap
|
|
||||||
)}
|
|
||||||
{/* {renderSelect('Select Origin Wallet', walletOrigins, setWalletOrigin, origins)}
|
|
||||||
{renderSelect(
|
|
||||||
'Select Destination Wallet',
|
|
||||||
walletDestinations,
|
|
||||||
setWalletDestination,
|
|
||||||
destinations
|
|
||||||
)} */}
|
|
||||||
|
|
||||||
{/* {renderSelect('Origin', walletOrigins, setWalletOrigin, origins)}
|
|
||||||
{renderSelect('Destination', walletDestinations, setWalletDestination, destinations)} */}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -19,6 +19,13 @@ interface WalletProps {
|
|||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getOneMonthsAgo = () => {
|
||||||
|
const today = new Date();
|
||||||
|
return new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatDate = (date: Date): string => date.toISOString().split('T')[0];
|
||||||
|
|
||||||
const ListToolbar = () => {
|
const ListToolbar = () => {
|
||||||
const { table, reload } = useDataGrid();
|
const { table, reload } = useDataGrid();
|
||||||
const { GetData } = useCallApi();
|
const { GetData } = useCallApi();
|
||||||
@ -32,12 +39,10 @@ const ListToolbar = () => {
|
|||||||
);
|
);
|
||||||
const [wallets, setWallets] = useState<WalletProps[]>([]);
|
const [wallets, setWallets] = useState<WalletProps[]>([]);
|
||||||
|
|
||||||
const formatDate = (date: Date): string => date.toISOString().split('T')[0];
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
|
const threeMonthsAgo = getOneMonthsAgo();
|
||||||
setDateRange({ from: formatDate(firstDayOfMonth), to: formatDate(today) });
|
setDateRange({ from: formatDate(threeMonthsAgo), to: formatDate(today) });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -45,7 +50,6 @@ const ListToolbar = () => {
|
|||||||
table.getColumn('msisdn')?.setFilterValue(searchValue);
|
table.getColumn('msisdn')?.setFilterValue(searchValue);
|
||||||
table.setPageIndex(0);
|
table.setPageIndex(0);
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
}, [searchValue, table]);
|
}, [searchValue, table]);
|
||||||
|
|
||||||
@ -89,35 +93,50 @@ const ListToolbar = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleClearAllFilters = () => {
|
const handleClearAllFilters = () => {
|
||||||
|
const today = new Date();
|
||||||
|
const oneMonthAgo = getOneMonthsAgo();
|
||||||
|
const resetDateRange = {
|
||||||
|
from: formatDate(oneMonthAgo),
|
||||||
|
to: formatDate(today)
|
||||||
|
};
|
||||||
|
|
||||||
setSearchValue('');
|
setSearchValue('');
|
||||||
setWalletId('');
|
setWalletId('');
|
||||||
|
setDateRange(resetDateRange);
|
||||||
|
|
||||||
|
table.getColumn('msisdn')?.setFilterValue('');
|
||||||
|
table.getColumn('id_wallet')?.setFilterValue('');
|
||||||
|
table.getColumn('CreatedAt')?.setFilterValue(resetDateRange);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
table.setPageIndex(0);
|
||||||
|
reload();
|
||||||
|
}, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const handleRefresh = () => {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
|
const threeMonthsAgo = getOneMonthsAgo();
|
||||||
setDateRange({
|
const resetDateRange = {
|
||||||
from: formatDate(firstDayOfMonth),
|
from: formatDate(threeMonthsAgo),
|
||||||
to: formatDate(today)
|
to: formatDate(today)
|
||||||
});
|
};
|
||||||
|
|
||||||
table.getAllColumns().forEach((column) => {
|
setSearchValue('');
|
||||||
if (column.id !== 'CreatedAt') {
|
setWalletId('');
|
||||||
column.setFilterValue(undefined);
|
setDateRange(resetDateRange);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
table.setColumnFilters([{ id: 'CreatedAt', value: resetDateRange }]);
|
||||||
table.setPageIndex(0);
|
table.setPageIndex(0);
|
||||||
|
reload();
|
||||||
table.getColumn('CreatedAt')?.setFilterValue({
|
|
||||||
from: formatDate(firstDayOfMonth),
|
|
||||||
to: formatDate(today)
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card-header flex-wrap gap-2 border-b-0 px-5">
|
<div className="card-header flex-wrap gap-2 border-b-0 px-5">
|
||||||
<div className="flex flex-wrap gap-2 lg:gap-5 w-full">
|
<div className="flex flex-wrap gap-2 lg:gap-5 w-full">
|
||||||
<div className="flex justify-between w-full items-center">
|
<div className="flex justify-between w-full items-center">
|
||||||
<div className="flex gap-3 items-center flex-wrap">
|
<div className="flex flex-wrap items-end gap-3 w-full">
|
||||||
<label className="input input-sm w-[160px]">
|
<label className="input input-sm w-[160px]">
|
||||||
From
|
From
|
||||||
<input
|
<input
|
||||||
@ -136,7 +155,7 @@ const ListToolbar = () => {
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label className="input input-sm w-1/3">
|
<label className="input input-sm w-[160px]">
|
||||||
<KeenIcon icon="magnifier" />
|
<KeenIcon icon="magnifier" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@ -146,14 +165,9 @@ const ListToolbar = () => {
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div className="w-[220px]">
|
<div className="w-[160px]">
|
||||||
<Select
|
<Select value={walletId} onValueChange={(value) => setWalletId(value)}>
|
||||||
value={walletId}
|
<SelectTrigger className="h-[32px]">
|
||||||
onValueChange={(value) => {
|
|
||||||
setWalletId(value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Select Wallet" />
|
<SelectValue placeholder="Select Wallet" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@ -165,25 +179,20 @@ const ListToolbar = () => {
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
<DefaultTooltip title={'Reset Filter'} placement={'top'}>
|
||||||
<Button variant="outline" size="sm" onClick={handleClearAllFilters}>
|
<Button
|
||||||
Clear Filter
|
variant="outline"
|
||||||
</Button>
|
className="h-7.5 disabled:bg-gray-400"
|
||||||
|
onClick={handleClearAllFilters}
|
||||||
|
>
|
||||||
|
<KeenIcon icon="arrow-circle-left" />
|
||||||
|
</Button>
|
||||||
|
</DefaultTooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-3 items-center">
|
<div className="flex gap-3 items-center">
|
||||||
<DefaultTooltip title={'Refresh'} placement={'top'}>
|
<DefaultTooltip title={'Refresh'} placement={'top'}>
|
||||||
<Button
|
<Button variant="outline" className="h-7.5" onClick={handleRefresh}>
|
||||||
variant="outline"
|
|
||||||
className="h-7.5"
|
|
||||||
onClick={() => {
|
|
||||||
setSearchValue('');
|
|
||||||
setWalletId('');
|
|
||||||
table.setColumnFilters((prev) => prev.filter((f) => f.id === 'CreatedAt'));
|
|
||||||
table.setPageIndex(0);
|
|
||||||
reload();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<KeenIcon icon="arrows-circle" />
|
<KeenIcon icon="arrows-circle" />
|
||||||
</Button>
|
</Button>
|
||||||
</DefaultTooltip>
|
</DefaultTooltip>
|
||||||
|
|||||||
Reference in New Issue
Block a user