ALL tab/table on bids page. + Fix bids export.

This commit is contained in:
gerlofvanek
2025-05-09 20:43:31 +02:00
parent b3c0ad7e9c
commit e7b47486f5
4 changed files with 720 additions and 397 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -4,17 +4,18 @@ const BidExporter = {
return 'No data to export'; return 'No data to export';
} }
const isSent = type === 'sent'; const isAllTab = type === 'all';
const headers = [ const headers = [
'Date/Time', 'Date/Time',
'Bid ID', 'Bid ID',
'Offer ID', 'Offer ID',
'From Address', 'From Address',
isSent ? 'You Send Amount' : 'You Receive Amount', ...(isAllTab ? ['Type'] : []),
isSent ? 'You Send Coin' : 'You Receive Coin', 'You Send Amount',
isSent ? 'You Receive Amount' : 'You Send Amount', 'You Send Coin',
isSent ? 'You Receive Coin' : 'You Send Coin', 'You Receive Amount',
'You Receive Coin',
'Status', 'Status',
'Created At', 'Created At',
'Expires At' 'Expires At'
@@ -23,11 +24,13 @@ const BidExporter = {
let csvContent = headers.join(',') + '\n'; let csvContent = headers.join(',') + '\n';
bids.forEach(bid => { bids.forEach(bid => {
const isSent = isAllTab ? (bid.source === 'sent') : (type === 'sent');
const row = [ const row = [
`"${formatTime(bid.created_at)}"`, `"${formatTime(bid.created_at)}"`,
`"${bid.bid_id}"`, `"${bid.bid_id}"`,
`"${bid.offer_id}"`, `"${bid.offer_id}"`,
`"${bid.addr_from}"`, `"${bid.addr_from}"`,
...(isAllTab ? [`"${bid.source}"`] : []),
isSent ? bid.amount_from : bid.amount_to, isSent ? bid.amount_from : bid.amount_to,
`"${isSent ? bid.coin_from : bid.coin_to}"`, `"${isSent ? bid.coin_from : bid.coin_to}"`,
isSent ? bid.amount_to : bid.amount_from, isSent ? bid.amount_to : bid.amount_from,
@@ -103,6 +106,15 @@ const BidExporter = {
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
setTimeout(function() { setTimeout(function() {
if (typeof state !== 'undefined' && typeof EventManager !== 'undefined') { if (typeof state !== 'undefined' && typeof EventManager !== 'undefined') {
const exportAllButton = document.getElementById('exportAllBids');
if (exportAllButton) {
EventManager.add(exportAllButton, 'click', (e) => {
e.preventDefault();
state.currentTab = 'all';
BidExporter.exportCurrentView();
});
}
const exportSentButton = document.getElementById('exportSentBids'); const exportSentButton = document.getElementById('exportSentBids');
if (exportSentButton) { if (exportSentButton) {
EventManager.add(exportSentButton, 'click', (e) => { EventManager.add(exportSentButton, 'click', (e) => {
@@ -128,9 +140,14 @@ const originalCleanup = window.cleanup || function(){};
window.cleanup = function() { window.cleanup = function() {
originalCleanup(); originalCleanup();
const exportAllButton = document.getElementById('exportAllBids');
const exportSentButton = document.getElementById('exportSentBids'); const exportSentButton = document.getElementById('exportSentBids');
const exportReceivedButton = document.getElementById('exportReceivedBids'); const exportReceivedButton = document.getElementById('exportReceivedBids');
if (exportAllButton && typeof EventManager !== 'undefined') {
EventManager.remove(exportAllButton, 'click');
}
if (exportSentButton && typeof EventManager !== 'undefined') { if (exportSentButton && typeof EventManager !== 'undefined') {
EventManager.remove(exportSentButton, 'click'); EventManager.remove(exportSentButton, 'click');
} }

View File

@@ -1,15 +1,28 @@
(function() { (function() {
'use strict'; 'use strict';
const originalOnload = window.onload;
document.addEventListener('DOMContentLoaded', initBidsTabNavigation); window.onload = function() {
window.addEventListener('load', handleHashChange); if (typeof originalOnload === 'function') {
window.addEventListener('hashchange', preventScrollOnHashChange); originalOnload();
}
setTimeout(function() {
initBidsTabNavigation();
handleInitialNavigation();
}, 100);
};
document.addEventListener('DOMContentLoaded', function() {
initBidsTabNavigation();
});
window.addEventListener('hashchange', handleHashChange);
window.bidsTabNavigationInitialized = false;
function initBidsTabNavigation() { function initBidsTabNavigation() {
const sentTabButton = document.getElementById('sent-tab'); if (window.bidsTabNavigationInitialized) {
const receivedTabButton = document.getElementById('received-tab');
if (!sentTabButton || !receivedTabButton) {
return; return;
} }
@@ -17,20 +30,10 @@
link.addEventListener('click', function(e) { link.addEventListener('click', function(e) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
const targetTabId = this.getAttribute('data-tab-target'); const targetTabId = this.getAttribute('data-tab-target');
if (targetTabId) { if (targetTabId) {
if (window.location.pathname === '/bids') { if (window.location.pathname === '/bids') {
const oldScrollPosition = window.scrollY; navigateToTabDirectly(targetTabId);
activateTab(targetTabId);
setTimeout(function() {
window.scrollTo(0, oldScrollPosition);
history.replaceState(null, null, '#' + targetTabId.replace('#', ''));
}, 0);
} else { } else {
localStorage.setItem('bidsTabToActivate', targetTabId.replace('#', '')); localStorage.setItem('bidsTabToActivate', targetTabId.replace('#', ''));
window.location.href = '/bids'; window.location.href = '/bids';
@@ -39,35 +42,28 @@
}); });
}); });
const tabToActivate = localStorage.getItem('bidsTabToActivate'); window.bidsTabNavigationInitialized = true;
if (tabToActivate) { console.log('Bids tab navigation initialized');
localStorage.removeItem('bidsTabToActivate');
activateTab('#' + tabToActivate);
} else if (window.location.pathname === '/bids' && !window.location.hash) {
activateTab('#sent');
}
} }
function preventScrollOnHashChange(e) { function handleInitialNavigation() {
if (window.location.pathname !== '/bids') { if (window.location.pathname !== '/bids') {
return; return;
} }
e.preventDefault(); const tabToActivate = localStorage.getItem('bidsTabToActivate');
const oldScrollPosition = window.scrollY; if (tabToActivate) {
const hash = window.location.hash; //console.log('Activating tab from localStorage:', tabToActivate);
localStorage.removeItem('bidsTabToActivate');
if (hash) { activateTabWithRetry('#' + tabToActivate);
const tabId = `#${hash.replace('#', '')}`; } else if (window.location.hash) {
activateTab(tabId); //console.log('Activating tab from hash:', window.location.hash);
activateTabWithRetry(window.location.hash);
} else { } else {
activateTab('#sent'); //console.log('Activating default tab: #all');
activateTabWithRetry('#all');
} }
setTimeout(function() {
window.scrollTo(0, oldScrollPosition);
}, 0);
} }
function handleHashChange() { function handleHashChange() {
@@ -75,50 +71,141 @@
return; return;
} }
const oldScrollPosition = window.scrollY;
const hash = window.location.hash; const hash = window.location.hash;
if (hash) { if (hash) {
const tabId = `#${hash.replace('#', '')}`; //console.log('Hash changed, activating tab:', hash);
activateTab(tabId); activateTabWithRetry(hash);
} else { } else {
activateTab('#sent'); //console.log('Hash cleared, activating default tab: #all');
activateTabWithRetry('#all');
} }
}
function activateTabWithRetry(tabId, retryCount = 0) {
const normalizedTabId = tabId.startsWith('#') ? tabId : '#' + tabId;
if (normalizedTabId !== '#all' && normalizedTabId !== '#sent' && normalizedTabId !== '#received') {
//console.log('Invalid tab ID, defaulting to #all');
activateTabWithRetry('#all');
return;
}
const tabButtonId = normalizedTabId === '#all' ? 'all-tab' :
(normalizedTabId === '#sent' ? 'sent-tab' : 'received-tab');
const tabButton = document.getElementById(tabButtonId);
if (!tabButton) {
if (retryCount < 5) {
//console.log('Tab button not found, retrying...', retryCount + 1);
setTimeout(() => {
activateTabWithRetry(normalizedTabId, retryCount + 1);
}, 100);
} else {
//console.error('Failed to find tab button after retries');
}
return;
}
//console.log('Activating tab:', normalizedTabId);
tabButton.click();
if (window.Tabs) {
const tabsEl = document.querySelector('[data-tabs-toggle="#bidstab"]');
if (tabsEl) {
const allTabs = Array.from(tabsEl.querySelectorAll('[role="tab"]'));
const targetTab = allTabs.find(tab => tab.getAttribute('data-tabs-target') === normalizedTabId);
if (targetTab) {
allTabs.forEach(tab => {
tab.setAttribute('aria-selected', tab === targetTab ? 'true' : 'false');
if (tab === targetTab) {
tab.classList.add('bg-gray-100', 'dark:bg-gray-600', 'text-gray-900', 'dark:text-white');
tab.classList.remove('hover:text-gray-600', 'hover:bg-gray-50', 'dark:hover:bg-gray-500');
} else {
tab.classList.remove('bg-gray-100', 'dark:bg-gray-600', 'text-gray-900', 'dark:text-white');
tab.classList.add('hover:text-gray-600', 'hover:bg-gray-50', 'dark:hover:bg-gray-500');
}
});
const allContent = document.getElementById('all');
const sentContent = document.getElementById('sent');
const receivedContent = document.getElementById('received');
if (allContent && sentContent && receivedContent) {
allContent.classList.toggle('hidden', normalizedTabId !== '#all');
sentContent.classList.toggle('hidden', normalizedTabId !== '#sent');
receivedContent.classList.toggle('hidden', normalizedTabId !== '#received');
}
}
}
}
const allPanel = document.getElementById('all');
const sentPanel = document.getElementById('sent');
const receivedPanel = document.getElementById('received');
if (allPanel && sentPanel && receivedPanel) {
allPanel.classList.toggle('hidden', normalizedTabId !== '#all');
sentPanel.classList.toggle('hidden', normalizedTabId !== '#sent');
receivedPanel.classList.toggle('hidden', normalizedTabId !== '#received');
}
const newHash = normalizedTabId.replace('#', '');
if (window.location.hash !== '#' + newHash) {
history.replaceState(null, null, '#' + newHash);
}
triggerDataLoad(normalizedTabId);
}
function triggerDataLoad(tabId) {
setTimeout(() => {
if (window.state) {
window.state.currentTab = tabId === '#all' ? 'all' :
(tabId === '#sent' ? 'sent' : 'received');
if (typeof window.updateBidsTable === 'function') {
//console.log('Triggering data load for', tabId);
window.updateBidsTable();
}
}
const event = new CustomEvent('tabactivated', {
detail: {
tabId: tabId,
type: tabId === '#all' ? 'all' :
(tabId === '#sent' ? 'sent' : 'received')
}
});
document.dispatchEvent(event);
if (window.TooltipManager && typeof window.TooltipManager.cleanup === 'function') {
setTimeout(() => {
window.TooltipManager.cleanup();
if (typeof window.initializeTooltips === 'function') {
window.initializeTooltips();
}
}, 200);
}
}, 100);
}
function navigateToTabDirectly(tabId) {
const oldScrollPosition = window.scrollY;
activateTabWithRetry(tabId);
setTimeout(function() { setTimeout(function() {
window.scrollTo(0, oldScrollPosition); window.scrollTo(0, oldScrollPosition);
}, 0); }, 0);
} }
function activateTab(tabId) {
if (tabId !== '#sent' && tabId !== '#received') {
tabId = '#sent';
}
const tabButtonId = tabId === '#sent' ? 'sent-tab' : 'received-tab';
const tabButton = document.getElementById(tabButtonId);
if (tabButton) {
const oldScrollPosition = window.scrollY;
tabButton.click();
setTimeout(function() {
window.scrollTo(0, oldScrollPosition);
}, 0);
}
}
window.navigateToBidsTab = function(tabId) { window.navigateToBidsTab = function(tabId) {
if (window.location.pathname === '/bids') { if (window.location.pathname === '/bids') {
const oldScrollPosition = window.scrollY; navigateToTabDirectly('#' + tabId);
activateTab('#' + (tabId === 'sent' || tabId === 'received' ? tabId : 'sent'));
setTimeout(function() {
window.scrollTo(0, oldScrollPosition);
history.replaceState(null, null, '#' + tabId);
}, 0);
} else { } else {
localStorage.setItem('bidsTabToActivate', tabId); localStorage.setItem('bidsTabToActivate', tabId);
window.location.href = '/bids'; window.location.href = '/bids';

View File

@@ -10,7 +10,7 @@
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt=""> <img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
<div class="relative z-20 flex flex-wrap items-center -m-3"> <div class="relative z-20 flex flex-wrap items-center -m-3">
<div class="w-full md:w-1/2 p-3"> <div class="w-full md:w-1/2 p-3">
<h2 class="mb-3 text-2xl font-bold text-white tracking-tighter">Sent Bids / Received Bids</h2> <h2 class="mb-3 text-2xl font-bold text-white tracking-tighter">All Bids / Sent Bids / Received Bids</h2>
<p class="font-normal text-coolGray-200 dark:text-white">View, and manage bids.</p> <p class="font-normal text-coolGray-200 dark:text-white">View, and manage bids.</p>
</div> </div>
</div> </div>
@@ -28,7 +28,12 @@
<div class="mb-4 border-b pb-5 border-gray-200 dark:border-gray-500"> <div class="mb-4 border-b pb-5 border-gray-200 dark:border-gray-500">
<ul class="flex flex-wrap text-sm font-medium text-center text-gray-500 dark:text-gray-400" id="myTab" data-tabs-toggle="#bidstab" role="tablist"> <ul class="flex flex-wrap text-sm font-medium text-center text-gray-500 dark:text-gray-400" id="myTab" data-tabs-toggle="#bidstab" role="tablist">
<li class="mr-2"> <li class="mr-2">
<button class="inline-block px-4 py-3 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white focus:outline-none focus:ring-0" id="sent-tab" data-tabs-target="#sent" type="button" role="tab" aria-controls="sent" aria-selected="true"> <button class="inline-block px-4 py-3 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white focus:outline-none focus:ring-0" id="all-tab" data-tabs-target="#all" type="button" role="tab" aria-controls="all" aria-selected="true">
All Bids <span class="text-gray-500 dark:text-gray-400">({{ sent_bids_count + received_bids_count }})</span>
</button>
</li>
<li class="mr-2">
<button class="inline-block px-4 py-3 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white focus:outline-none focus:ring-0" id="sent-tab" data-tabs-target="#sent" type="button" role="tab" aria-controls="sent" aria-selected="false">
Sent Bids <span class="text-gray-500 dark:text-gray-400">({{ sent_bids_count }})</span> Sent Bids <span class="text-gray-500 dark:text-gray-400">({{ sent_bids_count }})</span>
</button> </button>
</li> </li>
@@ -167,7 +172,106 @@
</section> </section>
<div id="bidstab"> <div id="bidstab">
<div class="rounded-lg lg:px-6" id="sent" role="tabpanel" aria-labelledby="sent-tab"> <!-- All Bids Tab -->
<div class="rounded-lg lg:px-6" id="all" role="tabpanel" aria-labelledby="all-tab">
<div id="all-content">
<div class="xl:container mx-auto lg:px-0">
<div class="pt-0 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
<div class="px-0">
<div class="w-auto overflow-auto lg:overflow-hidden">
<table class="w-full lg:min-w-max">
<thead class="uppercase">
<tr class="text-left">
<th class="p-0">
<div class="py-3 pl-16 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Date/Time</span>
</div>
</th>
<th class="p-0 hidden lg:block">
<div class="p-3 bg-coolGray-200 dark:bg-gray-600">
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Details</span>
</div>
</th>
<th class="p-0">
<div class="p-3 bg-coolGray-200 dark:bg-gray-600">
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">You Send</span>
</div>
</th>
<th class="p-0">
<div class="p-3 bg-coolGray-200 dark:bg-gray-600">
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">You Receive</span>
</div>
</th>
<th class="p-0">
<div class="p-3 text-center bg-coolGray-200 dark:bg-gray-600">
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Status</span>
</div>
</th>
<th class="p-0">
<div class="p-3 pr-6 text-center rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Actions</span>
</div>
</th>
</tr>
</thead>
<tbody id="all-tbody">
</tbody>
</table>
</div>
<div class="rounded-b-md">
<div class="w-full">
<div class="flex flex-wrap justify-between items-center pl-6 pt-6 pr-6 border-t border-gray-100 dark:border-gray-400">
<div class="flex items-center">
<div class="flex items-center mr-4">
<span id="status-dot-all" class="w-2.5 h-2.5 rounded-full bg-gray-500 mr-2"></span>
<span id="status-text-all" class="text-sm text-gray-500">Connecting...</span>
</div>
<p class="text-sm font-heading dark:text-gray-400">
All Bids: <span id="allBidsCount">0</span>
</p>
{% if debug_ui_mode == true %}
<button id="refreshAllBids" class="ml-4 inline-flex items-center px-4 py-2.5 font-medium text-sm text-white bg-blue-600 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
<span id="refreshAllText">Refresh</span>
</button>
{% endif %}
<button id="exportAllBids" class="ml-4 inline-flex items-center px-4 py-2.5 font-medium text-sm text-white bg-green-600 hover:bg-green-700 hover:border-green-700 rounded-lg transition duration-200 border border-green-600 rounded-md shadow-button focus:ring-0 focus:outline-none">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<span>Export CSV</span>
</button>
</div>
<div id="pagination-controls-all" class="flex items-center space-x-2" style="display: none;">
<button id="prevPageAll" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-green-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
</svg>
Previous
</button>
<p class="text-sm font-heading dark:text-white">Page <span id="currentPageAll">1</span></p>
<button id="nextPageAll" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-green-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
Next
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Sent Bids Tab -->
<div class="hidden rounded-lg lg:px-6" id="sent" role="tabpanel" aria-labelledby="sent-tab">
<div id="sent-content"> <div id="sent-content">
<div class="xl:container mx-auto lg:px-0"> <div class="xl:container mx-auto lg:px-0">
<div class="pt-0 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl"> <div class="pt-0 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
@@ -208,7 +312,7 @@
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody id="sent-tbody">
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -264,6 +368,7 @@
</div> </div>
</div> </div>
<!-- Received Bids Tab -->
<div class="hidden rounded-lg lg:px-6" id="received" role="tabpanel" aria-labelledby="received-tab"> <div class="hidden rounded-lg lg:px-6" id="received" role="tabpanel" aria-labelledby="received-tab">
<div id="received-content"> <div id="received-content">
<div class="xl:container mx-auto lg:px-0"> <div class="xl:container mx-auto lg:px-0">
@@ -305,7 +410,7 @@
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody id="received-tbody">
</tbody> </tbody>
</table> </table>
</div> </div>