dashboard: fix gauge charts (install gauge.js, render after polls), force dark theme with white text and red/green accents, add icons

This commit is contained in:
Harwinder Sekhon
2026-08-16 20:55:25 -04:00
parent 750f6bee3e
commit f6bea859e5
6 changed files with 134 additions and 90 deletions
+32 -35
View File
@@ -80,32 +80,9 @@ const badgeCls = s => s === "buy" ? "bg-success-subtle text-success-emphasis bor
: "bg-secondary-subtle text-secondary-emphasis border-secondary-subtle";
/* ── theme ────────────────────────────────────────────────────── */
(function () {
const KEY = "xau-theme";
const sysDark = matchMedia("(prefers-color-scheme: dark)");
const stored = () => localStorage.getItem(KEY);
const apply = theme => {
document.documentElement.setAttribute("data-bs-theme", theme);
const btn = $("theme-toggle");
if (!btn) return;
const icon = btn.querySelector("i");
if (theme === "dark") {
btn.title = "Switch to light mode";
if (icon) icon.className = "bi bi-sun-fill";
} else {
btn.title = "Switch to dark mode";
if (icon) icon.className = "bi bi-moon-fill";
}
};
apply(stored() || (sysDark.matches ? "dark" : "light"));
sysDark.addEventListener("change", e => { if (!stored()) apply(e.matches ? "dark" : "light"); });
const btn = $("theme-toggle");
if (btn) btn.addEventListener("click", () => {
const next = document.documentElement.getAttribute("data-bs-theme") === "dark" ? "light" : "dark";
localStorage.setItem(KEY, next);
apply(next);
});
})();
// Dark theme is forced site-wide (owner decision 2026-08-16): dark
// background, pure-white text, red/green data accents.
document.documentElement.setAttribute("data-bs-theme", "dark");
/* ── hero ─────────────────────────────────────────────────────── */
function renderHero() {
@@ -176,29 +153,30 @@ function scoreBar(label, score) {
const v = Math.max(-100, Math.min(100, score ?? 0));
return `<div class="scorebar">
<span class="lbl">${label}</span>
<div class="gauge-container" style="height: 160px;" data-label="${label}" data-score="${v}"></div>
<span class="val num">${v >= 0 ? "+" : ""}${v.toFixed(1)}</span>
<div class="gauge-container" data-label="${label}" data-score="${v}"></div>
<span class="val num ${v > 0 ? "text-success" : v < 0 ? "text-danger" : ""}">${v >= 0 ? "+" : ""}${v.toFixed(1)}</span>
</div>`;
}
function setupCard(ts) {
if (!ts) return "";
const dir = ts.direction === "BUY" ? "buy" : "sell";
const dirIcon = ts.direction === "BUY" ? "bi-arrow-up-circle-fill" : "bi-arrow-down-circle-fill";
const trail = (ts.trail_arm_r != null && ts.trail_dist_r != null)
? `arm ${ts.trail_arm_r}R · dist ${ts.trail_dist_r}R` : "platform default";
return `<div class="card border-signal sig-${dir} bg-body-tertiary">
<div class="card-body py-2 px-3">
<div class="d-flex align-items-baseline gap-2 mb-2">
<span class="fw-bold text-signal sig-${dir}">${ts.direction}</span>
<span class="fw-bold text-signal sig-${dir}"><i class="bi ${dirIcon} me-1"></i>${ts.direction}</span>
<span class="small text-secondary font-monospace">${esc(ts.strategy || "")}</span>
<span class="small text-secondary ms-auto font-monospace">R:R ${(+ts.risk_reward).toFixed(1)}</span>
</div>
<table class="table table-sm table-borderless mb-0">
<tr><td class="text-secondary">Entry</td><td class="num">${fmt$(ts.entry)}</td>
<td class="text-secondary ps-4">TP1</td><td class="num">${fmt$(ts.take_profit_1)}</td></tr>
<tr><td class="text-secondary">Stop</td><td class="num">${fmt$(ts.stop_loss)}</td>
<td class="text-secondary ps-4">TP2</td><td class="num">${fmt$(ts.take_profit_2)}</td></tr>
<tr><td class="text-secondary">Trail</td><td class="num" colspan="3">${esc(trail)}</td></tr>
<tr><td class="text-secondary"><i class="bi bi-box-arrow-in-right me-1"></i>Entry</td><td class="num">${fmt$(ts.entry)}</td>
<td class="text-secondary ps-4"><i class="bi bi-flag-fill me-1 text-success"></i>TP1</td><td class="num text-success">${fmt$(ts.take_profit_1)}</td></tr>
<tr><td class="text-secondary"><i class="bi bi-shield-x me-1 text-danger"></i>Stop</td><td class="num text-danger">${fmt$(ts.stop_loss)}</td>
<td class="text-secondary ps-4"><i class="bi bi-flag-fill me-1 text-success"></i>TP2</td><td class="num text-success">${fmt$(ts.take_profit_2)}</td></tr>
<tr><td class="text-secondary"><i class="bi bi-signpost-split me-1"></i>Trail</td><td class="num" colspan="3">${esc(trail)}</td></tr>
</table>
</div>
</div>`;
@@ -252,7 +230,25 @@ const tile = (k, v, cls = "") =>
<span class="v ${cls}">${v}</span>
</div></div></div>`;
const sec = (title, html) => html ? `<section class="mb-4"><h6 class="text-uppercase text-secondary small fw-bold mb-2 d-flex align-items-center gap-2"><span>${esc(title)}</span><span class="flex-fill border-bottom"></span></h6>${html}</section>` : "";
const SEC_ICON = {
"At a glance": "bi-speedometer2",
"Trade setup": "bi-bullseye",
"Why this signal": "bi-lightbulb",
"Multi-timeframe confirmation": "bi-layers",
"News context": "bi-newspaper",
"AI panel (advisory)": "bi-robot",
"Scan detail": "bi-clock-history",
"Score": "bi-graph-up",
"From the source": "bi-quote",
"AI gold-impact summary": "bi-stars",
"Timing": "bi-alarm",
"Numbers": "bi-calculator"
};
const sec = (title, html) => {
if (!html) return "";
const icon = SEC_ICON[title];
return `<section class="mb-4"><h6 class="text-uppercase text-secondary small fw-bold mb-2 d-flex align-items-center gap-2">${icon ? `<i class="bi ${icon} text-primary"></i>` : ""}<span>${esc(title)}</span><span class="flex-fill border-bottom"></span></h6>${html}</section>`;
};
function indTable(src) {
const rows = IND_ORDER.map(name => {
@@ -748,6 +744,7 @@ async function poll() {
if (snapChanged) {
renderHero();
renderCards();
if (window.renderGauges) window.renderGauges();
renderDetail();
}
if (newsChanged) {