ui: Chart and dynamic table(s) fixes

This commit is contained in:
gerlofvanek 2024-10-11 22:03:14 +02:00
parent 39aad231cd
commit c6f3c684a8
3 changed files with 180 additions and 136 deletions

View file

@ -485,7 +485,8 @@ function fetchOffers(manualRefresh = false) {
created_at: Number(offer.created_at || 0), created_at: Number(offer.created_at || 0),
expire_at: Number(offer.expire_at || 0), expire_at: Number(offer.expire_at || 0),
is_own_offer: Boolean(offer.is_own_offer), is_own_offer: Boolean(offer.is_own_offer),
amount_negotiable: Boolean(offer.amount_negotiable) amount_negotiable: Boolean(offer.amount_negotiable),
unique_id: `${offer.offer_id}_${offer.created_at}_${offer.coin_from}_${offer.coin_to}`
})); }));
if (!isSentOffers) { if (!isSentOffers) {
@ -610,7 +611,7 @@ function filterAndSortData() {
const formData = new FormData(filterForm); const formData = new FormData(filterForm);
const filters = Object.fromEntries(formData); const filters = Object.fromEntries(formData);
console.log('Raw filters:', filters); console.log('Processed filters:', filters);
if (filters.coin_to !== 'any') { if (filters.coin_to !== 'any') {
filters.coin_to = coinIdToName[filters.coin_to] || filters.coin_to; filters.coin_to = coinIdToName[filters.coin_to] || filters.coin_to;
@ -623,41 +624,49 @@ function filterAndSortData() {
const currentTime = Math.floor(Date.now() / 1000); const currentTime = Math.floor(Date.now() / 1000);
let filteredData = originalJsonData.filter(offer => { const uniqueOffersMap = new Map();
originalJsonData.forEach(offer => {
const coinFrom = (offer.coin_from || '').toLowerCase(); const coinFrom = (offer.coin_from || '').toLowerCase();
const coinTo = (offer.coin_to || '').toLowerCase(); const coinTo = (offer.coin_to || '').toLowerCase();
const isExpired = offer.expire_at <= currentTime; const isExpired = offer.expire_at <= currentTime;
if (!isSentOffers && isExpired) { if (!isSentOffers && isExpired) {
return false; return;
} }
let passesFilter = true;
if (isSentOffers) { if (isSentOffers) {
if (filters.coin_to !== 'any' && coinFrom.toLowerCase() !== filters.coin_to.toLowerCase()) { if (filters.coin_to !== 'any' && coinFrom.toLowerCase() !== filters.coin_to.toLowerCase()) {
return false; passesFilter = false;
} }
if (filters.coin_from !== 'any' && coinTo.toLowerCase() !== filters.coin_from.toLowerCase()) { if (filters.coin_from !== 'any' && coinTo.toLowerCase() !== filters.coin_from.toLowerCase()) {
return false; passesFilter = false;
} }
} else { } else {
if (filters.coin_to !== 'any' && coinTo.toLowerCase() !== filters.coin_to.toLowerCase()) { if (filters.coin_to !== 'any' && coinTo.toLowerCase() !== filters.coin_to.toLowerCase()) {
return false; passesFilter = false;
} }
if (filters.coin_from !== 'any' && coinFrom.toLowerCase() !== filters.coin_from.toLowerCase()) { if (filters.coin_from !== 'any' && coinFrom.toLowerCase() !== filters.coin_from.toLowerCase()) {
return false; passesFilter = false;
} }
} }
if (isSentOffers && filters.active && filters.active !== 'any') { if (isSentOffers && filters.active && filters.active !== 'any') {
const offerState = isExpired ? 'expired' : 'active'; const offerState = isExpired ? 'expired' : 'active';
if (filters.active !== offerState) { if (filters.active !== offerState) {
return false; passesFilter = false;
} }
} }
return true; if (passesFilter) {
uniqueOffersMap.set(offer.unique_id, offer);
}
}); });
let filteredData = Array.from(uniqueOffersMap.values());
console.log('Filtered data length:', filteredData.length); console.log('Filtered data length:', filteredData.length);
const sortBy = filters.sort_by || 'created_at'; const sortBy = filters.sort_by || 'created_at';
@ -822,6 +831,7 @@ function prepareOfferData(offer, isSentOffers) {
}; };
} }
// to-do revoked
function getButtonProperties(isActuallyExpired, isSentOffers, isTreatedAsSentOffer, isRevoked) { function getButtonProperties(isActuallyExpired, isSentOffers, isTreatedAsSentOffer, isRevoked) {
if (isRevoked) { if (isRevoked) {
return { return {
@ -865,8 +875,7 @@ function updateProfitLoss(row, fromCoin, toCoin, fromAmount, toAmount) {
const colorClass = getProfitColorClass(profitLossPercentage); const colorClass = getProfitColorClass(profitLossPercentage);
profitLossElement.textContent = `${profitLossPercentage > 0 ? '+' : ''}${profitLossPercentage}%`; profitLossElement.textContent = `${profitLossPercentage > 0 ? '+' : ''}${profitLossPercentage}%`;
profitLossElement.className = `profit-loss text-lg font-bold ${colorClass}`; profitLossElement.className = `profit-loss text-lg font-bold ${colorClass}`;
// Update the tooltip content
const tooltipId = `percentage-tooltip-${row.getAttribute('data-offer-id')}`; const tooltipId = `percentage-tooltip-${row.getAttribute('data-offer-id')}`;
const tooltipElement = document.getElementById(tooltipId); const tooltipElement = document.getElementById(tooltipId);
if (tooltipElement) { if (tooltipElement) {
@ -955,6 +964,7 @@ function getMarketRate(fromCoin, toCoin) {
}); });
} }
// todo
function getTimerColor(offer) { function getTimerColor(offer) {
const now = Math.floor(Date.now() / 1000); const now = Math.floor(Date.now() / 1000);
const offerAge = now - offer.created_at; const offerAge = now - offer.created_at;
@ -1015,21 +1025,19 @@ function createDetailsColumn(offer) {
`; `;
} }
function createTakerAmountColumn(offer, coinFrom, coinTo) { function createOrderbookColumn(offer, coinFrom, coinTo) {
const fromAmount = parseFloat(offer.amount_from); const toAmount = parseFloat(offer.amount_to);
const fromSymbol = getCoinSymbol(coinFrom); const fromSymbol = getCoinSymbol(coinFrom);
const fromPriceUSD = latestPrices[coinNameToSymbol[coinFrom]]?.usd || 0;
const fromValueUSD = fromAmount * fromPriceUSD;
return ` return `
<td class="py-0 px-0 text-left text-sm"> <td class="py-0">
<a data-tooltip-target="tooltip-wallet${offer.offer_id}" href="/wallet/${fromSymbol}" class="items-center monospace"> <div class="py-3 px-4 text-right">
<div class="coinname bold w-32" data-coinname="${coinFrom}"> <a data-tooltip-target="tooltip-wallet${offer.offer_id}" href="/wallet/${fromSymbol}" class="items-center monospace">
${fromAmount.toFixed(4)} <div class="pr-2">
<div class="text-gray-600 dark:text-gray-300 text-xs">${coinFrom}</div> <div class="text-sm font-semibold">${toAmount.toFixed(4)}</div>
<div class="text-gray-600 dark:text-gray-300 text-xs">USD: (${fromValueUSD.toFixed(2)})</div> <div class="text-xs text-gray-500 dark:text-gray-400">${coinFrom}</div>
</div> </div>
</a> </a>
</div>
</td> </td>
`; `;
} }
@ -1053,19 +1061,16 @@ function createSwapColumn(offer, coinFrom, coinTo) {
`; `;
} }
function createOrderbookColumn(offer, coinTo, coinFrom) { function createTakerAmountColumn(offer, coinTo, coinFrom) {
const toAmount = parseFloat(offer.amount_to); const fromAmount = parseFloat(offer.amount_from);
const toSymbol = getCoinSymbol(coinTo); const toSymbol = getCoinSymbol(coinTo);
const toPriceUSD = latestPrices[coinNameToSymbol[coinTo]]?.usd || 0;
const toValueUSD = toAmount * toPriceUSD;
return ` return `
<td class="p-0"> <td class="p-0">
<div class="py-3 px-4 text-right"> <div class="py-3 px-4 text-left">
<a data-tooltip-target="tooltip-wallet-maker${escapeHtml(offer.offer_id)}" href="/wallet/${escapeHtml(toSymbol)}" class="block"> <a data-tooltip-target="tooltip-wallet-maker${escapeHtml(offer.offer_id)}" href="/wallet/${escapeHtml(toSymbol)}" class="items-center monospace">
<div class="pr-2"> <div class="pr-2">
<div class="text-sm font-semibold">${toAmount.toFixed(4)}</div> <div class="text-sm font-semibold">${fromAmount.toFixed(4)}</div>
<div class="text-xs text-gray-500 dark:text-gray-400">${coinTo}</div> <div class="text-xs text-gray-500 dark:text-gray-400">${coinTo}</div>
<div class="text-xs text-gray-500 dark:text-gray-400">USD: (${toValueUSD.toFixed(2)})</div>
</div> </div>
</a> </a>
</div> </div>
@ -1083,11 +1088,9 @@ function createRateColumn(offer, coinFrom, coinTo) {
const fromSymbol = getCoinSymbol(coinFrom); const fromSymbol = getCoinSymbol(coinFrom);
const toSymbol = getCoinSymbol(coinTo); const toSymbol = getCoinSymbol(coinTo);
// Get USD prices
const fromPriceUSD = latestPrices[coinNameToSymbol[coinFrom]]?.usd || 0; const fromPriceUSD = latestPrices[coinNameToSymbol[coinFrom]]?.usd || 0;
const toPriceUSD = latestPrices[coinNameToSymbol[coinTo]]?.usd || 0; const toPriceUSD = latestPrices[coinNameToSymbol[coinTo]]?.usd || 0;
// Calculate USD equivalent of the rate
const rateInUSD = rate * toPriceUSD; const rateInUSD = rate * toPriceUSD;
console.log(`Rate calculation for ${fromSymbol} to ${toSymbol}:`); console.log(`Rate calculation for ${fromSymbol} to ${toSymbol}:`);
@ -1101,15 +1104,15 @@ function createRateColumn(offer, coinFrom, coinTo) {
<td class="py-3 pl-6 bold monospace text-xs text-right items-center rate-table-info"> <td class="py-3 pl-6 bold monospace text-xs text-right items-center rate-table-info">
<div class="relative"> <div class="relative">
<div class="flex flex-col items-end" data-tooltip-target="tooltip-rate-${offer.offer_id}"> <div class="flex flex-col items-end" data-tooltip-target="tooltip-rate-${offer.offer_id}">
<span class="font-bold text-gray-700 dark:text-white"> <span class="text-gray-700 dark:text-white">
$${rateInUSD.toFixed(2)}/${fromSymbol}
</span>
<span class="text-gray-700 dark:text-white">
${rate.toFixed(6)} ${toSymbol}/${fromSymbol} ${rate.toFixed(6)} ${toSymbol}/${fromSymbol}
</span> </span>
<span class="text-gray-500 dark:text-white"> <span class="text-gray-700 dark:text-white">
${inverseRate.toFixed(6)} ${fromSymbol}/${toSymbol} ${inverseRate.toFixed(6)} ${fromSymbol}/${toSymbol}
</span> </span>
<span class="text-gray-400 dark:text-gray-400">
($${rateInUSD.toFixed(2)})
</span>
</div> </div>
</div> </div>
</td> </td>
@ -1152,10 +1155,8 @@ function createTooltips(offer, isSentOffers, coinFrom, coinTo, postedTime, expir
const toPriceUSD = latestPrices[toSymbol]?.usd || 0; const toPriceUSD = latestPrices[toSymbol]?.usd || 0;
const rateInUSD = rate * toPriceUSD; const rateInUSD = rate * toPriceUSD;
const combinedRateTooltip = createCombinedRateTooltip(offer, coinFrom, coinTo); const combinedRateTooltip = createCombinedRateTooltip(offer, coinFrom, coinTo);
const fromAmount = parseFloat(offer.amount_from); const fromAmount = parseFloat(offer.amount_from);
const toAmount = parseFloat(offer.amount_to); const toAmount = parseFloat(offer.amount_to);
const percentageTooltipContent = createTooltipContent(isSentOffers, coinFrom, coinTo, fromAmount, toAmount); const percentageTooltipContent = createTooltipContent(isSentOffers, coinFrom, coinTo, fromAmount, toAmount);
@ -1177,7 +1178,7 @@ function createTooltips(offer, isSentOffers, coinFrom, coinTo, postedTime, expir
</div> </div>
<div id="tooltip-wallet${offer.offer_id}" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip"> <div id="tooltip-wallet${offer.offer_id}" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
<div class="active-revoked-expired"><span class="bold">${isSentOffers ? 'My' : ''} ${coinFrom} Wallet</span></div> <div class="active-revoked-expired"><span class="bold">${isSentOffers ? 'My' : ''} ${coinTo} Wallet</span></div>
<div class="tooltip-arrow pl-1" data-popper-arrow></div> <div class="tooltip-arrow pl-1" data-popper-arrow></div>
</div> </div>
@ -1187,7 +1188,7 @@ function createTooltips(offer, isSentOffers, coinFrom, coinTo, postedTime, expir
</div> </div>
<div id="tooltip-wallet-maker${offer.offer_id}" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip"> <div id="tooltip-wallet-maker${offer.offer_id}" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
<div class="active-revoked-expired"><span class="bold">${isSentOffers ? 'My' : ''} ${coinTo} Wallet</span></div> <div class="active-revoked-expired"><span class="bold">${isSentOffers ? 'My' : ''} ${coinFrom} Wallet</span></div>
<div class="tooltip-arrow pl-1" data-popper-arrow></div> <div class="tooltip-arrow pl-1" data-popper-arrow></div>
</div> </div>
@ -1260,6 +1261,7 @@ function createTooltipContent(isSentOffers, coinFrom, coinTo, fromAmount, toAmou
"As a buyer, a positive percentage indicates potential </br> savings compared to current market rates."}</p> "As a buyer, a positive percentage indicates potential </br> savings compared to current market rates."}</p>
`; `;
} }
function createCombinedRateTooltip(offer, coinFrom, coinTo) { function createCombinedRateTooltip(offer, coinFrom, coinTo) {
const rate = parseFloat(offer.rate); const rate = parseFloat(offer.rate);
const inverseRate = 1 / rate; const inverseRate = 1 / rate;

View file

@ -866,69 +866,75 @@ const app = {
}, },
init: () => { init: () => {
window.addEventListener('load', app.onLoad); window.addEventListener('load', async () => {
app.loadLastRefreshedTime(); ui.showLoader();
}, try {
volumeToggle.init();
onLoad: async () => { await app.updateBTCPrice();
ui.showLoader();
try { chartModule.initChart();
volumeToggle.init(); chartModule.showChartLoader();
await app.updateBTCPrice(); } else {
const chartContainer = document.getElementById('coin-chart'); console.warn('Chart container not found, skipping chart initialization');
if (chartContainer) { }
chartModule.initChart();
chartModule.showChartLoader(); const coinDataPromises = config.coins.map(coin => app.loadCoinData(coin));
} else { await Promise.all(coinDataPromises);
console.warn('Chart container not found, skipping chart initialization');
} if (chartModule.chart) {
for (const coin of config.coins) { config.currentResolution = 'month';
await app.loadCoinData(coin); await chartModule.updateChart('BTC');
} app.updateResolutionButtons('BTC');
if (chartModule.chart) { }
config.currentResolution = 'month';
await chartModule.updateChart('BTC'); ui.setActiveContainer('btc-container');
app.updateResolutionButtons('BTC');
} // Set up event listeners for coin containers
ui.setActiveContainer('btc-container'); config.coins.forEach(coin => {
config.coins.forEach(coin => { const container = document.getElementById(`${coin.symbol.toLowerCase()}-container`);
const container = document.getElementById(`${coin.symbol.toLowerCase()}-container`); if (container) {
if (container) { container.addEventListener('click', () => {
container.addEventListener('click', () => { ui.setActiveContainer(`${coin.symbol.toLowerCase()}-container`);
ui.setActiveContainer(`${coin.symbol.toLowerCase()}-container`); if (chartModule.chart) {
if (chartModule.chart) { if (coin.symbol === 'WOW') {
if (coin.symbol === 'WOW') { config.currentResolution = 'day';
config.currentResolution = 'day'; }
} chartModule.updateChart(coin.symbol);
chartModule.updateChart(coin.symbol); app.updateResolutionButtons(coin.symbol);
app.updateResolutionButtons(coin.symbol); }
} });
}); }
});
app.initializeSelectImages();
app.initAutoRefresh();
const refreshAllButton = document.getElementById('refresh-all');
if (refreshAllButton) {
refreshAllButton.addEventListener('click', app.refreshAllData);
}
const headers = document.querySelectorAll('th');
headers.forEach((header, index) => {
header.addEventListener('click', () => app.sortTable(index, header.classList.contains('disabled')));
});
const closeErrorButton = document.getElementById('close-error');
if (closeErrorButton) {
closeErrorButton.addEventListener('click', ui.hideErrorMessage);
}
} catch (error) {
console.error('Error during initialization:', error);
ui.displayErrorMessage('Failed to initialize the dashboard. Please try refreshing the page.');
} finally {
ui.hideLoader();
if (chartModule.chart) {
chartModule.hideChartLoader();
} }
});
const refreshAllButton = document.getElementById('refresh-all');
if (refreshAllButton) {
refreshAllButton.addEventListener('click', app.refreshAllData);
} }
app.initializeSelectImages(); });
const headers = document.querySelectorAll('th'); app.loadLastRefreshedTime();
headers.forEach((header, index) => {
header.addEventListener('click', () => app.sortTable(index, header.classList.contains('disabled')));
});
const closeErrorButton = document.getElementById('close-error');
if (closeErrorButton) {
closeErrorButton.addEventListener('click', ui.hideErrorMessage);
}
app.initAutoRefresh();
} catch (error) {
console.error('Error during initialization:', error);
ui.displayErrorMessage('Failed to initialize the dashboard. Please try refreshing the page.');
} finally {
ui.hideLoader();
if (chartModule.chart) {
chartModule.hideChartLoader();
}
}
}, },
loadCoinData: async (coin) => { loadCoinData: async (coin) => {

View file

@ -195,7 +195,6 @@ function getAPIKeys() {
{% endif %} {% endif %}
<script src="/static/js/pricechart.js"></script> <script src="/static/js/pricechart.js"></script>
<section> <section>
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden"> <div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden">
<div class="border-coolGray-100"> <div class="border-coolGray-100">
@ -208,34 +207,69 @@ function getAPIKeys() {
<div class="container flex flex-wrap justify-center"> <div class="container flex flex-wrap justify-center">
<div class="md:w-auto p-1.5 hover-container"> <div class="md:w-auto p-1.5 hover-container">
<div class="flex"> <div class="flex">
<!-- Coin From Filter (now for Bids) --> {% if sent_offers %}
<button id="coin_from_button" class="bg-gray-50 text-gray-900 appearance-none w-10 dark:bg-gray-500 dark:text-white border-l border-t border-b 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"> <button id="coin_to_button" class="bg-gray-50 text-gray-900 appearance-none w-10 dark:bg-gray-500 dark:text-white border-l border-t border-b 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>
{{ input_arrow_down_svg | safe }} <div class="relative">
<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-none rounded-r-lg outline-none block w-full p-2.5 focus:ring-0"> {{ input_arrow_down_svg | safe }}
<option value="any" {% if filters.coin_from==-1 %} selected{% endif %}>Filter {% if sent_offers %}Sending{% else %}Bids{% endif %}</option> <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-none rounded-r-lg outline-none block w-full p-2.5 focus:ring-0">
{% for c in coins %} <option value="any" {% if filters.coin_to==-1 %} selected{% endif %}>Filter {% if sent_offers %}Sending{% else %}Bids{% endif %}</option>
<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> {% for c in coins_from %}
{% endfor %} <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>
</select> {% endfor %}
</div> </select>
<div class="flex items-center">
<div class="w-full md:w-auto p-1.5">
<p class="text-sm font-heading">{{ arrow_right_svg | safe }}</p>
</div> </div>
</div>
<!-- Coin To Filter (now for Offers) --> <div class="flex items-center">
<button id="coin_to_button" class="bg-gray-50 text-gray-900 appearance-none w-10 dark:bg-gray-500 dark:text-white border-l border-t border-b 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="w-full md:w-auto p-1.5">
<div class="relative"> <p class="text-sm font-heading">{{ arrow_right_svg | safe }}</p>
{{ input_arrow_down_svg | safe }} </div>
<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-none rounded-r-lg outline-none block w-full p-2.5 focus:ring-0"> </div>
<option value="any" {% if filters.coin_to==-1 %} selected{% endif %}>Filter {% if sent_offers %}Receiving{% else %}Offers{% endif %}</option>
{% for c in coins_from %} <button id="coin_from_button" class="bg-gray-50 text-gray-900 appearance-none w-10 dark:bg-gray-500 dark:text-white border-l border-t border-b 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>
<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> <div class="relative">
{% endfor %} {{ input_arrow_down_svg | safe }}
</select> <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-none rounded-r-lg outline-none block w-full p-2.5 focus:ring-0">
</div> <option value="any" {% if filters.coin_from==-1 %} selected{% endif %}>Filter {% if sent_offers %}Receiving{% else %}Offers{% endif %}</option>
</div> {% for c in coins %}
<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>
{% else %}
<div class="md:w-auto p-1.5 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-l border-t border-b 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-none rounded-r-lg outline-none block w-full p-2.5 focus:ring-0">
<option value="any" {% if filters.coin_from==-1 %} selected{% endif %}>Filter {% if sent_offers %}Sending{% else %}Bids{% endif %}</option>
{% for c in coins %}
<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">{{ 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-l border-t border-b 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-none rounded-r-lg outline-none block w-full p-2.5 focus:ring-0">
<option value="any" {% if filters.coin_to==-1 %} selected{% endif %}>Filter {% if sent_offers %}Receiving{% else %}Offers{% endif %}</option>
{% for c in coins_from %}
<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>
{% endif %}
</div> </div>
<div class="w-full md:w-auto mt-3"> <div class="w-full md:w-auto mt-3">
<div class="w-full md:w-auto p-1.5"> <div class="w-full md:w-auto p-1.5">
@ -287,7 +321,7 @@ function getAPIKeys() {
<option value="any" {% if filters.active=='any' %} selected{% endif %}>Any</option> <option value="any" {% if filters.active=='any' %} selected{% endif %}>Any</option>
<option value="active" {% if filters.active=='active' %} selected{% endif %}>Active</option> <option value="active" {% if filters.active=='active' %} selected{% endif %}>Active</option>
<option value="expired" {% if filters.active=='expired' %} selected{% endif %}>Expired</option> <option value="expired" {% if filters.active=='expired' %} selected{% endif %}>Expired</option>
<option value="revoked" {% if filters.active=='revoked' %} selected{% endif %}>Revoked</option> <!-- todo <option value="revoked" {% if filters.active=='revoked' %} selected{% endif %}>Revoked</option>!-->
</select> </select>
</div> </div>
</div> </div>
@ -334,7 +368,9 @@ function getAPIKeys() {
</div> </div>
</div> </div>
</section> </section>
<section> <section>
<div id="jsonView" class="hidden mb-4"> <div id="jsonView" class="hidden mb-4">
<pre id="jsonContent" class="bg-gray-100 p-4 rounded overflow-auto" style="max-height: 300px;"></pre> <pre id="jsonContent" class="bg-gray-100 p-4 rounded overflow-auto" style="max-height: 300px;"></pre>