// ───────────────────────────────────────────────────────────── // APP — Clínica Digital · Dr. Ulisses Nakagawa // Home (carrossel) → Lista → Detalhe // Mesmo template para Suplementos, Manipulados, Peptídeos // ───────────────────────────────────────────────────────────── const { useState, useEffect, useRef, useMemo } = React; // ───────── STICKY AGENDAR (interno, topo da tela) ───────── // Aparece no topo da Lista e do Detalhe, sticky no scroll. // NÃO aparece na home (a carrossel já é a ação principal). function InlineAgendar({ t }) { return ( Agendar Consulta Com o Dr. Ulisses Nakagawa {/* flash de luz que passa na horizontal */} ); } // ───────── BOTTOM NAV ───────── function BottomNav({ active, onChange, t, isLight, onToggleTheme }) { const items = [ { id: "home", label: "Início", icon: "home" }, { id: "consulta", label: "Consulta", icon: "chat" }, { id: "social", label: "Social", icon: "user" }, ]; return (
{items.map((it) => ( ))} {/* ───── BOTÃO MODO CLARO / ESCURO — sempre visível ───── */}
); } // ───────── HOME SCREEN ───────── function HomeScreen({ t, onOpenModule, onBuyModule, onRedeemCode, isUnlocked, frameWidth, frameHeight }) { return (
{/* HEADER editorial */}
Clínica Digital
Dr. Ulisses Nakagawa
Médico e Farmacêutico
CRM-MS 16293
Conteúdo educativo · Não substitui consulta médica
{/* Acesso para quem JÁ comprou — discreto, fora do caminho da venda */}
{/* CARROSSEL */}
); } // ───────── LIST SCREEN ───────── function ListScreen({ t, type, onBack, onOpenProduct, density, showVisual }) { const meta = PRODUCT_TYPES[type]; const products = useMemo(() => PRODUCTS.filter(p => p.type === type), [type]); const [query, setQuery] = useState(""); const [activeCat, setActiveCat] = useState("Todos"); // Filtros por sessão: // · Suplementos: Todos · ★ Eu uso · (sem categorias, suplemento age em vários sistemas) // · Manipulados: Todos · categorias funcionais // · Peptídeos: Todos · Aprovados · categorias funcionais const showCats = type !== "suplemento"; const cats = useMemo(() => { const out = ["Todos"]; if (type === "suplemento" && products.some(p => p.eu_uso)) out.push("★ Eu uso"); if (type === "suplemento") out.push("Referências"); if (type === "peptideo" && products.some(p => p.aprovado)) out.push("Aprovados"); if (showCats) { const seen = new Set(); products.forEach(p => { if (!seen.has(p.category)) { seen.add(p.category); out.push(p.category); } }); } return out; }, [products, type, showCats]); const filtered = useMemo(() => { return products.filter(p => { const q = query.toLowerCase().trim(); const matchQ = !q || p.name.toLowerCase().includes(q) || p.tagline.toLowerCase().includes(q); let matchC = true; if (activeCat === "Todos") matchC = true; else if (activeCat === "★ Eu uso") matchC = !!p.eu_uso; else if (activeCat === "Aprovados") matchC = !!p.aprovado; else matchC = p.category === activeCat; return matchQ && matchC; }); }, [products, query, activeCat]); return (
{/* TOPO STICKY — Back + Agendar Consulta */}
{/* HEADER (não sticky) */}
{meta.label}
{meta.description}
{/* SEARCH + CATEGORIAS */}
setQuery(e.target.value)} placeholder={`Buscar em ${meta.label.toLowerCase()}…`} style={{ width: "100%", padding: "12px 14px 12px 38px", background: t.bg1, border: `1px solid ${t.border}`, borderRadius: 12, color: t.ink1, fontFamily: TYPE.sans, fontSize: 14, outline: "none", }} />
{/* COUNT — escondido na aba de Referências */} {activeCat !== "Referências" && (
{filtered.length} {filtered.length === 1 ? "registro" : "registros"} de {products.length}
)} {/* CONTEÚDO — lista de produtos OU vista de referências */} {activeCat === "Referências" ? ( ) : (
{filtered.map((p) => ( onOpenProduct(p)} /> ))} {filtered.length === 0 && (
Nenhum resultado para "{query}"
)}
)} {/* FERRAMENTAS — só em Peptídeos */} {type === "peptideo" && (
Ferramentas
)} {/* DISCLAIMER LEGAL */}
); } // ───────── DETAIL SCREEN ───────── function DetailScreen({ t, product, onBack }) { const meta = PRODUCT_TYPES[product.type]; return (
{/* TOPO STICKY — Back + Agendar Consulta */}
{/* HERO */}
{meta.breadcrumb} · {product.category}

