844 lines
53 KiB
TypeScript
844 lines
53 KiB
TypeScript
import { useTransactionContext } from '../hooks/useTransactionContext';
|
|
import { useCallApi } from '@/hooks';
|
|
import { apiConfig } from '@/config/api.config';
|
|
import { useCallback, useEffect, useState } from 'react';
|
|
import ResendTransaction from './ResendTransaction';
|
|
import {
|
|
Dialog,
|
|
DialogBody,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle
|
|
} from '@/components/ui/dialog';
|
|
import { useDataGrid } from '@/components';
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
const API_URL = apiConfig.transaction;
|
|
const API_URL_CURRENT_BALANCE = apiConfig.service_wallet;
|
|
|
|
|
|
const DetailTransaction = () => {
|
|
const { GetData } = useCallApi();
|
|
|
|
// const { reload } = useDataGrid();
|
|
const {
|
|
showDetailDialog,
|
|
setShowDetailDialog,
|
|
selectedTransactionId
|
|
} = useTransactionContext();
|
|
|
|
const [transactionDetails, setTransactionDetails] = useState<any>(null);
|
|
|
|
const [showResend, setResend] = useState(false);
|
|
|
|
const handleResendOpen = () => {
|
|
setResend(true);
|
|
};
|
|
|
|
const handleResendClose = () => {
|
|
setResend(false);
|
|
};
|
|
|
|
useEffect(() => {
|
|
const fetchTransactionDetails = async () => {
|
|
if (selectedTransactionId) {
|
|
try {
|
|
const response = await GetData(`${API_URL}/transaction/history/detail/${selectedTransactionId}`, {
|
|
id: selectedTransactionId
|
|
});
|
|
setTransactionDetails(response?.data);
|
|
// console.log(response?.data);
|
|
// console.log(selectedTransactionId);
|
|
} catch (error) {
|
|
console.error('Error fetching transaction', error);
|
|
}
|
|
}
|
|
};
|
|
|
|
if (showDetailDialog && selectedTransactionId) {
|
|
fetchTransactionDetails();
|
|
}
|
|
}, [showDetailDialog, selectedTransactionId, GetData]);
|
|
|
|
const [activeTab, setActiveTab] = useState('detail');
|
|
|
|
// Reset ke tab awal saat dialog dibuka
|
|
useEffect(() => {
|
|
if (showDetailDialog) {
|
|
setActiveTab('detail');
|
|
}
|
|
}, [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 (
|
|
<Dialog open={showDetailDialog} onOpenChange={setShowDetailDialog}>
|
|
<DialogContent className="container-fixed max-w-[1024px] flex flex-col p-5 overflow-hidden">
|
|
<DialogHeader>
|
|
<DialogTitle>Transaction Details</DialogTitle>
|
|
</DialogHeader>
|
|
<DialogDescription></DialogDescription>
|
|
<DialogBody>
|
|
{/* Tabs Navigation */}
|
|
<div className="flex border-b border-gray-200">
|
|
<button
|
|
className={`py-2 px-4 font-medium text-sm focus:outline-none ${activeTab === 'detail' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-500 hover:text-gray-700'}`}
|
|
onClick={() => setActiveTab('detail')}
|
|
>
|
|
Detail Transaction
|
|
</button>
|
|
<button
|
|
className={`py-2 px-4 font-medium text-sm focus:outline-none ${activeTab === 'origincustomer' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-500 hover:text-gray-700'}`}
|
|
onClick={() => setActiveTab('origincustomer')}
|
|
>
|
|
Origin Customer
|
|
</button>
|
|
<button
|
|
className={`py-2 px-4 font-medium text-sm focus:outline-none ${activeTab === 'destinationcustomer' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-500 hover:text-gray-700'}`}
|
|
onClick={() => setActiveTab('destinationcustomer')}
|
|
>
|
|
Destination Customer
|
|
</button>
|
|
<button
|
|
className={`py-2 px-4 font-medium text-sm focus:outline-none ${activeTab === 'originwallet' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-500 hover:text-gray-700'}`}
|
|
onClick={() => setActiveTab('originwallet')}
|
|
>
|
|
Origin Wallet
|
|
</button>
|
|
<button
|
|
className={`py-2 px-4 font-medium text-sm focus:outline-none ${activeTab === 'destinationwallet' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-500 hover:text-gray-700'}`}
|
|
onClick={() => setActiveTab('destinationwallet')}
|
|
>
|
|
Destination Wallet
|
|
</button>
|
|
<button
|
|
className={`py-2 px-4 font-medium text-sm focus:outline-none ${activeTab === 'log' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-500 hover:text-gray-700'}`}
|
|
onClick={() => setActiveTab('log')}
|
|
>
|
|
Transaction Log
|
|
</button>
|
|
<button
|
|
className={`py-2 px-4 font-medium text-sm focus:outline-none ${activeTab === 'approve' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-500 hover:text-gray-700'}`}
|
|
onClick={() => setActiveTab('approve')}
|
|
>
|
|
Approval Log
|
|
</button>
|
|
<button
|
|
className={`py-2 px-4 font-medium text-sm focus:outline-none ${activeTab === 'p24' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-500 hover:text-gray-700'}`}
|
|
onClick={() => setActiveTab('p24')}
|
|
>
|
|
Log P24
|
|
</button>
|
|
</div>
|
|
|
|
{/* Tab Content */}
|
|
<div className="py-4 overflow-y-auto max-h-[400px]">
|
|
{activeTab === 'detail' && transactionDetails?.kind === 'P' && (
|
|
<div className="space-y-4">
|
|
<h3 className="font-semibold flex items-center">
|
|
Transaction Information
|
|
<span className="ml-2 bg-blue-100 text-blue-800 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">
|
|
Info
|
|
</span>
|
|
</h3>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<p className="text-sm text-gray-500">Transaction Date</p>
|
|
<p className="font-medium">
|
|
{transactionDetails?.transaction_date
|
|
? new Date(transactionDetails.transaction_date).toLocaleDateString('en-GB', {
|
|
day: '2-digit',
|
|
month: '2-digit',
|
|
year: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit',
|
|
hour12: false
|
|
})
|
|
: ''}
|
|
</p>
|
|
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Full Name</p>
|
|
<p className="font-medium">{transactionDetails?.origin_customer?.fullname ?? transactionDetails?.origin_customer?.origin_name}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Amount</p>
|
|
<p className="font-medium">
|
|
{(transactionDetails?.purchase?.amount ?? transactionDetails?.transfer?.amount) != null
|
|
? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
|
|
transactionDetails.purchase?.amount ?? transactionDetails.transfer?.amount
|
|
)
|
|
: "-"}
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<p className="text-sm text-gray-500">Fee</p>
|
|
<p className="font-medium">
|
|
{(transactionDetails?.transfer?.fee_amount ?? transactionDetails?.purchase?.fee_amount ?? 0)
|
|
.toLocaleString('en-US', { style: 'currency', currency: 'USD' })}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Status</p>
|
|
<div>
|
|
{(() => {
|
|
let status;
|
|
let badgeClass;
|
|
if (transactionDetails?.status === 'C') {
|
|
status = 'COMPLETE';
|
|
badgeClass = 'bg-green-100 text-green-800';
|
|
} else if (transactionDetails?.status === 'F') {
|
|
status = 'FAILED';
|
|
badgeClass = 'bg-red-100 text-red-800';
|
|
} else if (transactionDetails?.status === 'O') {
|
|
status = 'ON PROCESS';
|
|
badgeClass = 'bg-blue-100 text-blue-800';
|
|
} else {
|
|
status = 'PENDING';
|
|
badgeClass = 'bg-gray-100 text-gray-800';
|
|
}
|
|
|
|
return (
|
|
<span className={`px-3 py-1 rounded-full text-xs font-semibold ${badgeClass}`}>
|
|
{status}
|
|
</span>
|
|
);
|
|
})()}
|
|
</div>
|
|
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Transaction Type</p>
|
|
<p className="font-medium">
|
|
{(() => {
|
|
let kind;
|
|
if (transactionDetails?.kind === 'T') {
|
|
kind = 'TRANSFER';
|
|
} else if (transactionDetails?.kind === 'P') {
|
|
kind = 'PURCHASE';
|
|
} else if (transactionDetails?.kind === 'W') {
|
|
kind = 'WITHDRAW';
|
|
} else if (transactionDetails?.kind === 'U') {
|
|
kind = 'TOP UP';
|
|
} else if (transactionDetails?.kind === 'R') {
|
|
kind = 'RETURN';
|
|
}else if (transactionDetails?.kind === 'E') {
|
|
kind = 'REWARD';
|
|
}
|
|
return kind;
|
|
})()}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Description</p>
|
|
<p className="font-medium">{transactionDetails?.description || '-'}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Name</p>
|
|
<p className="font-medium">{transactionDetails?.type?.name || '-'}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<h3 className="font-semibold flex items-center">
|
|
Purchase
|
|
<span className="ml-2 bg-blue-100 text-blue-800 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">
|
|
Purchase
|
|
</span>
|
|
</h3>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<p className="text-sm text-gray-500">Amount</p>
|
|
<p className="font-medium">
|
|
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
|
|
transactionDetails?.purchase?.amount ?? transactionDetails?.transfer?.amount ?? 0
|
|
)}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Cashback</p>
|
|
<p className="font-medium">
|
|
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
|
|
transactionDetails?.purchase?.cashback ?? transactionDetails?.transfer?.cashback ?? 0
|
|
)}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Cashback Point</p>
|
|
<p className="font-medium">
|
|
{transactionDetails?.purchase?.cashback_point ?? transactionDetails?.transfer?.cashback_point ?? 0}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Fee Amount</p>
|
|
<p className="font-medium">
|
|
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
|
|
transactionDetails?.purchase?.fee_amount ?? transactionDetails?.transfer?.fee_amount ?? 0
|
|
)}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
{activeTab === 'detail' && (
|
|
<div className="space-y-4">
|
|
<h3 className="font-semibold flex items-center">
|
|
Product Information
|
|
<span className="ml-2 bg-blue-100 text-blue-800 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">
|
|
Product Info
|
|
</span>
|
|
</h3>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<p className="text-sm text-gray-500">Product Name</p>
|
|
<p className="font-medium">
|
|
{transactionDetails?.purchase?.product?.name ?? "-"}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Price Cash</p>
|
|
<p className="font-medium">
|
|
{transactionDetails?.purchase?.product?.price_cash != null
|
|
? new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(transactionDetails.purchase.product.price_cash)
|
|
: "-"}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Price Point</p>
|
|
<p className="font-medium">
|
|
{transactionDetails?.purchase?.product?.price_point ?? "-"}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Product Type</p>
|
|
<p className="font-medium">
|
|
{transactionDetails?.purchase?.product?.type ?? "-"}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Provider Name</p>
|
|
<p className="font-medium">
|
|
{transactionDetails?.purchase?.product?.provider?.description ?? "-"}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Provider Type</p>
|
|
<p className="font-medium">
|
|
{transactionDetails?.purchase?.product?.provider?.type === "h2h"
|
|
? "HOST TO HOST"
|
|
: transactionDetails?.purchase?.product?.provider?.type === "agent"
|
|
? "AGENT"
|
|
: "-"}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'detail' && transactionDetails?.kind != 'P' && transactionDetails?.transfer != null && (
|
|
<div className="space-y-4">
|
|
<h3 className="font-semibold flex items-center">
|
|
Transaction Information
|
|
<span className="ml-2 bg-blue-100 text-blue-800 text-xs font-semibold mr-2 px-2.5 py-0.5 rounded">
|
|
Info
|
|
</span>
|
|
</h3>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<p className="text-sm text-gray-500">Transaction Date</p>
|
|
<p className="font-medium">{transactionDetails?.transaction_date
|
|
? new Date(transactionDetails.transaction_date).toLocaleDateString('en-GB', {
|
|
day: '2-digit',
|
|
month: '2-digit',
|
|
year: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit',
|
|
hour12: false
|
|
})
|
|
: ''}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Full Name</p>
|
|
<p className="font-medium">{transactionDetails?.origin_customer?.fullname ?? transactionDetails?.origin_customer?.origin_name}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Amount</p>
|
|
<p className="font-medium">{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(transactionDetails?.transfer.amount)}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Fee</p>
|
|
<p className="font-medium">
|
|
{(transactionDetails?.transfer?.fee_amount ?? transactionDetails?.purchase?.fee_amount ?? 0).toLocaleString('en-US', { style: 'currency', currency: 'USD' })}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Status</p>
|
|
<p className="font-medium">
|
|
{(() => {
|
|
let status;
|
|
if (transactionDetails?.status === 'C') {
|
|
status = 'COMPLETE';
|
|
} else if (transactionDetails?.status === 'F') {
|
|
status = 'FAILED';
|
|
} else if (transactionDetails?.status === 'O') {
|
|
status = 'ON PROCESS';
|
|
} else {
|
|
status = 'PENDING';
|
|
}
|
|
return status;
|
|
})()}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Transaction Type</p>
|
|
<p className="font-medium">
|
|
{(() => {
|
|
let kind;
|
|
if (transactionDetails?.kind === 'T') {
|
|
kind = 'TRANSFER';
|
|
} else if (transactionDetails?.kind === 'P') {
|
|
kind = 'PURCHASE';
|
|
} else if (transactionDetails?.kind === 'W') {
|
|
kind = 'WITHDRAW';
|
|
} else if (transactionDetails?.kind === 'U') {
|
|
kind = 'TOP UP';
|
|
} else if (transactionDetails?.kind === 'R') {
|
|
kind = 'RETURN';
|
|
}else if (transactionDetails?.kind === 'E') {
|
|
kind = 'REWARD';
|
|
}
|
|
return kind;
|
|
})()}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Description</p>
|
|
<p className="font-medium">{transactionDetails?.description}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Name</p>
|
|
<p className="font-medium">{transactionDetails?.type?.name || '-'}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Reference</p>
|
|
<p className="font-medium">{transactionDetails?.transfer.reference}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Destination Iban</p>
|
|
<p className="font-medium">{transactionDetails?.transfer.destination_iban}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'origincustomer' && (
|
|
<div className="space-y-4">
|
|
<h3 className="font-semibold">Origin Customer</h3>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<p className="text-sm text-gray-500">Full Name</p>
|
|
<p className="font-medium">{transactionDetails?.origin_customer?.origin_name}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Phone Number</p>
|
|
<p className="font-medium">{transactionDetails?.origin_msisdn}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Email</p>
|
|
<p className="font-medium">{transactionDetails?.origin_customer?.email ?? "-"}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Username</p>
|
|
<p className="font-medium">{transactionDetails?.origin_customer?.username ?? "-"}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'destinationcustomer' && (
|
|
<div className="space-y-4">
|
|
<h3 className="font-semibold">Destination Customer</h3>
|
|
|
|
{transactionDetails?.transfer?.destination_customer ? (
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<p className="text-sm text-gray-500">Name</p>
|
|
<p className="font-medium">
|
|
{transactionDetails.transfer.destination_customer.fullname ?? transactionDetails.transfer.destination_name}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">MSISDN</p>
|
|
<p className="font-medium">
|
|
{transactionDetails.transfer.destination_customer.msisdn ?? transactionDetails.transfer.destination_msisdn}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Email</p>
|
|
<p className="font-medium">
|
|
{transactionDetails.transfer.destination_customer.email ?? "-"}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Username</p>
|
|
<p className="font-medium">
|
|
{transactionDetails.transfer.destination_customer.username ?? "-"}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<p className="text-gray-500">No data available</p>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'originwallet' && (
|
|
<div className="space-y-4">
|
|
<h3 className="font-semibold">Origin Wallet</h3>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<p className="text-sm text-gray-500">Name</p>
|
|
<p className="font-medium">{transactionDetails?.origin_wallet.name}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-gray-500">Description</p>
|
|
<p className="font-medium">{transactionDetails?.origin_wallet.description}</p>
|
|
</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>
|
|
<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>
|
|
)}
|
|
|
|
{activeTab === 'destinationwallet' && (
|
|
<div className="space-y-4">
|
|
<h3 className="font-semibold">Destination Wallet</h3>
|
|
|
|
{!transactionDetails?.transfer ? (
|
|
<div className="text-sm text-gray-500">No Data available</div>
|
|
) : (
|
|
<>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<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>
|
|
<Button
|
|
className="btn btn-primary"
|
|
type="button"
|
|
onClick={async () => {
|
|
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>
|
|
)}
|
|
|
|
|
|
{activeTab === 'log' && (
|
|
<div className="space-y-4">
|
|
<h3 className="font-semibold">Transaction Logs</h3>
|
|
<div className="border rounded-lg overflow-x-auto">
|
|
<table className="min-w-full table-auto">
|
|
<thead>
|
|
<tr className="bg-gray-100">
|
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Type</th>
|
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Request Date</th>
|
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Response Date</th>
|
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Response Body</th>
|
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Response Code</th>
|
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Request Endpoint</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{transactionDetails?.log && transactionDetails?.log.length > 0 ? (
|
|
transactionDetails.log.map((log: { type: string, request_endpoint: string, status: string; request_date: string; response_date: string; request_body: string; response_body: string; response_code: number }, index: number) => (
|
|
<tr key={index} className="border-t">
|
|
<td className="px-4 py-2 text-sm text-gray-500">{log.type}</td>
|
|
<td className="px-4 py-2 text-sm text-gray-500">{log.request_date
|
|
? new Date(log.request_date).toLocaleDateString('en-GB', {
|
|
day: '2-digit',
|
|
month: '2-digit',
|
|
year: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit',
|
|
hour12: false
|
|
})
|
|
: ''}</td>
|
|
<td className="px-4 py-2 text-sm text-gray-500">{log.response_date
|
|
? new Date(log.response_date).toLocaleDateString('en-GB', {
|
|
day: '2-digit',
|
|
month: '2-digit',
|
|
year: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit',
|
|
hour12: false
|
|
})
|
|
: ''}</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.request_endpoint}</td>
|
|
</tr>
|
|
))
|
|
) : (
|
|
<tr>
|
|
<td colSpan={3} className="px-4 py-2 text-center text-sm text-gray-500">
|
|
No logs available
|
|
</td>
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'approve' && (
|
|
<div className="space-y-4">
|
|
<h3 className="font-semibold">Approval Logs</h3>
|
|
{transactionDetails?.log_approve.length === 0 ? (
|
|
<p className="text-sm text-gray-500">No data available</p>
|
|
) : (
|
|
<div className="border rounded-lg overflow-x-auto">
|
|
<table className="min-w-full table-auto">
|
|
<thead>
|
|
<tr className="bg-gray-100">
|
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Status</th>
|
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Date</th>
|
|
{/* <th className="px-4 py-2 text-left text-sm text-gray-500">Updated At</th> */}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{transactionDetails?.log_approve && transactionDetails?.log_approve.length > 0 ? (
|
|
transactionDetails.log_approve.map((log: { created_at: string; status: string; updated_at: string }, index: number) => (
|
|
<tr key={index} className="border-t">
|
|
<td className="px-4 py-2 font-medium">
|
|
{(() => {
|
|
let status;
|
|
if (log.status === 'W') {
|
|
status = 'WAITING';
|
|
} else if (log.status === 'Y') {
|
|
status = 'APPROVE';
|
|
} else if (log.status === 'N') {
|
|
status = 'REJECT';
|
|
} else if (log.status === 'T') {
|
|
status = 'NO NEED';
|
|
}
|
|
return status;
|
|
})()}
|
|
</td>
|
|
<td className="px-4 py-2 text-sm text-gray-500">
|
|
{new Date(log.created_at).toLocaleString('sv-SE', {
|
|
timeZone: 'Asia/Jakarta', // kalau kamu mau waktu lokal (optional)
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit',
|
|
hour12: false,
|
|
}).replace(' ', ' ')}
|
|
</td>
|
|
{/* <td className="px-4 py-2 text-sm text-gray-500">{log.updated_at}</td> */}
|
|
</tr>
|
|
))
|
|
) : (
|
|
<tr>
|
|
<td colSpan={3} className="px-4 py-2 text-center text-sm text-gray-500">
|
|
No logs available
|
|
</td>
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'p24' && (
|
|
<div className="space-y-4">
|
|
<h3 className="font-semibold">P24 Logs</h3>
|
|
{transactionDetails?.p24.length === 0 ? (
|
|
<p className="text-sm text-gray-500">No data available</p>
|
|
) : (
|
|
<div className="border rounded-lg overflow-x-auto">
|
|
<table className="min-w-full table-auto">
|
|
<thead>
|
|
<tr className="bg-gray-100">
|
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Type</th>
|
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Request Date</th>
|
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Response Date</th>
|
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Response Body</th>
|
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Response Code</th>
|
|
<th className="px-4 py-2 text-left text-sm text-gray-500">Request Endpoint</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{transactionDetails?.p24 && transactionDetails?.p24.length > 0 ? (
|
|
transactionDetails.p24.map((log: { request_endpoint: string, type: string; request_date: string; response_date: string; request_body: string; response_body: string; response_code: number }, index: number) => (
|
|
<tr key={index} className="border-t">
|
|
<td className="px-4 py-2 text-sm text-gray-500">{log.type}</td>
|
|
<td className="px-4 py-2 text-sm text-gray-500">{log.request_date
|
|
? new Date(log.request_date).toLocaleDateString('en-GB', {
|
|
day: '2-digit',
|
|
month: '2-digit',
|
|
year: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit',
|
|
hour12: false
|
|
})
|
|
: ''}</td>
|
|
|
|
<td className="px-4 py-2 text-sm text-gray-500">{log.response_date
|
|
? new Date(log.response_date).toLocaleDateString('en-GB', {
|
|
day: '2-digit',
|
|
month: '2-digit',
|
|
year: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit',
|
|
hour12: false
|
|
})
|
|
: ''}</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.request_endpoint ?? '-'}</td>
|
|
</tr>
|
|
))
|
|
) : (
|
|
<tr>
|
|
<td colSpan={3} className="px-4 py-2 text-center text-sm text-gray-500">
|
|
No logs available
|
|
</td>
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
</div>
|
|
|
|
{/* {showResendDialog} */}
|
|
{(transactionDetails?.status === 'F' || transactionDetails?.status === 'P') && transactionDetails?.status_approve !== 'W' && (
|
|
<div className="flex justify-end mt-4">
|
|
<button className="btn btn-primary" onClick={handleResendOpen}>Retry Transaction</button>
|
|
<ResendTransaction isOpen={showResend} onClose={handleResendClose} selectedTransactionForResend={transactionDetails} />
|
|
</div>
|
|
)}
|
|
|
|
|
|
</DialogBody>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
};
|
|
|
|
export default DetailTransaction; |