/* ============================================================
FIFA EDGE — reusable components
============================================================ */
const { useState, useEffect, useMemo, useRef } = React;
/* ----- Icons (inline SVG, 16px) ----- */
const Icon = ({ name, size = 16 }) => {
const paths = {
home: <>>,
live: <>>,
signals: <>>,
trophy: <>>,
stats: <>>,
perf: <>>,
settings: <>>,
search: <>>,
bell: <>>,
arrow_up: <>>,
arrow_dn: <>>,
filter: <>>,
refresh: <>>,
expand: <>>,
telegram: <>>,
play: <>>,
pause: <>>,
dot: ,
chev: <>>,
};
return (
);
};
/* ----- Sparkline ----- */
const Sparkline = ({ data, w = 72, h = 28, color = 'currentColor' }) => {
const max = Math.max(...data), min = Math.min(...data);
const range = max - min || 1;
const step = w / (data.length - 1);
const points = data.map((v, i) => `${i*step},${h - ((v - min) / range) * (h - 4) - 2}`).join(' ');
const areaPoints = `0,${h} ${points} ${w},${h}`;
return (
);
};
/* ----- KPI tile ----- */
const Kpi = ({ label, value, unit, delta, spark, sparkColor }) => (
{label}
{value}{unit && {unit}}
{delta !== undefined && (
= 0 ? 'pos' : 'neg'}`}>
= 0 ? 'arrow_up' : 'arrow_dn'} size={10} />
{delta >= 0 ? '+' : ''}{delta}%
)}
{spark &&
}
);
/* ----- EV circular meter ----- */
const EvMeter = ({ value, conf }) => {
const pct = Math.max(0, Math.min(100, (value / 20) * 100));
const r = 18, C = 2 * Math.PI * r;
const off = C - (C * pct) / 100;
return (
);
};
/* ----- Form dots (W/D/L) ----- */
const FormDots = ({ form }) => (
{form.slice(-10).map((r, i) => {r.toUpperCase()})}
);
/* ----- Match row (for Today list) ----- */
const MatchRow = ({ m, showEv }) => {
const oddKey = m.ev.pick === '1' ? 'h' : m.ev.pick === 'X' ? 'd' : m.ev.pick === '2' ? 'a' : null;
const pill = m.ev.value >= 7 ? 'pos' : m.ev.value >= 3 ? 'mid' : 'flat';
return (
window.openH2H && window.openH2H(m.id)}>
{window.fmtHM(m.kickoff)}
T-{m.minuteToKO}m
{m.home.handle}{m.home.team}{m.home.elo}
{m.away.handle}{m.away.team}{m.away.elo}
{m.league}
1{m.odds.h.toFixed(2)}
X{m.odds.d.toFixed(2)}
2{m.odds.a.toFixed(2)}
{showEv && (
m.ev.pick
?
{m.ev.pick} · +{m.ev.value.toFixed(1)}%
:
no edge
)}
);
};
/* ----- Signal queue item ----- */
const SignalQueueItem = ({ m }) => {
const [countdown, setCountdown] = useState(m.minuteToKO * 60 - 15 * 60); // seconds until T-15min dispatch
useEffect(() => {
const t = setInterval(() => setCountdown(c => Math.max(0, c - 1)), 1000);
return () => clearInterval(t);
}, []);
const mm = Math.floor(Math.abs(countdown) / 60).toString().padStart(2,'0');
const ss = Math.floor(Math.abs(countdown) % 60).toString().padStart(2,'0');
const oddPicked = m.ev.pick === '1' ? m.odds.h : m.ev.pick === 'X' ? m.odds.d : m.ev.pick === '2' ? m.odds.a : null;
const status = countdown > 0 ? 'queued' : 'dispatched';
return (
{m.league} · Kickoff {window.fmtHM(m.kickoff)}
·
{status === 'queued' ? Dispara em {mm}:{ss} : dispatched}
{m.home.handle} vs {m.away.handle}
ELO{m.home.elo}/{m.away.elo}
Conf{m.ev.conf}%
Stake2.0u
{m.ev.pick && (
TIP{m.ev.pick}@{oddPicked && oddPicked.toFixed(2)}
)}
);
};
/* ----- Live match row ----- */
const LiveRow = ({ m }) => (
{m.home.handle} {m.home.team}{m.score.h}
{m.away.handle} {m.away.team}{m.score.a}
{m.odds.h.toFixed(2)}
{m.odds.d.toFixed(2)}
{m.odds.a.toFixed(2)}
);
/* ----- Heatmap ----- */
const Heat = ({ data }) => (
);
/* ----- Sidebar ----- */
const Sidebar = ({ current, onNav }) => {
const nav = [
{ id: 'today', label: 'Hoje', icon: 'home', count: MATCHES.length },
{ id: 'live', label: 'Ao Vivo', icon: 'live', live: true },
{ id: 'signals', label: 'Sinais', icon: 'signals', count: SIGNALS.length },
{ id: 'elo', label: 'Leaderboard', icon: 'trophy' },
{ id: 'stats', label: 'Estatísticas', icon: 'stats' },
{ id: 'perf', label: 'Performance', icon: 'perf' },
];
const secondary = [];
return (
);
};
/* ----- Topbar ----- */
const Topbar = ({ section }) => {
const labels = { today:'Hoje', live:'Ao Vivo', signals:'Sinais', elo:'Leaderboard', stats:'Estatísticas', perf:'Performance' };
const ov = (typeof OVERVIEW !== 'undefined' ? OVERVIEW : {}) || {};
const roi = ov.roi_14d;
const wr = ov.winrate;
const n14 = ov.signals_14d ?? 0;
const fmt = (v, d=1) => v == null ? '—' : v.toFixed(d);
const roiCls = roi == null ? '' : roi >= 0 ? 'pos' : 'neg';
const roiStr = roi == null ? '—' : (roi >= 0 ? '+' : '') + roi.toFixed(1) + '%';
return (
FIFA_EDGE
/
e-Soccer
/
{labels[section]}
⌘K
ROI 14d
{roiStr}
WR
{fmt(wr)}%
Sinais 14d
{n14}
);
};
Object.assign(window, { Icon, Sparkline, Kpi, EvMeter, FormDots, MatchRow, SignalQueueItem, LiveRow, Heat, Sidebar, Topbar });