// Modals and secondary views (full list, debts page) const { useState: useStateM, useEffect: useEffectM, useRef: useRefM } = React; // ---------- MODAL SHELL ---------- function Modal({ title, onClose, children, footer }) { const backDown = useRefM(false); useEffectM(() => { const onKey = e => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [onClose]); return (
{ backDown.current = e.target === e.currentTarget; }} onMouseUp={e => { if (backDown.current && e.target === e.currentTarget) onClose(); backDown.current = false; }}>
e.stopPropagation()}>

{title}

{children}
{footer &&
{footer}
}
); } // ---------- ADD TX MODAL ---------- function AddTxModal({ kind, onClose, onSave, categorias, contas, defaultMonth, defaultYear }) { const isIncome = kind === "receita"; const today = new Date(); const defaultDate = `${defaultYear}-${String(defaultMonth + 1).padStart(2, "0")}-${String(today.getDate()).padStart(2, "0")}`; const [form, setForm] = useStateM({ description: "", date: defaultDate, amount: "", account: contas[0] || "PIX", category: isIncome ? "💸 Receita" : (categorias.find(c => c.tipo !== "Receita")?.nome || "🤷 Outros"), status: "Pago", redutivel: false, fixa: false, classe: "variavel", }); const cats = isIncome ? categorias.filter(c => c.tipo === "Receita") : categorias.filter(c => c.tipo !== "Receita"); function submit(e) { e.preventDefault(); const amount = window.parseValor(form.amount); if (!form.description || isNaN(amount) || amount <= 0) return; onSave({ id: window.uid(), description: form.description, date: form.date, amount: amount, account: form.account, category: form.category, type: isIncome ? "Receita" : "Despesa", status: form.status, redutivel: isIncome ? false : form.redutivel, fixa: form.fixa, classe: isIncome ? undefined : form.classe, }); onClose(); } const title = isIncome ? "Nova Receita" : "Novo Gasto"; return (
setForm({...form, description: e.target.value})} placeholder={isIncome ? "Ex: Salário" : "Ex: Mercado"} autoFocus required/>
setForm({...form, amount: e.target.value})} placeholder="0,00" required/>
setForm({...form, date: e.target.value})} required/>
{isIncome ? (
) : (
)}
{!isIncome && (
)}
); } // ---------- ADD DEBT MODAL ---------- function AddDebtModal({ onClose, onSave, edit }) { const [form, setForm] = useStateM(edit || { descricao: "", credor: "", valorInicial: "", saldoDevedor: "", dataInicio: new Date().toISOString().slice(0, 10), vencimento: "", parcelas: 1, parcelasPagas: 0, status: "Em aberto", }); function submit(e) { e.preventDefault(); const vi = window.parseValor(form.valorInicial); const sd = window.parseValor(form.saldoDevedor); onSave({ id: edit?.id || ("d" + window.uid()), descricao: form.descricao, credor: form.credor, valorInicial: isNaN(vi) ? 0 : vi, saldoDevedor: isNaN(sd) ? (isNaN(vi) ? 0 : vi) : sd, dataInicio: form.dataInicio, vencimento: form.vencimento, parcelas: Number(form.parcelas), parcelasPagas: Number(form.parcelasPagas), status: form.status, }); onClose(); } return (
setForm({...form, descricao: e.target.value})} required autoFocus/>
setForm({...form, credor: e.target.value})}/>
setForm({...form, valorInicial: e.target.value, saldoDevedor: form.saldoDevedor || e.target.value})} required/>
setForm({...form, saldoDevedor: e.target.value})}/>
setForm({...form, dataInicio: e.target.value})}/>
setForm({...form, vencimento: e.target.value})}/>
setForm({...form, parcelas: e.target.value})} min="1"/>
setForm({...form, parcelasPagas: e.target.value})} min="0"/>
); } // ---------- DÍVIDAS PAGE ---------- function DividasPage({ dividas, onAdd, onEdit, onDelete }) { const total = dividas.reduce((s, d) => s + d.saldoDevedor, 0); const pago = dividas.reduce((s, d) => s + (d.valorInicial - d.saldoDevedor), 0); const abertas = dividas.filter(d => d.status === "Em aberto").length; const semNeg = dividas.filter(d => d.status === "Sem Negociação").length; const finalizadas = dividas.filter(d => d.status === "Finalizado").length; return (

📋 Rastreador de Dívidas

Total de Dívidas
s + d.valorInicial, 0)}/>
Saldo Devedor
Saldo Pago
Em aberto{abertas}
Sem negociação{semNeg}
Finalizadas{finalizadas}
{dividas.map(d => { const pct = d.valorInicial > 0 ? Math.round(((d.valorInicial - d.saldoDevedor) / d.valorInicial) * 100) : 0; return ( ); })} {dividas.length === 0 && ( )}
Descrição Credor Valor Inicial Saldo Devedor Vencimento Parcelas Status
{d.descricao}
{pct}%
{d.credor || "—"} {window.fmtBRLString(d.valorInicial)} {window.fmtBRLString(d.saldoDevedor)} {d.vencimento ? d.vencimento.split("-").reverse().join("/") : "—"} {d.parcelasPagas}/{d.parcelas} {d.status}
Nenhuma dívida cadastrada.
); } // ---------- LANÇAMENTOS: ver src/checklist.jsx (checklist mensal) ---------- Object.assign(window, { Modal, AddTxModal, AddDebtModal, DividasPage });