Meta Description" name="description" />
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Reporte Ejecutivo · Interbank</title>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0/dist/chartjs-plugin-datalabels.min.js"></script>
<style>
:root {
--white: #FFFFFF;
--ibk-green-claro: rgb(0, 190, 80);
--ibk-green-oscuro: rgb(0, 95, 30);
--ibk-green-suave: rgb(198, 255, 192);
--ibk-blue: rgb(31, 69, 146);
--ink: #0F1B2C;
--radius: 12px;
}
* { margin:0; padding:0; box-sizing:border-box; }
body {
font-family: 'Poppins', sans-serif;
background: #f4f7f6;
color: var(--ink);
padding: 20px;
display: flex;
justify-content: center;
min-height: 100vh;
}
.pagina {
width: 100%;
max-width: 1600px;
background: var(--white);
padding: 40px;
border-radius: var(--radius);
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
}
.header-reporte {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 35px;
border-bottom: 2px solid var(--ibk-green-suave);
padding-bottom: 20px;
}
.titulo-principal {
color: var(--ibk-blue);
font-weight: 700;
font-size: 28px;
}
.periodo-tag {
background: var(--ibk-green-suave);
color: var(--ibk-green-oscuro);
padding: 8px 20px;
border-radius: 20px;
font-weight: 600;
font-size: 15px;
}
.fila-unica {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 30px;
margin-bottom: 30px;
}
.grafico-card {
background: var(--white);
border-radius: var(--radius);
padding: 25px;
border: 1px solid #eef2f6;
min-height: 400px;
display: flex;
flex-direction: column;
}
.grafico-titulo {
font-size: 13px;
font-weight: 700;
color: var(--ibk-blue);
margin-bottom: 20px;
text-align: center;
text-transform: uppercase;
}
.chart-wrapper {
flex: 1;
position: relative;
min-height: 320px;
}
.leyenda-footer {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 25px;
padding: 20px;
background: #f9fbfb;
border-radius: var(--radius);
margin-top: 15px;
}
.leyenda-item {
display: flex;
align-items: center;
gap: 10px;
font-size: 14px;
font-weight: 600;
color: var(--ibk-green-oscuro);
}
.leyenda-color {
width: 16px;
height: 16px;
border-radius: 3px;
}
.footer-text {
text-align: center;
font-size: 14px;
color: #94a3b8;
margin-top: 25px;
}
@media print {
body { padding: 0; background: white; }
.pagina { box-shadow: none; max-width: 100%; }
}
@media (max-width: 1400px) {
.fila-unica { grid-template-columns: 1fr; }
.grafico-card { min-height: 450px; }
.chart-wrapper { min-height: 380px; }
}
@media (min-width: 1401px) and (max-width: 1800px) {
.pagina { max-width: 1400px; }
}
</style>
</head>
<body>
<div class="pagina">
<div class="header-reporte">
<h1 class="titulo-principal">Desempeño Consolidado Nacional</h1>
<span class="periodo-tag">Septiembre - Noviembre 2025</span>
</div>
<div class="fila-unica">
<div class="grafico-card">
<div class="grafico-titulo">% Alertas</div>
<div class="chart-wrapper"><canvas id="leadsChart"></canvas></div>
</div>
<div class="grafico-card">
<div class="grafico-titulo">% Efectividad P1-P4</div>
<div class="chart-wrapper"><canvas id="efecChart"></canvas></div>
</div>
<div class="grafico-card">
<div class="grafico-titulo">% Casos con Riesgo</div>
<div class="chart-wrapper"><canvas id="desemChart"></canvas></div>
</div>
</div>
<div class="leyenda-footer" id="leyenda"></div>
<!-- Tabla promedio efectividad -->
<div style="margin-top: 30px; overflow-x: auto;">
<table id="tablaPromedio" style="width: 100%; border-collapse: collapse; font-family: 'Poppins', sans-serif;">
<thead>
<tr>
<th style="background: rgb(31,69,146); color: #fff; padding: 12px 20px; text-align: left; font-size: 13px; text-transform: uppercase; border-radius: 8px 0 0 0;">Segmento</th>
<th style="background: rgb(31,69,146); color: #fff; padding: 12px 20px; text-align: center; font-size: 13px; text-transform: uppercase;">Sep</th>
<th style="background: rgb(31,69,146); color: #fff; padding: 12px 20px; text-align: center; font-size: 13px; text-transform: uppercase;">Oct</th>
<th style="background: rgb(31,69,146); color: #fff; padding: 12px 20px; text-align: center; font-size: 13px; text-transform: uppercase;">Nov</th>
<th style="background: rgb(31,69,146); color: #fff; padding: 12px 20px; text-align: center; font-size: 13px; text-transform: uppercase; border-radius: 0 8px 0 0;">Promedio</th>
</tr>
</thead>
<tbody id="tablaBody"></tbody>
</table>
</div>
<div class="footer-text">
© 2025 Interbank · Información Confidencial para uso Interno
</div>
</div>
<script>
window.onload = function() {
Chart.register(ChartDataLabels);
Chart.defaults.font.family = "'Poppins', sans-serif";
const periodos = ["Sep", "Oct", "Nov"];
// Orden apilado de abajo hacia arriba
const prioBase = ["P4", "P3", "P2", "P1"];
const colores = {
"P1": 'rgb(0, 190, 80)',
"P2": '#888888',
"P3": '#AAAAAA',
"P4": '#CCCCCC'
};
const fondosPastel = {
"P1": 'rgba(198, 255, 192, 0.9)',
"P2": 'rgba(220, 220, 220, 0.9)',
"P3": 'rgba(210, 210, 210, 0.9)',
"P4": 'rgba(235, 235, 235, 0.9)'
};
// ── DATOS REALES ──────────────────────────────────────────
const data = {
alertas: {
P1: [24, 21, 19],
P2: [15, 21, 22],
P3: [17, 20, 24],
P4: [44, 38, 36]
},
efectividad: {
P1: [60.8, 35.3, 38.0],
P2: [18.3, 17.0, 18.0],
P3: [11.4, 5.9, 9.0],
P4: [ 2.2, 0.4, 2.5]
},
riesgos: (function() {
const raw = {
P1: [69, 80, 76],
P2: [14, 13, 14],
P3: [ 9, 6, 8],
P4: [ 8, 1, 5]
};
const keys = ["P1","P2","P3","P4"];
const normalized = { P1:[], P2:[], P3:[], P4:[] };
[0,1,2].forEach(i => {
const total = keys.reduce((s,p) => s + raw[p][i], 0);
keys.forEach(p => normalized[p].push(parseFloat((raw[p][i] / total * 100).toFixed(1))));
});
return normalized;
})()
};
// Promedio de efectividad (P1+P2+P3+P4) / 4
const efecPromedio = periodos.map((_, i) =>
parseFloat(((data.efectividad.P1[i] + data.efectividad.P2[i] +
data.efectividad.P3[i]) / 3).toFixed(1))
);
// ── BARRA APILADA ─────────────────────────────────────────
function renderBar(id, dataset) {
new Chart(document.getElementById(id), {
type: 'bar',
data: {
labels: periodos,
datasets: prioBase.map(p => ({
label: p,
data: dataset[p],
backgroundColor: colores[p],
barPercentage: 0.95,
categoryPercentage: 0.9
}))
},
options: {
responsive: true,
maintainAspectRatio: false,
animation: false,
plugins: {
legend: { display: false },
datalabels: {
font: { weight: 'bold', size: 14 },
anchor: 'center',
align: 'center',
clamp: true,
formatter: (v, ctx) => {
if (v <= 4) return '';
return Math.round(v) + '%';
},
color: (ctx) => {
const p = prioBase[ctx.datasetIndex];
return (p === 'P3' || p === 'P4') ? '#444' : '#fff';
}
}
},
scales: {
x: {
stacked: true,
grid: { display: false },
border: { display: false },
ticks: { font: { size: 14, weight: '600' } }
},
y: { display: false, stacked: true }
}
}
});
}
// ── LÍNEAS CON PROMEDIO PUNTEADO ──────────────────────────
function renderLine(id) {
const alineacion = { "P1": "top", "P2": "top", "P3": "bottom" };
const offsetVal = { "P1": 12, "P2": 12, "P3": 12 };
const ds = ["P1", "P2", "P3"].map(p => ({
label: p,
data: data.efectividad[p],
borderColor: colores[p],
backgroundColor: colores[p],
borderWidth: 4,
tension: 0.3,
pointRadius: 6,
pointBackgroundColor: '#fff',
pointBorderColor: colores[p],
pointBorderWidth: 2,
datalabels: {
align: alineacion[p],
offset: offsetVal[p],
font: { weight: 'bold', size: 14 },
color: '#fff',
backgroundColor: colores[p],
borderRadius: 6,
padding: { top: 4, bottom: 4, left: 7, right: 7 },
clamp: true,
formatter: v => v + '%'
}
}));
// Línea promedio punteada
ds.push({
label: 'Promedio',
data: efecPromedio,
borderColor: '#000',
borderDash: [5, 5],
borderWidth: 2,
pointRadius: 4,
pointBackgroundColor: '#000',
pointBorderWidth: 0,
datalabels: {
display: true,
align: 'right',
offset: 6,
font: { weight: 'bold', size: 11 },
color: '#000',
backgroundColor: 'rgba(255,255,255,0.85)',
borderRadius: 4,
padding: 3,
formatter: v => v + '%'
}
});
new Chart(document.getElementById(id), {
type: 'line',
data: { labels: periodos, datasets: ds },
options: {
responsive: true,
maintainAspectRatio: false,
animation: false,
layout: {
padding: { top: 60, bottom: 35, left: 50, right: 40 }
},
plugins: {
legend: { display: false },
tooltip: { enabled: false }
},
scales: {
x: {
grid: { display: false },
border: { display: false },
ticks: { font: { size: 14, weight: '600' } }
},
y: {
display: false,
beginAtZero: false,
min: 0,
suggestedMax: 90
}
}
}
});
}
// ── RENDER ────────────────────────────────────────────────
renderBar('leadsChart', data.alertas);
renderLine('efecChart');
renderBar('desemChart', data.riesgos);
// Leyenda
const ley = document.getElementById('leyenda');
["P1", "P2", "P3", "P4"].forEach(p => {
ley.innerHTML += `<div class="leyenda-item"><div class="leyenda-color" style="background:${colores[p]}"></div>${p}</div>`;
});
ley.innerHTML += `<div class="leyenda-item"><div class="leyenda-color" style="border:2px dashed #000; background:transparent"></div>Efectividad Promedio</div>`;
// Tabla promedio efectividad
const tbody = document.getElementById('tablaBody');
const segRows = [
{ label: 'P1', color: 'rgb(0,190,80)', text: '#fff' },
{ label: 'P2', color: '#888888', text: '#fff' },
{ label: 'P3', color: '#AAAAAA', text: '#fff' },
{ label: 'P4', color: '#CCCCCC', text: '#444' }
];
segRows.forEach((seg, idx) => {
const vals = data.efectividad[seg.label];
const prom = (vals.reduce((a,b) => a+b, 0) / vals.length).toFixed(1);
const bg = idx % 2 === 0 ? '#f9fbfb' : '#fff';
tbody.innerHTML += `
<tr style="background:${bg};">
<td style="padding: 12px 20px; font-weight: 700; display: flex; align-items: center; gap: 10px;">
<span style="display:inline-block; width:14px; height:14px; border-radius:3px; background:${seg.color};"></span>
${seg.label}
</td>
<td style="padding: 12px 20px; text-align: center; font-weight: 600;">${vals[0]}%</td>
<td style="padding: 12px 20px; text-align: center; font-weight: 600;">${vals[1]}%</td>
<td style="padding: 12px 20px; text-align: center; font-weight: 600;">${vals[2]}%</td>
<td style="padding: 12px 20px; text-align: center; font-weight: 700; background: rgb(198,255,192); color: rgb(0,95,30); border-radius: ${idx === segRows.length-1 ? '0 0 8px 0' : '0'};">${prom}%</td>
</tr>`;
});
// Fila total promedio general
const allVals = ["P1","P2","P3","P4"].map(p => data.efectividad[p]);
const totals = [0,1,2].map(i => parseFloat((allVals.reduce((s,v) => s+v[i], 0)/4).toFixed(1)));
const totalProm = (totals.reduce((a,b)=>a+b,0)/3).toFixed(1);
tbody.innerHTML += `
<tr style="background: rgb(31,69,146);">
<td style="padding: 12px 20px; font-weight: 700; color:#fff; border-radius: 0 0 0 8px;">Promedio General</td>
<td style="padding: 12px 20px; text-align: center; font-weight: 700; color:#fff;">${totals[0]}%</td>
<td style="padding: 12px 20px; text-align: center; font-weight: 700; color:#fff;">${totals[1]}%</td>
<td style="padding: 12px 20px; text-align: center; font-weight: 700; color:#fff;">${totals[2]}%</td>
<td style="padding: 12px 20px; text-align: center; font-weight: 700; color: rgb(198,255,192); border-radius: 0 0 8px 0;">${totalProm}%</td>
</tr>`;
};
</script>
</body>
</html>7
4
112KB
249KB
255.0ms
288.0ms
347.0ms