);
}
// ---------- RIGHT PANEL (guia de saúde financeira por etapa) ----------
function guiaEtapa(view, plano, appState) {
const money = v => window.fmtBRLString(Math.max(0, v)).replace(",00", "");
const g = plano.gastos;
const inc = plano.income;
const aporte10 = inc * 0.10;
const seguros = (appState?.seguros || []).length;
switch (view) {
case "familia":
return { icon: "👨👩👧👦", titulo: "Família e objetivos", texto: "Estrutura familiar e objetivos definem o tamanho dos 3 baldes de 10% (caridade, capital ativo e passivo). Comece por aqui." };
case "orcamento":
return inc <= 0
? { icon: "💳", titulo: "Orçamento", texto: "Lance a receita do mês para ativar a regra 70·10·10·10: 70% é o teto de gastos, 30% viram caridade e capital." }
: g.over
? { icon: "🚨", titulo: "Gastos acima dos 70%", texto: `Corte ${money(g.redutivelPendente)} nos ✂️ redutíveis ainda não validados para voltar ao teto de ${money(g.target)}. Depois valide tudo e feche o mês.` }
: { icon: "✅", titulo: "Dentro dos 70%", texto: `Folga de ${money(g.target - g.used)} no teto. Valide os lançamentos e feche o balanço do mês para consolidar.` };
case "patrimonio":
return { icon: "📊", titulo: "Patrimônio", texto: inc > 0 ? `Os 10% de Capital Passivo (${money(aporte10)}/mês) são o motor deste patrimônio. Constância vale mais que valor.` : "O patrimônio cresce com os 10% de Capital Passivo aportados todo mês." };
case "protecao":
return seguros > 0
? { icon: "🛡️", titulo: "Proteção", texto: "Prêmios de seguro entram como classe Proteção dentro dos 70% de gastos — fixos e não redutíveis. Revise coberturas 1x ao ano." }
: { icon: "🛡️", titulo: "Sem proteção cadastrada", texto: "Antes de acelerar os 10% de investimento, proteja a renda: seguros cabem nos 70% como gasto fixo essencial." };
case "reserva": return { icon: "💸", titulo: "Reserva de emergência", texto: inc > 0 ? `Direcione os 10% de Capital Passivo (${money(aporte10)}/mês) para a reserva até completar o alvo — só depois migre para investimentos de longo prazo.` : "A reserva vem antes dos investimentos: é o primeiro destino dos 10% de Capital Passivo." };
case "aposentadoria":
return { icon: "🌴", titulo: "Independência", texto: inc > 0 ? `Mantidos os 10% (${money(aporte10)}/mês) aportados sem falha, a projeção de capital se sustenta. O teto de 70% é o que garante o aporte.` : "A aposentadoria é financiada pelos 10% de Capital Passivo — que só existem se os gastos respeitarem os 70%." };
case "imoveis":
case "automoveis":
return { icon: "🏘️", titulo: "Bens de uso", texto: "Custos de imóveis e veículos (financiamento, IPVA, manutenção) são gastos fixos — precisam caber dentro dos 70%." };
case "irpf":
return { icon: "🧾", titulo: "IRPF", texto: "Previdência dedutível e doações incentivadas fazem os baldes de 10% renderem também no imposto." };
default:
return inc > 0 && g.over
? { icon: "🧭", titulo: "Próximo passo", texto: `Seus gastos passaram do teto de 70% em ${money(g.overBy)}. Vá ao Orçamento e corte nos redutíveis.` }
: { icon: "🧭", titulo: "Regra 70·10·10·10", texto: "Viva com 70% da receita; doe 10%; invista 10% em você (capital ativo) e 10% em ativos (capital passivo)." };
}
}
function RightPanel({ user, plano, monthLabel, onGoTo, monthKey, planoCheck, onSavePlanoCheck, view, appState }) {
const { income, buckets, gastos } = plano;
const onTrack = income > 0 && !gastos.over;
const guia = guiaEtapa(view, plano, appState);
const money = v => window.fmtBRLString(Math.max(0, v)).replace(",00", "");
const checks = (planoCheck && planoCheck[monthKey]) || {};
const [editing, setEditing] = useState(null); // bucket key aberto para edição
const [note, setNote] = useState("");
function openEditor(key) {
const cur = checks[key] || {};
setNote(cur.nota || "");
setEditing(e => (e === key ? null : key));
}
function saveNote(key) {
onSavePlanoCheck(monthKey, key, { nota: note });
setEditing(null);
}
return (
);
}
// ---------- AI ASSISTANT ----------
function AIAssistant() {
const [open, setOpen] = useState(false);
const [q, setQ] = useState("");
const [busy, setBusy] = useState(false);
const [answer, setAnswer] = useState("");
async function ask(question) {
if (!question.trim()) return;
setBusy(true); setAnswer(""); setOpen(true);
try {
const ctx = window.__appContext?.();
const prompt = `Você é um assistente financeiro pessoal. Responda em português, de forma direta e prática, em no máximo 3 frases.\n\nDados do usuário (resumo):\n${JSON.stringify(ctx, null, 2)}\n\nPergunta: ${question}`;
const res = await window.claude.complete(prompt);
setAnswer(res);
} catch (e) {
setAnswer("Não consegui responder agora. Tente novamente.");
} finally {
setBusy(false);
}
}
return (