// Student · Channels — a locked preview of the coach/admin broadcast channels // (Announcement, All Members, Free Trial Tier, VIP Members, Signal View). // Free/Trial members see every channel listed but locked; VIP members get a // read-only viewer. Boardroom is coach/admin-internal and never shown here. // Exposes window.ChannelsPanel({client, isVIP, onNeedVip}). (function(){ const { useState, useEffect } = React; // Channel identity. Emoji were replaced with gold line-art SVGs — emoji render // inconsistently across Windows/Android and read as unpolished next to the rest // of the brand. VIP Members carries the real VIP Signal mark, the same one used // on the Telegram broadcasts, so members recognise it instantly. const ICON = { announcement: , all: , trial: , vip: , signal: , lock: , }; const CH_LIST=[ {key:'announcement',label:'Announcement',icon:ICON.announcement,desc:'Broadcasts from your coaches'}, {key:'broadcast:all',label:'All Members',icon:ICON.all,desc:'Community room for every student & staff'}, {key:'broadcast:trial',label:'Free Trial Tier',icon:ICON.trial,desc:'Space for trial members'}, {key:'broadcast:vip',label:'VIP Members',icon:ICON.vip,desc:'VIP-only room with your coaches',brand:'assets/brand/vip-128.png'}, {key:'signalview',label:'Signal View',icon:ICON.signal,desc:'Live trade signal feed'}, ]; // One tile renderer for both the list row and the open viewer header. function ChIcon({ch, size}){ const s = size || 38; if (ch.brand) return ; return {React.cloneElement(ch.icon,{style:{width:'100%',height:'100%',fill:'none',stroke:'var(--gold-2)', strokeWidth:1.7,strokeLinecap:'round',strokeLinejoin:'round'}})} ; } const fmtTime = d => new Date(d).toLocaleString(undefined,{month:'short',day:'numeric',hour:'2-digit',minute:'2-digit'}); function ChannelViewer({client,ch,onClose}){ const [rows,setRows]=useState(null); useEffect(()=>{ let stop=false; async function load(){ if(stop) return; if(ch.key==='signalview'){ const { data } = await client.from('signals').select('*').order('created_at',{ascending:false}).limit(30); setRows(data||[]); } else if(ch.key==='announcement'){ const { data } = await client.from('announcements').select('*').eq('active',true).order('created_at',{ascending:false}); setRows(data||[]); } else { const { data } = await client.from('chat_messages').select('*').eq('thread_key',ch.key).eq('deleted',false).order('created_at',{ascending:false}).limit(30); setRows(data||[]); } } load(); const iv=setInterval(load,8000); return ()=>{ stop=true; clearInterval(iv); }; },[ch.key]); return
{ch.label}
{rows===null &&
} {rows && rows.length===0 &&

Nothing here yet.

} {ch.key==='signalview' && (rows||[]).map(r=>{ const dc=r.direction==='buy'?'var(--green)':'var(--red)'; return
{r.pair}{r.direction==='buy'?'▲ BUY':'▼ SELL'}
{fmtTime(r.created_at)}
; })} {ch.key==='announcement' && (rows||[]).map(a=>(
{a.title &&
{a.title}
}
{a.body}
))} {ch.key!=='signalview' && ch.key!=='announcement' && (rows||[]).slice().reverse().map(m=>(
{m.sender_name||m.sender_email} {m.body}
))}
; } function ChannelsPanel({client, isVIP, onNeedVip}){ const [open,setOpen]=useState(null); return
Channels

{isVIP ? 'Browse the community rooms your coaches use.' : 'A look at the community rooms \u2014 unlock with VIP to view them.'}

{CH_LIST.map(ch=>{ const locked=!isVIP; return
{!locked && open===ch.key && setOpen(null)}/>}
; })}
; } window.ChannelsPanel = ChannelsPanel; })();