// ───────────────────────────────────────────────────────────── // ABA ANÁLISE DE COMPORTAMENTO // Re-skin da ferramenta do Dr. Ulisses no design do app. // A LÓGICA (perguntas, perfis, diagnóstico) vem 100% de // analise-logic.js (APP_DATA + diagnoseProfile) — NÃO é alterada aqui. // Estado e localStorage TOTALMENTE separados dos Hábitos. // ───────────────────────────────────────────────────────────── const { useState: useStateAnalise, useEffect: useEffectAnalise, useRef: useRefAnalise } = React; const ANALISE_KEY = "analise_progresso_v1"; // chave própria — não toca em "habitos_v1" function AnaliseScreen({ t, onBack, initialToken, acessoLivre }) { // ── estado isolado (começa SEMPRE do zero; não resume sessão antiga) ── // Pelo botão: exige a senha (GRAXA). Pelo link público ou link com token // (?token=...): pula o portão e cai direto no aviso legal. const [step, setStep] = useStateAnalise(() => { if (acessoLivre) { window.USER_TOKEN = null; return "legal"; } if (initialToken) { window.USER_TOKEN = initialToken; return "legal"; } return "token"; }); const [qi, setQi] = useStateAnalise(0); const [answers, setAnswers] = useStateAnalise({}); const [resultCode, setResultCode] = useStateAnalise(null); const [tokenInput, setTokenInput] = useStateAnalise(""); const [burning, setBurning] = useStateAnalise(false); const [tokenErro, setTokenErro] = useStateAnalise(null); const QUESTIONS = APP_DATA.questions; const total = QUESTIONS.length; const setAnswer = (id, val) => setAnswers((a) => ({ ...a, [id]: val })); const isValid = (q) => { const v = answers[q.id]; if (q.type === "slider") return true; if (v === undefined || v === null || v === "") return false; if (q.type === "text") return String(v).trim().length > 0; if (q.type === "number") { const n = parseFloat(v); if (isNaN(n)) return false; if (q.min !== undefined && n < q.min) return false; if (q.max !== undefined && n > q.max) return false; return true; } if (q.type === "single") return !!v; if (q.type === "multiple") return Array.isArray(v) && v.length > 0; return true; }; // avança SEM revalidar (usado pelo clique em opção única — evita o bug de // estado defasado que pedia 2 cliques) const goForward = () => { if (qi < total - 1) { setQi(qi + 1); scrollTop(); } else { setStep("evaluating"); scrollTop(); } }; const next = () => { const q = QUESTIONS[qi]; if (!isValid(q)) { setShake(true); setTimeout(() => setShake(false), 480); return; } goForward(); }; const back = () => { if (qi > 0) { setQi(qi - 1); scrollTop(); } }; const [shake, setShake] = useStateAnalise(false); const scrollRef = useRefAnalise(null); const scrollTop = () => { if (scrollRef.current) scrollRef.current.scrollTop = 0; }; // ── PROGRESSO ─────────────────────────────────────────────── const pct = step === "question" ? Math.round(((qi + 1) / total) * 100) : ["evaluating", "result", "microcopy"].includes(step) ? 100 : 0; // ── TOKEN GATE → vai pro Aviso Legal (a queima acontece ao confirmar o legal, // a validação/queima acontece JÁ AQUI — código inválido é barrado // na hora, antes do Aviso Legal.) ── const entrarToken = async (e) => { e.preventDefault(); const token = tokenInput.trim(); if (!token) { setShake(true); setTimeout(() => setShake(false), 480); return; } window.USER_TOKEN = token; setTokenErro(null); // senha GRAXA / link livre → acesso livre, NÃO queima, vai pro legal if (typeof tokenEhLivre === "function" && tokenEhLivre(token)) { window.USER_TOKEN_BURNED = false; setStep("legal"); scrollTop(); return; } // valida + queima AGORA; só avança se o servidor confirmar SUCESSO setBurning(true); try { const resp = await fetch("burn_token.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ token }), }); const data = await resp.json(); if (resp.ok && data && data.status === "success") { window.USER_TOKEN_BURNED = true; setBurning(false); setStep("legal"); scrollTop(); } else { setTokenErro("invalido"); setShake(true); setTimeout(() => setShake(false), 480); setBurning(false); } } catch (err) { // Sem servidor (preview/local): NÃO libera código aleatório. setTokenErro("invalido"); setShake(true); setTimeout(() => setShake(false), 480); setBurning(false); } }; // CONFIRMAÇÃO do Aviso Legal: o token já foi validado/queimado no gate // (ou é acesso livre / link com token). Aqui só queima o token de LINK // que ainda não passou pelo gate, e segue. const confirmarLegal = async () => { const token = window.USER_TOKEN; // acesso livre (GRAXA / link público) ou token já queimado no gate → segue if (acessoLivre || window.USER_TOKEN_BURNED || (typeof tokenEhLivre === "function" && tokenEhLivre(token))) { if (window.track) track("iniciou"); setStep("intro0"); scrollTop(); return; } // entrou por LINK com ?token= (não passou pelo gate) → queima aqui setBurning(true); setTokenErro(null); try { const resp = await fetch("burn_token.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ token }), }); const data = await resp.json(); if (resp.ok && data && data.status === "success") { if (window.track) track("iniciou"); window.USER_TOKEN_BURNED = true; setBurning(false); setStep("intro0"); scrollTop(); } else { setTokenErro("invalido"); setBurning(false); setStep("token"); } } catch (e) { setTokenErro("invalido"); setBurning(false); setStep("token"); } }; // ════════════════════════════════════════════════════════════ return (
{/* keyframes locais */} {/* TOPO STICKY */}
Análise de Comportamento
{/* BARRA DE PROGRESSO */}
{/* CONTEÚDO POR ETAPA */}
{step === "token" && } {step === "legal" && } {step === "intro0" && { setStep("intro1"); scrollTop(); }} />} {step === "intro1" && { setStep("question"); setQi(0); scrollTop(); }} />} {step === "question" && } {step === "evaluating" && { let code; try { code = diagnoseProfile(answers); } catch (e) { code = "I"; } if (window.track) track("concluiu"); setResultCode(code); setStep("result"); scrollTop(); }} />} {step === "result" && resultCode && { setStep("microcopy"); scrollTop(); }} />} {step === "microcopy" && resultCode && }
); } // ── helper: HTML controlado vindo do APP_DATA (apenas
e ) ── function htm(s) { return { dangerouslySetInnerHTML: { __html: s } }; } // ───────────────────────────────────────────────────────────── // TOKEN GATE // ───────────────────────────────────────────────────────────── function TokenGate({ t, value, onChange, onSubmit, shake, erro }) { return (

