// 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('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,
}}>
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 &&
}
Sair da conta
{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 (
);
};
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 (
onChange(o.id)} style={{
padding: 14, borderRadius: 14, textAlign: 'left',
background: active ? CDV.brandSoft : CDV.surface,
border: `1.5px solid ${active ? CDV.brand : CDV.stroke}`,
color: CDV.text, fontFamily: 'inherit', display: 'flex', alignItems: 'center', gap: 12,
cursor: 'pointer',
}}>
{active && (
)}
);
})}
);
};
const ThemePreview = ({ mode, active }) => {
const palette = mode === 'light' ? (window.LIGHT_PALETTE || {}) : (window.DARK_PALETTE || {});
return (
{mode === 'light' ? 'Claro' : 'Escuro'}
);
};
const ComingSoon = ({ icon, 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
{
if (typeof window !== 'undefined' && window.confirm && window.confirm('Apagar TODOS os dados do app neste dispositivo? Esta ação não pode ser desfeita.')) {
onReset && onReset();
}
}} style={{
width: '100%', height: 50, borderRadius: 14,
background: CDV.coral + '18', border: `1px solid ${CDV.coral}40`, color: CDV.coral,
fontWeight: 600, fontSize: 14, fontFamily: 'inherit',
}}>Excluir minha conta e dados
);
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 }} />
Salvar alterações
);
};
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 (
persist({ ...prefs, leadMinutes: m })} style={{
flex: 1, height: 38, borderRadius: 10,
background: active ? CDV.brandSoft : CDV.surfaceHi,
border: `1.5px solid ${active ? CDV.brand : CDV.stroke}`,
color: active ? CDV.brand : CDV.textDim,
fontSize: 12, fontWeight: 600, fontFamily: 'inherit',
}}>{m}m
);
})}
Disparar notificação de teste
);
};
const SettingToggle = ({ label, desc, checked, onChange }) => (
onChange(!checked)} style={{
width: 44, height: 26, borderRadius: 99,
background: checked ? CDV.brand : CDV.surfaceHi, border: 'none',
position: 'relative', transition: 'background .2s', cursor: 'pointer',
}}>
);
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 }) => (
);
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 (
{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 (
);
};
const AchievementBadge = ({ a, locked }) => (
);
// ── 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 (
);
};
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
)}
{current ? 'Plano atual' : `Escolher ${plan.name}`}
);
};
// ── 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 && (
Marcar tudo
)
} />
{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.desc}
{n.smart && (
{ e.stopPropagation(); openDetails(n); }} style={{
background: CDV.brandGrad, color: '#fff', border: 'none', padding: '5px 10px',
borderRadius: 99, fontSize: 11, fontWeight: 600, fontFamily: 'inherit', cursor: 'pointer',
}}>Ver detalhes
dismiss(n.id, e)} style={{
background: CDV.surfaceHi, color: CDV.textDim, border: `1px solid ${CDV.stroke}`, padding: '5px 10px',
borderRadius: 99, fontSize: 11, fontWeight: 600, fontFamily: 'inherit', cursor: 'pointer',
}}>Dispensar
)}
))}
);
};
// ── 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 });