{nl2br(product.name)}

{product.tagline}

{/* Apenas o badge "Eu uso" no hero para suplementos (sem pills de classificação aqui — só no rodapé) */} {product.eu_uso && product.type === "suplemento" && (
★ Eu uso
)} {/* STATS GRID removido para Manipulados — informação já vive em "Como usar" */} {false && product.stats && product.type === "manipulado" && (
{product.stats.map((s, i) => (
{s.k}
{s.v}
))}
)}
{/* SEPARADOR EDITORIAL */}
{/* SEÇÕES */}
{product.sections.map((s, i) => (
))}
{/* CTA FINAL — Agendar Consulta */}
Agendar Consulta Com o Dr. Ulisses Nakagawa
); } // ───────── APP ROOT ───────── function App() { const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "theme": "editorial", "density": "spacious", "showVisual": true, "viewAs": "real" }/*EDITMODE-END*/; const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS); const t = THEMES[tweaks.theme] || THEMES.editorial; // navegação const [screen, setScreen] = useState("home"); // home | list | detail | consulta | habitos | social | analise const [listType, setListType] = useState(null); const [product, setProduct] = useState(null); const [deepToken, setDeepToken] = useState(null); // token vindo de link (?token=...) const [acessoLivre, setAcessoLivre] = useState(false); // entrou pelo link público (sem palavra) // ROTAS por URL (funciona com .htaccess de redirect ou rewrite): // • ?token=XXX ou /comportamentos/analise/XXX → abre a Análise já com o token // • ?area=criador ou /admin → abre a Área do Criador (pede senha) useEffect(() => { try { const p = new URLSearchParams(window.location.search); const path = window.location.pathname || ""; // token: query (?token= / ?analise=) ou caminho /comportamentos/analise/ let tk = (p.get("token") || p.get("analise") || "").trim(); let linkPublico = false; if (!tk) { const m = path.match(/\/comportamentos\/analise\/([^\/?#]+)/i); if (m) tk = decodeURIComponent(m[1]).trim(); // caminho público SEM token (/comportamentos/analise) → acesso livre por LINK else if (/\/comportamentos\/analise\/?$/i.test(path) || p.get("acesso") === "publico") { linkPublico = true; } } // área do criador: ?area=criador / ?admin ou caminho /admin const isAdmin = p.get("area") === "criador" || p.has("admin") || /\/admin\/?$/i.test(path); // Subpastas dos módulos (vitrine): /suplementos /manipulados /peptideos /caneta /combo // /comportamentos /gestao — também aceita ?p=. Abre direto na seção certa. let modId = (p.get("p") || "").trim().toLowerCase(); if (!modId) { const mm = path.match(/^\/(suplementos|manipulados|peptideos|caneta|combo|comportamentos|gestao)\/?$/i); if (mm) modId = mm[1].toLowerCase(); } if (modId === "comportamentos") modId = "habitos"; // alias bonito → módulo if (isAdmin) { setScreen("admin"); } else if (linkPublico) { setAcessoLivre(true); setScreen("analise"); } else if (tk) { setDeepToken(tk); setScreen("analise"); } else if (modId) { const m = MODULES.find((x) => x.id === modId); if (m) { if (m.type === "gestao") { window.location.replace(encodeURI("gestao-tempo/Gestão de Tempo.html")); return; } else if (m.type === "habitos") { setScreen("habitos"); } else { setListType(m.type); setScreen("list"); } } } } catch (e) {} }, []); // ─── ACESSO (desbloqueio por código, sem login, sem servidor) ─── const [unlocked, setUnlocked] = useState(() => getUnlocked()); const [accessModule, setAccessModule] = useState(null); // módulo aberto no modal de código const viewAs = tweaks.viewAs || "real"; const isCreator = viewAs === "creator" || unlocked.has("creator"); const moduleUnlocked = (m) => { if (m.free || m.id === "habitos") return true; // produto grátis: aberto pra todos if (viewAs === "creator") return true; // criador vê tudo if (viewAs === "visitor") return false; // simula quem não comprou if (m.isCombo) return unlocked.has("all"); return unlocked.has("all") || unlocked.has(m.id); }; const handleRedeemed = (scope) => { setUnlocked(getUnlocked()); setAccessModule(null); if (scope && scope !== "all") { const m = MODULES.find(x => x.id === scope); if (m) { setListType(m.type); setScreen("list"); } } }; // tab ativa no bottom nav (derivado do screen) const activeTab = (screen === "consulta" || screen === "atendimento") ? "consulta" : (screen === "social") ? "social" : "home"; // dimensões reais da janela (app preenche a tela — SEM moldura de iPhone) const [vw, setVw] = useState(typeof window !== "undefined" ? window.innerWidth : 390); const [vh, setVh] = useState(typeof window !== "undefined" ? window.innerHeight : 844); useEffect(() => { const on = () => { setVw(window.innerWidth); setVh(window.innerHeight); }; window.addEventListener("resize", on); window.addEventListener("orientationchange", on); return () => { window.removeEventListener("resize", on); window.removeEventListener("orientationchange", on); }; }, []); const FW = Math.min(vw, 480); // coluna do app (centralizada no desktop) const FH = vh; const openModule = (m) => { if (m.id === "habitos") { setScreen("habitos"); return; } // produto grátis → abre o tracker if (m.id === "gestao") { window.location.href = encodeURI("gestao-tempo/Gestão de Tempo.html"); return; } // app de gestão de tempo if (moduleUnlocked(m)) { if (m.isCombo) return; // combo já é acesso total setListType(m.type); setScreen("list"); return; } buyModule(m); // bloqueado / combo / em breve → direto pra venda }; // Botão "Comprar" do pôster — vai direto pro Hotmart (ou WhatsApp como fallback) const buyModule = (m) => { // Funciona mesmo para módulos "soon" — usa o link da Hotmart se houver, // ou cai no WhatsApp como fallback (mensagem adequada para cada caso). if (m.status === "soon") { const url = (typeof HOTMART_LINKS !== "undefined" && HOTMART_LINKS[m.id]?.link) || ""; window.open( url || waLink(`Olá, quero ser avisado quando ${m.name} estiver disponível.`), "_blank" ); return; } const url = buyLink(m.id, m.name); window.open(url, "_blank"); }; // debug nav (apenas para teste rápido) useEffect(() => { window.__nav = (type) => { setListType(type); setScreen("list"); }; window.__detail = (id) => { const p = PRODUCTS.find(x => x.id === id); if (p) { setProduct(p); setScreen("detail"); } }; }, []); const openProduct = (p) => { setProduct(p); setScreen("detail"); }; return ( <> {/* Tweaks panel */} setTweak("theme", v)} /> setTweak("viewAs", v)} /> { resetAccess(); setUnlocked(new Set()); }} /> setTweak("density", v)} /> setTweak("showVisual", v)} /> {/* STAGE — app preenche a tela inteira, sem moldura de protótipo */}
{screen === "home" && ( setAccessModule({ generic: true })} /> )} {screen === "list" && listType && ( setScreen("home")} onOpenProduct={openProduct} /> )} {screen === "detail" && product && ( setScreen("list")} /> )} {screen === "consulta" && setScreen("atendimento")} />} {screen === "atendimento" && setScreen("consulta")} />} {screen === "habitos" && { setScreen("home"); setListType(null); setProduct(null); }} onIniciarAnalise={() => setScreen("analise")} />} {screen === "analise" && { setDeepToken(null); setAcessoLivre(false); setScreen("habitos"); }} />} {screen === "social" && } {screen === "admin" && setScreen("home")} />} {/* BOTTOM NAV — sempre visível (oculto na Análise e no Admin, fluxos focados) */} {screen !== "analise" && screen !== "admin" && ( setTweak("theme", tweaks.theme === "clinical" ? "editorial" : "clinical")} onChange={(id) => { if (id === "home") { setScreen("home"); setListType(null); setProduct(null); } else { setScreen(id); } }} t={t} /> )} {/* MODAL DE ACESSO — resgate de código / comprar */} {accessModule && ( setAccessModule(null)} onRedeemed={handleRedeemed} /> )}
); } ReactDOM.createRoot(document.getElementById("root")).render();