Carregando...
Prosperité
Entre na sua conta
E-mail
Senha
Não tem conta? Criar conta
Criar conta
Comece a organizar suas finanças
Nome
E-mail
Senha
Já tem conta? Entrar
Confirme seu e-mail
Enviamos um link de confirmação para o seu e-mail. Clique no link e depois volte aqui para entrar.
Olá, bem-vinda de volta
Saldo disponível
R$ 0,00
Saúde financeira
Calculando...
Adicione lançamentos para ver seu score
Reserva de emergência
Projeção dos próximos meses
Receitas
R$ 0
Gastos
R$ 0
Maior gasto
Faturas abertas
R$ 0
Gastos por categoria
Orçado × realizado
Orçado
Gasto
Comparativo mensal
Receitas
Gastos
Dica para você
Dica
Carregando...

Lançamentos

Orçamento

Limites por categoria — mês atual

Dívidas

Financiamentos e cartões de crédito

Metas

Acompanhe seus objetivos financeiros

Cartão

Fatura atual

, '').trim(); // normaliza valor (aceita 1.234,56 ou 1234.56 ou -50,00) let negativo = valorRaw.includes('-'); valorRaw = valorRaw.replace('-', ''); if (valorRaw.includes(',')) valorRaw = valorRaw.replace(/\./g, '').replace(',', '.'); const valor = parseFloat(valorRaw); if (isNaN(valor)) continue; // normaliza data (aceita dd/mm/aaaa ou aaaa-mm-dd) let data; if (dataRaw.includes('/')) { const [d, m, y] = dataRaw.split('/'); data = `${y.length === 2 ? '20'+y : y}-${m.padStart(2,'0')}-${d.padStart(2,'0')}`; } else { data = dataRaw; } items.push({ desc, valor: Math.abs(valor), data, tipo: negativo ? 'saida' : 'entrada' }); } return items; } function handleFileSelect(event) { const file = event.target.files[0]; if (!file) return; document.getElementById('import-error').style.display = 'none'; const reader = new FileReader(); reader.onload = (e) => { const text = e.target.result; let items = []; try { if (file.name.toLowerCase().endsWith('.ofx')) { items = parseOFX(text); } else { items = parseCSV(text); } } catch (err) { showImportError('Não foi possível ler o arquivo. Verifique o formato e tente novamente.'); return; } if (!items.length) { showImportError('Nenhum lançamento foi encontrado no arquivo. Confira se o formato está correto.'); return; } importedItems = items.map(it => ({ ...it, categoria: it.tipo === 'entrada' ? 'Renda' : categorizarAutomatico(it.desc), selecionado: true })); renderImportReview(); document.querySelectorAll('.import-step').forEach(s => s.classList.remove('active')); document.getElementById('import-step-2').classList.add('active'); }; reader.onerror = () => showImportError('Erro ao ler o arquivo.'); reader.readAsText(file, 'ISO-8859-1'); } function renderImportReview() { const totalEntradas = importedItems.filter(i => i.tipo === 'entrada').reduce((a,i) => a+i.valor, 0); const totalSaidas = importedItems.filter(i => i.tipo === 'saida').reduce((a,i) => a+i.valor, 0); document.getElementById('import-summary').innerHTML = `${importedItems.length} lançamentos encontrados · Entradas: ${fmt(totalEntradas)} · Saídas: ${fmt(totalSaidas)}
Desmarque os que não quiser importar e ajuste as categorias se necessário.`; const optsHtml = state.categorias.map(c => ``).join(''); document.getElementById('import-review-list').innerHTML = importedItems.map((it, idx) => { const d = new Date(it.data + 'T12:00:00'); const dataFmt = isNaN(d) ? it.data : d.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit' }); const sinal = it.tipo === 'entrada' ? '+' : '-'; const cls = it.tipo === 'entrada' ? 'entrada' : 'saida'; return `
${it.desc}
${dataFmt}
${sinal}${fmt(it.valor)}
`; }).join(''); } function toggleImportItem(idx, checked) { importedItems[idx].selecionado = checked; } function updateImportCategoria(idx, cat) { importedItems[idx].categoria = cat; } async function confirmarImportacao() { const selecionados = importedItems.filter(i => i.selecionado); if (!selecionados.length) { alert('Selecione ao menos um lançamento.'); return; } const btn = document.getElementById('btn-confirmar-import'); btn.disabled = true; btn.textContent = 'Importando...'; const rows = selecionados.map(i => ({ user_id: currentUser.id, descricao: i.desc, valor: i.valor, tipo: i.tipo, categoria: i.categoria, data: i.data })); const { data: inserted, error } = await sb.from('lancamentos').insert(rows).select(); btn.disabled = false; btn.innerHTML = ' Importar selecionados'; if (error) { alert('Erro ao importar: ' + error.message); return; } inserted.forEach(l => { state.lancamentos.unshift({ id: l.id, desc: l.descricao, valor: parseFloat(l.valor), tipo: l.tipo, categoria: l.categoria, data: l.data }); }); document.getElementById('import-success-title').textContent = 'Importação concluída!'; document.getElementById('import-success-sub').textContent = `${selecionados.length} lançamento(s) adicionado(s) com sucesso.`; document.querySelectorAll('.import-step').forEach(s => s.classList.remove('active')); document.getElementById('import-step-3').classList.add('active'); renderLanc(); renderDash(); } // ===== INIT ===== async function init() { buildMonthSelect(); const hoje = new Date().toISOString().split('T')[0]; document.getElementById('new-data').value = hoje; selTipo('entrada'); buildCatChips(); renderCatList(); const { data: { session } } = await sb.auth.getSession(); if (session) { currentUser = session.user; await loadUserData(); buildCatChips(); renderCatList(); showApp(); } else { showAuth('login'); } sb.auth.onAuthStateChange((event, session) => { if (event === 'SIGNED_IN' && !currentUser) { currentUser = session.user; loadUserData().then(() => { buildCatChips(); renderCatList(); showApp(); }); } }); } init(); if ('serviceWorker' in navigator) { navigator.serviceWorker.register('./sw.js'); }