Skip to content

Instantly share code, notes, and snippets.

@Jcbertorello
Created December 26, 2025 21:15
Show Gist options
  • Select an option

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

Select an option

Save Jcbertorello/ebf97b847ef266953f4e56c78ac49ddc to your computer and use it in GitHub Desktop.
Dashboard Ajonjolí - 2025-12-26 21:15
<!DOCTYPE html>
<html lang="es-MX">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<title>Dashboard de Empresas Activas - Estado de Certificados y Régimen Fiscal</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
<style>
:root {
--color-primary: #60B99A;
--color-primary-light: #7FCDB2;
--color-primary-dark: #4A9D80;
--color-secondary: #1F3B4D;
--color-secondary-light: #2D5066;
--color-dark: #1F3B4D;
--color-dark-soft: #2D4A5E;
--color-muted: #5A7A8A;
--color-light: #8FA5B2;
--color-bg: #F8FAFB;
--color-bg-alt: #EEF3F6;
--color-card: #FFFFFF;
--color-border: #D4DEE4;
--color-success: #16a34a;
--color-success-bg: #dcfce7;
--color-danger: #dc2626;
--color-danger-bg: #fee2e2;
--color-warning: #f59e0b;
--color-warning-bg: #fef3c7;
--font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
background-color: var(--color-bg);
font-family: var(--font-family);
color: var(--color-secondary);
line-height: 1.4;
}
.dashboard {
max-width: 1200px;
margin: 16px auto 40px;
padding: 0 16px 32px;
background-color: var(--color-bg);
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 2px solid var(--color-primary-light);
padding: 12px 0 20px;
}
.header-left {
display: flex;
align-items: center;
gap: 12px;
}
.logo {
font-weight: 900;
font-size: 40px;
color: var(--color-primary);
border-radius: 8px;
width: 52px;
height: 52px;
display: flex;
justify-content: center;
align-items: center;
background-color: var(--color-primary-light);
user-select: none;
}
.header-title {
margin: 0;
font-weight: 700;
font-size: 1.6rem;
color: var(--color-dark);
}
.header-subtitle {
margin: 2px 0 0 0;
font-weight: 500;
font-size: 0.9rem;
color: var(--color-muted);
}
.header-right {
display: flex;
flex-direction: column;
gap: 4px;
text-align: right;
color: var(--color-muted);
font-weight: 600;
font-size: 0.9rem;
}
.header-client {
font-weight: 700;
color: var(--color-primary-dark);
}
.kpi-container {
margin-top: 24px;
display: flex;
gap: 24px;
flex-wrap: wrap;
}
.kpi-card {
background: var(--color-card);
flex: 1 1 160px;
border: 1px solid var(--color-border);
border-radius: 12px;
padding: 20px 24px;
box-shadow: 0 2px 6px rgb(96 185 154 / 0.15);
color: var(--color-secondary);
}
.kpi-title {
font-weight: 700;
font-size: 0.95rem;
margin-bottom: 12px;
user-select: none;
}
.kpi-value {
font-size: 2.5rem;
font-weight: 800;
color: var(--color-primary);
user-select: text;
}
.card {
background: var(--color-card);
border: 1px solid var(--color-border);
border-radius: 14px;
padding: 20px 24px;
box-shadow: 0 2px 10px rgb(31 59 77 / 0.07);
margin-top: 28px;
}
.card h2 {
margin: 0 0 18px 0;
font-weight: 700;
font-size: 1.3rem;
color: var(--color-primary-dark);
}
.charts-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 30px;
}
canvas {
width: 100% !important;
height: 260px !important;
}
table {
width: 100%;
border-collapse: separate;
border-spacing: 0 6px;
font-size: 0.9rem;
color: var(--color-dark-soft);
}
thead tr th {
text-align: left;
padding: 10px 12px 6px;
color: var(--color-primary-dark);
font-weight: 700;
font-size: 0.95rem;
border-bottom: 2px solid var(--color-primary-light);
}
tbody tr {
background-color: var(--color-bg-alt);
border-radius: 10px;
box-shadow: inset 0 0 0 1px var(--color-border);
}
tbody tr td {
padding: 10px 12px;
vertical-align: middle;
}
tbody tr td:first-child {
font-weight: 700;
color: var(--color-secondary);
}
.status-badge {
padding: 4px 10px;
border-radius: 14px;
font-weight: 700;
font-size: 0.8rem;
white-space: nowrap;
user-select: none;
display: inline-block;
}
.status-vigente {
background-color: var(--color-success-bg);
color: var(--color-success);
}
.status-por-vencer {
background-color: var(--color-warning-bg);
color: var(--color-warning);
}
.status-vencido {
background-color: var(--color-danger-bg);
color: var(--color-danger);
}
.status-sin-fecha {
background-color: var(--color-bg-alt);
color: var(--color-muted);
font-weight: 600;
}
.alert {
display: flex;
align-items: center;
gap: 8px;
padding: 14px 20px;
border-radius: 14px;
font-weight: 700;
font-size: 0.9rem;
margin-bottom: 18px;
}
.alert-icon {
font-size: 1.3rem;
user-select: none;
}
.alert-warning {
background-color: var(--color-warning-bg);
color: var(--color-warning);
}
.alert-danger {
background-color: var(--color-danger-bg);
color: var(--color-danger);
}
.footer {
border-top: 2px solid var(--color-primary-light);
margin-top: 56px;
padding: 24px 16px 16px;
font-size: 0.85rem;
color: var(--color-muted);
display: flex;
justify-content: center;
}
.footer-content {
max-width: 1200px;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.footer-brand {
display: flex;
align-items: center;
gap: 9px;
user-select: none;
}
.footer-logo {
font-weight: 900;
font-size: 36px;
color: var(--color-primary);
width: 44px;
height: 44px;
border-radius: 10px;
background: var(--color-primary-light);
display: flex;
justify-content: center;
align-items: center;
}
.footer-tagline {
font-style: italic;
flex: 1;
text-align: center;
color: var(--color-primary-dark);
}
.footer-date {
min-width: 98px;
text-align: right;
flex-shrink: 0;
}
.action-buttons {
margin: 12px 0 12px;
display: flex;
gap: 12px;
justify-content: flex-end;
}
button.pdf-btn, button.print-btn {
padding: 10px 16px;
font-weight: 700;
cursor: pointer;
border-radius: 12px;
border: none;
font-size: 1rem;
user-select: none;
color: var(--color-card);
background-color: var(--color-primary);
transition: background-color 0.3s ease;
}
button.pdf-btn:hover, button.print-btn:hover {
background-color: var(--color-primary-dark);
}
@media (max-width: 720px) {
.header {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.header-right {
width: 100%;
text-align: left;
flex-direction: row;
justify-content: space-between;
gap: 6px;
}
.kpi-container {
flex-direction: column;
}
.charts-grid {
grid-template-columns: 1fr !important;
}
}
</style>
</head>
<body>
<div class="dashboard" id="dashboard">
<div class="header">
<div class="header-left">
<div class="logo" aria-label="Logo Estudio Estrada" title="Estudio Estrada">E</div>
<div>
<h1 class="header-title">Dashboard de Empresas Activas - Estado de Certificados y Régimen Fiscal</h1>
<p class="header-subtitle">Actual</p>
</div>
</div>
<div class="header-right" aria-label="Información del cliente y fecha de generación">
<div class="header-client">Estudio Estrada</div>
<div class="header-date">Generado: 26/12/2025 22:13</div>
</div>
</div>
<div class="action-buttons" role="region" aria-label="Acciones de impresión y descarga PDF">
<button class="pdf-btn" aria-label="Descargar reporte en PDF" onclick="downloadPDF()">Descargar PDF</button>
<button class="print-btn" aria-label="Imprimir reporte" onclick="window.print()">Imprimir</button>
</div>
<section class="kpi-container" aria-label="Indicadores clave">
<div class="kpi-card" role="region" aria-live="polite" aria-atomic="true">
<div class="kpi-title">Total Empresas Activas</div>
<div class="kpi-value" id="kpiTotalEmpresas">5</div>
</div>
</section>
<div class="card" aria-label="Listado de empresas con estado de certificados y régimen fiscal">
<h2>Listado de Empresas</h2>
<table role="table" aria-describedby="table-desc">
<thead>
<tr>
<th scope="col">Empresa</th>
<th scope="col">RFC</th>
<th scope="col">Régimen Fiscal</th>
<th scope="col">Estado FIEL</th>
<th scope="col">Vencimiento FIEL</th>
<th scope="col">Estado CSD</th>
<th scope="col">Vencimiento CSD</th>
</tr>
</thead>
<tbody>
<tr>
<td>Casa Libélula Holbox</td>
<td>CLH030303ZZZ</td>
<td>Régimen General</td>
<td><span class="status-badge status-sin-fecha">Sin fecha</span></td>
<td>-</td>
<td><span class="status-badge status-sin-fecha">Sin fecha</span></td>
<td>-</td>
</tr>
<tr>
<td>Empresa Nueva 1764258861666</td>
<td>RFCGENERICO01</td>
<td>Régimen General</td>
<td><span class="status-badge status-sin-fecha">Sin fecha</span></td>
<td>-</td>
<td><span class="status-badge status-sin-fecha">Sin fecha</span></td>
<td>-</td>
</tr>
<tr>
<td>GODIUS SA DE CV</td>
<td>GODx0xxxxxI7</td>
<td>(601)-GENERAL DE LEY DE PERSONAS MORALES</td>
<td><span class="status-badge status-vigente">🟢 Vigente</span></td>
<td>2029-05-12</td>
<td><span class="status-badge status-por-vencer">🟡 Por vencer</span></td>
<td>2025-12-31</td>
</tr>
<tr>
<td>No More Fear</td>
<td>NMF020202X7X</td>
<td>(625)-ACTIVIDADES EMPRESARIALES CON INGRESOS A TRAVES DE PLATAFORMAS TECNOLÓGICAS</td>
<td><span class="status-badge status-sin-fecha">Sin fecha</span></td>
<td>-</td>
<td><span class="status-badge status-sin-fecha">Sin fecha</span></td>
<td>-</td>
</tr>
<tr>
<td>Tacos El Patrón</td>
<td>TAC020202YYY</td>
<td>Régimen Simplificado de Confianza</td>
<td><span class="status-badge status-sin-fecha">Sin fecha</span></td>
<td>-</td>
<td><span class="status-badge status-sin-fecha">Sin fecha</span></td>
<td>-</td>
</tr>
</tbody>
</table>
</div>
<div class="card" aria-label="Gráficos resumen de estado de certificados y régimen fiscal">
<h2>Resumen Visual</h2>
<div class="charts-grid">
<div role="region" aria-label="Estado de certificados FIEL" style="min-height:280px;">
<canvas id="chartFIEL" role="img" aria-describedby="descFIEL"></canvas>
<div id="descFIEL" class="sr-only">Gráfico de pastel mostrando cantidad de empresas por estado de certificados FIEL</div>
</div>
<div role="region" aria-label="Estado de certificados CSD" style="min-height:280px;">
<canvas id="chartCSD" role="img" aria-describedby="descCSD"></canvas>
<div id="descCSD" class="sr-only">Gráfico de pastel mostrando cantidad de empresas por estado de certificados CSD</div>
</div>
<div role="region" aria-label="Empresas por régimen fiscal" style="min-height:280px;">
<canvas id="chartRegimen" role="img" aria-describedby="descRegimen"></canvas>
<div id="descRegimen" class="sr-only">Gráfico de pastel mostrando cantidad de empresas por tipo de régimen fiscal</div>
</div>
</div>
</div>
</div>
<div class="footer" role="contentinfo">
<div class="footer-content">
<div class="footer-brand" aria-label="Marca powered by MostachIA">
<div class="footer-logo" title="MostachIA">M</div>
<span>Powered by <strong>MostachIA</strong></span>
</div>
<div class="footer-tagline">"Procesos inteligentes, resultados superiores"</div>
<div class="footer-date" aria-label="fecha de generación de reporte">26/12/2025 22:13</div>
</div>
</div>
<script>
const chartColors = {
primary: '#60B99A',
primaryLight: '#7FCDB2',
primaryDark: '#4A9D80',
secondary: '#1F3B4D',
palette: ['#60B99A', '#1F3B4D', '#7FCDB2', '#2D5066', '#4A9D80', '#5A7A8A', '#8FA5B2', '#A8D5C2'],
ingresos: '#16a34a',
egresos: '#dc2626',
gradientStart: 'rgba(96, 185, 154, 0.3)',
gradientEnd: 'rgba(96, 185, 154, 0.05)'
};
function isDarkColor(color) {
const c = color.substring(1); // strip #
const rgb = parseInt(c, 16); // convert rrggbb to decimal
const r = (rgb >> 16) & 0xff; // extract red
const g = (rgb >> 8) & 0xff; // extract green
const b = (rgb >> 0) & 0xff; // extract blue
const luma = 0.2126*r + 0.7152*g + 0.0722*b; // per ITU-R BT.709
return luma < 100;
}
const estadosFIEL = ["Vigente", "Por vencer", "Vencido", "Sin fecha"];
const cantidadFIEL = [1, 0, 0, 4];
const estadosCSD = ["Vigente", "Por vencer", "Vencido", "Sin fecha"];
const cantidadCSD = [0, 1, 0, 4];
const regimenes = [
"Régimen General",
"Régimen Simplificado de Confianza",
"(625)",
"(601)"
];
const cantidadRegimenes = [2, 1, 1, 1];
// Map state to color for badges and charts
const estadoColorMap = {
"Vigente": chartColors.primary,
"Por vencer": chartColors.palette[2],
"Vencido": chartColors.egresos,
"Sin fecha": chartColors.palette[7]
};
// Chart FIEL
new Chart(document.getElementById('chartFIEL'), {
type: 'doughnut',
data: {
labels: estadosFIEL,
datasets: [{
data: cantidadFIEL,
backgroundColor: estadosFIEL.map(e => estadoColorMap[e]),
borderWidth: 0
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
cutout: '60%',
plugins: {
legend: { position: 'bottom', labels: { color: chartColors.secondary, font: { weight: '600' } } },
tooltip: {
callbacks: {
label: ctx => ctx.label + ': ' + ctx.parsed + ' empresas'
}
}
}
}
});
// Chart CSD
new Chart(document.getElementById('chartCSD'), {
type: 'doughnut',
data: {
labels: estadosCSD,
datasets: [{
data: cantidadCSD,
backgroundColor: estadosCSD.map(e => estadoColorMap[e]),
borderWidth: 0
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
cutout: '60%',
plugins: {
legend: { position: 'bottom', labels: { color: chartColors.secondary, font: { weight: '600' } } },
tooltip: {
callbacks: {
label: ctx => ctx.label + ': ' + ctx.parsed + ' empresas'
}
}
}
}
});
// Colors for regimen chart
const regimenColors = [chartColors.primary, chartColors.primaryLight, chartColors.secondary, chartColors.palette[3]];
// Chart Regimen Fiscal
new Chart(document.getElementById('chartRegimen'), {
type: 'doughnut',
data: {
labels: regimenes,
datasets: [{
data: cantidadRegimenes,
backgroundColor: regimenColors,
borderWidth: 0
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
cutout: '60%',
plugins: {
legend: { position: 'bottom', labels: { color: chartColors.secondary, font: { weight: '600' } } },
tooltip: {
callbacks: {
label: ctx => ctx.label + ': ' + ctx.parsed + ' empresas'
}
}
}
}
});
function downloadPDF() {
const buttons = document.querySelector('.action-buttons');
buttons.style.display = 'none';
const element = document.querySelector('.dashboard');
const title = document.querySelector('.header-title')?.textContent || 'Dashboard';
const filename = 'estudio-estrada-' + title.toLowerCase()
.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
.replace(/[^a-z0-9]+/g, '-') + '.pdf';
html2pdf().set({
margin: [8, 8, 8, 8],
filename: filename,
image: { type: 'jpeg', quality: 0.95 },
html2canvas: { scale: 2, useCORS: true, backgroundColor: '#ffffff', windowWidth: 1200 },
jsPDF: { unit: 'mm', format: 'a4', orientation: 'landscape' },
pagebreak: { mode: ['avoid-all', 'css', 'legacy'], avoid: '.card, .kpi-card' }
}).from(element).save().then(() => buttons.style.display = 'flex');
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment