05/31 Phase 7: fixed fetch fx rate
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
.fx-card { background: #0f172a; border-color: #1e293b; color: #f1f5f9; cursor: pointer; transition: all .2s; }
|
||||
.fx-card:hover { border-color: #3b82f6 !important; }
|
||||
.period-btn.active { background: #3b82f6; color: #fff; border-color: #3b82f6; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
{% endblock %}
|
||||
|
||||
{% block topbar_actions %}
|
||||
@@ -93,17 +94,21 @@
|
||||
<div class="col-12 col-xl-4 d-flex flex-column gap-3">
|
||||
<!-- USD/VND Widget -->
|
||||
{% if fx %}
|
||||
<div class="pcard fx-card" onclick="toggleFxChart()">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div class="pcard fx-card" id="fxWidget">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div onclick="toggleFxChart()" style="cursor:pointer;flex:1;">
|
||||
<div style="font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.07em;color:#475569;">USD → VND</div>
|
||||
<div class="mono" style="font-size:24px;font-weight:600;margin-top:4px;">₫{{ "{:,.0f}".format(fx.rate) }}</div>
|
||||
<div style="font-size:11px;color:#64748b;margin-top:2px;">
|
||||
1 USD · {{ fx.date.strftime('%b %d, %Y') }}
|
||||
{% if fx.is_stale %}<span title="Rate may be outdated">⚠️</span>{% endif %}
|
||||
<div class="mono" id="fxRate" style="font-size:24px;font-weight:600;margin-top:4px;">₫{{ "{:,.0f}".format(fx.rate) }}</div>
|
||||
<div style="font-size:11px;color:#64748b;margin-top:2px;" id="fxMeta">
|
||||
1 USD · <span id="fxDate">{{ fx.date.strftime('%b %d, %Y') }}</span>
|
||||
· <span id="fxSource" style="opacity:.6;">{{ fx.source }}</span>
|
||||
{% if fx.is_stale %}<span id="fxStale" title="Rate may be outdated" style="color:#f59e0b;"> ⚠ stale</span>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div style="font-size:32px;opacity:.15;font-family:serif;">₫</div>
|
||||
<button id="fxRefreshBtn" onclick="refreshFxRate()" title="Refresh rate"
|
||||
style="background:none;border:none;color:#475569;cursor:pointer;font-size:14px;padding:2px 4px;line-height:1;margin-top:2px;">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="fxChartWrap" style="display:none;margin-top:12px;">
|
||||
<div style="height:70px;"><canvas id="fxChart"></canvas></div>
|
||||
@@ -111,8 +116,15 @@
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="pcard" style="background:#1e293b;border-color:#334155;">
|
||||
<div style="font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.07em;color:#475569;">USD → VND</div>
|
||||
<div style="font-size:13px;color:#94a3b8;margin-top:6px;">Rate unavailable</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div style="font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.07em;color:#475569;">USD → VND</div>
|
||||
<div style="font-size:13px;color:#94a3b8;margin-top:6px;">Rate unavailable</div>
|
||||
</div>
|
||||
<button onclick="refreshFxRate()" style="background:none;border:none;color:#475569;cursor:pointer;font-size:14px;">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -285,6 +297,28 @@
|
||||
})();
|
||||
|
||||
var fxChart = null;
|
||||
|
||||
function refreshFxRate() {
|
||||
const btn = document.getElementById('fxRefreshBtn');
|
||||
if (btn) { btn.innerHTML = '<i class="bi bi-arrow-clockwise" style="animation:spin .7s linear infinite;display:inline-block;"></i>'; }
|
||||
fetch('/api/fx-refresh', { method:'POST', headers:{'Content-Type':'application/json','X-CSRFToken': csrfToken} })
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.rate) {
|
||||
const rateEl = document.getElementById('fxRate');
|
||||
const dateEl = document.getElementById('fxDate');
|
||||
const srcEl = document.getElementById('fxSource');
|
||||
const staleEl = document.getElementById('fxStale');
|
||||
if (rateEl) rateEl.textContent = '₫' + Math.round(data.rate).toLocaleString();
|
||||
if (dateEl) dateEl.textContent = data.date;
|
||||
if (srcEl) srcEl.textContent = data.source;
|
||||
if (staleEl) staleEl.style.display = data.is_stale ? '' : 'none';
|
||||
}
|
||||
if (btn) btn.innerHTML = '<i class="bi bi-arrow-clockwise"></i>';
|
||||
})
|
||||
.catch(() => { if (btn) btn.innerHTML = '<i class="bi bi-arrow-clockwise"></i>'; });
|
||||
}
|
||||
|
||||
function toggleFxChart(){
|
||||
const wrap = document.getElementById('fxChartWrap');
|
||||
wrap.style.display = wrap.style.display === 'none' ? 'block' : 'none';
|
||||
|
||||
Reference in New Issue
Block a user