Añadir ranking.html

This commit is contained in:
2026-02-24 22:51:10 +00:00
parent 5c0d2c7ed4
commit 972eddbfb1

285
ranking.html Normal file
View File

@@ -0,0 +1,285 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>Ranking - IntegraRepara</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<style>
/* VARIABLES CORPORATIVAS DINÁMICAS */
:root {
--primary: #2563eb;
--secondary: #f59e0b;
--app-bg: #f4f7f9;
}
body { background-color: var(--app-bg); -webkit-tap-highlight-color: transparent; }
.fade-in { animation: fadeIn 0.4s ease-out forwards; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } }
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
.pb-safe { padding-bottom: calc(env(safe-area-inset-bottom) + 80px); }
.bg-primary-dynamic { background-color: var(--primary) !important; }
.text-primary-dynamic { color: var(--primary) !important; }
.border-primary-dynamic { border-color: var(--primary) !important; }
/* Círculo de puntuación animado */
.score-circle {
background: conic-gradient(var(--primary) var(--score, 0%), #e2e8f0 0);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
transition: --score 1s ease-out;
}
.score-inner {
background-color: white;
border-radius: 50%;
width: 85%;
height: 85%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: inset 0 2px 10px rgba(0,0,0,0.05);
}
</style>
</head>
<body class="text-slate-800 font-sans antialiased h-screen flex flex-col overflow-hidden relative">
<header class="bg-white px-5 pt-8 pb-4 shadow-sm z-20 shrink-0 border-b border-slate-100 flex justify-between items-center relative">
<div>
<p class="text-[10px] font-black text-primary-dynamic uppercase tracking-widest mb-0.5">Rendimiento Global</p>
<h1 class="text-2xl font-black tracking-tight text-slate-800 leading-none">Mi Ranking</h1>
</div>
<a href="menu.html" class="w-10 h-10 bg-slate-50 rounded-full flex items-center justify-center text-slate-600 border border-slate-200 active:bg-slate-100 transition-colors">
<i data-lucide="x" class="w-5 h-5"></i>
</a>
</header>
<main class="flex-1 overflow-y-auto no-scrollbar px-5 pt-6 pb-safe relative z-10" id="mainArea">
<div id="loader" class="text-center py-20 opacity-50">
<i data-lucide="loader-2" class="w-10 h-10 animate-spin mx-auto text-primary-dynamic mb-3"></i>
<p class="text-xs font-bold uppercase tracking-widest text-slate-400">Calculando estadísticas...</p>
</div>
<div id="rankingContent" class="hidden fade-in space-y-6">
<div class="bg-white rounded-[2.5rem] p-6 shadow-xl shadow-slate-200/40 border border-white text-center relative overflow-hidden">
<div class="absolute -top-10 -right-10 w-32 h-32 bg-primary-dynamic/5 rounded-full blur-2xl"></div>
<div class="absolute -bottom-10 -left-10 w-32 h-32 bg-secondary/5 rounded-full blur-2xl"></div>
<h2 class="text-sm font-black text-slate-500 uppercase tracking-widest mb-6">Puntuación de Calidad</h2>
<div class="w-48 h-48 mx-auto score-circle shadow-lg mb-6" id="scoreCircle" style="--score: 0%;">
<div class="score-inner">
<span class="text-5xl font-black text-slate-800 tracking-tighter leading-none" id="mainScore">0</span>
<span class="text-[10px] font-bold text-slate-400 uppercase tracking-widest mt-1">sobre 100</span>
</div>
</div>
<div id="tierBadge" class="inline-flex items-center gap-2 px-4 py-2 rounded-xl text-sm font-black uppercase tracking-widest">
</div>
</div>
<div>
<h3 class="text-xs font-black text-slate-400 uppercase tracking-widest mb-3 ml-2">Desglose de Puntos</h3>
<div class="space-y-3">
<div class="bg-white p-4 rounded-2xl border border-slate-100 shadow-sm flex flex-col gap-2">
<div class="flex justify-between items-center">
<div class="flex items-center gap-2">
<div class="w-8 h-8 bg-blue-50 text-blue-500 rounded-lg flex items-center justify-center"><i data-lucide="check-circle-2" class="w-4 h-4"></i></div>
<h4 class="font-black text-sm text-slate-700">Cierre Rápido</h4>
</div>
<span class="font-black text-lg text-slate-800"><span id="ptCierre">0</span><span class="text-xs text-slate-400">/30</span></span>
</div>
<div class="w-full bg-slate-100 rounded-full h-1.5 mt-1 overflow-hidden">
<div class="bg-blue-500 h-1.5 rounded-full transition-all duration-1000" id="barCierre" style="width: 0%"></div>
</div>
<p class="text-[9px] font-bold text-slate-400 uppercase">Tiempo medio en terminar un aviso</p>
</div>
<div class="bg-white p-4 rounded-2xl border border-slate-100 shadow-sm flex flex-col gap-2">
<div class="flex justify-between items-center">
<div class="flex items-center gap-2">
<div class="w-8 h-8 bg-emerald-50 text-emerald-500 rounded-lg flex items-center justify-center"><i data-lucide="zap" class="w-4 h-4"></i></div>
<h4 class="font-black text-sm text-slate-700">Agilidad al Citar</h4>
</div>
<span class="font-black text-lg text-slate-800"><span id="ptCita">0</span><span class="text-xs text-slate-400">/30</span></span>
</div>
<div class="w-full bg-slate-100 rounded-full h-1.5 mt-1 overflow-hidden">
<div class="bg-emerald-500 h-1.5 rounded-full transition-all duration-1000" id="barCita" style="width: 0%"></div>
</div>
<p class="text-[9px] font-bold text-slate-400 uppercase">Avisos citados en menos de 24h</p>
</div>
<div class="bg-white p-4 rounded-2xl border border-slate-100 shadow-sm flex flex-col gap-2">
<div class="flex justify-between items-center">
<div class="flex items-center gap-2">
<div class="w-8 h-8 bg-purple-50 text-purple-500 rounded-lg flex items-center justify-center"><i data-lucide="layers" class="w-4 h-4"></i></div>
<h4 class="font-black text-sm text-slate-700">Volumen Diario</h4>
</div>
<span class="font-black text-lg text-slate-800"><span id="ptVolumen">0</span><span class="text-xs text-slate-400">/20</span></span>
</div>
<div class="w-full bg-slate-100 rounded-full h-1.5 mt-1 overflow-hidden">
<div class="bg-purple-500 h-1.5 rounded-full transition-all duration-1000" id="barVolumen" style="width: 0%"></div>
</div>
<p class="text-[9px] font-bold text-slate-400 uppercase">Cantidad de avisos cerrados al día</p>
</div>
<div class="bg-white p-4 rounded-2xl border border-slate-100 shadow-sm flex flex-col gap-2">
<div class="flex justify-between items-center">
<div class="flex items-center gap-2">
<div class="w-8 h-8 bg-amber-50 text-amber-500 rounded-lg flex items-center justify-center"><i data-lucide="target" class="w-4 h-4"></i></div>
<h4 class="font-black text-sm text-slate-700">Eficacia Citas</h4>
</div>
<span class="font-black text-lg text-slate-800"><span id="ptRecitas">0</span><span class="text-xs text-slate-400">/20</span></span>
</div>
<div class="w-full bg-slate-100 rounded-full h-1.5 mt-1 overflow-hidden">
<div class="bg-amber-500 h-1.5 rounded-full transition-all duration-1000" id="barRecitas" style="width: 0%"></div>
</div>
<p class="text-[9px] font-bold text-slate-400 uppercase">Evitar marear/re-citar al asegurado</p>
</div>
</div>
</div>
<div class="grid grid-cols-2 gap-3">
<div class="bg-rose-50 border border-rose-100 p-4 rounded-2xl flex flex-col items-center justify-center text-center">
<p class="text-[9px] font-black text-rose-500 uppercase tracking-widest mb-1">Penalización</p>
<p class="text-2xl font-black text-rose-600 leading-none mb-1">-<span id="ptPenalizacion">0</span> pts</p>
<p class="text-[9px] font-bold text-rose-400 uppercase">Por avisos atascados</p>
</div>
<div class="bg-slate-50 border border-slate-200 p-4 rounded-2xl flex flex-col items-center justify-center text-center">
<p class="text-[9px] font-black text-slate-500 uppercase tracking-widest mb-1">Mes Actual</p>
<p class="text-2xl font-black text-slate-700 leading-none mb-1"><span id="ptCerrados">0</span></p>
<p class="text-[9px] font-bold text-slate-400 uppercase">Siniestros Cerrados</p>
</div>
</div>
<p class="text-center text-[9px] font-bold text-slate-400 mt-4 px-4 uppercase tracking-widest leading-relaxed">
El ranking se calcula en base a tu rendimiento de los últimos 30 días.
</p>
</div>
</main>
<script>
const API_URL = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'
? 'http://localhost:3000'
: 'https://integrarepara-api.integrarepara.es';
async function applyTheme() {
try {
let theme = JSON.parse(localStorage.getItem('app_theme'));
const res = await fetch(`${API_URL}/config/company`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
const data = await res.json();
if(data.ok && data.config && data.config.portal_settings && data.config.portal_settings.app_settings) {
theme = data.config.portal_settings.app_settings;
localStorage.setItem('app_theme', JSON.stringify(theme));
}
if(theme) {
document.documentElement.style.setProperty('--primary', theme.primary);
document.documentElement.style.setProperty('--secondary', theme.secondary);
document.documentElement.style.setProperty('--app-bg', theme.bg);
}
} catch (e) { console.warn("Tema por defecto"); }
}
document.addEventListener("DOMContentLoaded", async () => {
if (!localStorage.getItem("token")) {
window.location.href = "index.html"; return;
}
await applyTheme();
lucide.createIcons();
fetchRanking();
});
async function fetchRanking() {
try {
const res = await fetch(`${API_URL}/ranking`, {
headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
});
const data = await res.json();
if (data.ok && data.ranking) {
renderDashboard(data.ranking);
} else {
alert("No se pudo cargar el ranking.");
}
} catch (e) {
alert("Error de conexión");
} finally {
document.getElementById('loader').classList.add('hidden');
document.getElementById('rankingContent').classList.remove('hidden');
}
}
function renderDashboard(ranking) {
// Animación del número principal
animateValue("mainScore", 0, ranking.score, 1000);
// Animación del círculo CSS
setTimeout(() => {
document.getElementById('scoreCircle').style.setProperty('--score', `${ranking.score}%`);
}, 100);
// Tier Badge (Nivel)
const badge = document.getElementById('tierBadge');
if (ranking.score >= 90) {
badge.className = "inline-flex items-center gap-2 px-4 py-2 rounded-xl text-sm font-black uppercase tracking-widest bg-yellow-100 text-yellow-600 border border-yellow-200";
badge.innerHTML = `<i data-lucide="crown" class="w-5 h-5"></i> Leyenda`;
} else if (ranking.score >= 70) {
badge.className = "inline-flex items-center gap-2 px-4 py-2 rounded-xl text-sm font-black uppercase tracking-widest bg-emerald-100 text-emerald-600 border border-emerald-200";
badge.innerHTML = `<i data-lucide="award" class="w-5 h-5"></i> Profesional`;
} else if (ranking.score >= 50) {
badge.className = "inline-flex items-center gap-2 px-4 py-2 rounded-xl text-sm font-black uppercase tracking-widest bg-blue-100 text-blue-600 border border-blue-200";
badge.innerHTML = `<i data-lucide="thumbs-up" class="w-5 h-5"></i> Buen Trabajo`;
} else {
badge.className = "inline-flex items-center gap-2 px-4 py-2 rounded-xl text-sm font-black uppercase tracking-widest bg-rose-100 text-rose-600 border border-rose-200";
badge.innerHTML = `<i data-lucide="trending-up" class="w-5 h-5"></i> Toca Mejorar`;
}
// Textos de las métricas
document.getElementById('ptCierre').innerText = ranking.details.cierre;
document.getElementById('ptCita').innerText = ranking.details.cita;
document.getElementById('ptVolumen').innerText = ranking.details.volumen;
document.getElementById('ptRecitas').innerText = ranking.details.recitas;
document.getElementById('ptPenalizacion').innerText = ranking.details.penalizacion;
document.getElementById('ptCerrados').innerText = ranking.details.cerrados_mes;
// Animación de las barras
setTimeout(() => {
document.getElementById('barCierre').style.width = `${(ranking.details.cierre / 30) * 100}%`;
document.getElementById('barCita').style.width = `${(ranking.details.cita / 30) * 100}%`;
document.getElementById('barVolumen').style.width = `${(ranking.details.volumen / 20) * 100}%`;
document.getElementById('barRecitas').style.width = `${(ranking.details.recitas / 20) * 100}%`;
}, 300);
lucide.createIcons();
}
// Helper para animar números progresivamente
function animateValue(id, start, end, duration) {
if (start === end) { document.getElementById(id).innerHTML = end; return; }
let range = end - start;
let current = start;
let increment = end > start ? 1 : -1;
let stepTime = Math.abs(Math.floor(duration / range));
let obj = document.getElementById(id);
let timer = setInterval(function() {
current += increment;
obj.innerHTML = current;
if (current == end) clearInterval(timer);
}, stepTime);
}
</script>
</body>
</html>