Meta Description" name="description" />
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Gastos por categoría (01/01–24/01)</title>
<style>
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; margin: 24px; color: #222; }
h1 { margin: 0 0 4px; font-size: 20px; }
p { margin: 6px 0 20px; color: #555; }
.wrap { display: grid; grid-template-columns: 1fr; gap: 24px; max-width: 980px; }
.card { border: 1px solid #eee; border-radius: 10px; padding: 16px; }
.row { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
canvas { width: 100%; height: 360px; background: #fff; border-radius: 8px; }
table { width: 100%; border-collapse: collapse; font-size: 14px; margin-top: 12px; }
th, td { padding: 8px 10px; border-bottom: 1px solid #f0f0f0; text-align: left; }
th { background: #fafafa; }
.amount { text-align: right; font-variant-numeric: tabular-nums; }
.chip { display:inline-block; width:12px; height:12px; border-radius: 2px; margin-right:8px; vertical-align: -1px; }
.total { font-weight: 600; }
@media (max-width: 760px){ .row{ grid-template-columns: 1fr; } canvas{ height:280px; } }
</style>
</head>
<body>
<h1>Gastos por categoría</h1>
<p>Período: 01/01 al 24/01 — Total gastado: <strong>$ 846.973,17</strong> ARS</p>
<div class="wrap">
<div class="card row">
<div>
<h3 style="margin:0 0 12px;">Gráfico de barras</h3>
<canvas id="bar"></canvas>
</div>
<div>
<h3 style="margin:0 0 12px;">Gráfico de torta</h3>
<canvas id="pie"></canvas>
</div>
</div>
<div class="card">
<h3 style="margin:0 0 12px;">Detalle</h3>
<table>
<thead>
<tr>
<th>Categoría</th>
<th class="amount">Importe</th>
<th class="amount">% del total</th>
</tr>
</thead>
<tbody id="tbl"></tbody>
<tfoot>
<tr>
<td class="total">Total</td>
<td class="amount total">$ 846.973,17</td>
<td class="amount total">100%</td>
</tr>
</tfoot>
</table>
</div>
</div>
<script>
// Datos (copiados de tu resumen)
const data = [
{ label: "Pendiente de categoría", amount: 448391.31, pct: 52.94, color: "#009EE3" },
{ label: "Comidas y bebidas", amount: 179700.00, pct: 21.22, color: "#2DD36F" },
{ label: "Supermercado", amount: 115115.20, pct: 13.59, color: "#FFC107" },
{ label: "Electrónica", amount: 73966.66, pct: 8.73, color: "#FF6D00" },
{ label: "Transporte", amount: 29000.00, pct: 3.42, color: "#9C27B0" },
{ label: "Hogar", amount: 800.00, pct: 0.09, color: "#607D8B" },
];
const total = 846973.17;
// Utilidades
const fmtARS = v =>
new Intl.NumberFormat("es-AR", { style: "currency", currency: "ARS", maximumFractionDigits: 2 }).format(v);
// Tabla
const tbody = document.getElementById("tbl");
data.forEach(d => {
const tr = document.createElement("tr");
tr.innerHTML = `
<td><span class="chip" style="background:${d.color}"></span>${d.label}</td>
<td class="amount">${fmtARS(d.amount)}</td>
<td class="amount">${d.pct.toFixed(2)}%</td>
`;
tbody.appendChild(tr);
});
// Gráfico de barras (sin librerías)
function drawBarChart(canvasId, series) {
const c = document.getElementById(canvasId);
const ctx = c.getContext("2d");
const W = c.width = c.clientWidth * devicePixelRatio;
const H = c.height = c.clientHeight * devicePixelRatio;
ctx.scale(devicePixelRatio, devicePixelRatio);
const pad = { t: 20, r: 16, b: 60, l: 50 };
const plotW = c.clientWidth - pad.l - pad.r;
const plotH = c.clientHeight - pad.t - pad.b;
const max = Math.max(...series.map(s => s.amount));
// Ejes
ctx.strokeStyle = "#eaeaea";
ctx.beginPath();
ctx.moveTo(pad.l, pad.t);
ctx.lineTo(pad.l, pad.t + plotH);
ctx.lineTo(pad.l + plotW, pad.t + plotH);
ctx.stroke();
// Grid y labels Y
ctx.fillStyle = "#666";
ctx.font = "12px system-ui";
const steps = 4;
for (let i=0; i<=steps; i++){
const yVal = (max/steps)*i;
const y = pad.t + plotH - (yVal/max)*plotH;
ctx.strokeStyle = "#f5f5f5";
ctx.beginPath();
ctx.moveTo(pad.l, y);
ctx.lineTo(pad.l + plotW, y);
ctx.stroke();
ctx.fillStyle = "#888";
ctx.fillText(fmtARS(yVal).replace("ARS", "").trim(), 6, y+4);
}
// Barras
const bw = plotW / (series.length * 1.6);
const gap = bw * 0.6;
series.forEach((s, i) => {
const x = pad.l + i*(bw+gap) + gap/2;
const h = (s.amount/max)*plotH;
const y = pad.t + plotH - h;
ctx.fillStyle = s.color;
ctx.fillRect(x, y, bw, h);
// Label X
ctx.save();
ctx.translate(x + bw/2, pad.t + plotH + 14);
ctx.rotate(-Math.PI/10);
ctx.fillStyle = "#444";
ctx.textAlign = "center";
ctx.font = "12px system-ui";
ctx.fillText(s.label, 0, 0);
ctx.restore();
});
}
// Gráfico de torta (sin librerías)
function drawPieChart(canvasId, series) {
const c = document.getElementById(canvasId);
const ctx = c.getContext("2d");
const W = c.width = c.clientWidth * devicePixelRatio;
const H = c.height = c.clientHeight * devicePixelRatio;
ctx.scale(devicePixelRatio, devicePixelRatio);
const cx = c.clientWidth / 2;
const cy = c.clientHeight / 2;
const r = Math.min(cx, cy) - 10;
let start = -Math.PI / 2;
series.forEach(s => {
const angle = (s.amount / total) Math.PI 2;
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(cx, cy, r, start, start + angle);
ctx.closePath();
ctx.fillStyle = s.color;
ctx.fill();
start += angle;
});
// Leyenda
const legendX = 12, legendY = 12;
ctx.font = "12px system-ui";
let y = legendY;
series.forEach(s => {
ctx.fillStyle = s.color;
ctx.fillRect(legendX, y-10, 12, 12);
ctx.fillStyle = "#333";
ctx.fillText(${s.label} — ${s.pct.toFixed(2)}%, legendX + 18, y);
y += 18;
});
}
drawBarChart("bar", data);
drawPieChart("pie", data);
// Redibuja si cambia el tamaño
addEventListener("resize", () => { drawBarChart("bar", data); drawPieChart("pie", data); });
</script>
</body>
</html>
1
1
6KB
6KB
95.0ms
200.0ms
96.0ms