formula1/frontend/js/f1.js

227 lines
9.6 KiB
JavaScript

// Base API
const API = '/f1/api';
// ─── Tabs ─────────────────────────────────────────────────────
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
btn.classList.add('active');
document.getElementById('tab-' + btn.dataset.tab).classList.add('active');
});
});
// ─── Cuenta atrás próxima carrera (Ergast/Jolpi API) ──────────
async function initCuentaAtras() {
try {
const res = await fetch('https://api.jolpi.ca/ergast/f1/2026.json');
const data = await res.json();
const carreras = data.MRData.RaceTable.Races;
const ahora = new Date();
const proxima = carreras.find(c => new Date(`${c.date}T${c.time || '12:00:00Z'}`) > ahora);
if (!proxima) {
document.getElementById('nombre-proxima-carrera').textContent = 'Temporada terminada';
document.getElementById('countdown').textContent = '—';
return;
}
const fechaProxima = new Date(`${proxima.date}T${proxima.time || '12:00:00Z'}`);
document.getElementById('nombre-proxima-carrera').textContent =
`⏱️ Cuenta atrás — ${proxima.raceName}`;
function tick() {
const diff = fechaProxima - new Date();
if (diff <= 0) { document.getElementById('countdown').textContent = '🏁 ¡Carrera en curso!'; return; }
const d = Math.floor(diff / 86400000);
const h = Math.floor((diff % 86400000) / 3600000);
const m = Math.floor((diff % 3600000) / 60000);
const s = Math.floor((diff % 60000) / 1000);
document.getElementById('countdown').textContent = `${d}d ${h}h ${m}m ${s}s`;
}
tick();
setInterval(tick, 1000);
} catch (e) {
document.getElementById('countdown').textContent = '—';
}
}
// ─── Clasificación ────────────────────────────────────────────
async function cargarClasificacionPilotos() {
const res = await fetch(`${API}/resultados/clasificacion/pilotos?temporada=2025`);
return res.json();
}
async function cargarClasificacionConstructores() {
const res = await fetch(`${API}/resultados/clasificacion/constructores?temporada=2025`);
return res.json();
}
function renderClasificacionPilotos(data, tablaId, maxRows = 999) {
const tbody = document.querySelector(`#${tablaId} tbody`);
tbody.innerHTML = '';
const esCompleta = tablaId.includes('clasificacion');
data.slice(0, maxRows).forEach((p, i) => {
const tr = document.createElement('tr');
if (i === 0) tr.classList.add('campeon');
tr.innerHTML = `
<td class="pos">${i + 1}</td>
<td>${p.nombre} <strong>${p.apellido}</strong></td>
<td>${p.equipo}</td>
<td><strong>${p.puntos}</strong></td>
<td>${p.victorias}</td>
${esCompleta ? `<td>${p.podios}</td><td>${p.vueltasRapidas}</td>` : ''}
`;
tbody.appendChild(tr);
});
}
function renderClasificacionConstructores(data, tablaId, maxRows = 999) {
const tbody = document.querySelector(`#${tablaId} tbody`);
tbody.innerHTML = '';
const esCompleta = tablaId.includes('clasificacion');
data.slice(0, maxRows).forEach((c, i) => {
const tr = document.createElement('tr');
if (i === 0) tr.classList.add('campeon');
tr.innerHTML = `
<td class="pos">${i + 1}</td>
<td><strong>${c.nombre}</strong></td>
<td><strong>${c.puntos}</strong></td>
<td>${c.victorias}</td>
${esCompleta ? `<td>${c.podios}</td>` : ''}
`;
tbody.appendChild(tr);
});
}
// ─── Calendario ───────────────────────────────────────────────
async function cargarCalendario() {
const res = await fetch(`${API}/gp?temporada=2025`);
return res.json();
}
function renderCalendario(gps) {
const cont = document.getElementById('lista-gp');
const hoy = new Date();
cont.innerHTML = '';
gps.forEach((gp, i) => {
const fecha = new Date(gp.fecha);
const pasado = fecha < hoy;
const card = document.createElement('div');
card.className = 'gp-card ' + (pasado ? 'pasado' : 'proximo');
card.innerHTML = `
<div class="gp-num">${String(i + 1).padStart(2, '0')}</div>
<div class="gp-info">
<strong>${gp.nombre}</strong>
<span>${gp.circuito}</span>
<span>${gp.ciudad}, ${gp.pais}</span>
<span>${fecha.toLocaleDateString('es-ES', {day:'numeric',month:'long',year:'numeric'})}</span>
</div>
<div class="gp-meta">
<span>${gp.numVueltas} vueltas · ${gp.distanciaKm} km</span>
${pasado
? `<button class="btn-resultados" onclick="abrirResultados(${gp.id}, '${gp.nombre.replace(/'/g, "\\'")}')">Ver resultados →</button>`
: '<span class="badge-proximo">Próxima</span>'}
</div>
`;
cont.appendChild(card);
});
}
function renderUltimasCarreras(gps) {
const hoy = new Date();
const pasados = gps.filter(g => new Date(g.fecha) < hoy).slice(-3).reverse();
const cont = document.getElementById('ultimas-carreras');
cont.innerHTML = '';
pasados.forEach(gp => {
const div = document.createElement('div');
div.className = 'carrera-mini';
div.innerHTML = `<strong>${gp.nombre}</strong>
<button class="btn-resultados" onclick="abrirResultados(${gp.id}, '${gp.nombre.replace(/'/g, "\\'")}')">Ver resultados →</button>`;
cont.appendChild(div);
});
}
// ─── Modal resultados ─────────────────────────────────────────
async function abrirResultados(gpId, nombre) {
document.getElementById('modal-titulo').textContent = nombre;
document.querySelector('#modal-resultados tbody').innerHTML = '<tr><td colspan="5">Cargando...</td></tr>';
document.getElementById('modal-carrera').classList.remove('hidden');
const res = await fetch(`${API}/resultados/gp/${gpId}`);
const data = await res.json();
const tbody = document.querySelector('#modal-resultados tbody');
tbody.innerHTML = '';
data.forEach(r => {
const tr = document.createElement('tr');
if (r.posicion === 1) tr.classList.add('ganador');
tr.innerHTML = `
<td class="pos">${r.posicion ?? 'DNF'}</td>
<td>${r.piloto.nombre} <strong>${r.piloto.apellido}</strong>${r.vueltaRapida ? ' ⚡' : ''}</td>
<td>${r.escuderia.nombre}</td>
<td>${r.puntos}</td>
<td>${r.estado}</td>
`;
tbody.appendChild(tr);
});
}
function cerrarModal() {
document.getElementById('modal-carrera').classList.add('hidden');
}
document.getElementById('modal-carrera').addEventListener('click', e => {
if (e.target === e.currentTarget) cerrarModal();
});
// ─── Pilotos ───────────────────────────────────────────────────
async function cargarPilotos() {
const res = await fetch(`${API}/pilotos`);
const pilotos = await res.json();
const tbody = document.querySelector('#pilotos-table tbody');
tbody.innerHTML = '';
pilotos.forEach(p => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td class="pos">${p.numero}</td>
<td><strong>${p.codigo}</strong></td>
<td>${p.nombre}</td>
<td>${p.apellido}</td>
<td>—</td>
<td>${p.nacionalidad}</td>
`;
tbody.appendChild(tr);
});
}
// ─── Escuderías ───────────────────────────────────────────────
async function cargarEscuderias() {
const res = await fetch(`${API}/escuderias`);
const data = await res.json();
const tbody = document.querySelector('#escuderias-table tbody');
tbody.innerHTML = '';
data.forEach((e, i) => {
const tr = document.createElement('tr');
tr.innerHTML = `<td>${i + 1}</td><td><strong>${e.nombre}</strong></td><td>${e.pais}</td><td>${e.motor}</td>`;
tbody.appendChild(tr);
});
}
// ─── INIT ──────────────────────────────────────────────────────
async function init() {
initCuentaAtras();
const [clasificPilotos, clasificConst, gps] = await Promise.all([
cargarClasificacionPilotos(),
cargarClasificacionConstructores(),
cargarCalendario()
]);
renderClasificacionPilotos(clasificPilotos, 'tabla-top-pilotos', 5);
renderClasificacionConstructores(clasificConst, 'tabla-top-constructores', 5);
renderUltimasCarreras(gps);
renderClasificacionPilotos(clasificPilotos, 'tabla-clasificacion-pilotos');
renderClasificacionConstructores(clasificConst, 'tabla-clasificacion-constructores');
renderCalendario(gps);
cargarPilotos();
cargarEscuderias();
}
document.addEventListener('DOMContentLoaded', init);