fix: market cards fixed order; collapsible with arrow indicator

This commit is contained in:
Manohar 2026-05-09 04:04:09 +00:00
parent 5bdf7fbffe
commit 8564dcb8b2

View file

@ -86,12 +86,16 @@
.btn-save:hover{background:#e55a3a;transform:translateY(-1px)} .btn-save:hover{background:#e55a3a;transform:translateY(-1px)}
.save-msg{font-size:.72rem;color:var(--green);display:none;margin-left:10px} .save-msg{font-size:.72rem;color:var(--green);display:none;margin-left:10px}
.collapsible .card-head{cursor:pointer;user-select:none}
.collapsible .card-head::after{content:"\u2303";font-size:.8rem;color:var(--text3);transition:transform .2s;margin-left:8px}
.collapsible.collapsed .card-head::after{transform:rotate(180deg)} /* ── Collapsible ── */
.collapse-body{overflow:hidden;transition:max-height .35s cubic-bezier(.4,0,.2,1);max-height:4000px} .collapsible > .card-head { cursor:pointer; user-select:none; }
.collapsible.collapsed .collapse-body{max-height:0} .collapse-arrow { margin-left:auto; font-size:.75rem; color:var(--text3); transition:transform .25s ease; display:inline-block; }
</style> .collapsible.collapsed .collapse-arrow { transform:rotate(-90deg); }
.collapse-body { overflow:hidden; transition:max-height .35s cubic-bezier(.4,0,.2,1); max-height:5000px; }
.collapsible.collapsed .collapse-body { max-height:0 !important; }
</style>
</head> </head>
<body> <body>
<div class="wrap"> <div class="wrap">
@ -210,7 +214,12 @@ async function loadMarket(){
const {ok,data=[]}=await fetch('/api/market').then(r=>r.json()).catch(()=>({ok:false,data:[]})); const {ok,data=[]}=await fetch('/api/market').then(r=>r.json()).catch(()=>({ok:false,data:[]}));
const grid=document.getElementById('market-grid'); const grid=document.getElementById('market-grid');
if(!ok||!data.length){grid.innerHTML='<div class="card mcard" style="grid-column:1/-1;text-align:center;padding:14px;color:var(--text3)">Market data unavailable</div>';return;} if(!ok||!data.length){grid.innerHTML='<div class="card mcard" style="grid-column:1/-1;text-align:center;padding:14px;color:var(--text3)">Market data unavailable</div>';return;}
grid.innerHTML=data.map(q=>{const up=q.changePct>=0,cl=up?'up':'dn',arrow=up?'\u25b2':'\u25bc';const price=q.price>10000?q.price.toLocaleString('en-IN',{maximumFractionDigits:0}):q.price.toFixed(2);return'<div class="card mcard"><div class="mlbl">'+q.label+'</div><div class="mprice">'+price+' <span style="font-size:.62rem;color:var(--text2)">'+q.unit+'</span></div><div class="mchg '+cl+'">'+arrow+' '+Math.abs(q.changePct).toFixed(2)+'% <span style="opacity:.6">'+(q.change>=0?'+':'')+q.change.toFixed(0)+'</span></div>'+(q.stale?'<div class="mstale">prev close</div>':'')+'</div>';}).join(''); const ORDER=['SENSEX','NIFTY50','BANKNIFTY','INDIAVIX','CRUDEOIL','USDINR'];
const map=Object.fromEntries(data.map(q=>[q.key,q]));
const sorted=ORDER.map(k=>map[k]).filter(Boolean);
// append any keys not in ORDER
data.forEach(q=>{if(!ORDER.includes(q.key))sorted.push(q);});
grid.innerHTML=sorted.map(q=>{const up=q.changePct>=0,cl=up?'up':'dn',arrow=up?'\u25b2':'\u25bc';const price=q.price>10000?q.price.toLocaleString('en-IN',{maximumFractionDigits:0}):q.price.toFixed(2);return'<div class="card mcard"><div class="mlbl">'+q.label+'</div><div class="mprice">'+price+' <span style="font-size:.62rem;color:var(--text2)">'+q.unit+'</span></div><div class="mchg '+cl+'">'+arrow+' '+Math.abs(q.changePct).toFixed(2)+'% <span style="opacity:.6">'+(q.change>=0?'+':'')+q.change.toFixed(0)+'</span></div>'+(q.stale?'<div class="mstale">prev close'+(q.cachedAt?' \u00b7 '+q.cachedAt.slice(11,16)+' UTC':'')+'</div>':'')+'</div>';}).join('');
} }
async function loadPositions(){ async function loadPositions(){
const {data}=await fetch('/api/positions').then(r=>r.json()); const {data}=await fetch('/api/positions').then(r=>r.json());
@ -260,16 +269,21 @@ async function saveOverride(key){
} }
async function refresh(){await Promise.all([loadPositions(),loadAlerts(),loadHealth(),loadChart(curH),loadMarket()]);} async function refresh(){await Promise.all([loadPositions(),loadAlerts(),loadHealth(),loadChart(curH),loadMarket()]);}
function toggleCard(id){ function toggleCard(id){
const el=document.getElementById(id); const el=document.getElementById(id);
if(!el)return;
el.classList.toggle('collapsed'); el.classList.toggle('collapsed');
const s=JSON.parse(localStorage.getItem('collapse')||'{}'); const s=JSON.parse(localStorage.getItem('collapse')||'{}');
s[id]=el.classList.contains('collapsed'); s[id]=el.classList.contains('collapsed');
localStorage.setItem('collapse',JSON.stringify(s)); localStorage.setItem('collapse',JSON.stringify(s));
} }
(function(){ (function restoreCollapse(){
const s=JSON.parse(localStorage.getItem('collapse')||'{}'); try{
Object.entries(s).forEach(([id,c])=>{if(c)document.getElementById(id)?.classList.add('collapsed')}); const s=JSON.parse(localStorage.getItem('collapse')||'{}');
Object.entries(s).forEach(([id,c])=>{if(c){const el=document.getElementById(id);if(el)el.classList.add('collapsed');}});
}catch(e){}
})(); })();
loadConfig();refresh(); loadConfig();refresh();
setInterval(refresh,60000); setInterval(refresh,60000);