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