/* O Tal Picolé — Filtros de período + helpers de export/import */
/* ============================================================
PERÍODO — utilitários
============================================================ */
const DEFAULT_REF_DATE = new Date(2026, 4, 20); // base do mock — 20/mai/2026
function periodRange(period, refDate){
const ref = refDate || new Date();
const out = { start: null, end: null, label: "" };
const today = new Date(ref.getFullYear(), ref.getMonth(), ref.getDate());
if (period.kind === "day") {
out.start = today; out.end = today;
out.label = "Hoje · " + today.toLocaleDateString("pt-BR");
} else if (period.kind === "week") {
const s = new Date(today); s.setDate(today.getDate() - 6);
out.start = s; out.end = today;
out.label = "Últimos 7 dias";
} else if (period.kind === "month") {
const s = new Date(today); s.setDate(today.getDate() - 29);
out.start = s; out.end = today;
out.label = "Últimos 30 dias";
} else if (period.kind === "year") {
const s = new Date(today.getFullYear(), 0, 1);
out.start = s; out.end = today;
out.label = "Este ano (" + today.getFullYear() + ")";
} else if (period.kind === "custom") {
out.start = period.start ? new Date(period.start + "T00:00:00") : null;
out.end = period.end ? new Date(period.end + "T00:00:00") : null;
if (out.start && out.end) {
out.label = out.start.toLocaleDateString("pt-BR") + " — " + out.end.toLocaleDateString("pt-BR");
} else {
out.label = "Selecione um intervalo";
}
} else {
out.label = "Todo o histórico";
}
return out;
}
function inPeriod(dateStr, period, refDate){
if (!period || period.kind === "all") return true;
const r = periodRange(period, refDate);
if (!r.start || !r.end) return true;
const d = new Date((dateStr + "").slice(0,10) + "T00:00:00");
return d >= r.start && d <= r.end;
}
function ymd(d){
const dt = d instanceof Date ? d : new Date(d);
const m = String(dt.getMonth()+1).padStart(2,"0");
const da = String(dt.getDate()).padStart(2,"0");
return `${dt.getFullYear()}-${m}-${da}`;
}
/* ============================================================
PERIOD PICKER (componente)
============================================================ */
function PeriodPicker({ value, onChange, refDate, compact }) {
const [open, setOpen] = useState(false);
const [customStart, setCustomStart] = useState(value?.start || "");
const [customEnd, setCustomEnd] = useState(value?.end || "");
const ref = useRef(null);
useEffect(() => {
const close = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
if (open) document.addEventListener("mousedown", close);
return () => document.removeEventListener("mousedown", close);
}, [open]);
const set = (kind) => { onChange({ kind }); setOpen(false); };
const setCustom = () => {
if (customStart && customEnd) {
onChange({ kind:"custom", start: customStart, end: customEnd });
setOpen(false);
}
};
const current = value || { kind:"month" };
const r = periodRange(current, refDate);
const presets = [
{ kind:"day", label:"Hoje" },
{ kind:"week", label:"Últimos 7 dias" },
{ kind:"month", label:"Últimos 30 dias" },
{ kind:"year", label:"Este ano" },
{ kind:"all", label:"Todo o histórico" },
];
return (
{open && (
{presets.map(p => (
))}
)}
);
}
/* ============================================================
EXPORT (CSV) / IMPORT (JSON)
============================================================ */
function downloadFile(filename, content, mime){
const blob = new Blob([content], { type: mime || "text/plain;charset=utf-8" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url; a.download = filename;
document.body.appendChild(a); a.click();
setTimeout(()=>{ URL.revokeObjectURL(url); a.remove(); }, 200);
}
function toCSV(headers, rows){
const esc = (v) => {
if (v === null || v === undefined) return "";
const s = String(v).replace(/"/g, '""');
return /[",;\n]/.test(s) ? `"${s}"` : s;
};
const lines = [headers.map(esc).join(";")];
rows.forEach(r => lines.push(r.map(esc).join(";")));
return "\uFEFF" + lines.join("\n"); // BOM para Excel BR
}
function exportFinanceCsv(finance, period){
const r = periodRange(period || { kind:"all" });
const filtered = finance.filter(f => inPeriod(f.date, period || { kind:"all" }));
const headers = ["Data", "Tipo", "Categoria", "Descrição", "Status", "Valor"];
const rows = filtered.map(f => [
f.date, f.kind === "in" ? "Entrada" : "Saída", f.cat, f.ref,
f.status === "confirmado" ? "Confirmado" : "A receber",
(f.kind==="in" ? "+" : "-") + f.value.toFixed(2).replace(".",","),
]);
const period_lbl = r.label.replace(/[ ·/]/g,"_");
downloadFile(`movimentacoes_${period_lbl}.csv`, toCSV(headers, rows), "text/csv;charset=utf-8");
}
function exportSalesCsv(sales){
const headers = ["Nº", "Data", "Distribuidor", "Valor", "Status"];
const rows = sales.map(s => [s.id, s.date, s.ref, s.value.toFixed(2).replace(".",","), s.status==="confirmado"?"Pago":"A receber"]);
downloadFile(`vendas_distribuidores.csv`, toCSV(headers, rows), "text/csv;charset=utf-8");
}
function pickFile(accept){
return new Promise((resolve) => {
const input = document.createElement("input");
input.type = "file"; input.accept = accept || "*";
input.onchange = () => resolve(input.files && input.files[0] ? input.files[0] : null);
input.click();
});
}
async function readFileAsText(file){
return await new Promise((resolve, reject) => {
const r = new FileReader();
r.onload = () => resolve(r.result);
r.onerror = reject;
r.readAsText(file);
});
}
async function readFileAsDataURL(file){
return await new Promise((resolve, reject) => {
const r = new FileReader();
r.onload = () => resolve(r.result);
r.onerror = reject;
r.readAsDataURL(file);
});
}
Object.assign(window, {
periodRange, inPeriod, ymd, PeriodPicker,
downloadFile, toCSV, exportFinanceCsv, exportSalesCsv,
pickFile, readFileAsText, readFileAsDataURL,
DEFAULT_REF_DATE,
});