// Panels: spending overview (donut), AI insight, quick actions, recommendations, transactions, debts const { useState: useStateP, useMemo: useMemoP } = React; // ---------- DONUT (SVG) ---------- function Donut({ data, size = 220, thickness = 32 }) { const total = data.reduce((s, d) => s + d.value, 0) || 1; const r = size / 2 - thickness / 2; const cx = size / 2, cy = size / 2; const C = 2 * Math.PI * r; let offset = 0; // shades of green from dark to light to match reference const colors = ["#0e4a40", "#177a63", "#2a9d84", "#f0b84d", "#f6cd7c", "#e5d9b8", "#c4d6cf", "#d9e6e0", "#f3eedf"]; return (
{data.map((d, i) => { const len = (d.value / total) * C; const seg = ( ); offset += len; return seg; })}
Top Categoria
{data[0]?.category || "—"}
); } // ---------- SPENDING OVERVIEW ---------- function SpendingOverview({ catData, total, period, setPeriod, onGoTo }) { const colors = ["#0e4a40", "#177a63", "#2a9d84", "#f0b84d", "#f6cd7c", "#e5d9b8", "#c4d6cf", "#d9e6e0", "#f3eedf"]; const top = catData.slice(0, 6); return (
onGoTo("analise"))} title="Ver Análise completa">

Visão de Gastos

Total gasto
{top.map((c, i) => { const pct = total ? Math.round((c.value / total) * 100) : 0; return (
{c.category} {window.fmtBRLString(c.value)} {pct}%
); })} {top.length === 0 && (
Sem despesas neste período.
)}
); } // ---------- AI INSIGHT ---------- function AIInsight({ catData, monthIncome, monthExpense, monthLabel, onGoTo }) { const [insight, setInsight] = useStateP(null); const [loading, setLoading] = useStateP(false); async function generate() { setLoading(true); try { const top3 = catData.slice(0, 3).map(c => `${c.category}: R$ ${c.value.toFixed(2)}`).join(", "); const prompt = `Você é um consultor financeiro. Em PORTUGUÊS, em no máximo 2 frases curtas, analise estes dados de ${monthLabel}: Receita: R$ ${monthIncome.toFixed(2)} Despesa: R$ ${monthExpense.toFixed(2)} Top 3 categorias: ${top3} Identifique 1 padrão preocupante ou oportunidade e SUGIRA uma ação concreta. Não use cumprimentos. Comece direto.`; const res = await window.claude.complete(prompt); setInsight(res.trim()); } catch (e) { setInsight("Não consegui gerar agora."); } finally { setLoading(false); } } // Auto-generate a default insight based on the data const defaultInsight = useMemoP(() => { if (!catData[0]) return "Adicione lançamentos para receber análises."; const top = catData[0]; const pct = monthExpense ? Math.round((top.value / monthExpense) * 100) : 0; if (monthExpense > monthIncome) { const deficit = monthExpense - monthIncome; return `Seus gastos ultrapassaram a receita em ${window.fmtBRLString(deficit)} neste mês. ${top.category} representa ${pct}% — vale revisar.`; } return `${top.category} concentra ${pct}% dos seus gastos do mês. Quer dicas para reduzir?`; }, [catData, monthIncome, monthExpense]); return (
onGoTo("insights"))} title="Ver Insights">
IA · Smart Insight

{loading ? "Analisando seus números…" : (insight || defaultInsight)}

); } // ---------- QUICK ACTIONS ---------- function QuickActions({ onAdd }) { const actions = [ { id: "receita", label: "Nova Receita", icon: , color: "bg-green" }, { id: "despesa", label: "Novo Gasto", icon: , color: "bg-pink" }, { id: "divida", label: "Nova Dívida", icon: , color: "bg-yellow" }, { id: "fatura", label: "Pagar Fatura", icon: , color: "bg-green-soft" }, ]; return (

Ações Rápidas

