mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-05 18:38:09 +01:00
ALL tab/table on bids page. + Fix bids export.
This commit is contained in:
@@ -1,15 +1,19 @@
|
|||||||
const PAGE_SIZE = 50;
|
const PAGE_SIZE = 50;
|
||||||
|
let activeFetchController = null;
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
currentPage: {
|
currentPage: {
|
||||||
|
all: 1,
|
||||||
sent: 1,
|
sent: 1,
|
||||||
received: 1
|
received: 1
|
||||||
},
|
},
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isRefreshing: false,
|
isRefreshing: false,
|
||||||
currentTab: 'sent',
|
currentTab: 'all',
|
||||||
wsConnected: false,
|
wsConnected: false,
|
||||||
refreshPromise: null,
|
refreshPromise: null,
|
||||||
data: {
|
data: {
|
||||||
|
all: [],
|
||||||
sent: [],
|
sent: [],
|
||||||
received: []
|
received: []
|
||||||
},
|
},
|
||||||
@@ -24,6 +28,16 @@ const state = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
document.addEventListener('tabactivated', function(event) {
|
||||||
|
if (event.detail && event.detail.tabId) {
|
||||||
|
const tabType = event.detail.type || (event.detail.tabId === '#all' ? 'all' :
|
||||||
|
(event.detail.tabId === '#sent' ? 'sent' : 'received'));
|
||||||
|
//console.log('Tab activation event received for:', tabType);
|
||||||
|
state.currentTab = tabType;
|
||||||
|
updateBidsTable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const STATE_MAP = {
|
const STATE_MAP = {
|
||||||
1: ['Sent'],
|
1: ['Sent'],
|
||||||
2: ['Receiving'],
|
2: ['Receiving'],
|
||||||
@@ -61,33 +75,43 @@ const STATE_MAP = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const elements = {
|
const elements = {
|
||||||
sentBidsBody: document.querySelector('#sent tbody'),
|
allBidsBody: document.querySelector('#all-tbody'),
|
||||||
receivedBidsBody: document.querySelector('#received tbody'),
|
sentBidsBody: document.querySelector('#sent-tbody'),
|
||||||
|
receivedBidsBody: document.querySelector('#received-tbody'),
|
||||||
filterForm: document.querySelector('form'),
|
filterForm: document.querySelector('form'),
|
||||||
stateSelect: document.querySelector('select[name="state"]'),
|
stateSelect: document.querySelector('select[name="state"]'),
|
||||||
sortBySelect: document.querySelector('select[name="sort_by"]'),
|
sortBySelect: document.querySelector('select[name="sort_by"]'),
|
||||||
sortDirSelect: document.querySelector('select[name="sort_dir"]'),
|
sortDirSelect: document.querySelector('select[name="sort_dir"]'),
|
||||||
withExpiredSelect: document.querySelector('select[name="with_expired"]'),
|
withExpiredSelect: document.querySelector('select[name="with_expired"]'),
|
||||||
tabButtons: document.querySelectorAll('#myTab button'),
|
tabButtons: document.querySelectorAll('#myTab button'),
|
||||||
|
allContent: document.getElementById('all'),
|
||||||
sentContent: document.getElementById('sent'),
|
sentContent: document.getElementById('sent'),
|
||||||
receivedContent: document.getElementById('received'),
|
receivedContent: document.getElementById('received'),
|
||||||
|
|
||||||
|
allPaginationControls: document.getElementById('pagination-controls-all'),
|
||||||
sentPaginationControls: document.getElementById('pagination-controls-sent'),
|
sentPaginationControls: document.getElementById('pagination-controls-sent'),
|
||||||
receivedPaginationControls: document.getElementById('pagination-controls-received'),
|
receivedPaginationControls: document.getElementById('pagination-controls-received'),
|
||||||
|
prevPageAll: document.getElementById('prevPageAll'),
|
||||||
prevPageSent: document.getElementById('prevPageSent'),
|
prevPageSent: document.getElementById('prevPageSent'),
|
||||||
nextPageSent: document.getElementById('nextPageSent'),
|
|
||||||
prevPageReceived: document.getElementById('prevPageReceived'),
|
prevPageReceived: document.getElementById('prevPageReceived'),
|
||||||
|
nextPageAll: document.getElementById('nextPageAll'),
|
||||||
|
nextPageSent: document.getElementById('nextPageSent'),
|
||||||
nextPageReceived: document.getElementById('nextPageReceived'),
|
nextPageReceived: document.getElementById('nextPageReceived'),
|
||||||
|
currentPageAll: document.getElementById('currentPageAll'),
|
||||||
currentPageSent: document.getElementById('currentPageSent'),
|
currentPageSent: document.getElementById('currentPageSent'),
|
||||||
currentPageReceived: document.getElementById('currentPageReceived'),
|
currentPageReceived: document.getElementById('currentPageReceived'),
|
||||||
|
allBidsCount: document.getElementById('allBidsCount'),
|
||||||
sentBidsCount: document.getElementById('sentBidsCount'),
|
sentBidsCount: document.getElementById('sentBidsCount'),
|
||||||
receivedBidsCount: document.getElementById('receivedBidsCount'),
|
receivedBidsCount: document.getElementById('receivedBidsCount'),
|
||||||
|
|
||||||
|
statusDotAll: document.getElementById('status-dot-all'),
|
||||||
|
statusTextAll: document.getElementById('status-text-all'),
|
||||||
statusDotSent: document.getElementById('status-dot-sent'),
|
statusDotSent: document.getElementById('status-dot-sent'),
|
||||||
statusTextSent: document.getElementById('status-text-sent'),
|
statusTextSent: document.getElementById('status-text-sent'),
|
||||||
statusDotReceived: document.getElementById('status-dot-received'),
|
statusDotReceived: document.getElementById('status-dot-received'),
|
||||||
statusTextReceived: document.getElementById('status-text-received'),
|
statusTextReceived: document.getElementById('status-text-received'),
|
||||||
|
|
||||||
|
refreshAllBids: document.getElementById('refreshAllBids'),
|
||||||
refreshSentBids: document.getElementById('refreshSentBids'),
|
refreshSentBids: document.getElementById('refreshSentBids'),
|
||||||
refreshReceivedBids: document.getElementById('refreshReceivedBids')
|
refreshReceivedBids: document.getElementById('refreshReceivedBids')
|
||||||
};
|
};
|
||||||
@@ -213,6 +237,7 @@ function cleanup() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
cleanupTableBody('all-tbody');
|
||||||
cleanupTableBody('sent-tbody');
|
cleanupTableBody('sent-tbody');
|
||||||
cleanupTableBody('received-tbody');
|
cleanupTableBody('received-tbody');
|
||||||
|
|
||||||
@@ -234,11 +259,13 @@ function cleanup() {
|
|||||||
clearAllAnimationFrames();
|
clearAllAnimationFrames();
|
||||||
|
|
||||||
state.data = {
|
state.data = {
|
||||||
|
all: [],
|
||||||
sent: [],
|
sent: [],
|
||||||
received: []
|
received: []
|
||||||
};
|
};
|
||||||
|
|
||||||
state.currentPage = {
|
state.currentPage = {
|
||||||
|
all: 1,
|
||||||
sent: 1,
|
sent: 1,
|
||||||
received: 1
|
received: 1
|
||||||
};
|
};
|
||||||
@@ -283,7 +310,7 @@ function cleanup() {
|
|||||||
if (window.CleanupManager) CleanupManager.clearAll();
|
if (window.CleanupManager) CleanupManager.clearAll();
|
||||||
if (window.WebSocketManager) WebSocketManager.disconnect();
|
if (window.WebSocketManager) WebSocketManager.disconnect();
|
||||||
|
|
||||||
state.data = { sent: [], received: [] };
|
state.data = { all: [], sent: [], received: [] };
|
||||||
state.isLoading = false;
|
state.isLoading = false;
|
||||||
|
|
||||||
Object.keys(elements).forEach(key => {
|
Object.keys(elements).forEach(key => {
|
||||||
@@ -366,7 +393,7 @@ function cleanupRow(row) {
|
|||||||
function optimizeMemoryUsage() {
|
function optimizeMemoryUsage() {
|
||||||
const MAX_BIDS_IN_MEMORY = 500;
|
const MAX_BIDS_IN_MEMORY = 500;
|
||||||
|
|
||||||
['sent', 'received'].forEach(type => {
|
['all', 'sent', 'received'].forEach(type => {
|
||||||
if (state.data[type] && state.data[type].length > MAX_BIDS_IN_MEMORY) {
|
if (state.data[type] && state.data[type].length > MAX_BIDS_IN_MEMORY) {
|
||||||
console.log(`Trimming ${type} bids data from ${state.data[type].length} to ${MAX_BIDS_IN_MEMORY}`);
|
console.log(`Trimming ${type} bids data from ${state.data[type].length} to ${MAX_BIDS_IN_MEMORY}`);
|
||||||
state.data[type] = state.data[type].slice(0, MAX_BIDS_IN_MEMORY);
|
state.data[type] = state.data[type].slice(0, MAX_BIDS_IN_MEMORY);
|
||||||
@@ -528,7 +555,9 @@ function filterAndSortData(bids) {
|
|||||||
const coinName = selectedOption?.textContent.trim();
|
const coinName = selectedOption?.textContent.trim();
|
||||||
|
|
||||||
if (coinName) {
|
if (coinName) {
|
||||||
const coinToMatch = state.currentTab === 'sent' ? bid.coin_to : bid.coin_from;
|
const coinToMatch = state.currentTab === 'all'
|
||||||
|
? (bid.source === 'sent' ? bid.coin_to : bid.coin_from)
|
||||||
|
: (state.currentTab === 'sent' ? bid.coin_to : bid.coin_from);
|
||||||
if (!coinMatches(coinToMatch, coinName)) {
|
if (!coinMatches(coinToMatch, coinName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -541,7 +570,10 @@ function filterAndSortData(bids) {
|
|||||||
const coinName = selectedOption?.textContent.trim();
|
const coinName = selectedOption?.textContent.trim();
|
||||||
|
|
||||||
if (coinName) {
|
if (coinName) {
|
||||||
const coinToMatch = state.currentTab === 'sent' ? bid.coin_from : bid.coin_to;
|
const coinToMatch = state.currentTab === 'all'
|
||||||
|
? (bid.source === 'sent' ? bid.coin_from : bid.coin_to)
|
||||||
|
: (state.currentTab === 'sent' ? bid.coin_from : bid.coin_to);
|
||||||
|
|
||||||
if (!coinMatches(coinToMatch, coinName)) {
|
if (!coinMatches(coinToMatch, coinName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -582,7 +614,8 @@ function filterAndSortData(bids) {
|
|||||||
let matchesDisplayedLabel = false;
|
let matchesDisplayedLabel = false;
|
||||||
if (!matchesLabel && document) {
|
if (!matchesLabel && document) {
|
||||||
try {
|
try {
|
||||||
const tableId = state.currentTab === 'sent' ? 'sent' : 'received';
|
const tableId = state.currentTab === 'sent' ? 'sent' :
|
||||||
|
(state.currentTab === 'received' ? 'received' : 'all');
|
||||||
const cells = document.querySelectorAll(`#${tableId} a[href^="/identity/"]`);
|
const cells = document.querySelectorAll(`#${tableId} a[href^="/identity/"]`);
|
||||||
|
|
||||||
for (const cell of cells) {
|
for (const cell of cells) {
|
||||||
@@ -680,7 +713,7 @@ function updateCoinFilterImages() {
|
|||||||
const updateLoadingState = (isLoading) => {
|
const updateLoadingState = (isLoading) => {
|
||||||
state.isLoading = isLoading;
|
state.isLoading = isLoading;
|
||||||
|
|
||||||
['Sent', 'Received'].forEach(type => {
|
['All', 'Sent', 'Received'].forEach(type => {
|
||||||
const refreshButton = elements[`refresh${type}Bids`];
|
const refreshButton = elements[`refresh${type}Bids`];
|
||||||
const refreshText = refreshButton?.querySelector(`#refresh${type}Text`);
|
const refreshText = refreshButton?.querySelector(`#refresh${type}Text`);
|
||||||
const refreshIcon = refreshButton?.querySelector('svg');
|
const refreshIcon = refreshButton?.querySelector('svg');
|
||||||
@@ -731,7 +764,7 @@ const updateConnectionStatus = (status) => {
|
|||||||
|
|
||||||
const config = statusConfig[status] || statusConfig.connected;
|
const config = statusConfig[status] || statusConfig.connected;
|
||||||
|
|
||||||
['sent', 'received'].forEach(type => {
|
['all', 'sent', 'received'].forEach(type => {
|
||||||
const dot = elements[`statusDot${type.charAt(0).toUpperCase() + type.slice(1)}`];
|
const dot = elements[`statusDot${type.charAt(0).toUpperCase() + type.slice(1)}`];
|
||||||
const text = elements[`statusText${type.charAt(0).toUpperCase() + type.slice(1)}`];
|
const text = elements[`statusText${type.charAt(0).toUpperCase() + type.slice(1)}`];
|
||||||
|
|
||||||
@@ -769,10 +802,12 @@ const processIdentityStats = (identity) => {
|
|||||||
|
|
||||||
const createIdentityTooltipContent = (identity) => {
|
const createIdentityTooltipContent = (identity) => {
|
||||||
if (!identity) return '';
|
if (!identity) return '';
|
||||||
|
const address = identity.address || '';
|
||||||
|
let statsSection = '';
|
||||||
|
|
||||||
|
try {
|
||||||
const stats = processIdentityStats(identity);
|
const stats = processIdentityStats(identity);
|
||||||
if (!stats) return '';
|
if (stats) {
|
||||||
|
|
||||||
const getSuccessRateColor = (rate) => {
|
const getSuccessRateColor = (rate) => {
|
||||||
const numRate = parseFloat(rate);
|
const numRate = parseFloat(rate);
|
||||||
if (numRate >= 80) return 'text-green-600';
|
if (numRate >= 80) return 'text-green-600';
|
||||||
@@ -780,29 +815,7 @@ const createIdentityTooltipContent = (identity) => {
|
|||||||
return 'text-red-600';
|
return 'text-red-600';
|
||||||
};
|
};
|
||||||
|
|
||||||
return `
|
statsSection = `
|
||||||
<div class="identity-info space-y-2">
|
|
||||||
${identity.label ? `
|
|
||||||
<div class="border-b border-gray-400 pb-2">
|
|
||||||
<div class="text-white text-xs tracking-wide font-semibold">Label:</div>
|
|
||||||
<div class="text-white">${identity.label}</div>
|
|
||||||
</div>
|
|
||||||
` : ''}
|
|
||||||
|
|
||||||
<div class="space-y-1">
|
|
||||||
<div class="text-white text-xs tracking-wide font-semibold">Bid From Address:</div>
|
|
||||||
<div class="monospace text-xs break-all bg-gray-500 p-2 rounded-md text-white">
|
|
||||||
${identity.address || ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
${identity.note ? `
|
|
||||||
<div class="space-y-1">
|
|
||||||
<div class="text-white text-xs tracking-wide font-semibold">Note:</div>
|
|
||||||
<div class="text-white text-sm italic">${identity.note}</div>
|
|
||||||
</div>
|
|
||||||
` : ''}
|
|
||||||
|
|
||||||
<div class="pt-2 mt-2">
|
<div class="pt-2 mt-2">
|
||||||
<div class="text-white text-xs tracking-wide font-semibold mb-2">Swap History:</div>
|
<div class="text-white text-xs tracking-wide font-semibold mb-2">Swap History:</div>
|
||||||
<div class="grid grid-cols-2 gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
@@ -838,6 +851,40 @@ const createIdentityTooltipContent = (identity) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Error processing identity stats:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
const addressSection = `
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold">Bid From Address:</div>
|
||||||
|
<div class="monospace text-xs break-all bg-gray-500 p-2 rounded-md text-white">
|
||||||
|
${address || identity.address || ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="identity-info space-y-2">
|
||||||
|
${identity.label ? `
|
||||||
|
<div class="border-b border-gray-400 pb-2">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold">Label:</div>
|
||||||
|
<div class="text-white">${identity.label}</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${addressSection}
|
||||||
|
|
||||||
|
${identity.note ? `
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold">Note:</div>
|
||||||
|
<div class="text-white text-sm italic">${identity.note}</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
${statsSection}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
@@ -954,12 +1001,117 @@ const forceTooltipDOMCleanup = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchAllBids() {
|
||||||
|
try {
|
||||||
|
const sentController = new AbortController();
|
||||||
|
const receivedController = new AbortController();
|
||||||
|
|
||||||
|
const sentPromise = fetch('/json/sentbids', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
sort_by: state.filters.sort_by || 'created_at',
|
||||||
|
sort_dir: state.filters.sort_dir || 'desc',
|
||||||
|
with_expired: true,
|
||||||
|
state: state.filters.state ?? -1,
|
||||||
|
with_extra_info: true
|
||||||
|
}),
|
||||||
|
signal: sentController.signal
|
||||||
|
});
|
||||||
|
|
||||||
|
const receivedPromise = fetch('/json/bids', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
sort_by: state.filters.sort_by || 'created_at',
|
||||||
|
sort_dir: state.filters.sort_dir || 'desc',
|
||||||
|
with_expired: true,
|
||||||
|
state: state.filters.state ?? -1,
|
||||||
|
with_extra_info: true
|
||||||
|
}),
|
||||||
|
signal: receivedController.signal
|
||||||
|
});
|
||||||
|
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
sentController.abort();
|
||||||
|
receivedController.abort();
|
||||||
|
}, 30000);
|
||||||
|
|
||||||
|
const [sentResponse, receivedResponse] = await Promise.all([sentPromise, receivedPromise]);
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (!sentResponse.ok || !receivedResponse.ok) {
|
||||||
|
throw new Error(`HTTP error! Sent status: ${sentResponse.status}, Received status: ${receivedResponse.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [sentData, receivedData] = await Promise.all([
|
||||||
|
sentResponse.json(),
|
||||||
|
receivedResponse.json()
|
||||||
|
]);
|
||||||
|
|
||||||
|
const filteredSentData = filterAndSortData(sentData).map(bid => ({ ...bid, source: 'sent' }));
|
||||||
|
const filteredReceivedData = filterAndSortData(receivedData).map(bid => ({ ...bid, source: 'received' }));
|
||||||
|
|
||||||
|
const combined = [...filteredSentData, ...filteredReceivedData].sort((a, b) => {
|
||||||
|
const direction = state.filters.sort_dir === 'asc' ? 1 : -1;
|
||||||
|
return direction * (a.created_at - b.created_at);
|
||||||
|
});
|
||||||
|
|
||||||
|
return combined;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching all bids:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const createTableRow = async (bid) => {
|
const createTableRow = async (bid) => {
|
||||||
const identity = await IdentityManager.getIdentityData(bid.addr_from);
|
const rawAddress = bid.addr_from || '';
|
||||||
|
let identity = null;
|
||||||
|
try {
|
||||||
|
identity = await IdentityManager.getIdentityData(rawAddress);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Error loading identity for bid:', bid.bid_id, e);
|
||||||
|
}
|
||||||
|
if (!identity) {
|
||||||
|
identity = { address: rawAddress, label: null };
|
||||||
|
} else if (!identity.address) {
|
||||||
|
identity.address = rawAddress;
|
||||||
|
}
|
||||||
const uniqueId = `${bid.bid_id}_${Date.now()}`;
|
const uniqueId = `${bid.bid_id}_${Date.now()}`;
|
||||||
tooltipIdsToCleanup.add(`tooltip-identity-${uniqueId}`);
|
tooltipIdsToCleanup.add(`tooltip-identity-${uniqueId}`);
|
||||||
tooltipIdsToCleanup.add(`tooltip-status-${uniqueId}`);
|
tooltipIdsToCleanup.add(`tooltip-status-${uniqueId}`);
|
||||||
const timeColor = getTimeStrokeColor(bid.expire_at);
|
const timeColor = getTimeStrokeColor(bid.expire_at);
|
||||||
|
const currentTabIsAll = state.currentTab === 'all';
|
||||||
|
const isSent = currentTabIsAll ? (bid.source === 'sent') : (state.currentTab === 'sent');
|
||||||
|
const sourceIndicator = currentTabIsAll ?
|
||||||
|
`<span class="ml-1 px-1.5 py-0.5 text-xs font-medium ${isSent ? 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300' : 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300'} rounded">
|
||||||
|
${isSent ? 'Sent' : 'Received'}
|
||||||
|
</span>` : '';
|
||||||
|
let tooltipContent = '';
|
||||||
|
try {
|
||||||
|
tooltipContent = createIdentityTooltipContent(identity);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Error creating tooltip content:', e);
|
||||||
|
}
|
||||||
|
if (!tooltipContent) {
|
||||||
|
tooltipContent = `
|
||||||
|
<div class="identity-info space-y-2">
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold">Bid From Address:</div>
|
||||||
|
<div class="monospace text-xs break-all bg-gray-500 p-2 rounded-md text-white">
|
||||||
|
${rawAddress}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
@@ -972,7 +1124,10 @@ const createTableRow = async (bid) => {
|
|||||||
<polyline points="12,6 12,12 18,12"></polyline>
|
<polyline points="12,6 12,12 18,12"></polyline>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="text-xs">${formatTime(bid.created_at)}</div>
|
<div class="text-xs flex items-center">
|
||||||
|
${formatTime(bid.created_at)}
|
||||||
|
${sourceIndicator}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
@@ -981,11 +1136,11 @@ const createTableRow = async (bid) => {
|
|||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="flex items-center min-w-max">
|
<div class="flex items-center min-w-max">
|
||||||
<div class="relative" data-tooltip-target="tooltip-identity-${uniqueId}">
|
<div class="relative" data-tooltip-target="tooltip-identity-${uniqueId}">
|
||||||
<a href="/identity/${bid.addr_from}" class="text-xs font-mono">
|
<a href="/identity/${rawAddress}" class="text-xs font-mono">
|
||||||
<span>
|
<span>
|
||||||
${state.currentTab === 'sent' ? 'Out:' : 'In:'}
|
${isSent ? 'Out:' : 'In:'}
|
||||||
</span>
|
</span>
|
||||||
${identity?.label || formatAddressSMSG(bid.addr_from)}
|
${identity?.label || formatAddressSMSG(rawAddress)}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1001,12 +1156,12 @@ const createTableRow = async (bid) => {
|
|||||||
<td class="p-3">
|
<td class="p-3">
|
||||||
<div class="flex items-center min-w-max">
|
<div class="flex items-center min-w-max">
|
||||||
<img class="w-8 h-8 mr-2"
|
<img class="w-8 h-8 mr-2"
|
||||||
src="/static/images/coins/${state.currentTab === 'sent' ? bid.coin_to.replace(' ', '-') : bid.coin_from.replace(' ', '-')}.png"
|
src="/static/images/coins/${isSent ? bid.coin_to.replace(' ', '-') : bid.coin_from.replace(' ', '-')}.png"
|
||||||
alt="${state.currentTab === 'sent' ? bid.coin_to : bid.coin_from}"
|
alt="${isSent ? bid.coin_to : bid.coin_from}"
|
||||||
onerror="this.src='/static/images/coins/default.png'">
|
onerror="this.src='/static/images/coins/default.png'">
|
||||||
<div>
|
<div>
|
||||||
<div class="text-sm font-medium monospace">${state.currentTab === 'sent' ? bid.amount_to : bid.amount_from}</div>
|
<div class="text-sm font-medium monospace">${isSent ? bid.amount_to : bid.amount_from}</div>
|
||||||
<div class="text-xs opacity-75 monospace">${state.currentTab === 'sent' ? bid.coin_to : bid.coin_from}</div>
|
<div class="text-xs opacity-75 monospace">${isSent ? bid.coin_to : bid.coin_from}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -1015,12 +1170,12 @@ const createTableRow = async (bid) => {
|
|||||||
<td class="p-3">
|
<td class="p-3">
|
||||||
<div class="flex items-center min-w-max">
|
<div class="flex items-center min-w-max">
|
||||||
<img class="w-8 h-8 mr-2"
|
<img class="w-8 h-8 mr-2"
|
||||||
src="/static/images/coins/${state.currentTab === 'sent' ? bid.coin_from.replace(' ', '-') : bid.coin_to.replace(' ', '-')}.png"
|
src="/static/images/coins/${isSent ? bid.coin_from.replace(' ', '-') : bid.coin_to.replace(' ', '-')}.png"
|
||||||
alt="${state.currentTab === 'sent' ? bid.coin_from : bid.coin_to}"
|
alt="${isSent ? bid.coin_from : bid.coin_to}"
|
||||||
onerror="this.src='/static/images/coins/default.png'">
|
onerror="this.src='/static/images/coins/default.png'">
|
||||||
<div>
|
<div>
|
||||||
<div class="text-sm font-medium monospace">${state.currentTab === 'sent' ? bid.amount_from : bid.amount_to}</div>
|
<div class="text-sm font-medium monospace">${isSent ? bid.amount_from : bid.amount_to}</div>
|
||||||
<div class="text-xs opacity-75 monospace">${state.currentTab === 'sent' ? bid.coin_from : bid.coin_to}</div>
|
<div class="text-xs opacity-75 monospace">${isSent ? bid.coin_from : bid.coin_to}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -1045,10 +1200,9 @@ const createTableRow = async (bid) => {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
<!-- Tooltips -->
|
<!-- Tooltips -->
|
||||||
<div id="tooltip-identity-${uniqueId}" role="tooltip" class="fixed z-50 py-3 px-4 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-600 max-w-sm pointer-events-none">
|
<div id="tooltip-identity-${uniqueId}" role="tooltip" class="fixed z-50 py-3 px-4 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-600 max-w-sm pointer-events-none">
|
||||||
${createIdentityTooltipContent(identity)}
|
${tooltipContent}
|
||||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1071,6 +1225,131 @@ const createTableRow = async (bid) => {
|
|||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function cleanupOffscreenTooltips() {
|
||||||
|
if (!window.TooltipManager) return;
|
||||||
|
|
||||||
|
const selector = '#' + state.currentTab + ' [data-tooltip-target]';
|
||||||
|
const tooltipTriggers = document.querySelectorAll(selector);
|
||||||
|
|
||||||
|
const farOffscreenTriggers = Array.from(tooltipTriggers).filter(trigger => {
|
||||||
|
const rect = trigger.getBoundingClientRect();
|
||||||
|
return (rect.bottom < -window.innerHeight * 2 ||
|
||||||
|
rect.top > window.innerHeight * 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
farOffscreenTriggers.forEach(trigger => {
|
||||||
|
const targetId = trigger.getAttribute('data-tooltip-target');
|
||||||
|
if (targetId) {
|
||||||
|
const tooltipElement = document.getElementById(targetId);
|
||||||
|
if (tooltipElement) {
|
||||||
|
window.TooltipManager.destroy(trigger);
|
||||||
|
trigger.addEventListener('mouseenter', () => {
|
||||||
|
createTooltipForTrigger(trigger);
|
||||||
|
}, { once: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function implementVirtualizedRows() {
|
||||||
|
const tbody = elements[`${state.currentTab}BidsBody`];
|
||||||
|
if (!tbody) return;
|
||||||
|
|
||||||
|
const tableRows = tbody.querySelectorAll('tr');
|
||||||
|
if (tableRows.length < 30) return;
|
||||||
|
|
||||||
|
Array.from(tableRows).forEach(row => {
|
||||||
|
const rect = row.getBoundingClientRect();
|
||||||
|
const isVisible = (
|
||||||
|
rect.bottom >= 0 &&
|
||||||
|
rect.top <= window.innerHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isVisible && (rect.bottom < -window.innerHeight || rect.top > window.innerHeight * 2)) {
|
||||||
|
const tooltipTriggers = row.querySelectorAll('[data-tooltip-target]');
|
||||||
|
tooltipTriggers.forEach(trigger => {
|
||||||
|
if (window.TooltipManager) {
|
||||||
|
window.TooltipManager.destroy(trigger);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchBids(type = state.currentTab) {
|
||||||
|
if (type === 'all') {
|
||||||
|
return fetchAllBids();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (activeFetchController) {
|
||||||
|
activeFetchController.abort();
|
||||||
|
}
|
||||||
|
activeFetchController = new AbortController();
|
||||||
|
const endpoint = type === 'sent' ? '/json/sentbids' : '/json/bids';
|
||||||
|
const withExpiredSelect = document.getElementById('with_expired');
|
||||||
|
const includeExpired = withExpiredSelect ? withExpiredSelect.value === 'true' : true;
|
||||||
|
|
||||||
|
//console.log(`Fetching ${type} bids, include expired:`, includeExpired);
|
||||||
|
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
if (activeFetchController) {
|
||||||
|
activeFetchController.abort();
|
||||||
|
}
|
||||||
|
}, 30000);
|
||||||
|
|
||||||
|
const response = await fetch(endpoint, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
sort_by: state.filters.sort_by || 'created_at',
|
||||||
|
sort_dir: state.filters.sort_dir || 'desc',
|
||||||
|
with_expired: true,
|
||||||
|
state: state.filters.state ?? -1,
|
||||||
|
with_extra_info: true
|
||||||
|
}),
|
||||||
|
signal: activeFetchController.signal
|
||||||
|
});
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
//console.log(`Received raw ${type} data:`, data.length, 'bids');
|
||||||
|
|
||||||
|
state.filters.with_expired = includeExpired;
|
||||||
|
|
||||||
|
let processedData;
|
||||||
|
if (data.length > 500) {
|
||||||
|
processedData = await new Promise(resolve => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const filtered = filterAndSortData(data);
|
||||||
|
resolve(filtered);
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
processedData = filterAndSortData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return processedData;
|
||||||
|
} catch (error) {
|
||||||
|
if (error.name === 'AbortError') {
|
||||||
|
console.log('Fetch request was aborted');
|
||||||
|
} else {
|
||||||
|
console.error(`Error in fetch${type.charAt(0).toUpperCase() + type.slice(1)}Bids:`, error);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
activeFetchController = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const updateTableContent = async (type) => {
|
const updateTableContent = async (type) => {
|
||||||
const tbody = elements[`${type}BidsBody`];
|
const tbody = elements[`${type}BidsBody`];
|
||||||
if (!tbody) return;
|
if (!tbody) return;
|
||||||
@@ -1220,146 +1499,6 @@ const createTooltipForTrigger = (trigger) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function optimizeForLargeDatasets() {
|
|
||||||
if (state.data[state.currentTab]?.length > 50) {
|
|
||||||
|
|
||||||
const simplifyTooltips = tooltipIdsToCleanup.size > 50;
|
|
||||||
|
|
||||||
implementVirtualizedRows();
|
|
||||||
|
|
||||||
let scrollTimeout;
|
|
||||||
window.addEventListener('scroll', () => {
|
|
||||||
clearTimeout(scrollTimeout);
|
|
||||||
scrollTimeout = setTimeout(() => {
|
|
||||||
cleanupOffscreenTooltips();
|
|
||||||
}, 150);
|
|
||||||
}, { passive: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanupOffscreenTooltips() {
|
|
||||||
if (!window.TooltipManager) return;
|
|
||||||
|
|
||||||
const selector = '#' + state.currentTab + ' [data-tooltip-target]';
|
|
||||||
const tooltipTriggers = document.querySelectorAll(selector);
|
|
||||||
|
|
||||||
const farOffscreenTriggers = Array.from(tooltipTriggers).filter(trigger => {
|
|
||||||
const rect = trigger.getBoundingClientRect();
|
|
||||||
return (rect.bottom < -window.innerHeight * 2 ||
|
|
||||||
rect.top > window.innerHeight * 3);
|
|
||||||
});
|
|
||||||
|
|
||||||
farOffscreenTriggers.forEach(trigger => {
|
|
||||||
const targetId = trigger.getAttribute('data-tooltip-target');
|
|
||||||
if (targetId) {
|
|
||||||
const tooltipElement = document.getElementById(targetId);
|
|
||||||
if (tooltipElement) {
|
|
||||||
window.TooltipManager.destroy(trigger);
|
|
||||||
trigger.addEventListener('mouseenter', () => {
|
|
||||||
createTooltipForTrigger(trigger);
|
|
||||||
}, { once: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function implementVirtualizedRows() {
|
|
||||||
const tbody = elements[`${state.currentTab}BidsBody`];
|
|
||||||
if (!tbody) return;
|
|
||||||
|
|
||||||
const tableRows = tbody.querySelectorAll('tr');
|
|
||||||
if (tableRows.length < 30) return;
|
|
||||||
|
|
||||||
Array.from(tableRows).forEach(row => {
|
|
||||||
const rect = row.getBoundingClientRect();
|
|
||||||
const isVisible = (
|
|
||||||
rect.bottom >= 0 &&
|
|
||||||
rect.top <= window.innerHeight
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!isVisible && (rect.bottom < -window.innerHeight || rect.top > window.innerHeight * 2)) {
|
|
||||||
const tooltipTriggers = row.querySelectorAll('[data-tooltip-target]');
|
|
||||||
tooltipTriggers.forEach(trigger => {
|
|
||||||
if (window.TooltipManager) {
|
|
||||||
window.TooltipManager.destroy(trigger);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let activeFetchController = null;
|
|
||||||
|
|
||||||
const fetchBids = async () => {
|
|
||||||
try {
|
|
||||||
if (activeFetchController) {
|
|
||||||
activeFetchController.abort();
|
|
||||||
}
|
|
||||||
activeFetchController = new AbortController();
|
|
||||||
const endpoint = state.currentTab === 'sent' ? '/json/sentbids' : '/json/bids';
|
|
||||||
const withExpiredSelect = document.getElementById('with_expired');
|
|
||||||
const includeExpired = withExpiredSelect ? withExpiredSelect.value === 'true' : true;
|
|
||||||
|
|
||||||
//console.log('Fetching bids, include expired:', includeExpired);
|
|
||||||
|
|
||||||
const timeoutId = setTimeout(() => {
|
|
||||||
if (activeFetchController) {
|
|
||||||
activeFetchController.abort();
|
|
||||||
}
|
|
||||||
}, 30000);
|
|
||||||
|
|
||||||
const response = await fetch(endpoint, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Accept': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
sort_by: state.filters.sort_by || 'created_at',
|
|
||||||
sort_dir: state.filters.sort_dir || 'desc',
|
|
||||||
with_expired: true,
|
|
||||||
state: state.filters.state ?? -1,
|
|
||||||
with_extra_info: true
|
|
||||||
}),
|
|
||||||
signal: activeFetchController.signal
|
|
||||||
});
|
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
//console.log('Received raw data:', data.length, 'bids');
|
|
||||||
|
|
||||||
state.filters.with_expired = includeExpired;
|
|
||||||
|
|
||||||
let processedData;
|
|
||||||
if (data.length > 500) {
|
|
||||||
processedData = await new Promise(resolve => {
|
|
||||||
setTimeout(() => {
|
|
||||||
const filtered = filterAndSortData(data);
|
|
||||||
resolve(filtered);
|
|
||||||
}, 10);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
processedData = filterAndSortData(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return processedData;
|
|
||||||
} catch (error) {
|
|
||||||
if (error.name === 'AbortError') {
|
|
||||||
console.log('Fetch request was aborted');
|
|
||||||
} else {
|
|
||||||
console.error('Error in fetchBids:', error);
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
activeFetchController = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateBidsTable = async () => {
|
const updateBidsTable = async () => {
|
||||||
if (state.isLoading) {
|
if (state.isLoading) {
|
||||||
return;
|
return;
|
||||||
@@ -1369,7 +1508,13 @@ const updateBidsTable = async () => {
|
|||||||
state.isLoading = true;
|
state.isLoading = true;
|
||||||
updateLoadingState(true);
|
updateLoadingState(true);
|
||||||
|
|
||||||
const bids = await fetchBids();
|
let bids;
|
||||||
|
|
||||||
|
if (state.currentTab === 'all') {
|
||||||
|
bids = await fetchAllBids();
|
||||||
|
} else {
|
||||||
|
bids = await fetchBids();
|
||||||
|
}
|
||||||
|
|
||||||
// Add identity preloading if we're searching
|
// Add identity preloading if we're searching
|
||||||
if (state.filters.searchQuery && state.filters.searchQuery.length > 0) {
|
if (state.filters.searchQuery && state.filters.searchQuery.length > 0) {
|
||||||
@@ -1416,7 +1561,11 @@ const updatePaginationControls = (type) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (currentPageSpan) {
|
if (currentPageSpan) {
|
||||||
currentPageSpan.textContent = totalPages > 0 ? state.currentPage[type] : 0;
|
if (totalPages > 0) {
|
||||||
|
currentPageSpan.innerHTML = `<span>${state.currentPage[type]}</span> of <span>${totalPages}</span>`;
|
||||||
|
} else {
|
||||||
|
currentPageSpan.textContent = "0";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prevButton) {
|
if (prevButton) {
|
||||||
@@ -1581,7 +1730,7 @@ function setupFilterEventListeners() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const setupRefreshButtons = () => {
|
const setupRefreshButtons = () => {
|
||||||
['Sent', 'Received'].forEach(type => {
|
['All', 'Sent', 'Received'].forEach(type => {
|
||||||
const refreshButton = elements[`refresh${type}Bids`];
|
const refreshButton = elements[`refresh${type}Bids`];
|
||||||
if (refreshButton) {
|
if (refreshButton) {
|
||||||
EventManager.add(refreshButton, 'click', async () => {
|
EventManager.add(refreshButton, 'click', async () => {
|
||||||
@@ -1597,7 +1746,10 @@ const setupRefreshButtons = () => {
|
|||||||
state.isLoading = true;
|
state.isLoading = true;
|
||||||
updateLoadingState(true);
|
updateLoadingState(true);
|
||||||
|
|
||||||
const response = await fetch(state.currentTab === 'sent' ? '/json/sentbids' : '/json/bids', {
|
if (lowerType === 'all') {
|
||||||
|
state.data.all = await fetchAllBids();
|
||||||
|
} else {
|
||||||
|
const response = await fetch(lowerType === 'sent' ? '/json/sentbids' : '/json/bids', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@@ -1621,6 +1773,8 @@ const setupRefreshButtons = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.data[lowerType] = data;
|
state.data[lowerType] = data;
|
||||||
|
}
|
||||||
|
|
||||||
await updateTableContent(lowerType);
|
await updateTableContent(lowerType);
|
||||||
updatePaginationControls(lowerType);
|
updatePaginationControls(lowerType);
|
||||||
|
|
||||||
@@ -1648,8 +1802,10 @@ const switchTab = (tabId) => {
|
|||||||
|
|
||||||
tooltipIdsToCleanup.clear();
|
tooltipIdsToCleanup.clear();
|
||||||
|
|
||||||
state.currentTab = tabId === '#sent' ? 'sent' : 'received';
|
state.currentTab = tabId === '#all' ? 'all' :
|
||||||
|
(tabId === '#sent' ? 'sent' : 'received');
|
||||||
|
|
||||||
|
elements.allContent.classList.add('hidden');
|
||||||
elements.sentContent.classList.add('hidden');
|
elements.sentContent.classList.add('hidden');
|
||||||
elements.receivedContent.classList.add('hidden');
|
elements.receivedContent.classList.add('hidden');
|
||||||
|
|
||||||
@@ -1669,11 +1825,31 @@ const switchTab = (tabId) => {
|
|||||||
tab.classList.add('hover:text-gray-600', 'hover:bg-gray-50', 'dark:hover:bg-gray-500');
|
tab.classList.add('hover:text-gray-600', 'hover:bg-gray-50', 'dark:hover:bg-gray-500');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
updateBidsTable();
|
updateBidsTable();
|
||||||
}, 10);
|
}, 10);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.switchTab = switchTab;
|
||||||
|
|
||||||
|
function optimizeForLargeDatasets() {
|
||||||
|
if (state.data[state.currentTab]?.length > 50) {
|
||||||
|
|
||||||
|
const simplifyTooltips = tooltipIdsToCleanup.size > 50;
|
||||||
|
|
||||||
|
implementVirtualizedRows();
|
||||||
|
|
||||||
|
let scrollTimeout;
|
||||||
|
window.addEventListener('scroll', () => {
|
||||||
|
clearTimeout(scrollTimeout);
|
||||||
|
scrollTimeout = setTimeout(() => {
|
||||||
|
cleanupOffscreenTooltips();
|
||||||
|
}, 150);
|
||||||
|
}, { passive: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const setupEventListeners = () => {
|
const setupEventListeners = () => {
|
||||||
const filterControls = document.querySelector('.flex.flex-wrap.justify-center');
|
const filterControls = document.querySelector('.flex.flex-wrap.justify-center');
|
||||||
if (filterControls) {
|
if (filterControls) {
|
||||||
@@ -1708,10 +1884,12 @@ const setupEventListeners = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
elements.allContent.classList.toggle('hidden', targetId !== '#all');
|
||||||
elements.sentContent.classList.toggle('hidden', targetId !== '#sent');
|
elements.sentContent.classList.toggle('hidden', targetId !== '#sent');
|
||||||
elements.receivedContent.classList.toggle('hidden', targetId !== '#received');
|
elements.receivedContent.classList.toggle('hidden', targetId !== '#received');
|
||||||
|
|
||||||
state.currentTab = targetId === '#sent' ? 'sent' : 'received';
|
state.currentTab = targetId === '#all' ? 'all' :
|
||||||
|
(targetId === '#sent' ? 'sent' : 'received');
|
||||||
state.currentPage[state.currentTab] = 1;
|
state.currentPage[state.currentTab] = 1;
|
||||||
|
|
||||||
if (window.TooltipManager) {
|
if (window.TooltipManager) {
|
||||||
@@ -1724,7 +1902,7 @@ const setupEventListeners = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
['Sent', 'Received'].forEach(type => {
|
['All', 'Sent', 'Received'].forEach(type => {
|
||||||
const lowerType = type.toLowerCase();
|
const lowerType = type.toLowerCase();
|
||||||
|
|
||||||
if (elements[`prevPage${type}`]) {
|
if (elements[`prevPage${type}`]) {
|
||||||
@@ -1863,6 +2041,11 @@ function setupMemoryMonitoring() {
|
|||||||
window.TooltipManager.cleanup();
|
window.TooltipManager.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.data.all.length > 1000) {
|
||||||
|
console.log('Trimming all bids data');
|
||||||
|
state.data.all = state.data.all.slice(0, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
if (state.data.sent.length > 1000) {
|
if (state.data.sent.length > 1000) {
|
||||||
console.log('Trimming sent bids data');
|
console.log('Trimming sent bids data');
|
||||||
state.data.sent = state.data.sent.slice(0, 1000);
|
state.data.sent = state.data.sent.slice(0, 1000);
|
||||||
@@ -1912,11 +2095,13 @@ function initialize() {
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
updateClearFiltersButton();
|
updateClearFiltersButton();
|
||||||
state.currentTab = 'sent';
|
state.currentTab = 'all';
|
||||||
state.filters.state = -1;
|
state.filters.state = -1;
|
||||||
updateBidsTable();
|
updateBidsTable();
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
|
setupMemoryMonitoring();
|
||||||
|
|
||||||
window.cleanupBidsTable = cleanup;
|
window.cleanupBidsTable = cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1925,74 +2110,3 @@ if (document.readyState === 'loading') {
|
|||||||
} else {
|
} else {
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
(function() {
|
|
||||||
function handleBidsTabFromHash() {
|
|
||||||
if (window.location.pathname !== '/bids') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const hash = window.location.hash;
|
|
||||||
|
|
||||||
if (hash) {
|
|
||||||
const tabName = hash.substring(1);
|
|
||||||
let tabId;
|
|
||||||
switch (tabName.toLowerCase()) {
|
|
||||||
case 'sent':
|
|
||||||
tabId = '#sent';
|
|
||||||
break;
|
|
||||||
case 'received':
|
|
||||||
tabId = '#received';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
tabId = '#sent';
|
|
||||||
}
|
|
||||||
switchTab(tabId);
|
|
||||||
} else {
|
|
||||||
switchTab('#sent');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function switchTab(tabId) {
|
|
||||||
const targetTabBtn = document.querySelector(`[data-tabs-target="${tabId}"]`);
|
|
||||||
if (targetTabBtn) {
|
|
||||||
targetTabBtn.click();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupBidsTabNavigation() {
|
|
||||||
handleBidsTabFromHash();
|
|
||||||
window.addEventListener('hashchange', handleBidsTabFromHash);
|
|
||||||
const originalSwitchTab = window.switchTab || null;
|
|
||||||
|
|
||||||
window.switchTab = function(tabId) {
|
|
||||||
const newTabName = tabId.replace('#', '');
|
|
||||||
if (window.location.hash !== `#${newTabName}`) {
|
|
||||||
history.replaceState(null, null, `#${newTabName}`);
|
|
||||||
}
|
|
||||||
if (originalSwitchTab && typeof originalSwitchTab === 'function') {
|
|
||||||
originalSwitchTab(tabId);
|
|
||||||
} else {
|
|
||||||
const targetTabBtn = document.querySelector(`[data-tabs-target="${tabId}"]`);
|
|
||||||
if (targetTabBtn) {
|
|
||||||
targetTabBtn.click();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const tabButtons = document.querySelectorAll('[data-tabs-target]');
|
|
||||||
tabButtons.forEach(btn => {
|
|
||||||
btn.addEventListener('click', function() {
|
|
||||||
const tabId = this.getAttribute('data-tabs-target');
|
|
||||||
const tabName = tabId.replace('#', '');
|
|
||||||
history.replaceState(null, null, `#${tabName}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (document.readyState === 'loading') {
|
|
||||||
document.addEventListener('DOMContentLoaded', setupBidsTabNavigation);
|
|
||||||
} else {
|
|
||||||
setupBidsTabNavigation();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|||||||
@@ -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');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(function() {
|
function activateTabWithRetry(tabId, retryCount = 0) {
|
||||||
window.scrollTo(0, oldScrollPosition);
|
const normalizedTabId = tabId.startsWith('#') ? tabId : '#' + tabId;
|
||||||
}, 0);
|
|
||||||
|
if (normalizedTabId !== '#all' && normalizedTabId !== '#sent' && normalizedTabId !== '#received') {
|
||||||
|
//console.log('Invalid tab ID, defaulting to #all');
|
||||||
|
activateTabWithRetry('#all');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
function activateTab(tabId) {
|
const tabButtonId = normalizedTabId === '#all' ? 'all-tab' :
|
||||||
if (tabId !== '#sent' && tabId !== '#received') {
|
(normalizedTabId === '#sent' ? 'sent-tab' : 'received-tab');
|
||||||
tabId = '#sent';
|
|
||||||
}
|
|
||||||
|
|
||||||
const tabButtonId = tabId === '#sent' ? 'sent-tab' : 'received-tab';
|
|
||||||
const tabButton = document.getElementById(tabButtonId);
|
const tabButton = document.getElementById(tabButtonId);
|
||||||
|
|
||||||
if (tabButton) {
|
if (!tabButton) {
|
||||||
const oldScrollPosition = window.scrollY;
|
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();
|
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);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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';
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user