mirror of
https://github.com/basicswap/basicswap.git
synced 2025-04-16 19:22:07 +00:00
New bids pages + various fixes. (#266)
* New bids pages + various fixes. * LINT * Fix styling.
This commit is contained in:
parent
f1c2b41714
commit
97bb615176
12 changed files with 2851 additions and 640 deletions
basicswap
|
@ -15,6 +15,7 @@ from .util import (
|
|||
)
|
||||
from .basicswap_util import (
|
||||
strBidState,
|
||||
strTxState,
|
||||
SwapTypes,
|
||||
NotificationTypes as NT,
|
||||
)
|
||||
|
@ -320,18 +321,36 @@ def formatBids(swap_client, bids, filters) -> bytes:
|
|||
with_extra_info = filters.get("with_extra_info", False)
|
||||
rv = []
|
||||
for b in bids:
|
||||
ci_from = swap_client.ci(b[9])
|
||||
offer = swap_client.getOffer(b[3])
|
||||
ci_to = swap_client.ci(offer.coin_to) if offer else None
|
||||
|
||||
amount_to = None
|
||||
if ci_to:
|
||||
amount_to = ci_to.format_amount(
|
||||
(b[4] * b[10]) // ci_from.COIN()
|
||||
)
|
||||
|
||||
bid_data = {
|
||||
"bid_id": b[2].hex(),
|
||||
"offer_id": b[3].hex(),
|
||||
"created_at": b[0],
|
||||
"expire_at": b[1],
|
||||
"coin_from": b[9],
|
||||
"amount_from": swap_client.ci(b[9]).format_amount(b[4]),
|
||||
"coin_from": ci_from.coin_name(),
|
||||
"coin_to": ci_to.coin_name() if ci_to else "Unknown",
|
||||
"amount_from": ci_from.format_amount(b[4]),
|
||||
"amount_to": amount_to,
|
||||
"bid_rate": swap_client.ci(b[14]).format_amount(b[10]),
|
||||
"bid_state": strBidState(b[5]),
|
||||
"addr_from": b[11],
|
||||
"addr_to": offer.addr_to if offer else None
|
||||
}
|
||||
|
||||
if with_extra_info:
|
||||
bid_data["addr_from"] = b[11]
|
||||
bid_data.update({
|
||||
"tx_state_a": strTxState(b[7]),
|
||||
"tx_state_b": strTxState(b[8])
|
||||
})
|
||||
rv.append(bid_data)
|
||||
return bytes(json.dumps(rv), "UTF-8")
|
||||
|
||||
|
|
Binary file not shown.
Before ![]() (image error) Size: 8.4 KiB After ![]() (image error) Size: 9 KiB ![]() ![]() |
899
basicswap/static/js/bids_available.js
Normal file
899
basicswap/static/js/bids_available.js
Normal file
|
@ -0,0 +1,899 @@
|
|||
// Constants and State
|
||||
const PAGE_SIZE = 50;
|
||||
const COIN_NAME_TO_SYMBOL = {
|
||||
'Bitcoin': 'BTC',
|
||||
'Litecoin': 'LTC',
|
||||
'Monero': 'XMR',
|
||||
'Particl': 'PART',
|
||||
'Particl Blind': 'PART',
|
||||
'Particl Anon': 'PART',
|
||||
'PIVX': 'PIVX',
|
||||
'Firo': 'FIRO',
|
||||
'Dash': 'DASH',
|
||||
'Decred': 'DCR',
|
||||
'Wownero': 'WOW',
|
||||
'Bitcoin Cash': 'BCH',
|
||||
'Dogecoin': 'DOGE'
|
||||
};
|
||||
|
||||
// Global state
|
||||
const state = {
|
||||
dentities: new Map(),
|
||||
currentPage: 1,
|
||||
wsConnected: false,
|
||||
jsonData: [],
|
||||
isLoading: false,
|
||||
isRefreshing: false,
|
||||
refreshPromise: null
|
||||
};
|
||||
|
||||
// DOM
|
||||
const elements = {
|
||||
bidsBody: document.getElementById('bids-body'),
|
||||
prevPageButton: document.getElementById('prevPage'),
|
||||
nextPageButton: document.getElementById('nextPage'),
|
||||
currentPageSpan: document.getElementById('currentPage'),
|
||||
paginationControls: document.getElementById('pagination-controls'),
|
||||
availableBidsCount: document.getElementById('availableBidsCount'),
|
||||
refreshBidsButton: document.getElementById('refreshBids'),
|
||||
statusDot: document.getElementById('status-dot'),
|
||||
statusText: document.getElementById('status-text')
|
||||
};
|
||||
|
||||
// Identity Manager
|
||||
const IdentityManager = {
|
||||
cache: new Map(),
|
||||
pendingRequests: new Map(),
|
||||
retryDelay: 2000,
|
||||
maxRetries: 3,
|
||||
cacheTimeout: 5 * 60 * 1000, // 5 minutes
|
||||
|
||||
async getIdentityData(address) {
|
||||
if (!address) {
|
||||
return { address: '' };
|
||||
}
|
||||
|
||||
const cachedData = this.getCachedIdentity(address);
|
||||
if (cachedData) {
|
||||
return { ...cachedData, address };
|
||||
}
|
||||
|
||||
if (this.pendingRequests.has(address)) {
|
||||
const pendingData = await this.pendingRequests.get(address);
|
||||
return { ...pendingData, address };
|
||||
}
|
||||
|
||||
const request = this.fetchWithRetry(address);
|
||||
this.pendingRequests.set(address, request);
|
||||
|
||||
try {
|
||||
const data = await request;
|
||||
this.cache.set(address, {
|
||||
data,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
return { ...data, address };
|
||||
} catch (error) {
|
||||
console.warn(`Error fetching identity for ${address}:`, error);
|
||||
return { address };
|
||||
} finally {
|
||||
this.pendingRequests.delete(address);
|
||||
}
|
||||
},
|
||||
|
||||
getCachedIdentity(address) {
|
||||
const cached = this.cache.get(address);
|
||||
if (cached && (Date.now() - cached.timestamp) < this.cacheTimeout) {
|
||||
return cached.data;
|
||||
}
|
||||
if (cached) {
|
||||
this.cache.delete(address);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
async fetchWithRetry(address, attempt = 1) {
|
||||
try {
|
||||
const response = await fetch(`/json/identities/${address}`, {
|
||||
signal: AbortSignal.timeout(5000)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return {
|
||||
...data,
|
||||
address,
|
||||
num_sent_bids_successful: safeParseInt(data.num_sent_bids_successful),
|
||||
num_recv_bids_successful: safeParseInt(data.num_recv_bids_successful),
|
||||
num_sent_bids_failed: safeParseInt(data.num_sent_bids_failed),
|
||||
num_recv_bids_failed: safeParseInt(data.num_recv_bids_failed),
|
||||
num_sent_bids_rejected: safeParseInt(data.num_sent_bids_rejected),
|
||||
num_recv_bids_rejected: safeParseInt(data.num_recv_bids_rejected),
|
||||
label: data.label || '',
|
||||
note: data.note || '',
|
||||
automation_override: safeParseInt(data.automation_override)
|
||||
};
|
||||
} catch (error) {
|
||||
if (attempt >= this.maxRetries) {
|
||||
console.warn(`Failed to fetch identity for ${address} after ${attempt} attempts`);
|
||||
return {
|
||||
address,
|
||||
num_sent_bids_successful: 0,
|
||||
num_recv_bids_successful: 0,
|
||||
num_sent_bids_failed: 0,
|
||||
num_recv_bids_failed: 0,
|
||||
num_sent_bids_rejected: 0,
|
||||
num_recv_bids_rejected: 0,
|
||||
label: '',
|
||||
note: '',
|
||||
automation_override: 0
|
||||
};
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, this.retryDelay * attempt));
|
||||
return this.fetchWithRetry(address, attempt + 1);
|
||||
}
|
||||
},
|
||||
|
||||
clearCache() {
|
||||
this.cache.clear();
|
||||
this.pendingRequests.clear();
|
||||
},
|
||||
|
||||
removeFromCache(address) {
|
||||
this.cache.delete(address);
|
||||
this.pendingRequests.delete(address);
|
||||
},
|
||||
|
||||
cleanup() {
|
||||
const now = Date.now();
|
||||
for (const [address, cached] of this.cache.entries()) {
|
||||
if (now - cached.timestamp >= this.cacheTimeout) {
|
||||
this.cache.delete(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Util
|
||||
const formatTimeAgo = (timestamp) => {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const diff = now - timestamp;
|
||||
|
||||
if (diff < 60) return `${diff} seconds ago`;
|
||||
if (diff < 3600) return `${Math.floor(diff / 60)} minutes ago`;
|
||||
if (diff < 86400) return `${Math.floor(diff / 3600)} hours ago`;
|
||||
return `${Math.floor(diff / 86400)} days ago`;
|
||||
};
|
||||
|
||||
const formatTime = (timestamp) => {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const diff = timestamp - now;
|
||||
|
||||
if (diff <= 0) return "Expired";
|
||||
if (diff < 60) return `${diff} seconds`;
|
||||
if (diff < 3600) return `${Math.floor(diff / 60)} minutes`;
|
||||
if (diff < 86400) return `${Math.floor(diff / 3600)} hours`;
|
||||
return `${Math.floor(diff / 86400)} days`;
|
||||
};
|
||||
|
||||
const formatAddress = (address, displayLength = 15) => {
|
||||
if (!address) return '';
|
||||
if (address.length <= displayLength) return address;
|
||||
return `${address.slice(0, displayLength)}...`;
|
||||
};
|
||||
|
||||
const getTimeStrokeColor = (expireTime) => {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const timeLeft = expireTime - now;
|
||||
|
||||
if (timeLeft <= 300) return '#9CA3AF'; // 5 minutes or less
|
||||
if (timeLeft <= 1800) return '#3B82F6'; // 30 minutes or less
|
||||
return '#10B981'; // More than 30 minutes
|
||||
};
|
||||
|
||||
const createTimeTooltip = (bid) => {
|
||||
const postedTime = formatTimeAgo(bid.created_at);
|
||||
const expiresIn = formatTime(bid.expire_at);
|
||||
return `
|
||||
<div class="active-revoked-expired">
|
||||
<span class="bold">
|
||||
<div class="text-xs"><span class="bold">Posted:</span> ${postedTime}</div>
|
||||
<div class="text-xs"><span class="bold">Expires in:</span> ${expiresIn}</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-5 text-xs">
|
||||
<p class="font-bold mb-3">Time Indicator Colors:</p>
|
||||
<p class="flex items-center">
|
||||
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#10B981" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points="12,6 12,12 18,12" stroke="#10B981"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
Green: More than 30 minutes left
|
||||
</p>
|
||||
<p class="flex items-center mt-3">
|
||||
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#3B82F6" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points="12,6 12,12 18,12" stroke="#3B82F6"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
Blue: Between 5 and 30 minutes left
|
||||
</p>
|
||||
<p class="flex items-center mt-3 mb-3">
|
||||
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#9CA3AF" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points="12,6 12,12 18,12" stroke="#9CA3AF"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
Grey: Less than 5 minutes left or expired
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
|
||||
const safeParseInt = (value) => {
|
||||
const parsed = parseInt(value);
|
||||
return isNaN(parsed) ? 0 : parsed;
|
||||
};
|
||||
|
||||
const processIdentityStats = (identity) => {
|
||||
if (!identity) return null;
|
||||
|
||||
const stats = {
|
||||
sentSuccessful: safeParseInt(identity.num_sent_bids_successful),
|
||||
recvSuccessful: safeParseInt(identity.num_recv_bids_successful),
|
||||
sentFailed: safeParseInt(identity.num_sent_bids_failed),
|
||||
recvFailed: safeParseInt(identity.num_recv_bids_failed),
|
||||
sentRejected: safeParseInt(identity.num_sent_bids_rejected),
|
||||
recvRejected: safeParseInt(identity.num_recv_bids_rejected)
|
||||
};
|
||||
|
||||
stats.totalSuccessful = stats.sentSuccessful + stats.recvSuccessful;
|
||||
stats.totalFailed = stats.sentFailed + stats.recvFailed;
|
||||
stats.totalRejected = stats.sentRejected + stats.recvRejected;
|
||||
stats.totalBids = stats.totalSuccessful + stats.totalFailed + stats.totalRejected;
|
||||
|
||||
stats.successRate = stats.totalBids > 0
|
||||
? ((stats.totalSuccessful / stats.totalBids) * 100).toFixed(1)
|
||||
: '0.0';
|
||||
|
||||
return stats;
|
||||
};
|
||||
|
||||
const createIdentityTooltip = (identity) => {
|
||||
if (!identity) return '';
|
||||
|
||||
const stats = processIdentityStats(identity);
|
||||
if (!stats) return '';
|
||||
|
||||
const getSuccessRateColor = (rate) => {
|
||||
const numRate = parseFloat(rate);
|
||||
if (numRate >= 80) return 'text-green-600';
|
||||
if (numRate >= 60) return 'text-yellow-600';
|
||||
return 'text-red-600';
|
||||
};
|
||||
|
||||
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>
|
||||
` : ''}
|
||||
|
||||
<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="text-white text-xs tracking-wide font-semibold mb-2">Swap History:</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="text-center p-2 bg-gray-500 rounded-md">
|
||||
<div class="text-lg font-bold ${getSuccessRateColor(stats.successRate)}">
|
||||
${stats.successRate}%
|
||||
</div>
|
||||
<div class="text-xs text-white">Success Rate</div>
|
||||
</div>
|
||||
<div class="text-center p-2 bg-gray-500 rounded-md">
|
||||
<div class="text-lg font-bold text-blue-500">${stats.totalBids}</div>
|
||||
<div class="text-xs text-white">Total Trades</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-2 mt-2 text-center text-xs">
|
||||
<div>
|
||||
<div class="text-green-600 font-semibold">
|
||||
${stats.totalSuccessful}
|
||||
</div>
|
||||
<div class="text-white">Successful</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-yellow-600 font-semibold">
|
||||
${stats.totalRejected}
|
||||
</div>
|
||||
<div class="text-white">Rejected</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-red-600 font-semibold">
|
||||
${stats.totalFailed}
|
||||
</div>
|
||||
<div class="text-white">Failed</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
|
||||
// WebSocket Manager
|
||||
const WebSocketManager = {
|
||||
ws: null,
|
||||
processingQueue: false,
|
||||
reconnectTimeout: null,
|
||||
maxReconnectAttempts: 5,
|
||||
reconnectAttempts: 0,
|
||||
reconnectDelay: 5000,
|
||||
|
||||
initialize() {
|
||||
this.connect();
|
||||
this.startHealthCheck();
|
||||
},
|
||||
|
||||
connect() {
|
||||
if (this.ws?.readyState === WebSocket.OPEN) return;
|
||||
|
||||
try {
|
||||
const wsPort = window.ws_port || '11700';
|
||||
this.ws = new WebSocket(`ws://${window.location.hostname}:${wsPort}`);
|
||||
this.setupEventHandlers();
|
||||
} catch (error) {
|
||||
console.error('WebSocket connection error:', error);
|
||||
this.handleReconnect();
|
||||
}
|
||||
},
|
||||
|
||||
setupEventHandlers() {
|
||||
this.ws.onopen = () => {
|
||||
state.wsConnected = true;
|
||||
this.reconnectAttempts = 0;
|
||||
updateConnectionStatus('connected');
|
||||
console.log('🟢 WebSocket connection established');
|
||||
updateBidsTable({ resetPage: true, refreshData: true });
|
||||
};
|
||||
|
||||
this.ws.onmessage = () => {
|
||||
if (!this.processingQueue) {
|
||||
this.processingQueue = true;
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
if (!state.isRefreshing) {
|
||||
await updateBidsTable({ resetPage: false, refreshData: true });
|
||||
}
|
||||
} finally {
|
||||
this.processingQueue = false;
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onclose = () => {
|
||||
state.wsConnected = false;
|
||||
updateConnectionStatus('disconnected');
|
||||
this.handleReconnect();
|
||||
};
|
||||
|
||||
this.ws.onerror = () => {
|
||||
updateConnectionStatus('error');
|
||||
};
|
||||
},
|
||||
|
||||
startHealthCheck() {
|
||||
setInterval(() => {
|
||||
if (this.ws?.readyState !== WebSocket.OPEN) {
|
||||
this.handleReconnect();
|
||||
}
|
||||
}, 30000);
|
||||
},
|
||||
|
||||
handleReconnect() {
|
||||
if (this.reconnectTimeout) {
|
||||
clearTimeout(this.reconnectTimeout);
|
||||
}
|
||||
|
||||
this.reconnectAttempts++;
|
||||
if (this.reconnectAttempts <= this.maxReconnectAttempts) {
|
||||
const delay = this.reconnectDelay * Math.pow(1.5, this.reconnectAttempts - 1);
|
||||
this.reconnectTimeout = setTimeout(() => this.connect(), delay);
|
||||
} else {
|
||||
updateConnectionStatus('error');
|
||||
setTimeout(() => {
|
||||
this.reconnectAttempts = 0;
|
||||
this.connect();
|
||||
}, 60000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// UI
|
||||
const updateConnectionStatus = (status) => {
|
||||
const { statusDot, statusText } = elements;
|
||||
if (!statusDot || !statusText) return;
|
||||
|
||||
const statusConfig = {
|
||||
connected: {
|
||||
dotClass: 'w-2.5 h-2.5 rounded-full bg-green-500 mr-2',
|
||||
textClass: 'text-sm text-green-500',
|
||||
message: 'Connected'
|
||||
},
|
||||
disconnected: {
|
||||
dotClass: 'w-2.5 h-2.5 rounded-full bg-red-500 mr-2',
|
||||
textClass: 'text-sm text-red-500',
|
||||
message: 'Disconnected - Reconnecting...'
|
||||
},
|
||||
error: {
|
||||
dotClass: 'w-2.5 h-2.5 rounded-full bg-yellow-500 mr-2',
|
||||
textClass: 'text-sm text-yellow-500',
|
||||
message: 'Connection Error'
|
||||
},
|
||||
default: {
|
||||
dotClass: 'w-2.5 h-2.5 rounded-full bg-gray-500 mr-2',
|
||||
textClass: 'text-sm text-gray-500',
|
||||
message: 'Connecting...'
|
||||
}
|
||||
};
|
||||
|
||||
const config = statusConfig[status] || statusConfig.default;
|
||||
statusDot.className = config.dotClass;
|
||||
statusText.className = config.textClass;
|
||||
statusText.textContent = config.message;
|
||||
};
|
||||
|
||||
const updateLoadingState = (isLoading) => {
|
||||
state.isLoading = isLoading;
|
||||
if (elements.refreshBidsButton) {
|
||||
elements.refreshBidsButton.disabled = isLoading;
|
||||
elements.refreshBidsButton.classList.toggle('opacity-75', isLoading);
|
||||
elements.refreshBidsButton.classList.toggle('cursor-wait', isLoading);
|
||||
|
||||
const refreshIcon = elements.refreshBidsButton.querySelector('svg');
|
||||
const refreshText = elements.refreshBidsButton.querySelector('#refreshText');
|
||||
|
||||
if (refreshIcon) {
|
||||
// Add CSS transition for smoother animation
|
||||
refreshIcon.style.transition = 'transform 0.3s ease';
|
||||
refreshIcon.classList.toggle('animate-spin', isLoading);
|
||||
}
|
||||
|
||||
if (refreshText) {
|
||||
refreshText.textContent = isLoading ? 'Refreshing...' : 'Refresh';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const createBidTableRow = async (bid) => {
|
||||
if (!bid || !bid.bid_id) {
|
||||
console.error('Invalid bid data:', bid);
|
||||
return '';
|
||||
}
|
||||
|
||||
const identity = await IdentityManager.getIdentityData(bid.addr_from);
|
||||
const fromAmount = parseFloat(bid.amount_from) || 0;
|
||||
const toAmount = parseFloat(bid.amount_to) || 0;
|
||||
const rate = toAmount > 0 ? toAmount / fromAmount : 0;
|
||||
const inverseRate = fromAmount > 0 ? fromAmount / toAmount : 0;
|
||||
|
||||
const fromSymbol = COIN_NAME_TO_SYMBOL[bid.coin_from] || bid.coin_from;
|
||||
const toSymbol = COIN_NAME_TO_SYMBOL[bid.coin_to] || bid.coin_to;
|
||||
|
||||
const timeColor = getTimeStrokeColor(bid.expire_at);
|
||||
const uniqueId = `${bid.bid_id}_${bid.created_at}`;
|
||||
|
||||
return `
|
||||
<tr class="relative opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600" data-bid-id="${bid.bid_id}">
|
||||
<td class="relative w-0 p-0 m-0">
|
||||
<div class="absolute top-0 bottom-0 left-0 w-1"></div>
|
||||
</td>
|
||||
|
||||
<!-- Time Column -->
|
||||
<td class="py-3 pl-1 pr-2 text-xs whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<div class="relative" data-tooltip-target="tooltip-time-${uniqueId}">
|
||||
<svg class="w-5 h-5 rounded-full mr-4 cursor-pointer" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="${timeColor}" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points="12,6 12,12 18,12"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex flex-col hidden xl:block">
|
||||
<div class="text-xs whitespace-nowrap">
|
||||
<span class="bold">Posted:</span> ${formatTimeAgo(bid.created_at)}
|
||||
</div>
|
||||
<div class="text-xs whitespace-nowrap">
|
||||
<span class="bold">Expires in:</span> ${formatTime(bid.expire_at)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- Details Column -->
|
||||
<td class="py-8 px-4 text-xs text-left hidden xl:block">
|
||||
<div class="flex flex-col gap-2 relative">
|
||||
<div class="flex items-center">
|
||||
<a href="/identity/${bid.addr_from}" data-tooltip-target="tooltip-identity-${uniqueId}" class="flex items-center">
|
||||
<svg class="w-4 h-4 mr-2 text-gray-400 dark:text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
<span class="monospace ${identity?.label ? 'dark:text-white' : 'dark:text-white'}">
|
||||
${identity?.label || formatAddress(bid.addr_from)}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="monospace text-xs text-gray-500 dark:text-gray-300">
|
||||
<span class="font-semibold">Offer ID:</span>
|
||||
<a href="/offer/${bid.offer_id}" data-tooltip-target="tooltip-offer-${uniqueId}" class="hover:underline">
|
||||
${formatAddress(bid.offer_id)}
|
||||
</a>
|
||||
</div>
|
||||
<div class="monospace text-xs text-gray-500 dark:text-gray-300">
|
||||
<span class="font-semibold">Bid ID:</span>
|
||||
<a href="/bid/${bid.bid_id}" data-tooltip-target="tooltip-bid-${uniqueId}" class="hover:underline">
|
||||
${formatAddress(bid.bid_id)}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- You Send Column -->
|
||||
<td class="py-0">
|
||||
<div class="py-3 px-4 text-left">
|
||||
<div class="items-center monospace">
|
||||
<div class="pr-2">
|
||||
<div class="text-sm font-semibold">${fromAmount.toFixed(8)}</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">${bid.coin_from}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- Swap Column -->
|
||||
<td class="py-0">
|
||||
<div class="py-3 px-4 text-center">
|
||||
<div class="flex items-center justify-center">
|
||||
<span class="inline-flex mr-3 align-middle items-center justify-center w-18 h-20 rounded">
|
||||
<img class="h-12"
|
||||
src="/static/images/coins/${bid.coin_from.replace(' ', '-')}.png"
|
||||
alt="${bid.coin_from}"
|
||||
onerror="this.src='/static/images/coins/default.png'">
|
||||
</span>
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"></path>
|
||||
</svg>
|
||||
<span class="inline-flex ml-3 align-middle items-center justify-center w-18 h-20 rounded">
|
||||
<img class="h-12"
|
||||
src="/static/images/coins/${bid.coin_to.replace(' ', '-')}.png"
|
||||
alt="${bid.coin_to}"
|
||||
onerror="this.src='/static/images/coins/default.png'">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- You Get Column -->
|
||||
<td class="py-0">
|
||||
<div class="py-3 px-4 text-right">
|
||||
<div class="items-center monospace">
|
||||
<div class="text-sm font-semibold">${toAmount.toFixed(8)}</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">${bid.coin_to}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- Rate Column -->
|
||||
<td class="py-3 px-4 text-right semibold monospace item-center text-xs">
|
||||
<div class="relative">
|
||||
<div class="flex flex-col items-end">
|
||||
<span class="bold text-gray-700 dark:text-white">
|
||||
${rate.toFixed(8)} ${toSymbol}/${fromSymbol}
|
||||
</span>
|
||||
<span class="semibold text-gray-400 dark:text-gray-300">
|
||||
${inverseRate.toFixed(8)} ${fromSymbol}/${toSymbol}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- Actions Column -->
|
||||
<td class="py-3 px-4 text-center">
|
||||
<a href="/bid/${bid.bid_id}/accept"
|
||||
class="inline-block w-20 py-1 px-2 font-medium text-center text-sm rounded-md bg-blue-500 text-white border border-blue-500 hover:bg-blue-600 transition duration-200">
|
||||
Accept
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Tooltips -->
|
||||
<div id="tooltip-time-${uniqueId}" role="tooltip" class="inline-block absolute z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-600">
|
||||
<div class="active-revoked-expired">
|
||||
<span class="bold">
|
||||
<div class="text-xs"><span class="bold">Posted:</span> ${formatTimeAgo(bid.created_at)}</div>
|
||||
<div class="text-xs"><span class="bold">Expires in:</span> ${formatTime(bid.expire_at)}</div>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-5 text-xs">
|
||||
<p class="font-bold mb-3">Time Indicator Colors:</p>
|
||||
<p class="flex items-center">
|
||||
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#10B981" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points="12,6 12,12 18,12" stroke="#10B981"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
Green: More than 30 minutes left
|
||||
</p>
|
||||
<p class="flex items-center mt-3">
|
||||
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#3B82F6" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points="12,6 12,12 18,12" stroke="#3B82F6"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
Blue: Between 5 and 30 minutes left
|
||||
</p>
|
||||
<p class="flex items-center mt-3 mb-3">
|
||||
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#9CA3AF" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points="12,6 12,12 18,12" stroke="#9CA3AF"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
Grey: Less than 5 minutes left or expired
|
||||
</p>
|
||||
</div>
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
${createIdentityTooltip(identity)}
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
|
||||
<div id="tooltip-offer-${uniqueId}" role="tooltip" class="inline-block absolute z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-600">
|
||||
<div class="space-y-1">
|
||||
<div class="text-white text-xs tracking-wide font-semibold">Offer ID:</div>
|
||||
<div class="monospace text-xs break-all">
|
||||
${bid.offer_id}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
|
||||
<div id="tooltip-bid-${uniqueId}" role="tooltip" class="inline-block absolute z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-600">
|
||||
<div class="space-y-1">
|
||||
<div class="text-white text-xs tracking-wide font-semibold">Bid ID:</div>
|
||||
<div class="monospace text-xs break-all">
|
||||
${bid.bid_id}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
|
||||
const getDisplayText = (identity, address) => {
|
||||
if (identity?.label) {
|
||||
return identity.label;
|
||||
}
|
||||
return formatAddress(address);
|
||||
};
|
||||
|
||||
const createDetailsColumn = (bid, identity, uniqueId) => `
|
||||
<td class="py-8 px-4 text-xs text-left hidden xl:block">
|
||||
<div class="flex flex-col gap-2 relative">
|
||||
<div class="flex items-center">
|
||||
<a href="/identity/${bid.addr_from}" data-tooltip-target="tooltip-identity-${uniqueId}" class="flex items-center">
|
||||
<svg class="w-4 h-4 mr-2 text-gray-400 dark:text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
<span class="monospace ${identity?.label ? 'dark:text-white' : 'dark:text-white'}">
|
||||
${getDisplayText(identity, bid.addr_from)}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="monospace text-xs text-gray-500 dark:text-gray-300">
|
||||
<span class="font-semibold">Offer ID:</span>
|
||||
<a href="/offer/${bid.offer_id}" data-tooltip-target="tooltip-offer-${uniqueId}" class="hover:underline">
|
||||
${formatAddress(bid.offer_id)}
|
||||
</a>
|
||||
</div>
|
||||
<div class="monospace text-xs text-gray-500 dark:text-gray-300">
|
||||
<span class="font-semibold">Bid ID:</span>
|
||||
<a href="/bid/${bid.bid_id}" data-tooltip-target="tooltip-bid-${uniqueId}" class="hover:underline">
|
||||
${formatAddress(bid.bid_id)}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
`;
|
||||
|
||||
async function updateBidsTable(options = {}) {
|
||||
const { resetPage = false, refreshData = true } = options;
|
||||
|
||||
if (state.refreshPromise) {
|
||||
await state.refreshPromise;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
updateLoadingState(true);
|
||||
|
||||
if (refreshData) {
|
||||
state.refreshPromise = (async () => {
|
||||
try {
|
||||
const response = await fetch('/json/bids', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
sort_by: "created_at",
|
||||
sort_dir: "desc",
|
||||
with_available_or_active: true
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const allBids = await response.json();
|
||||
if (!Array.isArray(allBids)) {
|
||||
throw new Error('Invalid response format');
|
||||
}
|
||||
|
||||
state.jsonData = allBids.filter(bid => bid.bid_state === "Received");
|
||||
state.originalJsonData = [...state.jsonData];
|
||||
} finally {
|
||||
state.refreshPromise = null;
|
||||
}
|
||||
})();
|
||||
|
||||
await state.refreshPromise;
|
||||
}
|
||||
|
||||
if (elements.availableBidsCount) {
|
||||
elements.availableBidsCount.textContent = state.jsonData.length;
|
||||
}
|
||||
|
||||
const totalPages = Math.ceil(state.jsonData.length / PAGE_SIZE);
|
||||
|
||||
if (resetPage && state.jsonData.length > 0) {
|
||||
state.currentPage = 1;
|
||||
}
|
||||
|
||||
state.currentPage = Math.min(Math.max(1, state.currentPage), Math.max(1, totalPages));
|
||||
|
||||
const startIndex = (state.currentPage - 1) * PAGE_SIZE;
|
||||
const endIndex = startIndex + PAGE_SIZE;
|
||||
const currentPageBids = state.jsonData.slice(startIndex, endIndex);
|
||||
|
||||
if (elements.bidsBody) {
|
||||
if (currentPageBids.length > 0) {
|
||||
const rowPromises = currentPageBids.map(bid => createBidTableRow(bid));
|
||||
const rows = await Promise.all(rowPromises);
|
||||
elements.bidsBody.innerHTML = rows.join('');
|
||||
|
||||
if (window.TooltipManager) {
|
||||
window.TooltipManager.cleanup();
|
||||
const tooltipTriggers = document.querySelectorAll('[data-tooltip-target]');
|
||||
tooltipTriggers.forEach(trigger => {
|
||||
const targetId = trigger.getAttribute('data-tooltip-target');
|
||||
const tooltipContent = document.getElementById(targetId);
|
||||
if (tooltipContent) {
|
||||
window.TooltipManager.create(trigger, tooltipContent.innerHTML, {
|
||||
placement: trigger.getAttribute('data-tooltip-placement') || 'top'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
elements.bidsBody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="8" class="text-center py-4 text-gray-500 dark:text-white">
|
||||
No available bids requests found
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
if (elements.paginationControls) {
|
||||
elements.paginationControls.style.display = totalPages > 1 ? 'flex' : 'none';
|
||||
}
|
||||
|
||||
if (elements.currentPageSpan) {
|
||||
elements.currentPageSpan.textContent = state.currentPage;
|
||||
}
|
||||
|
||||
if (elements.prevPageButton) {
|
||||
elements.prevPageButton.style.display = state.currentPage > 1 ? 'inline-flex' : 'none';
|
||||
}
|
||||
|
||||
if (elements.nextPageButton) {
|
||||
elements.nextPageButton.style.display = state.currentPage < totalPages ? 'inline-flex' : 'none';
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error updating bids table:', error);
|
||||
if (elements.bidsBody) {
|
||||
elements.bidsBody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="8" class="text-center py-4 text-red-500">
|
||||
Error loading bids. Please try again later.
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
} finally {
|
||||
updateLoadingState(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Event
|
||||
const setupEventListeners = () => {
|
||||
if (elements.refreshBidsButton) {
|
||||
elements.refreshBidsButton.addEventListener('click', async () => {
|
||||
if (state.isRefreshing) return;
|
||||
|
||||
updateLoadingState(true);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
try {
|
||||
await updateBidsTable({ resetPage: true, refreshData: true });
|
||||
} finally {
|
||||
updateLoadingState(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (elements.prevPageButton) {
|
||||
elements.prevPageButton.addEventListener('click', async () => {
|
||||
if (state.isLoading) return;
|
||||
if (state.currentPage > 1) {
|
||||
state.currentPage--;
|
||||
await updateBidsTable({ resetPage: false, refreshData: false });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (elements.nextPageButton) {
|
||||
elements.nextPageButton.addEventListener('click', async () => {
|
||||
if (state.isLoading) return;
|
||||
const totalPages = Math.ceil(state.jsonData.length / PAGE_SIZE);
|
||||
if (state.currentPage < totalPages) {
|
||||
state.currentPage++;
|
||||
await updateBidsTable({ resetPage: false, refreshData: false });
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Init
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
WebSocketManager.initialize();
|
||||
setupEventListeners();
|
||||
});
|
1426
basicswap/static/js/bids_sentreceived.js
Normal file
1426
basicswap/static/js/bids_sentreceived.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,412 +1,348 @@
|
|||
{% include 'header.html' %}
|
||||
{% from 'style.html' import breadcrumb_line_svg, page_back_svg, page_forwards_svg, filter_clear_svg, filter_apply_svg, circular_arrows_svg, input_arrow_down_svg %}
|
||||
{% from 'style.html' import breadcrumb_line_svg, page_back_svg, page_forwards_svg, filter_clear_svg, filter_apply_svg, circular_arrows_svg, input_arrow_down_svg, arrow_right_svg %}
|
||||
|
||||
<div class="container mx-auto">
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/"><p>Home</p></a></li>
|
||||
<li>{{ breadcrumb_line_svg | safe }}</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="#">Sent Bids / Received Bids</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.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="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Sent Bids / Received Bids</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">View, and manage bids</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% include 'inc_messages.html' %}
|
||||
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 mt-5 h-full overflow-hidden">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<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">
|
||||
<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">
|
||||
Sent Bids ({{ sent_bids_count }})
|
||||
</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="received-tab" data-tabs-target="#received" type="button" role="tab" aria-controls="received" aria-selected="false">
|
||||
Received Bids ({{ received_bids_count }})
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full mx-auto pt-2">
|
||||
<form method="post">
|
||||
<div class="flex items-center justify-center pb-4 dark:text-white">
|
||||
<div class="rounded-b-md">
|
||||
<div class="w-full md:w-0/12">
|
||||
<div class="flex flex-wrap justify-center -m-1.5">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
{{ input_arrow_down_svg | safe }}
|
||||
<select name="sort_by" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
||||
<option value="created_at" {% if filters.sort_by=='created_at' %} selected{% endif %}>Time At</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
{{ input_arrow_down_svg | safe }}
|
||||
<select name="sort_dir" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
||||
<option value="asc" {% if filters.sort_dir=='asc' %} selected{% endif %}>Ascending</option>
|
||||
<option value="desc" {% if filters.sort_dir=='desc' %} selected{% endif %}>Descending</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<p class="text-sm font-heading bold">State:</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
{{ input_arrow_down_svg | safe }}
|
||||
<select name="state" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
||||
<option value="-1" {% if filters.bid_state_ind==-1 %} selected{% endif %}>Any</option>
|
||||
{% for s in data.bid_states %}
|
||||
<option value="{{ s[0] }}" {% if filters.bid_state_ind==s[0] %} selected{% endif %}>{{ s[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<p class="text-sm font-heading bold">Include Expired:</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
{{ input_arrow_down_svg | safe }}
|
||||
<select name="with_expired" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
||||
<option value="true" {% if filters.with_expired==true %} selected{% endif %}>Include</option>
|
||||
<option value="false" {% if filters.with_expired==false %} selected{% endif %}>Exclude</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<button type="submit" name='clearfilters' value="Clear Filters" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm hover:text-white dark:text-white dark:bg-gray-500 bg-coolGray-200 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-coolGray-200 dark:border-gray-400 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Clear Filters</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<button type="submit" name='applyfilters' value="Apply Filters" class="flex flex-wrap justify-center w-full 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">
|
||||
{{ filter_apply_svg | safe }}
|
||||
<span>Apply Filters</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="bidstab">
|
||||
<div class="rounded-lg" id="sent" role="tabpanel" aria-labelledby="sent-tab">
|
||||
<div id="sent-content">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Date/Time at</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid ID</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Offer ID</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid From</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid Status</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">ITX Status</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">PTX Status</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Details</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for b in sent_bids %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<th scope="row" class="flex items-center py-7 px-46 text-gray-900 whitespace-nowrap">
|
||||
<svg class="w-5 h-5 rounded-full ml-5" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#6b7280" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points=" 12,6 12,12 18,12 " stroke="#6b7280"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
<div class="pl-3">
|
||||
<div class="font-semibold text-xs dark:text-white">{{ b[0] }}</div>
|
||||
</div>
|
||||
</th>
|
||||
<td class="py-3 px-6 text-xs monospace"><a href=/bid/{{ b[1] }}>{{ b[1]|truncate(20, True) }}</a></td>
|
||||
<td class="py-3 px-6 text-xs monospace"><a href=/offer/{{ b[2] }}>{{ b[2]|truncate(20, True) }}</a></td>
|
||||
<td class="py-3 px-6 text-xs monospace"><a href=/identity/{{ b[6] }}>{{ b[6] }}</a></td>
|
||||
<td class="py-3 px-6 text-xs">{{ b[3] }}</td>
|
||||
<td class="py-3 px-6 text-xs">{{ b[4] }}</td>
|
||||
<td class="py-3 px-6 text-xs">{{ b[5] }}</td>
|
||||
<td class="py-3 px-6 text-xs">
|
||||
<a class="inline-block w-20 py-1 px-2 font-medium text-center text-sm rounded-md bg-blue-500 text-white border border-blue-500 hover:bg-blue-600 transition duration-200" href="/bid/{{ b[1] }}">Details</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="rounded-b-md">
|
||||
<div class="w-full md:w-0/12">
|
||||
<div class="flex flex-wrap justify-end pt-6 pr-6 border-t border-gray-100 dark:border-gray-400">
|
||||
{% if filters.page_no > 1 %}
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<input type="hidden" name="filter_key" value="{{ filter_key }}">
|
||||
<input type="hidden" name="page_no" value="{{ filters.page_no - 1 }}">
|
||||
<button type="submit" name='pageback' value="Previous" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-blue-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
||||
{{ page_back_svg | safe }}
|
||||
<span>Previous</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="flex items-center">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<p class="text-sm font-heading dark:text-white">Page: {{ filters.page_no }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% if sent_bids_count >= filters.limit %}
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<input type="hidden" name="filter_key" value="{{ filter_key }}">
|
||||
<input type="hidden" name="page_no" value="{{ filters.page_no + 1 }}">
|
||||
<button type="submit" name='pageforwards' value="Next" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-blue-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
||||
<span>Next</span>
|
||||
{{ page_forwards_svg | safe }}
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hidden rounded-lg" id="received" role="tabpanel" aria-labelledby="received-tab">
|
||||
<div id="received-content">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Date/Time at</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid ID</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Offer ID</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid From</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid Status</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">ITX Status</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">PTX Status</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Details</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for b in received_bids %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<th scope="row" class="flex items-center py-7 px-46 text-gray-900 whitespace-nowrap">
|
||||
<svg class="w-5 h-5 rounded-full ml-5" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#6b7280" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points=" 12,6 12,12 18,12 " stroke="#6b7280"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
<div class="pl-3">
|
||||
<div class="font-semibold text-xs dark:text-white">{{ b[0] }}</div>
|
||||
</div>
|
||||
</th>
|
||||
<td class="py-3 px-6 text-xs monospace"><a href=/bid/{{ b[1] }}>{{ b[1]|truncate(20, True) }}</a></td>
|
||||
<td class="py-3 px-6 text-xs monospace"><a href=/offer/{{ b[2] }}>{{ b[2]|truncate(20, True) }}</a></td>
|
||||
<td class="py-3 px-6 text-xs monospace"><a href=/identity/{{ b[6] }}>{{ b[6] }}</a></td>
|
||||
<td class="py-3 px-6 text-xs">{{ b[3] }}</td>
|
||||
<td class="py-3 px-6 text-xs">{{ b[4] }}</td>
|
||||
<td class="py-3 px-6 text-xs">{{ b[5] }}</td>
|
||||
<td class="py-3 px-6 text-xs">
|
||||
<a class="inline-block w-20 py-1 px-2 font-medium text-center text-sm rounded-md bg-blue-500 text-white border border-blue-500 hover:bg-blue-600 transition duration-200" href="/bid/{{ b[1] }}">Details</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="rounded-b-md">
|
||||
<div class="w-full md:w-0/12">
|
||||
<div class="flex flex-wrap justify-end pt-6 pr-6 border-t border-gray-100 dark:border-gray-400">
|
||||
{% if filters.page_no > 1 %}
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<input type="hidden" name="filter_key" value="{{ filter_key }}">
|
||||
<input type="hidden" name="page_no" value="{{ filters.page_no - 1 }}">
|
||||
<button type="submit" name='pageback' value="Previous" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-blue-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
||||
{{ page_back_svg | safe }}
|
||||
<span>Previous</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="flex items-center">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<p class="text-sm font-heading dark:text-white">Page: {{ filters.page_no }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% if received_bids_count >= filters.limit %}
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<input type="hidden" name="filter_key" value="{{ filter_key }}">
|
||||
<input type="hidden" name="page_no" value="{{ filters.page_no + 1 }}">
|
||||
<button type="submit" name='pageforwards' value="Next" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-blue-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
||||
<span>Next</span>
|
||||
{{ page_forwards_svg | safe }}
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
<input type="hidden" name="pageno" value="{{ filters.page_no }}">
|
||||
<input type="hidden" name="filter_key" value="page_bids">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<section class="py-3 px-4 mt-6">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="relative py-8 px-8 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.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="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>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">View, and manage bids.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% include 'inc_messages.html' %}
|
||||
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 mt-5 h-full overflow-hidden">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<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">
|
||||
<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">
|
||||
Sent Bids ({{ sent_bids_count }})
|
||||
</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="received-tab" data-tabs-target="#received" type="button" role="tab" aria-controls="received" aria-selected="false">
|
||||
Received Bids ({{ received_bids_count }})
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="px-6 py-0 h-full overflow-hidden">
|
||||
<div class="pb-6 mt-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap justify-center -m-1.5">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<input type="text" id="searchInput" name="search" autocomplete="off" placeholder="Search bid ID, offer ID, address or label..." class="hover:border-blue-500 dark:hover:bg-gray-50 text-gray-900 pl-4 pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none block w-96 p-2.5 focus:ring-blue-500 focus:border-blue-500 focus:ring-0 dark:focus:bg-gray-500 dark:focus:text-white">
|
||||
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
|
||||
<svg class="w-5 h-5 text-gray-500 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-1.5 md:w-auto hover-container">
|
||||
<div class="flex">
|
||||
<button id="coin_from_button" class="bg-gray-50 text-gray-900 appearance-none w-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-l-lg flex items-center" disabled></button>
|
||||
<div class="relative">
|
||||
{{ input_arrow_down_svg | safe }}
|
||||
<select name="coin_from" id="coin_from" class="bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-r-lg outline-none block w-full p-2.5 focus:ring-0 border-l-0">
|
||||
<option value="any" {% if filters.coin_from==-1 %} selected{% endif %}>You Send</option>
|
||||
{% for c in coins_from %}
|
||||
<option class="text-sm" value="{{ c[0] }}" {% if filters.coin_from==c[0] %} selected{% endif %} data-image="/static/images/coins/{{ c[1]|replace(" ", "-") }}.png">{{ c[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<p class="text-sm font-heading text-gray-500 dark:text-white">{{ arrow_right_svg | safe }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button id="coin_to_button" class="bg-gray-50 text-gray-900 appearance-none w-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-l-lg flex items-center" disabled></button>
|
||||
<div class="relative">
|
||||
{{ input_arrow_down_svg | safe }}
|
||||
<select name="coin_to" id="coin_to" class="bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-r-lg outline-none block w-full p-2.5 focus:ring-0 border-l-0">
|
||||
<option value="any" {% if filters.coin_to==-1 %} selected{% endif %}>You Receive</option>
|
||||
{% for c in coins %}
|
||||
<option class="text-sm" value="{{ c[0] }}" {% if filters.coin_to==c[0] %} selected{% endif %} data-image="/static/images/coins/{{ c[1]|replace(" ", "-") }}.png">{{ c[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
{{ input_arrow_down_svg | safe }}
|
||||
<select name="state" id="state" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
||||
<option value="-1" selected="">Any State</option>
|
||||
<optgroup label="Active States">
|
||||
<option value="1">Sent</option>
|
||||
<option value="2">Receiving</option>
|
||||
<option value="3">Received</option>
|
||||
<option value="4">Receiving accept</option>
|
||||
<option value="5">Accepted</option>
|
||||
<option value="6">Initiated</option>
|
||||
<option value="7">Participating</option>
|
||||
</optgroup>
|
||||
<optgroup label="Completed States">
|
||||
<option value="8">Completed</option>
|
||||
<option value="15">Scriptless tx redeemed</option>
|
||||
<option value="13">Script tx redeemed</option>
|
||||
</optgroup>
|
||||
<optgroup label="Failed States">
|
||||
<option value="17">Failed, refunded</option>
|
||||
<option value="18">Failed, swiped</option>
|
||||
<option value="19">Failed</option>
|
||||
<option value="22">Abandoned</option>
|
||||
<option value="23">Error</option>
|
||||
<option value="31">Expired</option>
|
||||
</optgroup>
|
||||
<optgroup label="Other States">
|
||||
<option value="9">Script coin locked</option>
|
||||
<option value="10">Script coin spend tx valid</option>
|
||||
<option value="11">Scriptless coin locked</option>
|
||||
<option value="12">Script coin lock released</option>
|
||||
<option value="14">Script pre-refund tx in chain</option>
|
||||
<option value="16">Scriptless tx recovered</option>
|
||||
<option value="20">Delaying</option>
|
||||
<option value="21">Timed-out</option>
|
||||
<option value="24">Stalled (debug)</option>
|
||||
<option value="25">Rejected</option>
|
||||
<option value="26">Unknown bid state</option>
|
||||
<option value="27">Exchanged script lock tx sigs msg</option>
|
||||
<option value="28">Exchanged script lock spend tx msg</option>
|
||||
<option value="29">Request sent</option>
|
||||
<option value="30">Request accepted</option>
|
||||
<option value="32">Auto accept delay</option>
|
||||
<option value="33">Auto accept failed</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- todo
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
{{ input_arrow_down_svg | safe }}
|
||||
<select name="with_expired" id="with_expired" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
||||
<option value="true">Include Expired</option>
|
||||
<option value="false">Exclude Expired</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>-->
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<button type="button" id="clearFilters" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm hover:text-white dark:text-white dark:bg-gray-500 bg-coolGray-200 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-coolGray-200 dark:border-gray-400 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Clear Filters</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div id="bidstab">
|
||||
<div class="rounded-lg px-6" id="sent" role="tabpanel" aria-labelledby="sent-tab">
|
||||
<div id="sent-content">
|
||||
<div class="lg:container mx-auto lg:px-0 px-6">
|
||||
<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 min-w-max">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Date/Time</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Details</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">You Send</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">You Receive</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Status</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Actions</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<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-sent" class="w-2.5 h-2.5 rounded-full bg-gray-500 mr-2"></span>
|
||||
<span id="status-text-sent" class="text-sm text-gray-500">Connecting...</span>
|
||||
</div>
|
||||
<p class="text-sm font-heading dark:text-gray-400">
|
||||
Sent Bids: <span id="sentBidsCount">0</span>
|
||||
</p>
|
||||
{% if debug_ui_mode == true %}
|
||||
<button id="refreshSentBids" 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="refreshSentText">Refresh</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="pagination-controls-sent" class="flex items-center space-x-2" style="display: none;">
|
||||
<button id="prevPageSent" 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="currentPageSent">1</span></p>
|
||||
<button id="nextPageSent" 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>
|
||||
|
||||
<!-- Received Bids Tab -->
|
||||
<div class="hidden rounded-lg px-6" id="received" role="tabpanel" aria-labelledby="received-tab">
|
||||
<div id="received-content">
|
||||
<div class="lg: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 min-w-max">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Date/Time</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Details</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">You Send</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">You Receive</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Status</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Actions</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<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-received" class="w-2.5 h-2.5 rounded-full bg-gray-500 mr-2"></span>
|
||||
<span id="status-text-received" class="text-sm text-gray-500">Connecting...</span>
|
||||
</div>
|
||||
<p class="text-sm font-heading dark:text-gray-400">
|
||||
Received Bids: <span id="receivedBidsCount">0</span>
|
||||
</p>
|
||||
{% if debug_ui_mode == true %}
|
||||
<button id="refreshReceivedBids" 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="refreshReceivedText">Refresh</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="pagination-controls-received" class="flex items-center space-x-2" style="display: none;">
|
||||
<button id="prevPageReceived" 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="currentPageReceived">1</span></p>
|
||||
<button id="nextPageReceived" 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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const tabs = document.querySelectorAll('#myTab button');
|
||||
const contents = document.querySelectorAll('[role="tabpanel"]');
|
||||
|
||||
function switchTab(targetId) {
|
||||
contents.forEach(content => {
|
||||
content.classList.add('hidden');
|
||||
});
|
||||
const targetContent = document.querySelector(targetId);
|
||||
if (targetContent) {
|
||||
targetContent.classList.remove('hidden');
|
||||
}
|
||||
tabs.forEach(tab => {
|
||||
const selected = tab.dataset.tabsTarget === targetId;
|
||||
tab.setAttribute('aria-selected', selected);
|
||||
if (selected) {
|
||||
tab.classList.add('bg-gray-100', 'dark:bg-gray-600', 'text-gray-900', 'dark:text-white');
|
||||
} else {
|
||||
tab.classList.remove('bg-gray-100', 'dark:bg-gray-600', 'text-gray-900', 'dark:text-white');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
switchTab(tab.dataset.tabsTarget);
|
||||
});
|
||||
});
|
||||
|
||||
switchTab('#sent');
|
||||
});
|
||||
</script>
|
||||
<script src="/static/js/bids_sentreceived.js"></script>
|
||||
|
||||
{% include 'footer.html' %}
|
||||
|
|
|
@ -1,200 +1,118 @@
|
|||
{% include 'header.html' %}
|
||||
{% from 'style.html' import breadcrumb_line_svg, page_back_svg, page_forwards_svg, filter_clear_svg, filter_apply_svg, input_arrow_down_svg %}
|
||||
|
||||
<div class="container mx-auto">
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/"><p>Home</p></a></li>
|
||||
<li>{{ breadcrumb_line_svg | safe }}</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="#">Bids Requests</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.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="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Bids Requests</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">Review and accept bids from other users</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% include 'inc_messages.html' %}
|
||||
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full mx-auto pt-2">
|
||||
<form method="post">
|
||||
<div class="flex items-center justify-center pb-4 dark:text-white">
|
||||
<div class="rounded-b-md">
|
||||
<div class="w-full md:w-0/12">
|
||||
<div class="flex flex-wrap justify-center -m-1.5">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
{{ input_arrow_down_svg | safe }}
|
||||
<select name="sort_by" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
||||
<option value="created_at" {% if filters.sort_by=='created_at' %} selected{% endif %}>Time At</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
{{ input_arrow_down_svg | safe }}
|
||||
<select name="sort_dir" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
||||
<option value="asc" {% if filters.sort_dir=='asc' %} selected{% endif %}>Ascending</option>
|
||||
<option value="desc" {% if filters.sort_dir=='desc' %} selected{% endif %}>Descending</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<button type="submit" name='clearfilters' value="Clear Filters" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm hover:text-white dark:text-white dark:bg-gray-500 bg-coolGray-200 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-coolGray-200 dark:border-gray-400 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Clear Filters</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<button type="submit" name='applyfilters' value="Apply Filters" class="flex flex-wrap justify-center w-full 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">
|
||||
{{ filter_apply_svg | safe }}
|
||||
<span>Apply Filters</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Date/Time at</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid ID</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Offer ID</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">From</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Status</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Actions</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for b in bids %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<th scope="row" class="flex items-center py-7 px-46 text-gray-900 whitespace-nowrap">
|
||||
<svg class="w-5 h-5 rounded-full ml-5" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#6b7280" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points=" 12,6 12,12 18,12 " stroke="#6b7280"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
<div class="pl-3">
|
||||
<div class="font-semibold text-xs dark:text-white">{{ b[0] }}</div>
|
||||
</div>
|
||||
</th>
|
||||
<td class="py-3 px-6 text-xs monospace"><a href=/bid/{{ b[1] }}>{{ b[1]|truncate(20, True) }}</a></td>
|
||||
<td class="py-3 px-6 text-xs monospace"><a href=/offer/{{ b[2] }}>{{ b[2]|truncate(20, True) }}</a></td>
|
||||
<td class="py-3 px-6 text-xs monospace"><a href=/identity/{{ b[6] }}>{{ b[6] }}</a></td>
|
||||
<td class="py-3 px-6 text-xs">{{ b[3] }}</td>
|
||||
<td class="py-3 px-6">
|
||||
<div class="flex space-x-2">
|
||||
<a class="inline-block w-20 py-1 px-2 font-medium text-center text-sm rounded-md bg-blue-500 text-white border border-blue-500 hover:bg-blue-600 transition duration-200" href="/bid/{{ b[1] }}">View</a>
|
||||
{% if b[3] == "Received" %}
|
||||
<form method="post" action="/bid/{{ b[1] }}" class="inline">
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
<button type="submit" name="accept_bid" class="inline-block w-20 py-1 px-2 font-medium text-center text-sm rounded-md bg-green-500 text-white border border-green-500 hover:bg-green-600 transition duration-200">Accept</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="rounded-b-md">
|
||||
<div class="w-full md:w-0/12">
|
||||
<div class="flex flex-wrap justify-end pt-6 pr-6 border-t border-gray-100 dark:border-gray-400">
|
||||
{% if filters.page_no > 1 %}
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<input type="hidden" name="filter_key" value="{{ filter_key }}">
|
||||
<input type="hidden" name="page_no" value="{{ filters.page_no - 1 }}">
|
||||
<button type="submit" name='pageback' value="Previous" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-blue-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
||||
{{ page_back_svg | safe }}
|
||||
<span>Previous</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="flex items-center">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<p class="text-sm font-heading dark:text-white">Page: {{ filters.page_no }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% if bids_count >= filters.limit %}
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<input type="hidden" name="filter_key" value="{{ filter_key }}">
|
||||
<input type="hidden" name="page_no" value="{{ filters.page_no + 1 }}">
|
||||
<button type="submit" name='pageforwards' value="Next" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-blue-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
||||
<span>Next</span>
|
||||
{{ page_forwards_svg | safe }}
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
<input type="hidden" name="pageno" value="{{ filters.page_no }}">
|
||||
<input type="hidden" name="filter_key" value="page_available_bids">
|
||||
|
||||
</form>
|
||||
<section class="py-3 px-4 mt-6">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="relative py-8 px-8 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.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="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-3 text-2xl font-bold text-white tracking-tighter">Bid Requests</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">Review and accept bids from other users.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% include 'inc_messages.html' %}
|
||||
|
||||
<section>
|
||||
<div class="mt-5 lg:container mx-auto lg:px-0 px-6">
|
||||
<div class="pt-0 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-0">
|
||||
<div class="w-auto mt-6 overflow-auto lg:overflow-hidden">
|
||||
<table class="w-full min-w-max">
|
||||
<thead class="uppercase">
|
||||
<tr>
|
||||
<th class="p-0" data-sortable="true" data-column-index="0">
|
||||
<div class="py-3 pl-4 justify-center rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-sm mr-1 text-gray-600 dark:text-gray-300 font-semibold"></span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 pl-4 justify-center bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-sm mr-1 text-gray-600 dark:text-gray-300 font-semibold">Time</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0 hidden xl:block">
|
||||
<div class="py-3 px-4 text-left 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="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-left">
|
||||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">You Send</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-center">
|
||||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Swap</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-right">
|
||||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">You Get</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-right">
|
||||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Rate</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 rounded-tr-xl">
|
||||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Actions</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="bids-body"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</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" class="w-2.5 h-2.5 rounded-full bg-gray-500 mr-2"></span>
|
||||
<span id="status-text" class="text-sm text-gray-500">Connecting...</span>
|
||||
</div>
|
||||
<p class="text-sm font-heading dark:text-gray-400 mr-4">Available Bids: <span id="availableBidsCount">0</span></p>
|
||||
{% if debug_ui_mode == true %}
|
||||
<button id="refreshBids" class="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="refreshText">Refresh</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
<div id="pagination-controls" class="flex items-center space-x-2" style="display: none;">
|
||||
<button id="prevPage" 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">
|
||||
<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="currentPage">1</span></p>
|
||||
<button id="nextPage" 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">
|
||||
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>
|
||||
</section>
|
||||
|
||||
<script src="/static/js/bids_available.js"></script>
|
||||
|
||||
{% include 'footer.html' %}
|
||||
|
|
|
@ -449,7 +449,7 @@
|
|||
<span id="bid-requests-counter" class="inline-flex justify-center items-center text-xs
|
||||
font-semibold ml-3 px-2.5 py-1 text-white {% if summary.num_available_bids > 0 %}
|
||||
bg-blue-500{% else %}bg-gray-400{% endif %} rounded-full">
|
||||
{{ summary.num_recv_active_bids }}
|
||||
{{ summary.num_available_bids }}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -24,43 +24,24 @@ function getWebSocketConfig() {
|
|||
}
|
||||
</script>
|
||||
|
||||
{% if sent_offers %}
|
||||
<div class="lg:container mx-auto">
|
||||
<section class="p-3 mt-2">
|
||||
<div class="flex items-center">
|
||||
<div class="w-full">
|
||||
<ul class="flex items-center gap-x-2 mb-1">
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">Home</a></li>
|
||||
<li>{{ breadcrumb_line_svg | safe }}</li>
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="{% if page_type == 'offers' %}/offers{% elif page_type == 'sentoffers' %}/sentoffers{% endif %}">
|
||||
{{ page_type }}
|
||||
</a>
|
||||
</li>
|
||||
<li>{{ breadcrumb_line_svg | safe }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if sent_offers %}
|
||||
<section class="py-3">
|
||||
{% else %}
|
||||
<section class="py-3 px-4 mt-3">
|
||||
{% endif %}
|
||||
<section class="py-3 px-4 mt-6">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="relative py-6 px-8 bg-coolGray-900 dark:bg-gray-500 rounded-md overflow-hidden">
|
||||
|
||||
<img class="absolute h-48 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="wave">
|
||||
<div class="relative z-20 flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<h2 class="mb-3 text-3xl font-bold text-white tracking-tighter">{{ page_type }}</h2>
|
||||
<div class="relative py-8 px-8 bg-coolGray-900 dark:bg-gray-500 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.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="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-3 text-2xl font-bold text-white tracking-tighter">{{ page_type }}</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">{{ page_type_description }}</p>
|
||||
</div>
|
||||
<div class="rounded-full{{ page_button }} w-full md:w-1/2 p-2 lg:container flex flex-wrap items-center justify-end mx-auto">
|
||||
<a id="refresh" href="/newoffer" class="rounded-full flex flex-wrap justify-center px-4 py-2 bg-blue-500 hover:bg-green-600 hover:border-green-600 font-medium text-sm text-white border border-blue-500 rounded-md focus:ring-0 focus:outline-none">{{ place_new_offer_svg | safe }}<span>Place new Offer</span></a>
|
||||
<div class="w-full md:w-1/2 p-3 flex justify-end items-center hidden">
|
||||
<a id="refresh" href="/newoffer"
|
||||
class="rounded-full flex items-center justify-center px-4 py-2 bg-blue-500 hover:bg-green-600 hover:border-green-600 font-medium text-sm text-white border border-blue-500 rounded-md focus:ring-0 focus:outline-none">
|
||||
{{ place_new_offer_svg | safe }}
|
||||
<span>Place new Offer</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -442,5 +423,5 @@ function getWebSocketConfig() {
|
|||
</section>
|
||||
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
<script src="/static/js/offerstable.js"></script>
|
||||
<script src="/static/js/offers.js"></script>
|
||||
{% include 'footer.html' %}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
<link type="text/css" media="all" href="/static/css/libs/tailwind.min.css" rel="stylesheet">
|
||||
<link type="text/css" media="all" href="/static/css/style.css" rel="stylesheet">
|
||||
<script src="/static/js/main.js"></script>
|
||||
<script src="/static/js/libs/flowbite.js"></script>
|
||||
<script>
|
||||
const isDarkMode =
|
||||
localStorage.getItem('color-theme') === 'dark' ||
|
||||
|
|
|
@ -9,12 +9,14 @@ from .util import (
|
|||
PAGE_LIMIT,
|
||||
describeBid,
|
||||
get_data_entry,
|
||||
have_data_entry,
|
||||
get_data_entry_or,
|
||||
have_data_entry,
|
||||
listAvailableCoins,
|
||||
listBidActions,
|
||||
listBidStates,
|
||||
listOldBidStates,
|
||||
set_pagination_filters,
|
||||
setCoinFilter,
|
||||
)
|
||||
from basicswap.util import (
|
||||
ensure,
|
||||
|
@ -149,9 +151,7 @@ def page_bid(self, url_split, post_string):
|
|||
)
|
||||
|
||||
|
||||
def page_bids(
|
||||
self, url_split, post_string, sent=False, available=False, received=False
|
||||
):
|
||||
def page_bids(self, url_split, post_string, sent=False, available=False, received=False):
|
||||
server = self.server
|
||||
swap_client = server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
|
@ -165,6 +165,8 @@ def page_bids(
|
|||
"limit": PAGE_LIMIT,
|
||||
"sort_by": "created_at",
|
||||
"sort_dir": "desc",
|
||||
"coin_from": -1,
|
||||
"coin_to": -1,
|
||||
}
|
||||
if available:
|
||||
filters["bid_state_ind"] = BidStates.BID_RECEIVED
|
||||
|
@ -176,6 +178,9 @@ def page_bids(
|
|||
if have_data_entry(form_data, "clearfilters"):
|
||||
swap_client.clearFilters(filter_key)
|
||||
else:
|
||||
filters["coin_from"] = setCoinFilter(form_data, "coin_from")
|
||||
filters["coin_to"] = setCoinFilter(form_data, "coin_to")
|
||||
|
||||
if have_data_entry(form_data, "sort_by"):
|
||||
sort_by = get_data_entry(form_data, "sort_by")
|
||||
ensure(sort_by in ["created_at"], "Invalid sort by")
|
||||
|
@ -208,6 +213,8 @@ def page_bids(
|
|||
"bid_states": listBidStates(),
|
||||
}
|
||||
|
||||
coins_from, coins_to = listAvailableCoins(swap_client, split_from=True)
|
||||
|
||||
if available:
|
||||
bids = swap_client.listBids(sent=False, filters=filters)
|
||||
template = server.env.get_template("bids_available.html")
|
||||
|
@ -221,6 +228,8 @@ def page_bids(
|
|||
"data": page_data,
|
||||
"summary": summary,
|
||||
"filter_key": filter_key,
|
||||
"coins_from": coins_from,
|
||||
"coins": coins_to,
|
||||
"bids": [
|
||||
(
|
||||
format_timestamp(b[0]),
|
||||
|
@ -249,6 +258,8 @@ def page_bids(
|
|||
"data": page_data,
|
||||
"summary": summary,
|
||||
"filter_key": filter_key,
|
||||
"coins_from": coins_from,
|
||||
"coins": coins_to,
|
||||
"sent_bids": [
|
||||
(
|
||||
format_timestamp(b[0]),
|
||||
|
|
|
@ -122,8 +122,30 @@ def set_pagination_filters(form_data, filters):
|
|||
filters["page_no"] = 1
|
||||
elif form_data and have_data_entry(form_data, "pageforwards"):
|
||||
filters["page_no"] = int(form_data[b"pageno"][0]) + 1
|
||||
if filters["page_no"] > 1:
|
||||
filters["offset"] = (filters["page_no"] - 1) * PAGE_LIMIT
|
||||
|
||||
no_limit = False
|
||||
if form_data:
|
||||
if "is_json" in form_data:
|
||||
no_limit = form_data.get("no_limit", False)
|
||||
else:
|
||||
no_limit = b"no_limit" in form_data
|
||||
|
||||
if no_limit:
|
||||
filters["offset"] = 0
|
||||
filters["limit"] = None
|
||||
else:
|
||||
if filters["page_no"] > 1:
|
||||
filters["offset"] = (filters["page_no"] - 1) * PAGE_LIMIT
|
||||
filters["limit"] = PAGE_LIMIT
|
||||
|
||||
|
||||
def get_data_with_pagination(data, filters):
|
||||
if filters.get("limit") is None:
|
||||
return data
|
||||
|
||||
offset = filters.get("offset", 0)
|
||||
limit = filters.get("limit", PAGE_LIMIT)
|
||||
return data[offset:offset + limit]
|
||||
|
||||
|
||||
def getTxIdHex(bid, tx_type, suffix):
|
||||
|
|
Loading…
Reference in a new issue