This commit is contained in:
unknown
2025-05-21 14:38:12 +07:00
25 changed files with 879 additions and 260 deletions

View File

@ -683,11 +683,11 @@ const DetailApprovalTransaction = () => {
<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">Transaction Status</th>
<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">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">Request Body</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>
@ -861,14 +861,14 @@ const DetailApprovalTransaction = () => {
<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">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">Request Body</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: { status: string,request_endpoint: string, type: string; request_date: string; response_date: string; request_body: string; response_body: string; response_code: number }, index: number) => (
transactionDetails.p24.map((log: { status: string, 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">
{(() => {
@ -912,6 +912,22 @@ const DetailApprovalTransaction = () => {
hour12: false
})
: ''}</td>
<td className="px-4 py-2 text-sm text-gray-500">
<pre style={{ fontFamily: 'monospace', whiteSpace: 'pre-wrap' }}>
{(() => {
try {
if (log.request_body === null) {
return '';
}
const parsed = JSON.parse(log.request_body);
return JSON.stringify(parsed, null, 2) ?? '';
} catch (e) {
return log.request_body ?? ''; // fallback: tampilkan as-is jika gagal parse
}
})()}
</pre>
</td>
<td className="px-4 py-2 text-sm text-gray-500">
<pre style={{ fontFamily: 'monospace', whiteSpace: 'pre-wrap' }}>
{(() => {
@ -927,22 +943,7 @@ const DetailApprovalTransaction = () => {
})()}
</pre>
</td>
<td className="px-4 py-2 text-sm text-gray-500">
<pre style={{ fontFamily: 'monospace', whiteSpace: 'pre-wrap' }}>
{(() => {
try {
if (log.request_body === null) {
return '';
}
const parsed = JSON.parse(log.request_body);
return JSON.stringify(parsed, null, 2) ?? '';
} catch (e) {
return log.response_body ?? ''; // fallback: tampilkan as-is jika gagal parse
}
})()}
</pre>
</td>
<td className="px-4 py-2 text-sm text-gray-500">{log.request_endpoint ?? ''}</td>

View File

@ -175,6 +175,24 @@ const DetailTransaction = () => {
</button>
{/* )} */}
{transactionDetails?.kind == 'P' && (
<button
className={`py-2 px-4 font-medium text-sm focus:outline-none ${activeTab === 'purchase' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-500 hover:text-gray-700'}`}
onClick={() => setActiveTab('purchase')}
>
Purchase Detail
</button>
)}
{transactionDetails?.kind == 'P' && (
<button
className={`py-2 px-4 font-medium text-sm focus:outline-none ${activeTab === 'productdetail' ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-500 hover:text-gray-700'}`}
onClick={() => setActiveTab('productdetail')}
>
Product Detail
</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')}
@ -229,15 +247,105 @@ const DetailTransaction = () => {
</div>
{/* Tab Content */}
{activeTab === 'purchase' && (
<>
<h3 className="font-semibold flex items-center mt-4">
Purchase
</h3>
<div className="grid grid-cols-2 gap-4 mt-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>
</>
)}
{activeTab === 'productdetail' && (
<>
<div className="space-y-4">
<h3 className="font-semibold flex items-center mt-4">
Product Information
</h3>
<div className="grid grid-cols-2 gap-4 mt-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>
</>
)}
<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>
@ -259,7 +367,7 @@ const DetailTransaction = () => {
</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>
<p className="font-medium">{transactionDetails?.origin_customer?.origin_name ?? ""}</p>
</div>
<div>
<p className="text-sm text-gray-500">Amount</p>
@ -348,7 +456,24 @@ const DetailTransaction = () => {
kind = 'TRANSFER AGENT';
} else if (transactionDetails?.kind === 'M') {
kind = 'WITHDRAWAL AGENT';
}else if( transactionDetails?.kind === 'O') {
kind = 'TOPUP AGENT';
}else if (transactionDetails?.kind === 'S') {
kind = 'TOPUP P24';
}else if (transactionDetails?.kind === 'I') {
kind = 'WITHDRAW MERCHANT';
}else if (transactionDetails?.kind === 'D') {
kind = 'DONATION';
}else if (transactionDetails?.kind === 'F') {
kind = 'FEE';
}else if (transactionDetails?.kind === 'V') {
kind = 'REVERSAL';
}else if( transactionDetails?.kind === 'C') {
kind = 'CASHBACK CASH';
}else if( transactionDetails?.kind === 'H') {
kind = 'CASHBACK POINT';
}
return kind;
})()}
</p>
@ -363,100 +488,6 @@ const DetailTransaction = () => {
</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' && transactionDetails?.kind == 'P' && transactionDetails?.kind == 'P' && (
<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>
)}
@ -485,7 +516,7 @@ const DetailTransaction = () => {
</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>
<p className="font-medium">{transactionDetails?.origin_customer?.origin_name ?? ""}</p>
</div>
<div>
<p className="text-sm text-gray-500">Amount</p>
@ -597,7 +628,7 @@ const DetailTransaction = () => {
<div>
<p className="text-sm text-gray-500">Phone Number</p>
<p className="font-medium">{transactionDetails?.origin_msisdn}</p>
<p className="font-medium">{transactionDetails?.origin_customer?.fullname ?? transactionDetails?.origin_customer?.origin_name}</p>
<p className="font-medium">{transactionDetails?.origin_customer?.origin_name ?? ""}</p>
</div>
<div>
<p className="text-sm text-gray-500">Phone Number</p>
@ -748,8 +779,8 @@ const DetailTransaction = () => {
<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">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">Request Body</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>
@ -922,14 +953,14 @@ const DetailTransaction = () => {
<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">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">Request Body</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: { status: string,request_endpoint: string, type: string; request_date: string; response_date: string; request_body: string; response_body: string; response_code: number }, index: number) => (
transactionDetails.p24.map((log: { status: string, 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">
{(() => {
@ -992,10 +1023,10 @@ const DetailTransaction = () => {
<pre style={{ fontFamily: 'monospace', whiteSpace: 'pre-wrap' }}>
{(() => {
try {
if (log.request_body === null) {
if (log.response_body === null) {
return '';
}
const parsed = JSON.parse(log.request_body);
const parsed = JSON.parse(log.response_body);
return JSON.stringify(parsed, null, 2) ?? '';
} catch (e) {
return log.response_body ?? ''; // fallback: tampilkan as-is jika gagal parse

View File

@ -170,6 +170,7 @@ const ListToolbar = () => {
<SelectValue placeholder="Select Transaction Type" />
</SelectTrigger>
<SelectContent>
<SelectItem value="T">TRANSFER</SelectItem>
<SelectItem value="P">PURCHASE</SelectItem>
<SelectItem value="W">WITHDRAW</SelectItem>
@ -181,6 +182,14 @@ const ListToolbar = () => {
<SelectItem value="B">TOP UP P24</SelectItem>
<SelectItem value="A">TRANSFER AGENT</SelectItem>
<SelectItem value="M">WITHDRAWAL AGENT</SelectItem>
<SelectItem value="O">TOP UP AGENT</SelectItem>
<SelectItem value="S">TRANSFER P24</SelectItem>
<SelectItem value="I">WITHDRAW MERCHANT</SelectItem>
<SelectItem value="D">DONATION</SelectItem>
<SelectItem value="F">FEE</SelectItem>
<SelectItem value="V">REVERSAL</SelectItem>
<SelectItem value="C">CASHBACK CASH</SelectItem>
<SelectItem value="H">CASHBACK POINT</SelectItem>
</SelectContent>
</Select>

View File

@ -82,6 +82,14 @@ const TransactionProvider = ({ children }: { children: React.ReactNode }) => {
case 'B': return 'TOP UP P24';
case 'A': return 'TRANSFER AGENT';
case 'M': return 'WITHDRAWAL AGENT';
case 'O': return 'TOP UP AGENT';
case 'S': return 'TRANSFER P24';
case 'I': return 'WIJTDRAW MERCHANT';
case 'D': return 'DONATION';
case 'F': return 'FEE';
case 'V': return 'REVERSAL';
case 'C': return 'CASHBACK CASH';
case 'H': return 'CASHBACK POINT';
default: return '_';
}
},