<% if (typeof success !== 'undefined' && success) { %>
<%
const userLookup = new Map((users || []).map(u => [String(u.uid || ''), u]));
const rawTransactionLogs = Array.isArray(transactionLogs) ? transactionLogs : [];
const mappedTransactionLogs = rawTransactionLogs.map(log => {
const owner = userLookup.get(String(log.userId || '')) || {};
const userName = log.username || owner.username || log.donorName || 'Supporter';
const idGame = log.mlId
? `${log.mlId}${log.mlServer ? ` (${log.mlServer})` : ''}`
: (owner.mlId ? `${owner.mlId}${owner.mlServer ? ` (${owner.mlServer})` : ''}` : '-');
const activityType = String(log.activityType || '').toLowerCase();
const type = log.activityLabel || (
activityType === 'ticket' ? 'Beli Ticket' :
'Donate Biasa'
);
const tone = String(log.status || '').toLowerCase() === 'pending'
? 'pending'
: (activityType === 'ticket' ? 'buy' : 'donate');
const status = String(log.status || '').toLowerCase() === 'pending' ? 'pending' : 'success';
return {
at: Number(log.createdAt || log.updatedAt || 0),
userName,
idGame,
type,
tone,
status
};
}).filter(log => Number(log.at || 0) > 0);
const combinedTransactionLogs = mappedTransactionLogs
.sort((a, b) => Number(b.at || 0) - Number(a.at || 0));
const rawWebhookReceipts = Array.isArray(webhookReceipts) ? webhookReceipts : [];
const shortenMiddle = (value, start = 18, end = 8) => {
const text = String(value || '-');
return text.length > start + end + 3 ? `${text.slice(0, start)}...${text.slice(-end)}` : text;
};
const getDecisionLabel = decision => ({
ticket_issued: 'Issued',
ticket_recovered: 'Recovered',
pending_missing_ml_id: 'Pending ID',
pending_ml_id_not_registered: 'Pending Akun',
pending_user_not_found: 'Pending User',
pending_manual_review: 'Review',
ignored_non_ticket: 'Donate',
ignored_unpaid: 'Unpaid',
ignored_invalid_auth: 'Auth Fail',
duplicate_ticket: 'Duplicate',
duplicate_success_log: 'Duplicate',
test_notification: 'Test',
health_empty_post: 'Health',
processed: 'Processed',
received: 'Received'
}[String(decision || '')] || String(decision || 'received').replace(/^pending_/, 'Pending ').replace(/^ignored_/, 'Ignore ').replace(/_/g, ' '));
const getDecisionTone = decision => {
const text = String(decision || '');
if (text === 'ticket_issued' || text === 'ticket_recovered') return 'is-buy';
if (text.startsWith('pending_')) return 'is-pending';
if (text.startsWith('ignored_') || text.startsWith('duplicate_')) return 'is-use';
if (text === 'test_notification') return 'is-donate';
return '';
};
const getIssueHint = receipt => {
const decision = String(receipt.decision || '');
if (decision === 'pending_missing_ml_id') return 'ID ML belum terbaca';
if (decision === 'pending_ml_id_not_registered') return 'Akun belum terdaftar';
if (decision === 'ignored_invalid_auth') return 'Token notifikasi salah';
if (decision === 'ignored_non_ticket') return 'Nominal bukan tiket';
if (decision.indexOf('duplicate') === 0) return 'Duplikat lama';
return receipt.message || receipt.reason || 'Butuh cek admin';
};
const combinedWebhookReceipts = rawWebhookReceipts.map(receipt => ({
at: Number(receipt.createdAt || receipt.updatedAt || 0),
decision: receipt.decision || 'received',
decisionLabel: getDecisionLabel(receipt.decision || 'received'),
decisionTone: getDecisionTone(receipt.decision || 'received'),
auth: receipt.hasToken || receipt.hasSignature ? 'ada' : 'tidak ada',
paymentRef: receipt.paymentRef || '-',
paymentRefShort: shortenMiddle(receipt.paymentRef || '-', 18, 8),
amount: Number(receipt.amount || 0),
ml: receipt.mlId ? `${receipt.mlId}${receipt.mlServer ? ` (${receipt.mlServer})` : ''}` : '-',
message: receipt.reason || receipt.messagePreview || '-',
response: receipt.responseStatus ? `${receipt.responseStatus} ${receipt.responseBody || ''}` : '-'
})).map(receipt => ({
...receipt,
issueHint: getIssueHint(receipt)
})).filter(receipt => Number(receipt.at || 0) > 0)
.sort((a, b) => Number(b.at || 0) - Number(a.at || 0));
const totalTicketsCount = tickets.length;
const activeTicketsCount = tickets.filter(t => t.status === 'active').length;
const usedTicketsCount = tickets.filter(t => t.status === 'used').length;
const ticketSalesCount = tickets.filter(t => t.status === 'active' || t.status === 'used').length;
const pendingTransactionCount = combinedTransactionLogs.filter(log => log.status === 'pending').length;
const issueRows = combinedWebhookReceipts
.filter(receipt => String(receipt.decision || '').indexOf('ignored') === 0 || String(receipt.decision || '').indexOf('pending') === 0 || receipt.auth === 'tidak ada')
.slice(0, 4);
%>
<%= success %>
<% } %>
<% if (typeof error !== 'undefined' && error) { %>
<%= error %>
<% } %>