Skip to content

Instantly share code, notes, and snippets.

@Jcbertorello
Created December 28, 2025 17:33
Show Gist options
  • Select an option

  • Save Jcbertorello/51b4a5fc7486a851ec61500a15cb0056 to your computer and use it in GitHub Desktop.

Select an option

Save Jcbertorello/51b4a5fc7486a851ec61500a15cb0056 to your computer and use it in GitHub Desktop.
Dashboard Ajonjolí - 2025-12-28 17:33
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard Octubre 2025 - Rendimiento de Películas y Distribuidoras - Cinexo</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
:root {
--color-primary: #60B99A;
--color-primary-light: #7FCDB2;
--color-primary-dark: #4A9D80;
--color-secondary: #1F3B4D;
--color-secondary-light: #2D5066;
--color-dark: #1e293b;
--color-dark-soft: #334155;
--color-muted: #64748b;
--color-light: #94a3b8;
--color-bg: #f8fafc;
--color-bg-alt: #f1f5f9;
--color-card: #ffffff;
--color-border: #e2e8f0;
--color-success: #16a34a;
--color-success-bg: #dcfce7;
--color-danger: #dc2626;
--color-danger-bg: #fee2e2;
--color-warning: #FFB74D;
--color-warning-bg: #fff7ed;
--color-boleteria: #60B99A;
--color-candy: #FFB74D;
--color-ocupacion-alta: #16a34a;
--color-ocupacion-media: #FFB74D;
--color-ocupacion-baja: #dc2626;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
background-color: var(--color-bg);
color: var(--color-dark);
line-height: 1.6;
min-height: 100vh;
}
.dashboard {
max-width: 1200px;
margin: 0 auto;
padding: 32px 24px;
}
.action-buttons {
display: flex;
gap: 12px;
margin-bottom: 24px;
}
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 12px 24px;
border-radius: 12px;
font-weight: 600;
font-size: 0.95rem;
cursor: pointer;
border: none;
transition: all 0.2s ease;
}
.btn-primary {
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%);
color: white;
box-shadow: 0 4px 12px -2px rgba(96, 185, 154, 0.4);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px -2px rgba(96, 185, 154, 0.5);
}
.btn-secondary {
background-color: var(--color-card);
color: var(--color-dark);
border: 1px solid var(--color-border);
}
.btn-secondary:hover {
background-color: var(--color-bg-alt);
border-color: var(--color-primary);
}
.header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 32px;
flex-wrap: wrap;
gap: 20px;
}
.header-left {
display: flex;
align-items: center;
gap: 16px;
}
.logo {
width: 56px;
height: 56px;
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%);
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.5rem;
font-weight: 800;
box-shadow: 0 8px 20px -4px rgba(96, 185, 154, 0.4);
}
.header-title {
font-size: 1.75rem;
font-weight: 800;
color: var(--color-dark);
margin-bottom: 4px;
}
.header-subtitle {
font-size: 0.95rem;
color: var(--color-muted);
}
.header-date {
font-size: 0.85rem;
color: var(--color-light);
text-align: right;
}
.info-bar {
background: var(--color-card);
border: 1px solid var(--color-border);
border-left: 4px solid var(--color-primary);
border-radius: 12px;
padding: 16px 20px;
margin-bottom: 24px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 12px;
}
.info-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 0.875rem;
color: var(--color-muted);
}
.info-item strong { color: var(--color-dark); }
.card {
background-color: var(--color-card);
border: 1px solid var(--color-border);
border-radius: 20px;
padding: 24px;
margin-bottom: 24px;
box-shadow: 0 4px 20px -4px rgba(30, 41, 59, 0.08);
transition: box-shadow 0.3s ease, transform 0.3s ease;
break-inside: avoid;
page-break-inside: avoid;
}
.card:hover {
box-shadow: 0 8px 30px -4px rgba(30, 41, 59, 0.12);
transform: translateY(-2px);
}
.card-title {
font-size: 1.1rem;
font-weight: 700;
color: var(--color-dark);
margin-bottom: 4px;
}
.card-subtitle {
font-size: 0.85rem;
color: var(--color-muted);
margin-bottom: 20px;
}
.kpi-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 32px;
}
.kpi-card {
background-color: var(--color-card);
border: 1px solid var(--color-border);
border-radius: 20px;
padding: 24px;
text-align: center;
box-shadow: 0 4px 20px -4px rgba(30, 41, 59, 0.08);
transition: all 0.3s ease;
break-inside: avoid;
page-break-inside: avoid;
position: relative;
overflow: hidden;
}
.kpi-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--color-primary), var(--color-primary-light));
}
.kpi-card.total::before {
background: linear-gradient(90deg, var(--color-secondary), var(--color-secondary-light));
}
.kpi-card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 30px -4px rgba(30, 41, 59, 0.15);
}
.kpi-icon { font-size: 2rem; margin-bottom: 12px; }
.kpi-label {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--color-muted);
margin-bottom: 12px;
}
.kpi-value {
font-size: 2rem;
font-weight: 800;
color: var(--color-dark);
line-height: 1.1;
margin-bottom: 8px;
}
.kpi-detail {
font-size: 0.85rem;
color: var(--color-muted);
}
.grid-2 {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24px;
margin-bottom: 24px;
}
.chart-container {
height: 300px;
position: relative;
}
.chart-container.large { height: 350px; }
.table-wrapper { overflow-x: auto; }
.data-table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
}
.data-table thead { background-color: var(--color-bg-alt); }
.data-table th {
padding: 14px 16px;
text-align: left;
font-weight: 700;
color: var(--color-muted);
text-transform: uppercase;
font-size: 0.7rem;
letter-spacing: 0.08em;
border-bottom: 2px solid var(--color-border);
}
.data-table th.text-right { text-align: right; }
.data-table td {
padding: 14px 16px;
border-bottom: 1px solid var(--color-border);
color: var(--color-dark);
vertical-align: middle;
}
.data-table td.text-right { text-align: right; }
.data-table td.font-bold { font-weight: 700; }
.data-table tr:hover { background-color: var(--color-bg); }
.data-table tr:last-child td { border-bottom: none; }
.rank-badge {
width: 32px;
height: 32px;
border-radius: 10px;
display: inline-flex;
align-items: center;
justify-content: center;
font-weight: 800;
font-size: 0.85rem;
}
.rank-1 { background: linear-gradient(135deg, #FFD700, #FFA500); color: white; }
.rank-2 { background: linear-gradient(135deg, #C0C0C0, #A0A0A0); color: white; }
.rank-3 { background: linear-gradient(135deg, #CD7F32, #B8860B); color: white; }
.rank-default { background-color: var(--color-bg-alt); color: var(--color-muted); }
.badge {
display: inline-flex;
align-items: center;
padding: 4px 10px;
border-radius: 20px;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
margin-right: 4px;
margin-bottom: 4px;
}
.badge-2d { background: #e0f2fe; color: #0369a1; }
.badge-3d { background: #fef3c7; color: #b45309; }
.badge-imax { background: #fce7f3; color: #be185d; }
.badge-4dx { background: #f3e8ff; color: #7c3aed; }
.footer {
text-align: center;
padding: 32px 24px;
margin-top: 32px;
border-top: 1px solid var(--color-border);
}
.footer-content {
display: inline-flex;
align-items: center;
gap: 12px;
color: var(--color-light);
font-size: 0.9rem;
}
.footer-logo {
width: 32px;
height: 32px;
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark));
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 0.9rem;
font-weight: 800;
}
.footer-text { text-align: left; }
.footer-text small { display: block; font-size: 0.75rem; color: var(--color-light); }
@media (max-width: 900px) {
.grid-2, .grid-3 { grid-template-columns: 1fr; }
.kpi-grid { grid-template-columns: repeat(2, 1fr); }
.header { flex-direction: column; }
.header-date { text-align: left; }
}
@media (max-width: 500px) {
.kpi-grid { grid-template-columns: 1fr; }
.dashboard { padding: 20px 16px; }
.info-bar { flex-direction: column; align-items: flex-start; }
}
@media print {
body {
background-color: white !important;
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
}
.action-buttons { display: none !important; }
.card, .kpi-card {
break-inside: avoid !important;
page-break-inside: avoid !important;
box-shadow: none !important;
border: 1px solid #e0e0e0 !important;
}
.card:hover, .kpi-card:hover { transform: none !important; }
.dashboard { padding: 16px !important; max-width: 100% !important; }
.kpi-card::before { background: var(--color-primary) !important; }
.kpi-card.total::before { background: var(--color-secondary) !important; }
.logo, .footer-logo { background: var(--color-primary) !important; }
}
</style>
</head>
<body>
<div class="dashboard" id="dashboard-content">
<div class="action-buttons">
<button class="btn btn-primary" onclick="downloadPDF()">📥 Descargar PDF</button>
<button class="btn btn-secondary" onclick="window.print()">🖨️ Imprimir</button>
</div>
<div class="header">
<div class="header-left">
<div class="logo">C</div>
<div>
<h1 class="header-title" id="dashboardTitle"></h1>
<p class="header-subtitle" id="dashboardSubtitle"></p>
</div>
</div>
<div class="header-date" id="generationDate"></div>
</div>
<div class="info-bar">
<div class="info-item">📅 <strong>Período:</strong> <span id="periodDetail"></span></div>
<div class="info-item">🎬 <strong>Espectadores:</strong> <span id="totalSpectatorsInfo"></span></div>
<div class="info-item">📊 <strong>Tipo:</strong> Reporte de Películas y Distribuidoras</div>
</div>
<div class="kpi-grid" id="kpi-grid-container"></div>
<div class="grid-2">
<div class="card">
<div class="card-title">Top 5 Películas por Recaudación</div>
<div class="card-subtitle">Ingresos generados por las películas más taquilleras</div>
<div class="chart-container">
<canvas id="chartRecaudacion"></canvas>
</div>
</div>
<div class="card">
<div class="card-title">Distribución de Espectadores por Formato</div>
<div class="card-subtitle">Preferencia de formatos de proyección del público</div>
<div class="chart-container">
<canvas id="chartFormatos"></canvas>
</div>
</div>
</div>
<div class="card">
<div class="card-title">Recaudación por Distribuidora (Top 5)</div>
<div class="card-subtitle">Market share de las principales distribuidoras</div>
<div class="chart-container large">
<canvas id="chartDistribuidoras"></canvas>
</div>
</div>
<div class="card">
<div class="card-title">🎬 Ranking de Películas por Espectadores</div>
<div class="card-subtitle">Top 10 películas del período</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>#</th>
<th>Película</th>
<th>Formatos</th>
<th class="text-right">Espectadores</th>
<th class="text-right">Recaudación</th>
<th class="text-right">Ticket Prom.</th>
</tr>
</thead>
<tbody id="movie-ranking-table">
</tbody>
</table>
</div>
</div>
<div class="footer">
<div class="footer-content">
<div class="footer-logo">M</div>
<div class="footer-text">
<strong>Powered by MostachIA</strong>
<small>Procesos inteligentes, resultados superiores</small>
</div>
<span>•</span>
<span id="footerDate"></span>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
<script>
const jsonData = {
"dashboardType": "pelicula",
"dashboardTitle": "Dashboard Octubre 2025 - Rendimiento de Películas y Distribuidoras",
"clientName": "Cinexo",
"periodo": { "desde": "2025-10-01", "hasta": "2025-10-31", "descripcion": "Octubre 2025" },
"topPeliculasPorEspectadores": [
{"pelicula": "El Conjuro: Los Ultimos Ritos 2D 4D", "espectadores": 999, "recaudacion": 5123000},
{"pelicula": "Los tipos malos 2 3D", "espectadores": 765, "recaudacion": 3605000},
{"pelicula": "Homo argentum", "espectadores": 603, "recaudacion": 2568000},
{"pelicula": "TRON: Ares 3D+4D", "espectadores": 523, "recaudacion": 3134500},
{"pelicula": "Avatar: El camino del agua (RE) 3D+4D", "espectadores": 490, "recaudacion": 2493000},
{"pelicula": "La casa de muñecas de Gabby: La película", "espectadores": 434, "recaudacion": 1936000},
{"pelicula": "Los 4 Fantásticos: Primeros pasos 3D+4D", "espectadores": 368, "recaudacion": 2140000},
{"pelicula": "Teléfono negro 2 ATMOS", "espectadores": 365, "recaudacion": 1740000},
{"pelicula": "TRON: Ares 3D+4D", "espectadores": 173, "recaudacion": 979000},
{"pelicula": "Mascotas al rescate", "espectadores": 167, "recaudacion": 744000}
],
"topPeliculasPorRecaudacion": [
{"pelicula": "El Conjuro: Los Ultimos Ritos 2D 4D", "espectadores": 999, "recaudacion": 5123000},
{"pelicula": "Los tipos malos 2 3D", "espectadores": 765, "recaudacion": 3605000},
{"pelicula": "TRON: Ares 3D+4D", "espectadores": 523, "recaudacion": 3134500},
{"pelicula": "Homo argentum", "espectadores": 603, "recaudacion": 2568000},
{"pelicula": "Avatar: El camino del agua (RE) 3D+4D", "espectadores": 490, "recaudacion": 2493000},
{"pelicula": "Los 4 Fantásticos: Primeros pasos 3D+4D", "espectadores": 368, "recaudacion": 2140000},
{"pelicula": "La casa de muñecas de Gabby: La película", "espectadores": 434, "recaudacion": 1936000},
{"pelicula": "Teléfono negro 2 ATMOS", "espectadores": 365, "recaudacion": 1740000},
{"pelicula": "TRON: Ares 3D+4D", "espectadores": 173, "recaudacion": 979000},
{"pelicula": "TRON: Ares 2D+4D", "espectadores": 141, "recaudacion": 786000}
],
"espectadoresPorFormato": [
{"formato": "4D", "espectadores": 3093},
{"formato": "2D", "espectadores": 2609},
{"formato": "3D", "espectadores": 992}
],
"distribucionPorDistribuidora": [
{"distribuidora": "THE WALT DISNEY COMPANY SA", "espectadores": 2606, "recaudacion": 13533500},
{"distribuidora": "UNITED INTERNATIONAL PICTURES SRL", "espectadores": 2085, "recaudacion": 9813500},
{"distribuidora": "WARNER BROS", "espectadores": 1319, "recaudacion": 6639000},
{"distribuidora": "BF PARIS SRL.", "espectadores": 446, "recaudacion": 1987000},
{"distribuidora": "DIAMOND FILMS", "espectadores": 100, "recaudacion": 428000},
{"distribuidora": "LUPINO FILMS SRL", "espectadores": 65, "recaudacion": 244000},
{"distribuidora": "CINEMARK ARGENTINA S.R.L", "espectadores": 49, "recaudacion": 200000},
{"distribuidora": "CDI COMPANIA DISTRIBUIDORA INDEPENDIENTE SA", "espectadores": 19, "recaudacion": 98000},
{"distribuidora": "TRAPECIO CINE S.R.L", "espectadores": 5, "recaudacion": 28000}
]
};
document.addEventListener('DOMContentLoaded', () => {
populateDashboard(jsonData);
});
function populateDashboard(data) {
Chart.defaults.font.family = "'Segoe UI', system-ui, sans-serif";
Chart.defaults.font.size = 12;
Chart.defaults.color = '#64748b';
Chart.defaults.plugins.legend.labels.usePointStyle = true;
Chart.defaults.plugins.legend.labels.padding = 20;
Chart.defaults.plugins.legend.position = 'bottom';
const chartColors = {
primary: '#60B99A',
palette: ['#60B99A', '#1F3B4D', '#7FCDB2', '#2D5066', '#4A9D80', '#FFB74D', '#5A7A8A', '#A8D5C2']
};
const formatCurrency = (value) => '$' + value.toLocaleString('es-AR', { maximumFractionDigits: 0 });
const formatNumber = (value) => value.toLocaleString('es-AR', { maximumFractionDigits: 0 });
const formatDate = (dateString) => new Date(dateString + 'T00:00:00').toLocaleDateString('es-AR', { day: '2-digit', month: '2-digit', year: 'numeric' });
const currentDate = new Date().toLocaleDateString('es-AR', { day: '2-digit', month: '2-digit', year: 'numeric' });
document.getElementById('dashboardTitle').textContent = data.dashboardTitle;
document.getElementById('dashboardSubtitle').textContent = `${data.clientName} • ${data.periodo.descripcion}`;
document.getElementById('generationDate').textContent = `Generado: ${currentDate}`;
document.getElementById('footerDate').textContent = currentDate;
document.getElementById('periodDetail').textContent = `${formatDate(data.periodo.desde)} al ${formatDate(data.periodo.hasta)}`;
const totalEspectadores = data.distribucionPorDistribuidora.reduce((sum, item) => sum + item.espectadores, 0);
const totalRecaudacion = data.distribucionPorDistribuidora.reduce((sum, item) => sum + item.recaudacion, 0);
const ticketPromedio = totalEspectadores > 0 ? totalRecaudacion / totalEspectadores : 0;
const peliculaTop = data.topPeliculasPorRecaudacion[0];
document.getElementById('totalSpectatorsInfo').textContent = formatNumber(totalEspectadores);
const kpis = [
{ icon: '💰', label: 'Ingresos Totales', value: formatCurrency(totalRecaudacion), detail: 'Suma de toda la boletería', class: 'total' },
{ icon: '🎬', label: 'Total Espectadores', value: formatNumber(totalEspectadores), detail: `en ${data.periodo.descripcion}` },
{ icon: '🎫', label: 'Ticket Promedio', value: formatCurrency(ticketPromedio), detail: 'Ingreso por espectador' },
{ icon: '🏆', label: 'Película Top', value: peliculaTop.pelicula.split(' ').slice(0, 3).join(' '), detail: `Recaudó ${formatCurrency(peliculaTop.recaudacion)}` }
];
const kpiContainer = document.getElementById('kpi-grid-container');
kpiContainer.innerHTML = kpis.map(kpi => `
<div class="kpi-card ${kpi.class || ''}">
<div class="kpi-icon">${kpi.icon}</div>
<div class="kpi-label">${kpi.label}</div>
<div class="kpi-value">${kpi.value}</div>
<div class="kpi-detail">${kpi.detail}</div>
</div>
`).join('');
const getFormatBadges = (title) => {
let badges = '';
if (/\b2D\b/i.test(title)) badges += '<span class="badge badge-2d">2D</span>';
if (/\b3D\b/i.test(title)) badges += '<span class="badge badge-3d">3D</span>';
if (/\b4D\b/i.test(title)) badges += '<span class="badge badge-4dx">4D</span>';
if (/\bIMAX\b/i.test(title)) badges += '<span class="badge badge-imax">IMAX</span>';
return badges.trim() || '-';
};
const tableBody = document.getElementById('movie-ranking-table');
tableBody.innerHTML = data.topPeliculasPorEspectadores.map((p, index) => {
const rank = index + 1;
const rankClass = rank === 1 ? 'rank-1' : rank === 2 ? 'rank-2' : rank === 3 ? 'rank-3' : 'rank-default';
const ticketProm = p.espectadores > 0 ? p.recaudacion / p.espectadores : 0;
return `
<tr>
<td><span class="rank-badge ${rankClass}">${rank}</span></td>
<td class="font-bold">${p.pelicula}</td>
<td>${getFormatBadges(p.pelicula)}</td>
<td class="text-right">${formatNumber(p.espectadores)}</td>
<td class="text-right font-bold">${formatCurrency(p.recaudacion)}</td>
<td class="text-right">${formatCurrency(ticketProm)}</td>
</tr>`;
}).join('');
new Chart(document.getElementById('chartRecaudacion'), {
type: 'bar',
data: {
labels: data.topPeliculasPorRecaudacion.slice(0, 5).map(p => p.pelicula.length > 25 ? p.pelicula.substring(0,22)+'...' : p.pelicula),
datasets: [{
label: 'Recaudación',
data: data.topPeliculasPorRecaudacion.slice(0, 5).map(p => p.recaudacion),
backgroundColor: chartColors.palette,
borderRadius: 8,
}]
},
options: {
responsive: true, maintainAspectRatio: false,
plugins: { legend: { display: false }, tooltip: { callbacks: { label: ctx => formatCurrency(ctx.raw) } } },
scales: {
y: { beginAtZero: true, grid: { color: '#f1f5f9' }, ticks: { callback: value => '$' + (value/1000000).toFixed(1) + 'M' } },
x: { grid: { display: false } }
}
}
});
new Chart(document.getElementById('chartFormatos'), {
type: 'doughnut',
data: {
labels: data.espectadoresPorFormato.map(f => f.formato),
datasets: [{
data: data.espectadoresPorFormato.map(f => f.espectadores),
backgroundColor: [chartColors.palette[0], chartColors.palette[1], chartColors.palette[3]],
borderWidth: 0, hoverOffset: 10
}]
},
options: {
responsive: true, maintainAspectRatio: false, cutout: '60%',
plugins: {
legend: { position: 'right' },
tooltip: { callbacks: { label: ctx => `${ctx.label}: ${formatNumber(ctx.raw)} esp.` } }
}
}
});
new Chart(document.getElementById('chartDistribuidoras'), {
type: 'bar',
data: {
labels: data.distribucionPorDistribuidora.slice(0, 5).map(d => d.distribuidora.replace(/ S\.?A\.?| S\.?R\.?L\.?| C\.?I\.?A\.?/gi, '').trim()),
datasets: [{
label: 'Recaudación',
data: data.distribucionPorDistribuidora.slice(0, 5).map(d => d.recaudacion),
backgroundColor: chartColors.primary,
borderRadius: 8,
borderSkipped: false,
}]
},
options: {
indexAxis: 'y', responsive: true, maintainAspectRatio: false,
plugins: { legend: { display: false }, tooltip: { callbacks: { label: ctx => formatCurrency(ctx.raw) } } },
scales: {
x: { beginAtZero: true, grid: { color: '#f1f5f9' }, ticks: { callback: value => '$' + (value / 1000000).toFixed(0) + 'M' } },
y: { grid: { display: false } }
}
}
});
}
function downloadPDF() {
const buttons = document.querySelector('.action-buttons');
buttons.style.display = 'none';
const element = document.getElementById('dashboard-content');
const title = document.getElementById('dashboardTitle')?.textContent || 'Dashboard';
const date = new Date().toISOString().split('T')[0];
const filename = 'cinexo-' + title.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '') + '-' + date + '.pdf';
const opt = {
margin: [8, 8, 8, 8], filename: filename,
image: { type: 'jpeg', quality: 0.95 },
html2canvas: { scale: 2, useCORS: true, logging: false, backgroundColor: '#ffffff', windowWidth: 1200 },
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
pagebreak: { mode: ['avoid-all', 'css', 'legacy'], avoid: '.card, .kpi-card' }
};
html2pdf().set(opt).from(element).save().then(() => {
buttons.style.display = 'flex';
}).catch(() => {
buttons.style.display = 'flex';
alert('Error al generar PDF. Intentá con Imprimir.');
});
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment