/* ============================================================ FIFA EDGE — views per section ============================================================ */ const { useState: useStateV, useMemo: useMemoV } = React; /* ---------- TODAY view ---------- */ const TodayView = ({ showEv }) => { const [leagueFilter, setLeagueFilter] = useStateV('all'); const [onlyEv, setOnlyEv] = useStateV(false); const filtered = MATCHES.filter(m => (onlyEv ? m.ev.pick !== null : true)); const evSignals = MATCHES.filter(m => m.ev.pick); return ( <>

Painel de operação · Hoje

{new Date().toLocaleDateString('pt-BR', {day:'2-digit', month:'short', year:'numeric', timeZone:'America/Sao_Paulo'}).toUpperCase()} · e-Soccer Battle 8 · {MATCHES.length} partidas monitoradas
v*0.9)} /> v*0.7)} />
Próximas partidas {filtered.length}
{filtered.map(m => )}
Fila de sinais{evSignals.length}
{evSignals.slice(0,4).map(m => )}
Consistência · 70 últimos
há 14 dias hoje
); }; /* ---------- LIVE view ---------- */ const LiveView = () => ( <>

Partidas ao vivo

Tempo real · {LIVE.length} partidas em andamento
Em andamentoBattle 8
{LIVE.map(l => )}
Feed de eventos
4'12" GOAL · Leto09 {'>'} Drako · 2-1 · xG 0.41
4'03" SHOT · Drako missed · target 0.6m wide
3'47" CORNER · Leto09 · 5th of match
3'21" GOAL · Drako {'>'} Leto09 · 1-1 · xG 0.28
2'54" YELLOW · Drako
2'10" GOAL · Leto09 · 1-0 · xG 0.52
1'00" KICKOFF
Mercado inplay · Leto09 vs Drako
{[ ['Next goal · Leto09', 1.78, 1.65, '-8%'], ['Next goal · Drako', 2.90, 3.10, '+7%'], ['Over 3.5 FT', 1.52, 1.42, '-7%'], ['BTTS FT', 1.35, 1.30, '-4%'], ['Leto09 -1.5 AH', 2.15, 2.05, '-5%'], ].map((r,i)=>( {r[0]} {r[1].toFixed(2)} {r[2].toFixed(2)} {r[3]} ))}
); /* ---------- SIGNALS view ---------- */ const SignalsView = () => { const [filter, setFilter] = useStateV('all'); const rows = SIGNALS.filter(s => filter==='all' ? true : s.result===filter); const totals = SIGNALS.reduce((a, s) => { a.pnl += s.pnl; a[s.result] = (a[s.result]||0)+1; return a; }, { pnl: 0 }); return ( <>

Sinais enviados

Auditoria completa · {SIGNALS.length} sinais nas últimas 24h
= 0 ? '+':''}${totals.pnl.toFixed(2)}`} unit="u" delta={+12.3} spark={ROI_14D} />
Resultado Período Mercado
{rows.map(s => ( ))}
#HoraPartidaPick OddStake EVConf ResultadoPnL
{s.id.slice(1)} {window.fmtHM(s.ts)} {s.match} {s.pick} {s.odd.toFixed(2)} {s.stake.toFixed(1)}u +{s.ev.toFixed(1)}% {s.conf}% {s.result==='win' && ● WIN} {s.result==='loss' && ● LOSS} {s.result==='void' && ● VOID} 0 ? 'var(--win)' : s.pnl < 0 ? 'var(--loss)' : 'var(--text-3)', fontWeight: 600}}> {s.pnl > 0 ? '+':''}{s.pnl.toFixed(2)}
); }; /* ---------- LEADERBOARD view ---------- */ const EloView = () => ( <>

Leaderboard ELO

Rating temporal · {PLAYERS.length} pro-players · atualizado após cada match
a+p.elo,0)/PLAYERS.length)} />
{PLAYERS.map((p, i) => ( ))}
#PlayerTime base ELOΔ 24h JogosWR Forma (últ. 10)
{String(i+1).padStart(2,'0')}
{p.handle.slice(0,2).toUpperCase()}
{p.handle}
{p.team} {p.elo} =0?'var(--win)':'var(--loss)'}}>{p.delta>=0?'+':''}{p.delta} {p.games} {(p.wr*100).toFixed(1)}%
); /* ---------- STATS view (full table with many filters) ---------- */ const ALL_LEAGUES = ['Todas as ligas','Battle 8','H2H GG','Volta','GT League']; const TEAMS = ['Todos os times','Real Madrid','Man City','Bayern','PSG','Barcelona','Liverpool','Atlético MG','Inter','Dortmund','Chelsea','Juventus','Arsenal']; const StatsView = () => { const [tipo, setTipo] = useStateV('jogadores'); // jogadores | partidas | times const [tempo, setTempo] = useStateV('ambos'); // 1t | 2t | ambos const [cantos, setCantos] = useStateV('com'); // com | sem const [periodo, setPeriodo] = useStateV('hoje'); const [historico, setHistorico] = useStateV('completo'); const [league, setLeague] = useStateV('Todas as ligas'); const [team, setTeam] = useStateV('Todos os times'); const [minJogos, setMinJogos] = useStateV(5); const [overs, setOvers] = useStateV(70); const [exibicao, setExibicao] = useStateV(20); const [search, setSearch] = useStateV(''); const [sort, setSort] = useStateV({ col:'elo', dir:'desc' }); const rows = useMemoV(() => { let data = PLAYERS.map(p => { const w = Math.round(p.games*p.wr); const l = Math.round(p.games*(1-p.wr)*0.7); const d = p.games - w - l; const gf = Math.round(p.games * (1.6 + (p.elo-1700)/250)); const ga = Math.round(p.games * (1.3 - (p.elo-1700)/400)); const saldo = gf - ga; const wr = Math.round(p.wr*1000)/10; const o25 = Math.round(60 + (p.elo-1700)/5); const o35 = Math.round(40 + (p.elo-1700)/6); const btts = Math.round(55 + (p.elo-1700)/8); const cs = Math.round(18 + (p.elo-1700)/12); const avgGF = (gf / p.games).toFixed(2); const avgGA = (ga / p.games).toFixed(2); const noGoal = Math.round(8 + (1800-p.elo)/20); const comeback = Math.round(5 + (p.elo-1700)/30); return { ...p, w, l, d, gf, ga, saldo, wr, o25, o35, btts, cs, avgGF, avgGA, noGoal, comeback }; }); if (search) data = data.filter(r => r.handle.toLowerCase().includes(search.toLowerCase()) || r.team.toLowerCase().includes(search.toLowerCase())); if (team !== 'Todos os times') data = data.filter(r => r.team === team); data = data.filter(r => r.games >= minJogos); data = data.filter(r => r.o25 >= overs - 30); data.sort((a,b) => { const av = a[sort.col], bv = b[sort.col]; const n = typeof av === 'number' ? av - bv : String(av).localeCompare(String(bv)); return sort.dir === 'asc' ? n : -n; }); return data.slice(0, exibicao); }, [search, team, minJogos, overs, exibicao, sort, tipo, tempo, cantos, periodo, historico, league]); const toggleSort = (c) => setSort(s => ({ col: c, dir: s.col===c && s.dir==='desc' ? 'asc' : 'desc' })); const th = (id, label, num) => ( toggleSort(id)}> {label} {sort.col===id && {sort.dir==='desc'?'▼':'▲'}} ); return ( <>

e-Soccer · Tabela de estatísticas

Estatísticas de jogadores · filtros avançados · Battle 8 + outras ligas
a+p.games,0).toLocaleString()} delta={+5.2} />
{/* --- Filter block (mirrors the reference structure with original styling) --- */}
Filtros
{/* Row 1 — big segmented groups */}
{['jogadores','partidas','times'].map(t => ( ))}
{[['1t','1º tempo'],['2t','2º tempo'],['ambos','Ambos os tempos']].map(([v,l]) => ( ))}
{[['com','Com cantos'],['sem','Sem cantos']].map(([v,l]) => ( ))}
{[['hoje','Hoje'],['7d','7 dias'],['30d','30 dias']].map(([v,l]) => ( ))}
{/* Row 2 — inputs */}
setSearch(e.target.value)} />
setMinJogos(+e.target.value)} />
setOvers(+e.target.value)} />
Filtros aplicados {team !== 'Todos os times' && Time: {team} ×} {league !== 'Todas as ligas' && Liga: {league} ×} Mín. jogos ≥ {minJogos} Overs ≥ {overs}% {tempo === '1t' ? '1º T' : tempo === '2t' ? '2º T' : 'Ambos T'}
{/* --- Data table with sticky cols --- */}
Estatísticas · {rows.length} resultados
{th('handle','Player')} {th('team','Time')} {th('elo','ELO', true)} {th('games','Jogos', true)} {th('gf','Gols +', true)} {th('ga','Gols -', true)} {th('saldo','Saldo', true)} {th('wr','WR %', true)} {th('o25','Over 2.5 %', true)} {th('o35','Over 3.5 %', true)} {th('btts','BTTS %', true)} {th('avgGF','Avg GF', true)} {th('avgGA','Avg GA', true)} {th('cs','Clean sheet %', true)} {th('noGoal','Sem gols %', true)} {th('comeback','Comeback', true)} {rows.map(r => ( ))}
W-D-L
{r.handle.slice(0,2).toUpperCase()}
{r.handle}
{r.team} {r.elo} {r.games} {r.w} - {r.d} - {r.l} {r.gf} {r.ga} =0?'var(--win)':'var(--loss)', fontWeight:600}}>{r.saldo>=0?'+':''}{r.saldo} {r.avgGF} {r.avgGA} {r.cs}% {r.noGoal}% {r.comeback}
Exibindo 1–{rows.length} de {PLAYERS.length}
); }; /* heat cell for numeric % values in the stats table */ const HeatCell = ({ v, label }) => { const lvl = v >= 0.70 ? 4 : v >= 0.60 ? 3 : v >= 0.50 ? 2 : v >= 0.40 ? 1 : 0; const colors = ['transparent', 'oklch(0.78 0.16 155 / 0.10)', 'oklch(0.78 0.16 155 / 0.20)', 'oklch(0.78 0.16 155 / 0.35)', 'oklch(0.78 0.16 155 / 0.55)']; const fg = lvl >= 3 ? 'var(--accent)' : 'var(--text-1)'; return ( = 3 ? 600 : 400, textAlign:'right' }}>{label} ); }; /* ---------- PERFORMANCE view ---------- */ const PerfView = () => { const months = ['J','F','M','A','M','J','J','A','S','O','N','D']; const monthly = [2.1, 4.8, 6.2, 8.9, 11.4, 9.8, 13.2, 14.6, 12.1, 15.8, 17.9, 17.4]; return ( <>

Performance do bot

ROI acumulado · drawdown · distribuição por mercado
-v*0.3)} />
ROI mensal · 12 meses
{monthly.map((v,i) => (
{months[i]}
))}
Melhor mês: Nov +17.9% Pior mês: Jan +2.1%
Distribuição por mercado
{[ ['1X2 · Home', 42, '+14.2%'], ['1X2 · Away', 18, '+8.6%'], ['Draw', 4, '-2.1%'], ['Over 2.5', 21, '+18.4%'], ['Under 2.5', 5, '+3.2%'], ['BTTS', 10, '+11.8%'], ].map((r,i) => (
{r[0]}
{r[1]}% {r[2]}
))}
); }; /* ---------- SETTINGS view ---------- */ const SettingsView = () => { const [cfg, setCfg] = useStateV({ botToken: '', chatId: '-100', affiliateBase: 'https://stake.com/?c=', channelName: 'FIFA EDGE · VIP', msgTemplate: '🎯 {pick} @ {odd}\n\n{home} vs {away}\n{league} · {kickoff}\n\nEV {ev}% · Conf {conf}%\nStake: {stake}u\n\n🔗 Stake: {aff}', testResult: null, }); const set = (k, v) => setCfg(c => ({...c, [k]: v})); const validateToken = (t) => /^\d{8,10}:[A-Za-z0-9_-]{30,}$/.test(t); const validateChat = (c) => /^-?\d{5,}$/.test(c); const validateUrl = (u) => /^https?:\/\/.+/.test(u); const ready = validateToken(cfg.botToken) && validateChat(cfg.chatId) && validateUrl(cfg.affiliateBase); const runTest = () => { if (!ready) { set('testResult', {ok:false, msg:'Preencha token, chat_id e URL válidos antes de testar.'}); return; } set('testResult', {ok:true, msg:`Mensagem de teste enviada para ${cfg.chatId} via bot ****${cfg.botToken.slice(-6)}.`}); }; return ( <>

Configurações

Engine · fontes · disparo Telegram · multi-tenant
Engine
{[ ['K-factor ELO', '32'], ['Momentum lookback', '10 jogos'], ['EV mínimo', '+5.0%'], ['Confiança mínima', '70%'], ['Stake base', '2.0u (Kelly ¼)'], ['Dispatch offset', 'T-15min'], ].map((r,i) => (
{r[0]} {r[1]}
))}
Fontes
{[ ['BetsAPI · schedule', 'OK · 03:00 UTC diário'], ['BetsAPI · results', 'OK · a cada 30min'], ['Odds · RapidAPI', 'DEGRADED · sem campo markets'], ['Scheduler', 'OK · last tick 12s'], ['SQLite WAL', 'OK · 14.2 MB'], ].map((r,i) => { const warn = r[1].includes('DEGRADED'); return (
{r[0]} ● {r[1]}
); })}
{/* ===== Disparo — multi-tenant Telegram ===== */}
Disparo · Telegrammulti-tenant
{ready ? ● PRONTO : ● INCOMPLETO}
Configure as credenciais do seu bot do Telegram e o link de afiliado Stake. Cada operador pode ter seu próprio canal e link — o sistema envia os sinais usando essas credenciais isoladas.
set('botToken', e.target.value)} style={{fontFamily:'var(--f-mono)', fontSize:11.5}} />
Gere com @BotFather no Telegram
set('chatId', e.target.value)} style={{fontFamily:'var(--f-mono)', fontSize:11.5}} />
Grupos começam com -100. Use @getidsbot
set('affiliateBase', e.target.value)} style={{fontFamily:'var(--f-mono)', fontSize:11.5}} />
Usado no placeholder {'{aff}'} do template
set('channelName', e.target.value)} />