// Onboarding / Login flow — splash → login/signup → (cartão se signup) → personalização const OnboardingFlow = ({ onComplete }) => { const [step, setStep] = React.useState(0); const [authedUser, setAuthedUser] = React.useState(null); const [paymentMethod, setPaymentMethod] = React.useState(null); return ( <> {step === 0 && setStep(1)} />} {step === 1 && ( { setAuthedUser(user); setStep(3); }} onSignup={(user) => { setAuthedUser(user); setStep(2); }} /> )} {step === 2 && ( setStep(1)} onSubmit={(card) => { setPaymentMethod(card); setStep(3); }} /> )} {step === 3 && ( onComplete({ user: authedUser, profile, paymentMethod }) } /> )} ); }; // ── Splash ───────────────────────────────────────────────── const SplashScreen = ({ onNext }) => (
Central
da Vida
Sua rotina, finanças, hábitos e metas — guiados por inteligência artificial. Tudo num só lugar.
3 dias grátis · Sem cobrança até confirmação
); // ── Login / Signup ────────────────────────────────────────── const LoginScreen = ({ onSignin, onSignup, onLogin }) => { const [name, setName] = React.useState(''); const [email, setEmail] = React.useState(''); const [password, setPassword] = React.useState(''); const [confirmPassword, setConfirmPassword] = React.useState(''); const [mode, setMode] = React.useState('signin'); // 'signin' | 'signup' const [loading, setLoading] = React.useState(false); const [error, setError] = React.useState(''); const [info, setInfo] = React.useState(''); const configured = window.Auth && window.Auth.isConfigured(); const handle = async (fn, isSignup) => { setError(''); setInfo(''); setLoading(true); try { const user = await fn(); if (user) { // Compat: se onLogin existir (chamada antiga), usa ele if (typeof onLogin === 'function') onLogin(user); else if (isSignup && typeof onSignup === 'function') onSignup(user); else if (typeof onSignin === 'function') onSignin(user); } } catch (e) { setError(e && e.message ? e.message : 'Falha na autenticação'); } finally { setLoading(false); } }; const onApple = () => handle(() => window.Auth.signInWithProvider('apple')); const onGoogle = () => handle(() => window.Auth.signInWithProvider('google')); const onEmail = () => { if (mode === 'signup') { if (!name.trim()) return setError('Como devo te chamar? Preencha seu nome'); if (!email || !password) return setError('Preencha todos os campos'); if (password.length < 6) return setError('Senha precisa ter ao menos 6 caracteres'); if (password !== confirmPassword) return setError('As senhas não conferem'); } else { if (!email || !password) return setError('Preencha e-mail e senha'); } handle(() => mode === 'signup' ? window.Auth.signUpWithEmail(email, password, name.trim()) : window.Auth.signInWithEmail(email, password), mode === 'signup' ); }; const onForgot = async () => { if (!email) return setError('Digite seu e-mail para recuperar a senha'); setError(''); setLoading(true); try { const res = await window.Auth.resetPassword(email); setInfo(res.mock ? 'Modo demo: simulação de recuperação enviada.' : 'E-mail de recuperação enviado. Verifique sua caixa.'); } catch (e) { setError(e && e.message ? e.message : 'Falha ao enviar recuperação'); } finally { setLoading(false); } }; return (
{mode === 'signup' ? ( <>Sua nova vida
começa hoje ) : ( <>Bem-vindo
de volta )}
{mode === 'signup' ? 'Tarefas, finanças, hábitos e metas — guiados por IA. Tudo num só lugar.' : 'Continue de onde parou. Sofia está te esperando.'}
{/* Social proof — avatares + rating */}
{[ { c: '#FFB547', i: 'M' }, { c: '#5EE3A8', i: 'A' }, { c: '#B197FC', i: 'R' }, { c: '#FF8FB1', i: 'J' }, ].map((a, i) => (
{a.i}
))}
+8.500 brasileiros
★ ★ ★ ★ ★
4.9 · avaliação média
{/* Depoimento — só no signup */} {mode === 'signup' && (
M
"Em 1 mês organizei minha rotina inteira. A Sofia parece que me conhece."
Mariana, 28 · São Paulo
)} {/* Banner trial */} {mode === 'signup' && (
3 dias grátis · sem cobrança
Teste todos os módulos. Cancele com 1 clique se não amar.
)} {!configured && (
Modo demo · configure Supabase em auth-config.js para auth real
)}
{mode === 'signup' && ( setName(e.target.value)} placeholder="Seu nome" type="text" autoComplete="name" style={{ height: 54, borderRadius: 16, border: `1px solid ${CDV.stroke}`, background: CDV.surface, color: CDV.text, padding: '0 18px', fontSize: 15, outline: 'none', fontFamily: 'inherit', }} /> )} setEmail(e.target.value)} placeholder="seu@email.com" type="email" autoComplete="email" style={{ height: 54, borderRadius: 16, border: `1px solid ${CDV.stroke}`, background: CDV.surface, color: CDV.text, padding: '0 18px', fontSize: 15, outline: 'none', fontFamily: 'inherit', }} /> setPassword(e.target.value)} autoComplete={mode === 'signup' ? 'new-password' : 'current-password'} onKeyDown={e => { if (e.key === 'Enter' && mode === 'signin') onEmail(); }} style={{ height: 54, borderRadius: 16, border: `1px solid ${CDV.stroke}`, background: CDV.surface, color: CDV.text, padding: '0 18px', fontSize: 15, outline: 'none', fontFamily: 'inherit', }} /> {mode === 'signup' && ( setConfirmPassword(e.target.value)} autoComplete="new-password" onKeyDown={e => { if (e.key === 'Enter') onEmail(); }} style={{ height: 54, borderRadius: 16, border: `1px solid ${confirmPassword && confirmPassword !== password ? CDV.coral : CDV.stroke}`, background: CDV.surface, color: CDV.text, padding: '0 18px', fontSize: 15, outline: 'none', fontFamily: 'inherit', }} /> )} {error && (
⚠ {error}
)} {info && (
✓ {info}
)} {mode === 'signin' && (
Esqueci minha senha
)}
{mode === 'signup' ? 'Já tem conta?' : 'Novo aqui?'}{' '} { setMode(mode === 'signup' ? 'signin' : 'signup'); setError(''); setInfo(''); setConfirmPassword(''); }} style={{ color: CDV.brand, fontWeight: 600, cursor: 'pointer' }} > {mode === 'signup' ? 'Entrar' : 'Crie sua conta'}
{/* Trust badges */}
LGPD
Criptografado
Sem fidelidade
); }; // ── Card setup (trial de 3 dias) ──────────────────────────── const CardSetupScreen = ({ onBack, onSubmit }) => { const [number, setNumber] = React.useState(''); const [expiry, setExpiry] = React.useState(''); const [cvv, setCvv] = React.useState(''); const [holder, setHolder] = React.useState(''); const [error, setError] = React.useState(''); const formatNumber = (v) => v.replace(/\D/g, '').slice(0, 19).replace(/(\d{4})(?=\d)/g, '$1 '); const formatExpiry = (v) => { const d = v.replace(/\D/g, '').slice(0, 4); return d.length > 2 ? `${d.slice(0, 2)}/${d.slice(2)}` : d; }; // Detecta bandeira a partir do BIN const detectBrand = (n) => { const d = n.replace(/\D/g, ''); if (/^4/.test(d)) return 'visa'; if (/^(5[1-5]|2[2-7])/.test(d)) return 'mastercard'; if (/^3[47]/.test(d)) return 'amex'; if (/^(6011|65|64[4-9])/.test(d)) return 'elo'; return 'cartão'; }; const brand = detectBrand(number); const last4 = number.replace(/\D/g, '').slice(-4); const digitsOnly = number.replace(/\D/g, ''); const valid = digitsOnly.length >= 13 && /^\d{2}\/\d{2}$/.test(expiry) && cvv.length >= 3 && holder.trim().length > 0; const submit = () => { setError(''); if (!valid) { setError('Preencha todos os campos corretamente'); return; } // Não enviamos número completo: apenas últimos 4, bandeira e validade onSubmit({ brand, last4, expiry, holder: holder.trim(), addedAt: new Date().toISOString(), }); }; return (
{/* Progress dots */}
Adicione seu cartão
Você terá 3 dias grátis. Depois cobramos R$ 29,90/mês — cancele quando quiser.
{/* Preview do cartão ao vivo */}
Central da Vida
{brand}
{number || '•••• •••• •••• ••••'}
Titular
{holder || 'NOME NO CARTÃO'}
Validade
{expiry || 'MM/AA'}
{/* Form */}
setNumber(formatNumber(e.target.value))} placeholder="Número do cartão" inputMode="numeric" autoComplete="cc-number" style={{ height: 52, borderRadius: 14, border: `1px solid ${CDV.stroke}`, background: CDV.surface, color: CDV.text, padding: '0 16px', fontSize: 15, outline: 'none', fontFamily: 'inherit', letterSpacing: 1, }} />
setExpiry(formatExpiry(e.target.value))} placeholder="MM/AA" inputMode="numeric" autoComplete="cc-exp" style={{ height: 52, borderRadius: 14, border: `1px solid ${CDV.stroke}`, background: CDV.surface, color: CDV.text, padding: '0 16px', fontSize: 15, outline: 'none', fontFamily: 'inherit', letterSpacing: 1, }} /> setCvv(e.target.value.replace(/\D/g, '').slice(0, 4))} placeholder="CVV" inputMode="numeric" autoComplete="cc-csc" style={{ height: 52, borderRadius: 14, border: `1px solid ${CDV.stroke}`, background: CDV.surface, color: CDV.text, padding: '0 16px', fontSize: 15, outline: 'none', fontFamily: 'inherit', letterSpacing: 2, }} />
setHolder(e.target.value.toUpperCase())} placeholder="Nome impresso no cartão" autoComplete="cc-name" style={{ height: 52, borderRadius: 14, border: `1px solid ${CDV.stroke}`, background: CDV.surface, color: CDV.text, padding: '0 16px', fontSize: 15, outline: 'none', fontFamily: 'inherit', textTransform: 'uppercase', }} /> {error && (
⚠ {error}
)}
{/* Disclaimers */}
Não armazenamos o número completo nem o CVV. Salvamos apenas os últimos 4 dígitos e a validade pra identificar seu cartão. Pagamento processado por gateway seguro.
Você será lembrado 24h antes da cobrança · cancele a qualquer momento
); }; // ── Personalization ───────────────────────────────────────── const PersonalizationScreen = ({ onDone }) => { const [name, setName] = React.useState('Lucas'); const [goals, setGoals] = React.useState(['organizar', 'financas']); const opts = [ { id: 'organizar', label: 'Organizar minha rotina', icon: 'tasks' }, { id: 'financas', label: 'Controlar finanças', icon: 'wallet' }, { id: 'habitos', label: 'Criar hábitos', icon: 'flame' }, { id: 'metas', label: 'Atingir metas grandes', icon: 'target' }, { id: 'foco', label: 'Aumentar foco', icon: 'pomodoro' }, { id: 'paz', label: 'Reduzir ansiedade', icon: 'meditate' }, ]; const toggle = (id) => setGoals(g => g.includes(id) ? g.filter(x => x !== id) : [...g, id]); return (
Vamos personalizar sua experiência
A IA usará isso para sugerir o que importa para você.
Como podemos te chamar?
setName(e.target.value)} placeholder="Seu primeiro nome" style={{ marginTop: 8, height: 54, width: '100%', borderRadius: 16, border: `1px solid ${CDV.stroke}`, background: CDV.surface, color: CDV.text, padding: '0 18px', fontSize: 16, outline: 'none', fontFamily: 'inherit', }} />
O que você quer melhorar?
{opts.map(o => { const active = goals.includes(o.id); return ( ); })}
); }; Object.assign(window, { OnboardingFlow });