Acesso
exclusivo

Digite o código fornecido pelo
Dr. Ulisses Nakagawa para iniciar

onChange(e.target.value)} placeholder="Digite o código" autoComplete="off" style={{ width: "100%", padding: "16px 18px", textAlign: "center", background: t.bg1, border: `1px solid ${t.border}`, borderRadius: RADIUS.md, color: t.ink1, fontFamily: TYPE.sans, fontSize: 15, fontWeight: 600, letterSpacing: "0.04em", boxSizing: "border-box", outline: "none", }} />
{erro && (
Código inválido!
Converse com o Dr. Ulisses Nakagawa.
)}
); } // ── Prova social: "473 pessoas já fizeram" (conta real + base) ── function SocialProof({ t }) { const base = (window.LINKS && LINKS.analise_total_feitas) || 473; const [n, setN] = useStateAnalise(base); useEffectAnalise(() => { let vivo = true; (async () => { try { const r = await fetch("eventos.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "count", base, evento: "iniciou" }), }); const d = await r.json(); if (vivo && d && d.status === "success" && typeof d.total === "number") setN(d.total); } catch (e) { /* sem servidor: mantém a base */ } })(); return () => { vivo = false; }; }, []); return (
{[0, 1, 2].map((i) => ( ))} {n.toLocaleString("pt-BR")} pessoas já fizeram
); } // ───────────────────────────────────────────────────────────── // AVISO LEGAL (CONFIG_ALERTAS.tela1) // ───────────────────────────────────────────────────────────── function LegalCard({ t, onOk, busy }) { const c = CONFIG_ALERTAS.tela1; return (
{c.titulo &&

{c.titulo}

}

); } // ───────────────────────────────────────────────────────────── // CARDS DE INTRO // ───────────────────────────────────────────────────────────── function IntroCard({ t, data, label, cta, onNext }) { return (
{label} · Antes de começar
{data.title &&

}

); } // ───────────────────────────────────────────────────────────── // PERGUNTA // ───────────────────────────────────────────────────────────── function QuestionCard({ t, q, index, total, answers, setAnswer, onNext, onAuto, onBack, isValid, shake }) { const val = answers[q.id]; const valid = isValid(q); return (
Pergunta {index + 1} / {total}

{q.description &&

{q.description}

} {!q.description &&
} {/* INPUTS */} {(q.type === "text" || q.type === "number") && ( setAnswer(q.id, e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") onNext(); }} placeholder="Digite aqui..." min={q.min} max={q.max} autoFocus style={{ width: "100%", padding: "16px 18px", background: t.bg1, border: `1px solid ${t.border}`, borderRadius: RADIUS.md, color: t.ink1, fontFamily: TYPE.sans, fontSize: 16, fontWeight: 600, boxSizing: "border-box", outline: "none", }} /> )} {q.type === "single" && (
{q.options.map((opt, i) => { const sel = val === opt; return ( ); })}
)} {q.type === "multiple" && (
{q.options.map((opt, i) => { const arr = Array.isArray(val) ? val : []; const sel = arr.includes(opt); return ( ); })}
)} {q.type === "slider" && ( setAnswer(q.id, n)} /> )} {/* NAV */}
{q.type !== "single" && ( )}
); } function SliderInput({ t, q, val, onChange }) { return (
{val} / {q.max}
onChange(parseInt(e.target.value, 10))} style={{ width: "100%", accentColor: t.accent, height: 6, cursor: "pointer" }} />
{q.min}{q.max}
); } // ───────────────────────────────────────────────────────────── // AVALIANDO (8s — fiel ao original) // ───────────────────────────────────────────────────────────── function Evaluating({ t, onDone }) { const ringRef = useRefAnalise(null); useEffectAnalise(() => { const c = ringRef.current; if (c) { c.getBoundingClientRect(); requestAnimationFrame(() => { c.style.strokeDashoffset = "0"; }); } const id = setTimeout(onDone, 8000); return () => clearTimeout(id); }, []); return (

