// Root Cynamonurs app — switches between Landing, UploadGate, and the in-app view. function App() { const [view, setView] = React.useState('landing'); // 'landing' | 'gate' | 'app' | 'signup' | 'login' | 'legal' const [initialTab, setInitialTab] = React.useState('dx'); const [showFinal, setShowFinal] = React.useState(false); const [pendingSignup, setPendingSignup] = React.useState(null); const goToGate = () => { if (window.DATA?.loaded) { setView('app'); } else if (isGateAlreadyPassed()) { // Already passed in previous session — load data and skip gate UI. loadAllData().then(() => setView('app')); } else { setView('gate'); } window.scrollTo({ top: 0 }); }; const onUnlock = () => { setView('app'); window.scrollTo({ top: 0 }); }; // Account creation flow const goToSignup = () => { setView('signup'); window.scrollTo({ top: 0 }); }; const onSignupContinue = (data) => { setPendingSignup(data || {}); setView('legal'); window.scrollTo({ top: 0 }); }; const onTermsAccept = () => { setPendingSignup(null); goToGate(); }; const onTermsDecline = () => { setPendingSignup(null); setView('signup'); window.scrollTo({ top: 0 }); }; // Inicio de sesión (usuario que ya tiene cuenta y suscripción). // Un usuario que regresa NO debe volver a la pantalla de libros: entra directo a la app. const goToLogin = () => { setView('login'); window.scrollTo({ top: 0 }); }; const onLogin = () => { markGatePassed(); if (window.DATA?.loaded) { setView('app'); window.scrollTo({ top: 0 }); return; } loadAllData().then(() => { setView('app'); window.scrollTo({ top: 0 }); }); }; // Wrap setView so that requests to enter 'app' route through the gate first. const safeSetView = (v) => { if (v === 'app') { goToGate(); return; } if (v === 'gate') { goToGate(); return; } setView(v); }; const jumpTo = (id) => { const el = document.getElementById(id); if (!el) return; const y = el.getBoundingClientRect().top + window.scrollY - 80; window.scrollTo({ top: y, behavior: 'smooth' }); }; const resetSession = () => { resetGate(); if (window.DATA) { window.DATA.loaded = false; window.DATA.dxs = null; window.DATA.objs = null; window.DATA.ints = null; } PlanStore.reset(); setView('gate'); }; // Expose for Cuenta button React.useEffect(() => { window.__resetCynamonurs = resetSession; }, []); // Cerrar sesión desde la pestaña Cuenta / menú de usuario. React.useEffect(() => { window.__cynaLogout = () => { setView('landing'); window.scrollTo({ top: 0 }); }; return () => { delete window.__cynaLogout; }; }, []); // Abrir la pestaña Cuenta desde la nav (landing o dentro de la app). const openAccount = () => { if (view === 'app' && window.__cynaGoTab) { window.__cynaGoTab('cuenta'); window.scrollTo({ top: 0, behavior: 'smooth' }); return; } setInitialTab('cuenta'); goToGate(); }; // Allow IntDetail CTA to open the final table. React.useEffect(() => { window.__openFinalTable = () => setShowFinal(true); return () => { delete window.__openFinalTable; }; }, []); // ?demo=1 — skip the upload gate and land on the app shell so the user can build a plan from scratch. // ?demo=full — also auto-build an example plan (Dx 00132 + Obj 1605 + Int 1410) and open the final table. React.useEffect(() => { const params = new URLSearchParams(location.search); const demo = params.get('demo'); if (!demo) return; markGatePassed(); loadAllData().then(() => { if (demo === 'full') { const dx = window.DATA.dxs.items.find(d => d.codigo === '00132'); if (dx) { setPlanDx(dx); (dx.caracteristicas || []).slice(0, 2).forEach(c => togglePlanDxItem('selectedDefinitorias', c)); (dx.factores || []).slice(0, 1).forEach(f => togglePlanDxItem('selectedFactores', f)); } const obj = window.DATA.objs.items.find(o => o.codigo === '1605'); if (obj) { setPlanObjective(obj); (obj.indicadores || []).slice(0, 3).forEach(i => togglePlanObjectiveIndicator(i.codigo)); updatePlanObjective({ basal: 2, meta: 4 }); } const intv = window.DATA.ints.items.find(i => i.codigo === '1410'); if (intv) { setPlanIntervention(intv); (intv.actividades || []).slice(0, 6).forEach(a => togglePlanInterventionActivity(a)); } setView('app'); setTimeout(() => setShowFinal(true), 250); } else { setView('app'); } }); }, []); return ( <> {view !== 'gate' &&