// ───────────────────────────────────────────────────────────── // ÁREA DO CRIADOR — Gerador de Tokens (acesso só pelo caminho /admin) // Simples como o original: senha → gerar → lista verde/vermelho. // Conversa com tokens_api.php (senha validada NO SERVIDOR — nunca no app). // Mesmo banco do burn_token.php (tokens_db.txt / usados.txt). // ───────────────────────────────────────────────────────────── const { useState: useStateAdmin } = React; // link bonito que você compartilha: .../comportamentos/analise/TOKEN function linkDoToken(tk) { return `${location.origin}/comportamentos/analise/${encodeURIComponent(tk)}`; } // gerador local (só pra pré-visualização sem servidor) function tokenLocalDemo(prefix) { const b = new Uint8Array(6); (window.crypto || window.msCrypto).getRandomValues(b); const hex = [...b].map((x) => x.toString(16).padStart(2, "0")).join("").toUpperCase(); return (prefix ? prefix + "_" : "") + hex; } function AdminScreen({ t, onBack }) { const [auth, setAuth] = useStateAdmin(false); const [senha, setSenha] = useStateAdmin(""); const [prefix, setPrefix] = useStateAdmin(""); const [qty, setQty] = useStateAdmin(1); const [tokens, setTokens] = useStateAdmin([]); // [{token, used}] const [busy, setBusy] = useStateAdmin(false); const [erro, setErro] = useStateAdmin(null); const [demo, setDemo] = useStateAdmin(false); const [copied, setCopied] = useStateAdmin(null); const [stats, setStats] = useStateAdmin(null); // {totais, por_dia} const [leads, setLeads] = useStateAdmin([]); const API = "tokens_api.php"; // entrar: valida senha no servidor e já traz a lista (verde/vermelho) const entrar = async (e) => { e && e.preventDefault(); if (!senha.trim()) { setErro("Digite a senha."); return; } setBusy(true); setErro(null); try { const r = await fetch(API, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ senha, action: "list" }), }); const data = await r.json(); if (!r.ok || data.status !== "success") { setErro(data.message || "Senha incorreta."); setBusy(false); return; } setTokens(data.tokens || []); setAuth(true); setDemo(false); buscarStats(); buscarLeads(); } catch (err) { // sem servidor (preview): entra em modo demonstração setAuth(true); setDemo(true); setTokens([]); } finally { setBusy(false); } }; const gerar = async () => { const n = Math.max(1, Math.min(100, parseInt(qty, 10) || 1)); setBusy(true); setErro(null); try { const r = await fetch(API, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ senha, action: "generate", prefix: prefix.trim(), quantity: n }), }); const data = await r.json(); if (!r.ok || data.status !== "success") { setErro(data.message || "Erro ao gerar."); setBusy(false); return; } setTokens(data.tokens || []); } catch (err) { // preview: gera localmente só pra demonstração const novos = Array.from({ length: n }, () => ({ token: tokenLocalDemo(prefix.trim()), used: false })); setTokens((prev) => [...novos, ...prev]); setDemo(true); } finally { setBusy(false); } }; const buscarLeads = async () => { try { const r = await fetch("leads.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ senha, action: "list" }), }); const data = await r.json(); if (r.ok && data.status === "success") setLeads(data.leads || []); } catch (e) {} }; const buscarStats = async () => { try { const r = await fetch("eventos.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ senha, action: "stats" }), }); const data = await r.json(); if (r.ok && data.status === "success") setStats({ totais: data.totais || {}, por_dia: data.por_dia || {} }); } catch (e) {} }; const atualizar = async () => { setBusy(true); setErro(null); buscarStats(); buscarLeads(); try { const r = await fetch(API, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ senha, action: "list" }), }); const data = await r.json(); if (r.ok && data.status === "success") setTokens(data.tokens || []); } catch (err) {} finally { setBusy(false); } }; const copiar = async (texto, id) => { try { await navigator.clipboard.writeText(texto); } catch { const ta = document.createElement("textarea"); ta.value = texto; document.body.appendChild(ta); ta.select(); document.execCommand("copy"); ta.remove(); } setCopied(id); setTimeout(() => setCopied(null), 1500); }; const lbl = { fontFamily: TYPE.sans, fontSize: 9.5, color: t.ink3, letterSpacing: "0.18em", textTransform: "uppercase", fontWeight: 700, marginBottom: 8, }; const inp = { width: "100%", padding: "13px 15px", boxSizing: "border-box", background: t.bg0, border: `1px solid ${t.border}`, borderRadius: RADIUS.md, color: t.ink1, fontFamily: TYPE.sans, fontSize: 14, fontWeight: 600, outline: "none", }; return (