Added new calculator with embedded simple tournament

This commit is contained in:
2026-06-05 22:48:49 +02:00
parent 3269e05f6a
commit 1ad9534819
6 changed files with 842 additions and 243 deletions
+192 -1
View File
@@ -130,6 +130,81 @@
align-content: stretch;
}
/* ── Leaderboard display mode ── */
.main-wrapper {
display: flex;
height: calc(100vh - 50px);
}
.main-wrapper .grid {
flex: 1;
height: 100%;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
min-width: 0;
}
.lb-sidebar {
width: 320px;
flex-shrink: 0;
display: flex;
flex-direction: column;
background: #141414;
border-left: 2px solid #222;
overflow: hidden;
}
.lb-header {
height: 60px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
border-bottom: 1px solid #222;
flex-shrink: 0;
}
.lb-title { font-size: 1.1rem; font-weight: 800; color: #fff; }
.lb-count {
background: #28a745; color: white;
font-size: 0.72rem; font-weight: 700;
padding: 2px 9px; border-radius: 12px;
}
.lb-empty {
flex: 1; display: flex; flex-direction: column;
align-items: center; justify-content: center;
color: #444; gap: 10px; font-size: 0.9rem;
}
.lb-empty .icon { font-size: 2.5rem; }
.lb-list {
flex: 1; overflow-y: auto;
padding: 10px 12px;
display: flex; flex-direction: column; gap: 7px;
}
.lb-list::-webkit-scrollbar { width: 4px; }
.lb-list::-webkit-scrollbar-thumb { background: #333; border-radius: 4px; }
.lb-row {
display: flex; align-items: center; gap: 9px;
padding: 9px 12px;
border-radius: 10px;
background: #1e1e1e; border: 2px solid #2a2a2a;
}
.lb-row.r1 { border-color: #ffc107; background: #1c1900; }
.lb-row.r2 { border-color: #888; background: #181818; }
.lb-row.r3 { border-color: #a0522d; background: #1a1210; }
.lb-badge {
width: 30px; height: 30px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 0.78rem; font-weight: 800; flex-shrink: 0;
background: #2a2a2a; color: #aaa;
}
.lb-row.r1 .lb-badge { background: #ffc107; color: #7a5800; font-size: 1rem; }
.lb-row.r2 .lb-badge { background: #888; color: #fff; font-size: 1rem; }
.lb-row.r3 .lb-badge { background: #a0522d; color: #fff; font-size: 1rem; }
.lb-name {
flex: 1; min-width: 0;
font-size: 0.95rem; font-weight: 700; color: #eee;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.lb-tens { font-size: 0.7rem; font-weight: 700; color: #ffc107; flex-shrink: 0; }
.lb-pts { font-size: 1.2rem; font-weight: 900; color: #28a745; flex-shrink: 0; min-width: 36px; text-align: right; }
.camera-card {
overflow: hidden;
display: flex;
@@ -884,7 +959,8 @@
</div>
</div>
<div class="grid">
<div class="main-wrapper" id="mainWrapper">
<div class="grid" id="cameraGrid">
{% for i in range(1, 7) %}
<div class="camera-card" data-camera-id="{{ i }}"
{% if i in settings.get('disabled_cameras', []) %}style="display:none;"{% endif %}>
@@ -916,6 +992,17 @@
{% endfor %}
</div>
<!-- Leaderboard sidebar — injected by JS when calc display is enabled -->
<div class="lb-sidebar" id="lbSidebar" style="display:none;">
<div class="lb-header">
<span class="lb-title">🏆 Lestvica</span>
<span class="lb-count" id="lbCount">0</span>
</div>
<div class="lb-empty" id="lbEmpty"><span class="icon">🏅</span><span>Čakamo na rezultate…</span></div>
<div class="lb-list" id="lbList" style="display:none;"></div>
</div>
</div>
<script>
// Global settings and tournament state
let currentSettings = {{ settings|tojson }};
@@ -1618,6 +1705,9 @@
window.addEventListener('resize', reshapeGrid);
addTouchFeedback();
// Apply leaderboard display mode if enabled
initLeaderboardDisplay();
// Initialize language selector after i18n is ready
window.addEventListener('i18nReady', function() {
createLanguageSelector('languageSelectorContainer');
@@ -1698,6 +1788,107 @@
clearInterval(dashboardPollingInterval);
}
});
// ── Calculator leaderboard display mode ────────────────────────────────
let lbPollingInterval = null;
let lbDisplayEnabled = false;
function initLeaderboardDisplay() {
fetch('/api/calculator/display')
.then(r => r.json())
.then(d => applyLeaderboardMode(d.enabled, d.camera || 1))
.catch(() => {});
}
function applyLeaderboardMode(enabled, camera) {
lbDisplayEnabled = enabled;
const sidebar = document.getElementById('lbSidebar');
const grid = document.getElementById('cameraGrid');
if (!enabled) {
sidebar.style.display = 'none';
// restore camera visibility to match settings
for (let i = 1; i <= 6; i++) {
const card = document.querySelector(`.camera-card[data-camera-id="${i}"]`);
if (!card) continue;
const isDisabled = currentSettings.disabled_cameras && currentSettings.disabled_cameras.includes(i);
card.style.display = isDisabled ? 'none' : '';
}
grid.style.gridTemplateColumns = '';
if (lbPollingInterval) { clearInterval(lbPollingInterval); lbPollingInterval = null; }
reshapeGrid();
return;
}
// Show only selected camera
for (let i = 1; i <= 6; i++) {
const card = document.querySelector(`.camera-card[data-camera-id="${i}"]`);
if (!card) continue;
card.style.display = (i === camera) ? '' : 'none';
}
grid.style.gridTemplateColumns = '1fr';
grid.style.gridTemplateRows = '1fr';
sidebar.style.display = '';
reshapeGrid();
// Poll leaderboard
pollLeaderboard();
if (!lbPollingInterval) {
lbPollingInterval = setInterval(pollLeaderboard, 5000);
}
}
function pollLeaderboard() {
fetch('/api/calculator/leaderboard')
.then(r => r.json())
.then(data => { if (Array.isArray(data)) renderLbSidebar(data); })
.catch(() => {});
}
function renderLbSidebar(data) {
const list = document.getElementById('lbList');
const empty = document.getElementById('lbEmpty');
const count = document.getElementById('lbCount');
count.textContent = data.length;
if (data.length === 0) {
empty.style.display = '';
list.style.display = 'none';
return;
}
empty.style.display = 'none';
list.style.display = '';
list.innerHTML = data.map((e, idx) => {
const rank = idx + 1;
const rc = rank <= 3 ? `r${rank}` : '';
const med = rank === 1 ? '🥇' : rank === 2 ? '🥈' : rank === 3 ? '🥉' : rank;
return `<div class="lb-row ${rc}">
<div class="lb-badge">${med}</div>
<div class="lb-name" title="${e.name}">${e.name}</div>
<div class="lb-tens">${e.tens}×10</div>
<div class="lb-pts">${e.score}</div>
</div>`;
}).join('');
}
// Poll display setting every 5s so enabling/disabling from calculator
// takes effect on the main screen automatically
setInterval(() => {
fetch('/api/calculator/display')
.then(r => r.json())
.then(d => {
const enabled = !!d.enabled;
const camera = d.camera || 1;
if (enabled !== lbDisplayEnabled) {
applyLeaderboardMode(enabled, camera);
}
})
.catch(() => {});
}, 5000);
</script>
</body>
</html>