{actions.map(a => ( ))}
); } // ---------- AI RECOMMENDATIONS ---------- function Recommendations({ catData, dividas, monthIncome, monthExpense, onGoTo }) { const recs = useMemoP(() => { const items = []; const topCat = catData[0]; if (topCat) { items.push({ icon: "🍽️", title: `Reduzir ${topCat.category.replace(/[^\w\sÀ-ÿ]/g, "").trim()}`, sub: "Definir teto semanal pode economizar até 20%", type: "card-light", }); } if (dividas.length) { const total = dividas.reduce((s, d) => s + d.saldoDevedor, 0); items.push({ icon: "💰", title: `Quitar ${window.fmtBRLString(total).replace(",00","")}`, sub: `${dividas.length} dívidas pendentes`, type: "card-dark", }); } const subsCat = catData.find(c => c.category.includes("Assinatura")); if (subsCat) { items.push({ icon: "📺", title: "Auditar assinaturas", sub: `Você gasta ${window.fmtBRLString(subsCat.value).replace(",00","")}/mês`, type: "card-light", }); } if (monthExpense > monthIncome) { items.push({ icon: "⚠️", title: "Mês no vermelho", sub: `Déficit de ${window.fmtBRLString(monthExpense - monthIncome).replace(",00","")}`, type: "card-alert", }); } else { items.push({ icon: "✨", title: "Reserva de Emergência", sub: "Meta: 6× despesas mensais", type: "card-light", }); } return items.slice(0, 3); }, [catData, dividas, monthIncome, monthExpense]); return (
onGoTo("lancamentos"))} title="Agir no Checklist do mês">

Recomendações IA

{recs.map((r, i) => (
{r.icon}
{r.title}
{r.sub}
))}
); } // ---------- RECENT TRANSACTIONS ---------- function RecentTransactions({ txs, onDelete, onViewAll }) { const recent = [...txs].sort((a, b) => b.date.localeCompare(a.date)).slice(0, 5); function fmtDate(d) { const [y, m, dd] = d.split("-"); return `${dd}/${m}/${y.slice(2)}`; } return (

Lançamentos Recentes

{recent.map(t => { const isIncome = t.type === "Receita"; return (
{(t.category || "•").split(" ")[0]}
{t.description}
{fmtDate(t.date)} · {t.account}
{isIncome ? "+" : "-"}{window.fmtBRLString(t.amount).replace("R$ ", "R$ ").replace("-","")}
); })} {recent.length === 0 &&
Nenhum lançamento. Adicione um na barra de ações.
}
); } Object.assign(window, { SpendingOverview, AIInsight, QuickActions, Recommendations, RecentTransactions, Donut, PlanoIndependencia }); // ---------- PLANO 70 / 10 / 10 / 10 (Jim Rohn) ---------- function PlanoIndependencia({ plano, monthLabel, onGoToLancamentos }) { const { income, buckets, gastos } = plano; function money(v) { return window.fmtBRLString(Math.max(0, v)).replace(",00", ""); } return (
Independência Financeira · Jim Rohn

Plano 70 · 10 · 10 · 10

Receita base · {monthLabel}
{income <= 0 ? (
Sem receita lançada neste mês. As metas do plano aparecem assim que houver receita.
) : ( <>
{buckets.map(b => { const fill = Math.min(b.ratio, 1) * 100; const isGastos = b.key === "gastos"; return (
{b.icon} {Math.round(b.pct * 100)}%
{b.label}
{isGastos ? "máximo de gastos" : b.sub}
{money(b.used)}
de {money(b.target)}
{b.over ? `${money(b.used - b.target)} acima` : `falta ${money(b.remaining)}`}
); })}
{/* Lever analysis — fitting gastos into 70% */}
{gastos.over ? ( Gastos {money(gastos.overBy)} acima do teto de 70%. Para caber, corte gastos marcados como redutíveis — você tem {money(gastos.redutivelPendente)} ainda não pagos que podem ser reduzidos. ) : ( Gastos dentro do teto de 70%, com folga de {money(gastos.target - gastos.used)}. Mantenha o ritmo. )}
Pago{money(gastos.gastosPago)}
A pagar{money(gastos.gastosPendente)}
Redutível{money(gastos.gastosRedutivel)}
)}
); }