body.dark {
--bg: #121212;
--text: #f1f1f1;
--card-bg: #1e1e1e;
--header-bg: #222;
--header-text: #00d1ff;
--summary-bg: #2a2a2a;
--summary-border: #444;
--button-bg: #007BFF;
--button-hover: #0056b3;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0; padding: 0;
background: var(--bg);
color: var(--text);
transition: background 0.3s, color 0.3s;
}
header {
background: var(--header-bg);
color: var(--header-text);
padding: 15px 20px;
display: flex;
align-items: center;
justify-content: space-between;
}
header h1 {
margin: 0;
font-size: 1.5rem;
}
.container {
max-width: 900px;
margin: 20px auto;
background: var(--card-bg);
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
transition: background 0.3s;
}
select, input, button {
margin: 8px 0;
padding: 10px;
border-radius: 6px;
font-size: 1rem;
}
select, input {
width: 100%;
border: 1px solid #ccc;
background: var(--card-bg);
color: var(--text);
}
button {
cursor: pointer;
border: none;
transition: 0.3s;
}
.export-btn {
background: var(--button-bg);
color: white;
width: 100%;
font-weight: bold;
}
.export-btn:hover { background: var(--button-hover); }
.date-range { display: none; margin-top: 10px; }
label { font-weight: bold; }
.summary-box {
background: var(--summary-bg);
border: 1px solid var(--summary-border);
border-radius: 6px;
padding: 15px;
margin-top: 15px;
}
.summary-box h3 { margin: 0 0 10px 0; font-size: 1.2rem; }
canvas { margin-top: 15px; max-width: 100%; }
ul { list-style: none; padding: 0; margin: 10px 0 0 0; }
ul li { padding: 6px 0; border-bottom: 1px solid #eee; }
.dark-toggle {
background: transparent;
border: 2px solid var(--header-text);
color: var(--header-text);
padding: 6px 12px;
border-radius: 20px;
cursor: pointer;
font-size: 0.9rem;
transition: 0.3s;
}
.dark-toggle:hover { background: var(--header-text); color: var(--header-bg); }
🌙 Modo Escuro
📅 Escolha o período:
Todo histórico
Última semana
Último mês
Intervalo personalizado
<script>
const { jsPDF } = window.jspdf;
let history = JSON.parse(localStorage.getItem("pixHistory")) || [];
if (history.length === 0) {
history = [
{ valor: "50.00", data: "30/08/2025 14:35", pix: "000201|valor=50.00" },
{ valor: "120.75", data: "25/08/2025 19:12", pix: "000201|valor=120.75" },
{ valor: "10.00", data: "15/08/2025 09:50", pix: "000201|valor=10.00" },
{ valor: "75.00", data: "05/07/2025 16:40", pix: "000201|valor=75.00" }
];
localStorage.setItem("pixHistory", JSON.stringify(history));
}
const historyList = document.getElementById("historyList");
history.forEach(item => {
const li = document.createElement("li");
li.textContent = `R$ ${item.valor} - ${item.data}`;
historyList.appendChild(li);
});
function toggleDateRange() {
const filter = document.getElementById("filterPeriod").value;
document.getElementById("dateRange").style.display = (filter === "custom") ? "block" : "none";
}
function parseDate(dateStr) {
const [d, m, yAndTime] = dateStr.split("/");
const [y, time] = yAndTime.split(" ");
const [year, month, day] = [parseInt(y), parseInt(m) - 1, parseInt(d)];
const [hh, mm] = time.split(":").map(Number);
return new Date(year, month, day, hh, mm);
}
function generateQRCode(data) {
return new Promise((resolve) => {
const qr = qrcode(0, "L");
qr.addData(data);
qr.make();
resolve(qr.createDataURL(4, 0));
});
}
function filterHistory() {
const filter = document.getElementById("filterPeriod").value;
const now = new Date();
let filtered = history;
if (filter === "week") {
const lastWeek = new Date();
lastWeek.setDate(now.getDate() - 7);
filtered = history.filter(item => parseDate(item.data) >= lastWeek);
} else if (filter === "month") {
const lastMonth = new Date();
lastMonth.setMonth(now.getMonth() - 1);
filtered = history.filter(item => parseDate(item.data) >= lastMonth);
} else if (filter === "custom") {
const startDate = new Date(document.getElementById("startDate").value);
const endDate = new Date(document.getElementById("endDate").value);
if (!isNaN(startDate) && !isNaN(endDate)) {
filtered = history.filter(item => {
const itemDate = parseDate(item.data);
return itemDate >= startDate && itemDate <= endDate;
});
} else {
filtered = [];
}
}
return filtered;
}
let chart;
function updateSummary() {
const filtered = filterHistory();
const summaryText = document.getElementById("summaryText");
if (filtered.length === 0) {
summaryText.textContent = "Nenhuma cobrança encontrada neste período ❗";
if (chart) chart.destroy();
return;
}
const totalCobrancas = filtered.length;
const somaValores = filtered.reduce((sum, item) => sum + parseFloat(item.valor), 0).toFixed(2);
summaryText.textContent = `Total de cobranças: ${totalCobrancas} | Valor total: R$ ${somaValores}`;
const labels = filtered.map(item => item.data.split(" ")[0]);
const valores = filtered.map(item => parseFloat(item.valor));
if (chart) chart.destroy();
const ctx = document.getElementById("chartResumo").getContext("2d");
chart = new Chart(ctx, {
type: "bar",
data: {
labels: labels,
datasets: [{
label: "Valores (R$)",
data: valores,
backgroundColor: getComputedStyle(document.body).getPropertyValue("--button-bg")
}]
},
options: { responsive: true, plugins: { legend: { display: false } } }
});
}
async function exportPDF() {
const filteredHistory = filterHistory();
if (filteredHistory.length === 0) {
alert("Nenhuma cobrança encontrada ❗");
return;
}
const totalCobrancas = filteredHistory.length;
const somaValores = filteredHistory.reduce((sum, item) => sum + parseFloat(item.valor), 0).toFixed(2);
const doc = new jsPDF();
doc.setFillColor(30, 30, 30);
doc.rect(0, 0, 210, 25, "F");
doc.setTextColor(0, 209, 255);
doc.setFontSize(16);
doc.text("Relatório de Cobranças PIX", 14, 17);
doc.setTextColor(0, 0, 0);
doc.setFontSize(12);
doc.text("📌 Resumo do período", 14, 35);
doc.text(`- Total de cobranças: ${totalCobrancas}`, 14, 43);
doc.text(`- Valor total: R$ ${somaValores}`, 14, 51);
const chartCanvas = document.getElementById("chartResumo");
const chartImg = await html2canvas(chartCanvas).then(canvas => canvas.toDataURL("image/png"));
doc.addImage(chartImg, "PNG", 14, 60, 180, 80);
const body = [];
const qrImages = [];
for (let item of filteredHistory) {
const qrImg = await generateQRCode(item.pix);
body.push([`R$ ${item.valor}`, item.data, ""]);
qrImages.push(qrImg);
}
doc.autoTable({
startY: 150,
head: [["Valor", "Data", "QR Code"]],
body: body,
theme: "grid",
styles: { valign: "middle", halign: "center" },
columnStyles: { 0: { cellWidth: 40 }, 1: { cellWidth: 70 }, 2: { cellWidth: 60 } },
didDrawCell: function (data) {
if (data.column.index === 2 && data.cell.section === "body") {
const qrImg = qrImages[data.row.index];
if (qrImg) {
doc.addImage(qrImg, "PNG", data.cell.x + 15, data.cell.y + 2, 30, 30);
}
}
}
});
doc.save("relatorio_pix.pdf");
}
function toggleDarkMode() {
document.body.classList.toggle("dark");
updateSummary();
}
updateSummary();
</script>
<div class="date-range" id="dateRange">
<label for="startDate">Data inicial:</label>
<input type="date" id="startDate" onchange="updateSummary()">
<label for="endDate">Data final:</label>
<input type="date" id="endDate" onchange="updateSummary()">
</div>
<div class="summary-box" id="summaryBox">
<h3>📌 Resumo do período</h3>
<p id="summaryText">Selecione um período para ver o resumo.</p>
</div>
<canvas id="chartResumo"></canvas>
<button class="export-btn" onclick="exportPDF()">📄 Exportar PDF Completo</button>
<ul id="historyList"></ul>