/** * Transaction History Panel * Displays payment and payout history for the user */ const TransactionHistory = { _isLoading: false, _paymentsData: null, _payoutsData: null, init: function() { // Bind close button $('#close-history').on('click', function() { $('#history-panel').hide(); }); // Bind history button $('#historyButton').on('click', function() { TransactionHistory.show(); }); // Bind tab switching $('.history-tab').on('click', function() { const tab = $(this).data('tab'); TransactionHistory._switchTab(tab); }); }, _switchTab: function(tab) { // Update tab button styles $('.history-tab').each(function() { if ($(this).data('tab') === tab) { $(this).css({ background: '#4c1d95', color: '#fff' }); $(this).addClass('active'); } else { $(this).css({ background: '#333', color: '#aaa' }); $(this).removeClass('active'); } }); // Show/hide content if (tab === 'payouts') { $('#payouts-content').show(); $('#payments-content').hide(); } else { $('#payouts-content').hide(); $('#payments-content').show(); } }, show: function() { $('#history-panel').show(); this._loadData(); }, hide: function() { $('#history-panel').hide(); }, _loadData: function() { if (this._isLoading) return; this._isLoading = true; const socketId = window.socket?.id; if (!socketId) { this._showError('Not connected to server'); this._isLoading = false; return; } // Load both endpoints in parallel Promise.all([ this._fetchPayments(socketId), this._fetchPayouts(socketId) ]).then(([payments, payouts]) => { this._paymentsData = payments; this._payoutsData = payouts; this._render(); }).catch(err => { console.error('Failed to load transaction history:', err); this._showError('Failed to load history'); }).finally(() => { this._isLoading = false; }); }, // These reads are now session-ownership gated (contract C2), so send the session token // (users.anon_token, persisted as localStorage['wownerogue_token']) as 'X-Session-Token'. _sessionHeaders: function() { var token = null; try { token = localStorage.getItem('wownerogue_token'); } catch (e) {} return token ? { 'X-Session-Token': token } : {}; }, _fetchPayments: function(socketId) { return fetch(`/api/user/${encodeURIComponent(socketId)}/payments?limit=50`, { headers: this._sessionHeaders() }) .then(res => res.json()) .catch(() => ({ payments: [], total: 0 })); }, _fetchPayouts: function(socketId) { return fetch(`/api/user/${encodeURIComponent(socketId)}/payouts?limit=50`, { headers: this._sessionHeaders() }) .then(res => res.json()) .catch(() => ({ payouts: [], total: 0, totalReceived: 0 })); }, _render: function() { this._renderSummary(); this._renderPayouts(); this._renderPayments(); }, _renderSummary: function() { const payouts = this._payoutsData || {}; const payments = this._paymentsData || {}; const currency = payouts.currency || payments.currency || 'WOW'; const totalReceived = payouts.totalReceivedFormatted || '0'; const totalPaid = payments.totalPaidFormatted || '0'; $('#total-received').text(totalReceived + ' ' + currency); $('#total-payments').text(totalPaid + ' ' + currency); }, _renderPayouts: function() { const $list = $('#payouts-list'); const $empty = $('#payouts-empty'); const data = this._payoutsData || {}; const payouts = data.payouts || []; const currency = data.currency || 'WOW'; $list.empty(); if (payouts.length === 0) { $empty.show(); return; } $empty.hide(); payouts.forEach(payout => { const statusColor = this._getStatusColor(payout.status); const date = new Date(payout.createdAt).toLocaleDateString(); const multiplierText = payout.multiplier ? ` (${payout.multiplier}x)` : ''; const $item = $(`