239 lines
5.7 KiB
HTML
239 lines
5.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="cs">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<link rel="icon" href="icon.png">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>TVCOM Přenosy</title>
|
|
<style>
|
|
body {
|
|
font-family: system-ui, sans-serif;
|
|
margin: 20px;
|
|
background: #f5f5f5;
|
|
color: #333;
|
|
}
|
|
|
|
h1 {
|
|
text-align: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.filters {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
gap: 10px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
input, select, button {
|
|
padding: 6px 10px;
|
|
font-size: 15px;
|
|
}
|
|
|
|
.grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
gap: 15px;
|
|
}
|
|
|
|
.card {
|
|
background: white;
|
|
border-radius: 10px;
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
overflow: hidden;
|
|
transition: transform 0.2s;
|
|
}
|
|
|
|
.card:hover {
|
|
transform: scale(1.02);
|
|
}
|
|
|
|
.card img {
|
|
width: 100%;
|
|
height: 160px;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.card-content {
|
|
padding: 10px;
|
|
}
|
|
|
|
.title {
|
|
font-weight: bold;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.meta {
|
|
font-size: 0.9em;
|
|
color: #666;
|
|
}
|
|
|
|
#loading {
|
|
text-align: center;
|
|
font-size: 1.2em;
|
|
margin-top: 30px;
|
|
}
|
|
|
|
@media ( max-width : 500px) {
|
|
.card img {
|
|
height: 120px;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>📺 TVCOM Přenosy</h1>
|
|
|
|
<div class="filters">
|
|
<input id="search" type="text" placeholder="Hledat přenos..." /> <input
|
|
id="datePicker" type="date" /> <select id="sportFilter">
|
|
<option value="">Všechny sporty</option>
|
|
</select>
|
|
<button id="refreshBtn">♻️ Obnovit data</button>
|
|
</div>
|
|
|
|
<div id="loading">Načítám data...</div>
|
|
<div id="list" class="grid" style="display: none"></div>
|
|
|
|
<script>
|
|
const API_BASE = "";
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const apiKey = urlParams.get('apiKey');
|
|
const listEl = document.getElementById('list');
|
|
const loadingEl = document.getElementById('loading');
|
|
const searchEl = document.getElementById('search');
|
|
const sportFilterEl = document.getElementById('sportFilter');
|
|
const datePicker = document.getElementById('datePicker');
|
|
const refreshBtn = document.getElementById('refreshBtn');
|
|
let all = [];
|
|
|
|
// === inicializace ===
|
|
const today = new Date().toISOString().substring(0, 10);
|
|
datePicker.value = today;
|
|
fetchForDate(today);
|
|
|
|
// === změna datumu ===
|
|
datePicker.addEventListener('change', () => {
|
|
const dateStr = datePicker.value;
|
|
if (dateStr) fetchForDate(dateStr);
|
|
});
|
|
|
|
// === debounce funkce ===
|
|
function debounce(func, wait) {
|
|
let timeout;
|
|
return function(...args) {
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(() => func.apply(this, args), wait);
|
|
};
|
|
}
|
|
|
|
// === vyhledávání ===
|
|
const debouncedApplyFilters = debounce(applyFilters, 300);
|
|
searchEl.addEventListener('input', debouncedApplyFilters);
|
|
sportFilterEl.addEventListener('change', applyFilters);
|
|
|
|
// === tlačítko obnovit ===
|
|
refreshBtn.addEventListener('click', () => {
|
|
loadingEl.style.display = 'block';
|
|
listEl.style.display = 'none';
|
|
const now = new Date().toISOString().substring(0, 10);
|
|
datePicker.value = now;
|
|
|
|
fetch(`${API_BASE}/refresh?apiKey=${apiKey}`)
|
|
.then(r => {
|
|
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
return r.json();
|
|
})
|
|
.then(() => {
|
|
// po úspěšném refresh načteme aktuální den
|
|
const now = new Date().toISOString().substring(0, 10);
|
|
datePicker.value = now;
|
|
return fetchForDate(now);
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
loadingEl.textContent = "❌ Chyba při obnově dat: " + err;
|
|
});
|
|
});
|
|
|
|
// === načtení dat pro daný den ===
|
|
function fetchForDate(dateStr) {
|
|
loadingEl.style.display = 'block';
|
|
listEl.style.display = 'none';
|
|
return fetch(`${API_BASE}/transmissions?date=${encodeURIComponent(dateStr)}&apiKey=${apiKey}`)
|
|
.then(r => {
|
|
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
return r.json();
|
|
})
|
|
.then(data => {
|
|
all = data || [];
|
|
populateSports();
|
|
applyFilters();
|
|
loadingEl.style.display = 'none';
|
|
listEl.style.display = 'grid';
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
loadingEl.textContent = "❌ Chyba při načítání dat: " + err;
|
|
});
|
|
}
|
|
|
|
// === naplnění filtrů podle dostupných sportů ===
|
|
function populateSports() {
|
|
const sports = [...new Set(all.map(t => t.sport).filter(Boolean))].sort();
|
|
sportFilterEl.innerHTML = '<option value="">Všechny sporty</option>';
|
|
for (const s of sports) {
|
|
const opt = document.createElement('option');
|
|
opt.value = s;
|
|
opt.textContent = s;
|
|
sportFilterEl.appendChild(opt);
|
|
}
|
|
}
|
|
|
|
// === aplikace filtrů a vykreslení ===
|
|
function applyFilters() {
|
|
const query = searchEl.value.toLowerCase();
|
|
const sport = sportFilterEl.value;
|
|
const filtered = all.filter(t => {
|
|
return (!query || t.title.toLowerCase().includes(query) || (t.league||'').toLowerCase().includes(query)) &&
|
|
(!sport || t.sport === sport);
|
|
});
|
|
renderList(filtered);
|
|
}
|
|
|
|
// === formátování data a času ===
|
|
function formatDateTime(dateStr, timeStr) {
|
|
if (!dateStr || !timeStr) return '';
|
|
const dt = new Date(dateStr + 'T' + timeStr);
|
|
return dt.toLocaleString('cs-CZ', { dateStyle: 'short', timeStyle: 'short' });
|
|
}
|
|
|
|
// === vykreslení seznamu ===
|
|
function renderList(items) {
|
|
listEl.innerHTML = '';
|
|
if (!items.length) {
|
|
listEl.innerHTML = '<div style="grid-column:1/-1;text-align:center;">Žádné přenosy</div>';
|
|
return;
|
|
}
|
|
for (const t of items) {
|
|
const div = document.createElement('div');
|
|
div.className = 'card';
|
|
const dateTimeStr = formatDateTime(t.date, t.time);
|
|
div.innerHTML = `
|
|
<a href="${t.link}" target="_blank">
|
|
<img src="${t.image || 'https://via.placeholder.com/400x160?text=Žádný+obrázek'}" alt="">
|
|
<div class="card-content">
|
|
<div class="title">${t.title}</div>
|
|
<div class="meta">${dateTimeStr}</div>
|
|
<div class="meta">${t.sport} • ${t.league || ''} ${t.leaguePart || ''}</div>
|
|
</div>
|
|
</a>
|
|
`;
|
|
listEl.appendChild(div);
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|