Estou avaliando
tuas respostas

Um momento, por favor.
Tua análise merece atenção e respeito.

); } // ───────────────────────────────────────────────────────────── // RESULTADO // ───────────────────────────────────────────────────────────── function ResultCard({ t, code, onNext }) { const p = APP_DATA.results[code]; return (
Seu perfil

{p.title}

); } // ───────────────────────────────────────────────────────────── // MICROCOPY / CTA FINAL // ───────────────────────────────────────────────────────────── function MicrocopyCard({ t, code }) { return (

Entender é o primeiro passo.
Resolver é o próximo.

Leve essa análise pra uma consulta comigo
e a gente monta o teu plano de verdade.

{/* BOTÃO IDÊNTICO AO DA ABA CONSULTA */} { if (window.track) track("clicou_consulta"); }} className="cta-agendar" style={{ position: "relative", overflow: "hidden", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 4, padding: "20px 20px", background: t.ink1, color: t.bg0, borderRadius: RADIUS.md, textDecoration: "none", fontFamily: TYPE.sans, textAlign: "center", boxShadow: "0 10px 28px rgba(0,0,0,0.4)", }} > Agendar Consulta Com o Dr. Ulisses Nakagawa {/* COMPARTILHAR NOS STORIES — opcional, nunca invasivo */} {/* CONTATO OPCIONAL — recolhido por padrão, totalmente pulável */}
); } // ── Captura de contato opcional (recolhida; nada obrigatório) ── function LeadCapture({ t }) { const [open, setOpen] = useStateAnalise(false); const [nome, setNome] = useStateAnalise(""); const [wpp, setWpp] = useStateAnalise(""); const [done, setDone] = useStateAnalise(false); const [busy, setBusy] = useStateAnalise(false); const enviar = async () => { const digits = (wpp || "").replace(/\D/g, ""); if (!nome.trim() || digits.length < 10) { return; } setBusy(true); try { await fetch("leads.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ nome: nome.trim(), whatsapp: digits }), keepalive: true, }); } catch (e) {} if (window.track) track("lead"); setBusy(false); setDone(true); }; if (done) { return (

Recebido. O Dr. Ulisses vai falar com você. ✓

); } if (!open) { return ( ); } 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 (
setNome(e.target.value)} placeholder="Seu nome" style={inp} /> setWpp(e.target.value)} placeholder="Seu WhatsApp (com DDD)" inputMode="tel" style={inp} />
); } // ── Botão "Compartilhar nos Stories" (gera a imagem 1080×1920) ── function ShareStoriesButton({ t, code }) { const [busy, setBusy] = useStateAnalise(false); const onShare = async () => { if (busy) return; setBusy(true); try { if (typeof compartilharResultado === "function") await compartilharResultado(code); } catch (e) {} setBusy(false); }; return ( ); } // ── estilos compartilhados ────────────────────────────────── function primaryBtn(t) { return { width: "100%", marginTop: 26, padding: "18px", background: t.ink1, color: t.bg0, border: "none", borderRadius: RADIUS.md, cursor: "pointer", fontFamily: TYPE.sans, fontSize: 14, fontWeight: 800, letterSpacing: "0.12em", textTransform: "uppercase", }; } function primaryBtnInline(t) { return { padding: "16px 22px", background: t.ink1, color: t.bg0, border: "none", borderRadius: RADIUS.md, cursor: "pointer", fontFamily: TYPE.sans, fontSize: 13, fontWeight: 800, letterSpacing: "0.1em", textTransform: "uppercase", }; } function optBtn(t, sel, withBox) { return { width: "100%", padding: withBox ? "15px 16px" : "16px 18px", background: sel ? t.accentMuted : t.bg1, border: `1.5px solid ${sel ? t.accent : t.border}`, borderRadius: RADIUS.md, color: sel ? t.ink1 : t.ink2, cursor: "pointer", fontFamily: TYPE.sans, fontSize: 14.5, fontWeight: 600, lineHeight: 1.4, textAlign: withBox ? "left" : "center", display: "flex", alignItems: "center", gap: 12, justifyContent: withBox ? "flex-start" : "center", transition: "border-color .12s ease, background .12s ease", }; } Object.assign(window, { AnaliseScreen });