// Student portal · Signals feed (read-only). window.SignalsFeed // Shown at the top of the dashboard to PAID students (basic/inter/adv). // Polls every 15s; chimes + toast on a fresh signal. Same signal rows the // coaches see; students cannot edit — they just read and (if pushed) also // get the Telegram DM from n8n. (function(){ const { useState, useEffect, useRef } = React; const SOUND_KEY='cg_signal_sound'; function soundOn(){ return localStorage.getItem(SOUND_KEY)!=='0'; } function chime(){ if(!soundOn()) return; try{ const AC=window.AudioContext||window.webkitAudioContext; if(!AC) return; const ac=chime._ac||(chime._ac=new AC()); if(ac.state==='suspended') ac.resume(); const now=ac.currentTime; [[784,0],[1046.5,0.1],[1318.5,0.2]].forEach(p=>{ const o=ac.createOscillator(),g=ac.createGain(); o.type='sine'; o.frequency.value=p[0]; o.connect(g); g.connect(ac.destination); const t=now+p[1]; g.gain.setValueAtTime(0.0001,t); g.gain.exponentialRampToValueAtTime(0.13,t+0.02); g.gain.exponentialRampToValueAtTime(0.0001,t+0.24); o.start(t); o.stop(t+0.26); }); }catch(e){} } function toast(text){ const el=document.createElement('div'); el.textContent=text; el.style.cssText='position:fixed;left:50%;bottom:26px;transform:translateX(-50%);z-index:9999;background:var(--navy-900);color:var(--ink);border:1px solid var(--border-gold);border-radius:999px;padding:11px 20px;font-family:var(--font-mono);font-size:.82rem;box-shadow:0 12px 40px rgba(0,0,0,.5)'; document.body.appendChild(el); setTimeout(()=>{ el.style.transition='opacity .5s'; el.style.opacity='0'; setTimeout(()=>el.remove(),500); },4200); } const fmtWhen = d => d ? new Date(d).toLocaleString(undefined,{month:'short',day:'numeric',hour:'2-digit',minute:'2-digit'}) : ''; const STATUS_META={ open:{label:'Open',color:'var(--gold-2)',bg:'var(--gold-soft)'}, tp1_hit:{label:'TP1 hit — running',color:'#4a9eda',bg:'rgba(74,158,218,.12)'}, tp2_hit:{label:'TP2 hit — full win ✓',color:'var(--green)',bg:'rgba(46,160,90,.12)'}, sl_hit:{label:'SL hit ✕',color:'var(--red)',bg:'rgba(214,69,69,.12)'}, cancelled:{label:'Cancelled',color:'var(--ink-faint)',bg:'var(--surface-2)'}, closed:{label:'Closed',color:'#7aa2f7',bg:'rgba(122,162,247,.12)'} }; function Row({r, onImg}){ const sm=STATUS_META[r.status]||STATUS_META.open; const dc=r.direction==='buy'?'var(--green)':'var(--red)'; const faded = r.status==='cancelled'; return