// Profile / Gamification / Plans / Settings — all "more" content const MoreScreen = ({ state, setState, navigate, showToast, user, onLogout, syncStatus, profile, themePref, onChangeTheme }) => { const [openSheet, setOpenSheet] = React.useState(null); // 'lgpd' | 'help' | 'theme' | 'settings' | 'profile' | 'security' const confirmLogout = () => { if (window.confirm && window.confirm('Sair da conta? Seus dados locais permanecem salvos neste dispositivo.')) { onLogout && onLogout(); } }; const syncReal = user && !user.isMock && window.CloudSync && window.CloudSync.isConfigured(); const isAdmin = profile && profile.role === 'admin'; return (
navigate('profile')} />
navigate('goals')} /> navigate('calendar')} /> navigate('achievements')} /> navigate('plans')} /> navigate('vault')} />
{isAdmin && (
navigate('admin')} style={{ padding: 16, borderRadius: 18, position: 'relative', overflow: 'hidden', background: 'linear-gradient(135deg, rgba(177,151,252,0.18), rgba(255,143,177,0.10))', border: `1px solid ${CDV.brand}40`, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 12, }}>
Painel Admin
interno
Usuários, planos, métricas e receita
)}
navigate('notifications')} /> navigate('dashboard')} /> showToast('Disponível no plano Premium')} /> setOpenSheet('theme')} /> setOpenSheet('lgpd')} last />
setOpenSheet('profile')} /> setOpenSheet('security')} /> setOpenSheet('settings')} /> setOpenSheet('help')} last />
{onLogout && (
{syncReal && } {user && user.email && (
Logado como {user.email}{user.isMock ? ' · modo demo' : ''}
)}
)}
Central da Vida · versão 1.0 · feito com 💜 pela JuFlow Digital
setOpenSheet(null)} title="Privacidade & LGPD"> { if (window.CDV_RESET) window.CDV_RESET(); window.location.reload(); }} /> setOpenSheet(null)} title="Personalizar tema"> { onChangeTheme && onChangeTheme(p); showToast && showToast('Tema atualizado'); }} /> setOpenSheet(null)} title="Editar perfil"> { setOpenSheet(null); showToast('Perfil atualizado'); }} /> setOpenSheet(null)} title="Senha e segurança"> setOpenSheet(null)} title="Configurações"> setOpenSheet(null)} title="Ajuda e suporte">
); }; const SyncBadge = ({ status }) => { const s = status || { status: 'idle' }; const map = { idle: { dot: CDV.textMuted, label: 'Aguardando sincronização' }, loading: { dot: CDV.amber, label: 'Carregando da nuvem...' }, pending: { dot: CDV.amber, label: 'Alterações pendentes...' }, saving: { dot: CDV.brand, label: 'Salvando na nuvem...' }, saved: { dot: CDV.mint, label: s.lastSyncedAt ? `Sincronizado · ${formatSyncTime(s.lastSyncedAt)}` : 'Sincronizado' }, error: { dot: CDV.coral, label: 'Erro ao sincronizar — tente novamente' }, }; const info = map[s.status] || map.idle; return (
{info.label}
); }; function formatSyncTime(iso) { try { const d = new Date(iso); const today = new Date(); const isSameDay = d.toDateString() === today.toDateString(); const hh = String(d.getHours()).padStart(2, '0'); const mm = String(d.getMinutes()).padStart(2, '0'); if (isSameDay) return `hoje ${hh}:${mm}`; return d.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit' }) + ` ${hh}:${mm}`; } catch (e) { return ''; } } const ThemePanel = ({ pref, onChange }) => { const currentMode = window.CDV && window.CDV._mode; const options = [ { id: 'auto', label: 'Automático', desc: 'Claro durante o dia (7h-19h) · escuro à noite', icon: 'sparkle' }, { id: 'light', label: 'Claro', desc: 'Sempre o modo claro', icon: 'sun' }, { id: 'dark', label: 'Escuro', desc: 'Sempre o modo escuro', icon: 'moon' }, ]; return (
{/* Preview escuro */} {/* Preview claro */}
Modo · atualmente {currentMode === 'light' ? 'claro' : 'escuro'}
{options.map(o => { const active = pref === o.id; return ( ); })}
); }; const ThemePreview = ({ mode, active }) => { const palette = mode === 'light' ? (window.LIGHT_PALETTE || {}) : (window.DARK_PALETTE || {}); return (
{mode === 'light' ? 'Claro' : 'Escuro'}
); }; const ComingSoon = ({ icon, title, desc }) => (
{title}
{desc}
); const LGPDPanel = ({ onReset }) => (
Lei Geral de Proteção de Dados

Este aplicativo respeita a LGPD (Lei nº 13.709/2018). Resumo dos seus direitos:

  • Você pode solicitar exclusão de todos os seus dados a qualquer momento.
  • Seus dados são armazenados localmente neste dispositivo (localStorage). Nenhuma informação é enviada para servidores externos sem seu consentimento.
  • O assistente Sofia (IA) usa apenas os dados que você fornece, sem compartilhar com terceiros.
  • Você pode revogar o consentimento a qualquer momento na tela de Configurações.
Termos de uso e política completa
Documento legal completo disponível em juflow.com.br/lgpd
); const EditProfileForm = ({ state, setState, onDone }) => { const [name, setName] = React.useState((state && state.profile && state.profile.name) || 'Lucas Mendes'); const [email, setEmail] = React.useState((state && state.profile && state.profile.email) || 'lucas@email.com'); const save = () => { setState && setState(s => ({ ...s, profile: { ...(s.profile || {}), name, email } })); onDone && onDone(); }; return (
Nome
setName(e.target.value)} placeholder="Seu nome" style={{ width: '100%', height: 50, borderRadius: 14, border: `1px solid ${CDV.stroke}`, background: CDV.surfaceHi, color: CDV.text, padding: '0 16px', fontSize: 15, outline: 'none', fontFamily: 'inherit', marginBottom: 14 }} />
E-mail
setEmail(e.target.value)} placeholder="email@exemplo.com" style={{ width: '100%', height: 50, borderRadius: 14, border: `1px solid ${CDV.stroke}`, background: CDV.surfaceHi, color: CDV.text, padding: '0 16px', fontSize: 15, outline: 'none', fontFamily: 'inherit', marginBottom: 20 }} />
); }; const SettingsPanel = ({ showToast }) => { const initialPrefs = window.Push ? window.Push.loadPrefs() : { enabled: false, leadMinutes: 10, quiet: { enabled: true, start: 22, end: 7 } }; const [prefs, setPrefs] = React.useState(initialPrefs); const [permission, setPermission] = React.useState(window.Push ? window.Push.permission() : 'unsupported'); const [haptic, setHaptic] = React.useState(true); const persist = (next) => { setPrefs(next); window.Push && window.Push.savePrefs(next); }; const togglePush = async (next) => { if (next) { const result = window.Push ? await window.Push.requestPermission() : 'denied'; setPermission(result); if (result === 'granted') { persist({ ...prefs, enabled: true }); showToast && showToast('Notificações ativadas'); } else if (result === 'denied') { showToast && showToast('Permissão negada — habilite no navegador'); persist({ ...prefs, enabled: false }); } else if (result === 'unsupported') { showToast && showToast('Notificações não suportadas neste navegador'); } } else { persist({ ...prefs, enabled: false }); window.Push && window.Push.cancelAll(); showToast && showToast('Notificações desativadas'); } }; const test = () => { if (!window.Push) { showToast && showToast('Push não disponível'); return; } if (permission !== 'granted') { showToast && showToast('Habilite as notificações primeiro'); return; } const ok = window.Push.showNow('Central da Vida', 'Notificação de teste — tudo funcionando!', { bypassQuiet: true }); if (!ok) showToast && showToast('Suprimida pelo modo silencioso'); }; return (
{/* Status do navegador */}
{permission === 'granted' && 'Permissão concedida pelo navegador'} {permission === 'default' && 'Permissão ainda não solicitada'} {permission === 'denied' && 'Bloqueado no navegador — desbloqueie nas configurações do site'} {permission === 'unsupported' && 'Navegador não suporta notificações'}
persist({ ...prefs, quiet: { ...prefs.quiet, enabled: v } })} />
Avisar antes da tarefa
{[5, 10, 15, 30, 60].map(m => { const active = prefs.leadMinutes === m; return ( ); })}
); }; const SettingToggle = ({ label, desc, checked, onChange }) => (
{label}
{desc}
); const HelpPanel = () => (
SUPORTE
JuFlow Digital
Segunda a sexta · 9h às 18h
Sábado · 9h às 12h
Central da Vida · versão 1.0
Correção de bugs sem limite, conforme o briefing.
); const ListLink = ({ label, icon }) => (
{label}
); const ProfileCard = ({ state, onClick }) => { const { xp, level } = state; const nextLevel = 2400; const pct = (xp / nextLevel) * 100; const profileName = (state && state.profile && state.profile.name) || 'Lucas Mendes'; const profileEmail = (state && state.profile && state.profile.email) || 'lucas@email.com'; const initial = (profileName || 'L').charAt(0).toUpperCase(); return (
{initial}
{profileName}
Premium
{profileEmail}
47 dias de constância
{level}
Nível {level} · Mestre da Rotina
{xp} / {nextLevel} XP
); }; const ModuleTile = ({ icon, label, desc, color, onClick }) => ( ); const ListRow = ({ icon, iconColor = CDV.brand, title, detail, onClick, last }) => (
{title}
{detail &&
{detail}
}
); // ── Achievements screen ──────────────────────────────────── const AchievementsScreen = ({ state, navigate }) => { const unlocked = conquistasData.filter(c => c.unlocked).length; return (
navigate('more')} />
Próxima recompensa
Mestre dos Hábitos
53 dias para desbloquear
{conquistasData.filter(c => c.unlocked).map(c => )}
{conquistasData.filter(c => !c.unlocked).map(c => )}
); }; const AchievementBadge = ({ a, locked }) => (
{a.rarity}
{a.name}
{a.desc}
); // ── Plans screen ──────────────────────────────────────────── const PLANS = [ { id: 'mensal', name: 'Mensal', subtitle: 'Mais flexível', total: 29.90, months: 1, color: '#6FB8FF', icon: 'star', }, { id: 'semestral', name: 'Semestral', subtitle: 'Equilibrado', total: 149.40, months: 6, color: '#FFB547', icon: 'crown', save: '17%', }, { id: 'anual', name: 'Anual', subtitle: 'Melhor custo', total: 238.80, months: 12, color: '#B197FC', icon: 'diamond', recommended: true, save: '33%', }, ]; const fmtBRL = (v) => `R$ ${v.toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; const PlansScreen = ({ navigate, showToast, user, profile }) => { const trialEnds = profile && profile.trial_ends_at ? new Date(profile.trial_ends_at) : null; const trialActive = trialEnds && trialEnds > new Date() && (!profile || profile.plan === 'trial'); const daysLeft = trialEnds ? Math.max(0, Math.ceil((trialEnds - new Date()) / (1000 * 60 * 60 * 24))) : null; const currentPlan = profile && profile.plan; const selectPlan = async (planId) => { const plan = PLANS.find(p => p.id === planId); // Redireciona pro checkout Nexano (abre em nova aba) if (window.Checkout && window.Checkout.openCheckout(planId, user)) { showToast(`Abrindo checkout · ${plan.name}`); return; } showToast(`Plano ${plan.name} selecionado`); }; return (
navigate('more')} />
Central completa
Sofia 24/7, tarefas, hábitos, metas, finanças e backup em nuvem.
{/* Trial banner */} {trialActive && (
Período grátis ativo
{daysLeft === 0 ? 'Expira hoje' : `${daysLeft} dia${daysLeft === 1 ? '' : 's'} restante${daysLeft === 1 ? '' : 's'}`} · sem cobrança até você confirmar
)} {/* Tudo incluído em qualquer plano */}
Todos os planos incluem
{[ 'Sofia 24/7 com IA ilimitada', 'Tarefas, hábitos, finanças e agenda', 'Cofrinhos e metas ilimitados', 'Backup automático na nuvem', 'Sincronização entre dispositivos', 'Relatórios em PDF e suporte prioritário', ].map((f, i) => (
{f}
))}
A diferença entre os planos é só o ciclo de cobrança. Quanto mais longo, mais você economiza.
{/* Plan cards */}
{PLANS.map(plan => ( selectPlan(plan.id)} /> ))}
3 dias grátis · sem cobrança até confirmação
Cancele quando quiser.
Pagamento via App Store, Google Play ou PIX.
); }; const PlanCard = ({ plan, current, onSelect }) => { const perMonth = plan.total / plan.months; return (
{plan.recommended && (
MAIS POPULAR
)} {current && (
SEU PLANO
)}
{plan.name}
{plan.save && economize {plan.save}}
{plan.subtitle}
{fmtBRL(plan.total)}
{plan.months === 1 ? '/mês' : plan.months === 6 ? 'a cada 6 meses' : '/ano'}
{plan.months > 1 && (
equivale a {fmtBRL(perMonth)}/mês
)}
); }; // ── Notifications screen ──────────────────────────────────── const NotificationsScreen = ({ navigate, showToast }) => { const defaultItems = [ { id: 'n1', time: 'agora', icon: 'sparkle', color: CDV.brand, title: 'Sofia tem uma sugestão', desc: 'Você ainda não bateu sua meta de água hoje.', smart: true, route: 'habits' }, { id: 'n2', time: '8min', icon: 'flame', color: CDV.flame, title: 'Hábito de meditar à vista', desc: 'Falta 1 hora para seu horário ideal.', route: 'habits' }, { id: 'n3', time: '1h', icon: 'wallet', color: CDV.amber, title: 'Alerta financeiro', desc: 'Você gastou R$ 78 em delivery hoje (acima da média).', route: 'finance' }, { id: 'n4', time: '3h', icon: 'check', color: CDV.mint, title: 'Tarefa concluída', desc: 'Você ganhou +85 XP por concluir 7 tarefas.', route: 'tasks' }, { id: 'n5', time: '5h', icon: 'trophy', color: CDV.amber, title: 'Nova conquista 🎉', desc: 'Madrugador desbloqueado — 10 dias acordando cedo.', route: 'achievements' }, { id: 'n6', time: '12h', icon: 'bell', color: CDV.sky, title: 'Lembrete inteligente', desc: 'Reunião com diretoria em 30 minutos.', route: 'calendar' }, { id: 'n7', time: 'ontem', icon: 'target', color: CDV.coral, title: 'Meta avançada', desc: 'Você está 2 semanas adiantado em "Apartamento".', route: 'goals' }, ]; const [items, setItems] = React.useState(defaultItems); const markAll = () => { setItems([]); showToast && showToast('Todas marcadas como lidas'); }; const openDetails = (n) => { if (n.route) navigate(n.route); }; const dismiss = (id, e) => { e && e.stopPropagation(); setItems(arr => arr.filter(x => x.id !== id)); }; return (
navigate('more')} right={ items.length > 0 && ( ) } />
{items.length === 0 && (
Nenhuma notificação pendente.
)} {items.map((n) => (
openDetails(n)} style={{ padding: 14, paddingLeft: 16, borderRadius: 16, background: n.smart ? `linear-gradient(135deg, ${n.color}18 0%, ${n.color}06 40%, ${CDV.surface} 100%)` : `linear-gradient(135deg, ${n.color}0d 0%, ${CDV.surface} 50%)`, border: `1px solid ${n.smart ? n.color + '44' : n.color + '22'}`, display: 'flex', alignItems: 'flex-start', gap: 12, cursor: 'pointer', position: 'relative', overflow: 'hidden', boxShadow: n.smart ? `0 6px 18px -10px ${n.color}55` : `0 4px 14px -10px ${n.color}30`, }}> {/* Glow blob — só nas smart pra não poluir */} {n.smart && (
)}
{n.title}
{n.time}
{n.desc}
{n.smart && (
)}
))}
); }; // ── Profile detail screen ───────────────────────────────── const ProfileScreen = ({ state, navigate }) => { return (
navigate('more')} />
{}} />
{[40, 65, 80, 50, 90, 75, 95].map((v, i) => (
{['S','T','Q','Q','S','S','D'][i]}
))}
); }; Object.assign(window, { MoreScreen, AchievementsScreen, PlansScreen, NotificationsScreen, ProfileScreen });