// Configuración Supabase const SUPABASE_URL = "https://fokhpkrzbonieyvcujlw.supabase.co"; const SUPABASE_KEY = "sb_publishable_4vtENh_jt7CcxhYdHk-BJw_H9quDyht"; let dbClient; try { if (typeof window.supabase !== 'undefined') { dbClient = window.supabase.createClient(SUPABASE_URL, SUPABASE_KEY); } else { dbClient = supabase.createClient(SUPABASE_URL, SUPABASE_KEY); } } catch (e) { console.error("Supabase init error:", e); } // Estado Global const AppState = { isAuthenticated: false, userEmail: null, theme: localStorage.getItem('theme') || 'light', notificationsEnabled: localStorage.getItem('sanar_notifications') === 'true', notificationTime: localStorage.getItem('sanar_notification_time') || '20:00', lastNotificationDate: localStorage.getItem('sanar_last_notify') || '', answers: { paso1: '', paso2: '', paso3: '', paso4: '', paso5_1: '', paso5_2: '' } }; const root = document.getElementById('app-root'); // Recordatorios Locales const NotificationManager = { requestPermission: async () => { if (!("Notification" in window)) return false; if (Notification.permission === "granted") return true; const p = await Notification.requestPermission(); return p === "granted"; }, trigger: (title, body) => { const options = { body, icon: 'icon.png', badge: 'icon.png' }; if (navigator.serviceWorker && navigator.serviceWorker.ready) { navigator.serviceWorker.ready.then(reg => reg.showNotification(title, options)).catch(()=> new Notification(title, options)); } else { new Notification(title, options); } }, check: () => { if (!AppState.notificationsEnabled) return; const now = new Date(); const cTime = now.toTimeString().substring(0, 5); const tDate = now.toDateString(); if (cTime === AppState.notificationTime && AppState.lastNotificationDate !== tDate) { NotificationManager.trigger("Sanar la Relación", "Tómate un momento para cultivar la amabilidad contigo."); AppState.lastNotificationDate = tDate; localStorage.setItem('sanar_last_notify', tDate); } }, init: () => { setInterval(() => NotificationManager.check(), 60000); setTimeout(() => NotificationManager.check(), 5000); } }; NotificationManager.init(); // Gestor de Flujo de Pantallas const FlowManager = { currentStep: 0, init: () => { const sessionActive = sessionStorage.getItem('sanar_relacion_session'); const email = sessionStorage.getItem('sanar_relacion_email'); if (sessionActive === 'true' && email) { AppState.isAuthenticated = true; AppState.userEmail = email; FlowManager.goToStep(1); } else { FlowManager.goToStep(0); } }, goToStep: (index) => { root.style.opacity = '0'; root.style.transform = 'translateY(10px)'; setTimeout(() => { const views = [ Views.Login(), Views.Intro(), Views.Paso1(), Views.Paso2(), Views.Paso3(), Views.Paso4(), Views.Paso5(), Views.Cierre(), Views.Success(), "" // Index 9: Diario (rendered asynchronously) ]; if (index >= 0 && index < views.length) { FlowManager.currentStep = index; root.innerHTML = views[index]; if (window.feather) window.feather.replace(); FlowManager.attachEvents(); FlowManager.renderBottomMenu(); // Celebración Visual (Confeti) if (index === 8 && window.confetti) { setTimeout(() => { confetti({ particleCount: 120, spread: 80, origin: { y: 0.6 }, colors: ['#e8a598', '#d68c8c', '#f4c7bb', '#FFD700', '#ffffff'], disableForReducedMotion: true }); }, 300); } root.style.opacity = '1'; root.style.transform = 'translateY(0)'; } }, 300); }, renderBottomMenu: () => { const menuEl = document.getElementById('bottom-menu-container'); // Ocultar en Login(0) y Success(8). Mostrar en el resto. if (FlowManager.currentStep === 0 || FlowManager.currentStep === 8) { menuEl.classList.add('translate-y-full'); setTimeout(() => menuEl.classList.add('hidden'), 500); } else { if (menuEl.classList.contains('hidden') || !menuEl.innerHTML.trim()) { menuEl.innerHTML = Views.BottomNav(); menuEl.classList.remove('hidden'); if (window.feather) window.feather.replace(); FlowManager.attachMenuEvents(); setTimeout(() => menuEl.classList.remove('translate-y-full'), 50); } } }, attachMenuEvents: () => { // Reset colors ['nav-home', 'nav-diary', 'nav-settings'].forEach(id => { const el = document.getElementById(id); if (el) { el.classList.remove('text-peach-600', 'dark:text-peach-400'); el.classList.add('text-sand-900/50', 'dark:text-sand-50/50'); } }); // Highlight current if (FlowManager.currentStep >= 1 && FlowManager.currentStep <= 7) { document.getElementById('nav-home')?.classList.add('text-peach-600', 'dark:text-peach-400'); document.getElementById('nav-home')?.classList.remove('text-sand-900/50', 'dark:text-sand-50/50'); } else if (FlowManager.currentStep === 9) { // Diario document.getElementById('nav-diary')?.classList.add('text-peach-600', 'dark:text-peach-400'); document.getElementById('nav-diary')?.classList.remove('text-sand-900/50', 'dark:text-sand-50/50'); } document.getElementById('nav-home')?.addEventListener('click', () => { FlowManager.saveCurrentStepData(); if (FlowManager.currentStep !== 1) FlowManager.goToStep(1); // Intro }); document.getElementById('nav-diary')?.addEventListener('click', () => { if (FlowManager.currentStep === 9) return; FlowManager.saveCurrentStepData(); // Transition out root.style.opacity = '0'; root.style.transform = 'translateY(10px)'; FlowManager.currentStep = 9; // Re-highlight menu ['nav-home', 'nav-diary', 'nav-settings'].forEach(id => { const el = document.getElementById(id); if (el) { el.classList.remove('text-peach-600', 'dark:text-peach-400'); el.classList.add('text-sand-900/50', 'dark:text-sand-50/50'); } }); document.getElementById('nav-diary')?.classList.add('text-peach-600', 'dark:text-peach-400'); document.getElementById('nav-diary')?.classList.remove('text-sand-900/50', 'dark:text-sand-50/50'); // After transition, load diary setTimeout(() => { root.style.opacity = '1'; root.style.transform = 'translateY(0)'; FlowManager.loadDiary(); }, 310); }); document.getElementById('nav-settings')?.addEventListener('click', () => { FlowManager.saveCurrentStepData(); FlowManager.openSettings(); }); }, loadDiary: async () => { root.innerHTML = `
`; if (window.feather) window.feather.replace(); try { const memberId = sessionStorage.getItem('sanar_relacion_member_id'); const { data: entries, error } = await dbClient .from('sanar_relacion_progress') .select('*') .eq('member_id', memberId) .order('created_at', { ascending: false }); if (error) throw error; root.innerHTML = Views.Diario(entries); if (window.feather) window.feather.replace(); // Empty State Button document.getElementById('btn-start-diary')?.addEventListener('click', () => { FlowManager.goToStep(1); // Intro }); } catch(e) { console.error(e); root.innerHTML = `
Error al cargar tu diario. Por favor, verifica tu conexión a internet.
`; } }, openSettings: () => { if(document.getElementById('settings-overlay')) return; const overlay = document.createElement('div'); overlay.id = 'settings-overlay'; overlay.innerHTML = Views.Settings(); document.body.appendChild(overlay); if (window.feather) window.feather.replace(); // Eventos document.getElementById('btn-close-settings')?.addEventListener('click', () => overlay.remove()); // Theme toggle document.getElementById('set-theme')?.addEventListener('change', (e) => { const isDark = e.target.checked; document.documentElement.classList.toggle('dark', isDark); localStorage.setItem('theme', isDark ? 'dark' : 'light'); AppState.theme = isDark ? 'dark' : 'light'; }); // Notifications document.getElementById('set-notify')?.addEventListener('change', async (e) => { const isEnabled = e.target.checked; const configDiv = document.getElementById('notify-config'); if (isEnabled) { const granted = await NotificationManager.requestPermission(); if (!granted) { alert("Por favor habilita las notificaciones en tu navegador o celular primero."); e.target.checked = false; return; } configDiv.classList.remove('opacity-30', 'pointer-events-none'); } else { configDiv.classList.add('opacity-30', 'pointer-events-none'); } }); document.getElementById('btn-save-settings')?.addEventListener('click', () => { const notifyEnabled = document.getElementById('set-notify').checked; const notifyTime = document.getElementById('set-notify-time').value; localStorage.setItem('sanar_notifications', notifyEnabled); localStorage.setItem('sanar_notification_time', notifyTime); AppState.notificationsEnabled = notifyEnabled; AppState.notificationTime = notifyTime; overlay.remove(); }); // Change PIN Logic document.getElementById('btn-change-pin')?.addEventListener('click', async () => { const newPin = document.getElementById('new-pin').value; const msgEl = document.getElementById('pin-msg'); const btn = document.getElementById('btn-change-pin'); if (!/^\d{4}$/.test(newPin)) { msgEl.textContent = "El PIN debe tener 4 números."; msgEl.className = "text-[10px] text-center text-red-500 mt-1 pb-1"; msgEl.classList.remove('hidden'); return; } btn.disabled = true; btn.innerHTML = ''; try { const memberId = sessionStorage.getItem('sanar_relacion_member_id'); const { error } = await dbClient .from('members') .update({ pin: newPin }) .eq('id', memberId); if (error) throw error; msgEl.textContent = "¡PIN actualizado con éxito en todas tus apps!"; msgEl.className = "text-[10px] text-center text-green-500 mt-1 pb-1"; msgEl.classList.remove('hidden'); document.getElementById('new-pin').value = ''; btn.innerHTML = 'Actualizar PIN'; btn.disabled = false; } catch (err) { msgEl.textContent = "Error al actualizar el PIN. Intenta de nuevo."; msgEl.className = "text-[10px] text-center text-red-500 mt-1 pb-1"; msgEl.classList.remove('hidden'); btn.innerHTML = 'Actualizar PIN'; btn.disabled = false; } }); // Restart Application Flow document.getElementById('btn-show-restart')?.addEventListener('click', () => { document.getElementById('btn-show-restart').classList.add('hidden'); document.getElementById('restart-auth-area').classList.remove('hidden'); document.getElementById('restart-auth-area').classList.add('flex'); }); document.getElementById('btn-confirm-restart')?.addEventListener('click', async () => { const enteredPin = document.getElementById('restart-pin').value; const msgEl = document.getElementById('restart-msg'); const btn = document.getElementById('btn-confirm-restart'); if(!enteredPin) return; btn.disabled = true; btn.innerHTML = ''; msgEl.classList.add('hidden'); try { const email = sessionStorage.getItem('sanar_relacion_email'); const { data: member, error } = await dbClient .from('members') .select('id') .eq('email', email) .eq('pin', enteredPin) .single(); if (error || !member) throw new Error("PIN de confirmación incorrecto."); // Borrar todo el historial previo en Supabase const { error: deleteError } = await dbClient .from('sanar_relacion_progress') .delete() .eq('member_id', member.id); if (deleteError) { throw new Error("No se pudo limpiar tu Diario. " + deleteError.message); } // Restart app effectively AppState.answers = { paso1: '', paso2: '', paso3: '', paso4: '', paso5_1: '', paso5_2: '' }; document.querySelectorAll('.input-area').forEach(input => input.value = ''); overlay.remove(); FlowManager.goToStep(1); // Back to Intro } catch (err) { msgEl.textContent = err.message; msgEl.classList.remove('hidden'); btn.innerHTML = 'Confirmar Limpieza'; btn.disabled = false; } }); document.getElementById('btn-logout')?.addEventListener('click', () => { sessionStorage.clear(); AppState.isAuthenticated = false; AppState.userEmail = null; overlay.remove(); FlowManager.goToStep(0); }); }, attachEvents: () => { const step = FlowManager.currentStep; if (step === 0) { // Login const form = document.getElementById('login-form'); const errorMsg = document.getElementById('login-error'); const btn = document.getElementById('btn-login'); form?.addEventListener('submit', async (e) => { e.preventDefault(); const email = document.getElementById('email').value.trim(); const pin = document.getElementById('pin').value.trim(); if(!email || !pin) return; btn.innerHTML = ''; errorMsg.classList.add('hidden'); try { const { data: member, error } = await dbClient .from('members') .select('id, status') .eq('email', email) .eq('pin', pin) .single(); if (error || !member) throw new Error("Credenciales inválidas."); if (member.status !== 'active') throw new Error("Tu cuenta está deshabilitada."); sessionStorage.setItem('sanar_relacion_session', 'true'); sessionStorage.setItem('sanar_relacion_email', email); sessionStorage.setItem('sanar_relacion_member_id', member.id); AppState.isAuthenticated = true; AppState.userEmail = email; FlowManager.goToStep(1); } catch (err) { errorMsg.textContent = err.message; errorMsg.classList.remove('hidden'); btn.innerHTML = 'Iniciar Experiencia '; if (window.feather) window.feather.replace(); } }); } // Navegación Básica document.getElementById('btn-next')?.addEventListener('click', () => { FlowManager.saveCurrentStepData(); FlowManager.goToStep(step + 1); }); document.getElementById('btn-prev')?.addEventListener('click', () => { FlowManager.saveCurrentStepData(); FlowManager.goToStep(step - 1); }); // Guardar Progreso const btnFinish = document.getElementById('btn-finish'); btnFinish?.addEventListener('click', async () => { btnFinish.innerHTML = ' Guardando...'; try { const memberId = sessionStorage.getItem('sanar_relacion_member_id'); const { error } = await dbClient.from('sanar_relacion_progress').insert([{ member_id: memberId, answers: AppState.answers }]); if (error) { console.error("Supabase Error:", error); throw new Error(error.message); } FlowManager.goToStep(8); // Success } catch(e) { console.error("Error", e); btnFinish.innerHTML = 'Finalizar e Integrar '; alert("Error de Supabase: " + e.message); } }); // Final Screen document.getElementById('btn-restart')?.addEventListener('click', () => { AppState.answers = { paso1:'', paso2:'', paso3:'', paso4:'', paso5_1:'', paso5_2:'' }; FlowManager.goToStep(1); }); document.getElementById('btn-logout-final')?.addEventListener('click', () => { sessionStorage.clear(); AppState.isAuthenticated = false; FlowManager.goToStep(0); }); }, saveCurrentStepData: () => { const step = FlowManager.currentStep; if (step === 2) AppState.answers.paso1 = document.getElementById('ans-paso1')?.value || ''; if (step === 3) AppState.answers.paso2 = document.getElementById('ans-paso2')?.value || ''; if (step === 4) AppState.answers.paso3 = document.getElementById('ans-paso3')?.value || ''; if (step === 5) AppState.answers.paso4 = document.getElementById('ans-paso4')?.value || ''; if (step === 6) { AppState.answers.paso5_1 = document.getElementById('ans-paso5-1')?.value || ''; AppState.answers.paso5_2 = document.getElementById('ans-paso5-2')?.value || ''; } } }; document.addEventListener('DOMContentLoaded', () => { FlowManager.init(); });