Add tabbed period stats with lightweight charts on instance pages.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -166,6 +166,37 @@
|
||||
.list-window-bar label{color:#9aa;display:flex;align-items:center;gap:6px}
|
||||
.stats-segment-block{margin-top:20px;padding-top:14px;border-top:1px solid #3a4468}
|
||||
.stats-segment-block h2{font-size:1.05rem;color:#dbe4ff;margin-bottom:8px}
|
||||
.stats-period-tabs{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px}
|
||||
.stats-period-tab{background:#151a2a;color:#9aa3bf;border:1px solid #304164;border-radius:8px;padding:7px 14px;font-size:.84rem;cursor:pointer;transition:background .15s,border-color .15s,color .15s}
|
||||
.stats-period-tab:hover{background:#1c2438;color:#cfd3ef}
|
||||
.stats-period-tab.active{background:#1f3a5a;color:#8fc8ff;border-color:#3d5f8a;font-weight:600}
|
||||
.stats-period-pane[hidden]{display:none!important}
|
||||
.stats-period-range{font-size:.78rem;color:#8892b0;margin-bottom:12px;line-height:1.45}
|
||||
.inst-stats-viz{display:flex;flex-direction:column;gap:14px;margin-bottom:14px}
|
||||
.inst-stats-kpis{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}
|
||||
.inst-stats-kpi{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;padding:12px 10px;background:#151a2a;border:1px solid #2a3152;border-radius:10px;text-align:center;min-height:88px}
|
||||
.inst-stats-kpi-val{font-size:1.15rem;font-weight:700;font-variant-numeric:tabular-nums;line-height:1.2}
|
||||
.inst-stats-kpi-lbl{font-size:.72rem;color:#8892b0;line-height:1.3}
|
||||
.inst-stats-ring{--win-pct:0;width:56px;height:56px;border-radius:50%;background:conic-gradient(#4cd97f 0 calc(var(--win-pct) * 1%),#ff6b6b calc(var(--win-pct) * 1%) 100%);display:flex;align-items:center;justify-content:center;position:relative}
|
||||
.inst-stats-ring::before{content:"";position:absolute;inset:7px;border-radius:50%;background:#151a2a}
|
||||
.inst-stats-ring-label{position:relative;z-index:1;font-size:.78rem;font-weight:700;font-variant-numeric:tabular-nums}
|
||||
.inst-stats-block{padding:12px;background:#141923;border:1px solid #2a3150;border-radius:10px}
|
||||
.inst-stats-block-title{font-size:.72rem;color:#8892b0;margin-bottom:8px}
|
||||
.inst-stats-stacked-bar{display:flex;height:10px;border-radius:6px;overflow:hidden;background:#1e2438}
|
||||
.inst-stats-stacked-fill{height:100%;min-width:0;transition:width .2s ease}
|
||||
.inst-stats-stacked-fill--profit{background:#4cd97f}
|
||||
.inst-stats-stacked-fill--loss{background:#ff6b6b}
|
||||
.inst-stats-bar-labels{display:flex;justify-content:space-between;gap:10px;margin-top:8px;font-size:.76rem;font-variant-numeric:tabular-nums}
|
||||
.inst-stats-risk-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 12px}
|
||||
.inst-stats-risk-item{display:flex;flex-direction:column;gap:3px;min-width:0}
|
||||
.inst-stats-risk-item .k{font-size:.7rem;color:#8892b0}
|
||||
.inst-stats-risk-item .v{font-size:.84rem;font-weight:600;font-variant-numeric:tabular-nums;color:#e8ecf4;word-break:break-word}
|
||||
.inst-stats-empty{margin:0;padding:18px;text-align:center;color:#8892b0;font-size:.85rem;background:#141923;border:1px dashed #2a3348;border-radius:10px}
|
||||
.inst-stats-details{margin-top:4px}
|
||||
.inst-stats-details>summary{cursor:pointer;font-size:.84rem;color:#9aa3bf;padding:8px 0;user-select:none;list-style-position:inside}
|
||||
.inst-stats-details>summary::-webkit-details-marker{color:#6d7689}
|
||||
.inst-stats-details[open]>summary{margin-bottom:6px;color:#cfd3ef}
|
||||
@media (max-width:640px){.inst-stats-kpis{grid-template-columns:1fr}.inst-stats-risk-grid{grid-template-columns:1fr}}
|
||||
.key-history{margin-top:12px;padding-top:10px;border-top:1px solid #2a3150}
|
||||
.key-history h3{font-size:.88rem;color:#b8c4ff;margin-bottom:6px}
|
||||
.key-history .sub{font-size:.72rem;color:#8892b0;margin-bottom:6px}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
(function (global) {
|
||||
"use strict";
|
||||
|
||||
var PERIODS = ["day", "week", "month"];
|
||||
|
||||
function statsSegmentSelect() {
|
||||
return document.getElementById("stats-segment-select");
|
||||
}
|
||||
|
||||
function activeSegmentPanel() {
|
||||
var sel = statsSegmentSelect();
|
||||
if (!sel) return null;
|
||||
var key = sel.value;
|
||||
return document.querySelector(
|
||||
'.stats-segment-panel[data-stats-segment="' + key + '"]'
|
||||
);
|
||||
}
|
||||
|
||||
function replaceStatsUrl(params) {
|
||||
var q = new URLSearchParams(global.location.search);
|
||||
Object.keys(params).forEach(function (k) {
|
||||
if (params[k] == null || params[k] === "") q.delete(k);
|
||||
else q.set(k, params[k]);
|
||||
});
|
||||
var qs = q.toString();
|
||||
global.history.replaceState(
|
||||
null,
|
||||
"",
|
||||
qs ? global.location.pathname + "?" + qs : global.location.pathname
|
||||
);
|
||||
}
|
||||
|
||||
function switchStatsPeriod(periodKey) {
|
||||
var panel = activeSegmentPanel();
|
||||
if (!panel) return;
|
||||
var key = PERIODS.indexOf(periodKey) >= 0 ? periodKey : "day";
|
||||
panel.querySelectorAll(".stats-period-pane").forEach(function (pane) {
|
||||
var match = pane.getAttribute("data-stats-period") === key;
|
||||
if (match) pane.removeAttribute("hidden");
|
||||
else pane.setAttribute("hidden", "");
|
||||
});
|
||||
panel.querySelectorAll(".stats-period-tab").forEach(function (btn) {
|
||||
var on = btn.getAttribute("data-stats-period") === key;
|
||||
btn.classList.toggle("active", on);
|
||||
btn.setAttribute("aria-selected", on ? "true" : "false");
|
||||
});
|
||||
replaceStatsUrl({ stats_period: key });
|
||||
}
|
||||
|
||||
function switchStatsSegment() {
|
||||
var sel = statsSegmentSelect();
|
||||
if (!sel) return;
|
||||
var key = sel.value;
|
||||
document.querySelectorAll(".stats-segment-panel").forEach(function (p) {
|
||||
p.style.display =
|
||||
p.getAttribute("data-stats-segment") === key ? "block" : "none";
|
||||
});
|
||||
replaceStatsUrl({ stats_segment: key });
|
||||
var period =
|
||||
new URLSearchParams(global.location.search).get("stats_period") || "day";
|
||||
switchStatsPeriod(period);
|
||||
}
|
||||
|
||||
function bindPeriodTabs() {
|
||||
document.querySelectorAll(".stats-period-tab").forEach(function (btn) {
|
||||
if (btn.getAttribute("data-stats-bound") === "1") return;
|
||||
btn.setAttribute("data-stats-bound", "1");
|
||||
btn.addEventListener("click", function () {
|
||||
switchStatsPeriod(btn.getAttribute("data-stats-period") || "day");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initStatsFromUrl() {
|
||||
var sel = statsSegmentSelect();
|
||||
if (!sel) return;
|
||||
bindPeriodTabs();
|
||||
var url = new URLSearchParams(global.location.search);
|
||||
var segKey = url.get("stats_segment");
|
||||
if (
|
||||
segKey &&
|
||||
sel.querySelector('option[value="' + segKey.replace(/"/g, "") + '"]')
|
||||
) {
|
||||
sel.value = segKey;
|
||||
}
|
||||
switchStatsSegment();
|
||||
var period = url.get("stats_period") || "day";
|
||||
if (PERIODS.indexOf(period) < 0) period = "day";
|
||||
switchStatsPeriod(period);
|
||||
}
|
||||
|
||||
global.switchStatsSegment = switchStatsSegment;
|
||||
global.switchStatsPeriod = switchStatsPeriod;
|
||||
global.initStatsFromUrl = initStatsFromUrl;
|
||||
global.initStatsSegmentFromUrl = initStatsFromUrl;
|
||||
})(window);
|
||||
@@ -819,6 +819,46 @@ html[data-theme="light"] .stats-period-block {
|
||||
border-bottom-color: #d0dae4 !important;
|
||||
}
|
||||
|
||||
html[data-theme="light"] .stats-period-tab {
|
||||
background: #f4f7fb !important;
|
||||
color: #4a6078 !important;
|
||||
border-color: #c8d4e0 !important;
|
||||
}
|
||||
html[data-theme="light"] .stats-period-tab:hover {
|
||||
background: #e8eef5 !important;
|
||||
color: #142232 !important;
|
||||
}
|
||||
html[data-theme="light"] .stats-period-tab.active {
|
||||
background: #dce8f5 !important;
|
||||
color: #0d4a7a !important;
|
||||
border-color: #7eb0d8 !important;
|
||||
}
|
||||
html[data-theme="light"] .stats-period-range,
|
||||
html[data-theme="light"] .inst-stats-kpi-lbl,
|
||||
html[data-theme="light"] .inst-stats-block-title,
|
||||
html[data-theme="light"] .inst-stats-risk-item .k,
|
||||
html[data-theme="light"] .inst-stats-empty {
|
||||
color: #4a6078 !important;
|
||||
}
|
||||
html[data-theme="light"] .inst-stats-kpi,
|
||||
html[data-theme="light"] .inst-stats-block {
|
||||
background: #f8fafc !important;
|
||||
border-color: #c8d4e0 !important;
|
||||
}
|
||||
html[data-theme="light"] .inst-stats-ring::before {
|
||||
background: #f8fafc !important;
|
||||
}
|
||||
html[data-theme="light"] .inst-stats-stacked-bar {
|
||||
background: #e2e8f0 !important;
|
||||
}
|
||||
html[data-theme="light"] .inst-stats-risk-item .v,
|
||||
html[data-theme="light"] .inst-stats-details > summary {
|
||||
color: #142232 !important;
|
||||
}
|
||||
html[data-theme="light"] .inst-stats-details[open] > summary {
|
||||
color: #0d4a7a !important;
|
||||
}
|
||||
|
||||
html[data-theme="light"] .key-history {
|
||||
border-top-color: #d0dae4 !important;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user