227 lines
9.6 KiB
JavaScript
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); |