Files
Sdk_TV_app/templates/calculator.html
T

444 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="sl">
<head>
<meta charset="UTF-8" />
<title>Nabijanje</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/static/css/base.css">
<link rel="stylesheet" href="/static/css/navbar.css">
<link rel="stylesheet" href="/static/css/buttons.css">
<link rel="stylesheet" href="/static/css/pages/calculator.css">
<script src="/static/js/i18n.js"></script>
</head>
<body>
<div class="navbar">
<div class="navbar-title">💥 Nabijanje</div>
<div class="navbar-controls">
<a href="/" class="nav-btn">✕</a>
</div>
</div>
<div class="page">
<div class="main-card">
<!-- ── LEFT: config + stats + utils ── -->
<div class="left-panel">
<div class="left-header">
<h3>⚙️ Nastavitve</h3>
</div>
<div class="left-body">
<div class="setup-field">
<label>Število tarč</label>
<input type="number" id="inputTargets" class="setup-input" min="1" max="100" value="20" oninput="validateSetup()">
</div>
<div class="setup-field">
<label>Strelov na tarčo</label>
<input type="number" id="inputShots" class="setup-input" min="1" max="10" value="2" oninput="validateSetup()">
<span class="setup-hint">Maks. vrednost strela: 10</span>
</div>
<button class="gen-btn" id="startBtn" onclick="startCalculator()">Generiraj</button>
</div>
<div class="left-utils" id="leftUtils">
<button class="util-btn btn-zeros" onclick="fillZeros()">Zapolni 0</button>
<button class="util-btn btn-reset" onclick="resetScores()">Pobriši vse</button>
</div>
<div class="display-section">
<span class="display-section-title">📺 Prikaz na zaslonu</span>
<div class="display-toggle-row">
<span class="display-toggle-label">Omogoči prikaz</span>
<label class="toggle-switch">
<input type="checkbox" id="displayEnabled" onchange="saveDisplaySettings()">
<span class="toggle-track"></span>
</label>
</div>
<div class="display-camera-row">
<label for="displayCamera">Kamera</label>
<select id="displayCamera" class="camera-select" onchange="saveDisplaySettings()" disabled>
<option value="1">Kamera 1</option>
<option value="2">Kamera 2</option>
<option value="3">Kamera 3</option>
<option value="4">Kamera 4</option>
<option value="5">Kamera 5</option>
<option value="6">Kamera 6</option>
</select>
</div>
</div>
</div>
<!-- ── MIDDLE: name bar → targets → add bar ── -->
<div class="middle-panel">
<div class="name-bar">
<span class="name-bar-label">👤</span>
<input type="text" id="inputName" class="name-input" placeholder="Ime in priimek" autocomplete="off">
</div>
<div class="middle-empty" id="middleEmpty">
<span class="empty-icon">🎯</span>
<span>Nastavi konfiguracijo in klikni Generiraj</span>
</div>
<div class="targets-wrapper" id="targetsWrapper">
<div class="targets-grid" id="targetsGrid"></div>
</div>
<div class="add-bar" id="addBar">
<div class="add-bar-score">
<span class="add-bar-pts" id="addBarPts">0</span>
<span class="add-bar-tens" id="addBarTens">0 × 10</span>
</div>
<div class="add-bar-meta">
<div class="add-bar-meta-text" id="addBarMeta"></div>
<div class="add-bar-progress">
<div class="add-bar-progress-fill" id="addBarProgress" style="width:0%"></div>
</div>
</div>
<button class="btn-add" id="addBtn" onclick="addToScoreboard()" disabled>+ Dodaj na lestvico</button>
</div>
</div>
<!-- ── RIGHT: scoreboard ── -->
<div class="score-panel">
<div class="score-header">
<h3>🏆 Lestvica</h3>
<span class="score-count" id="scoreCount">0</span>
</div>
<div class="score-empty" id="scoreEmpty">
<span class="empty-icon">🏅</span>
<span>Dodaj tekmovalce z gumbom<br><em>Dodaj na lestvico</em></span>
</div>
<div class="score-list" id="scoreList" style="display:none;"></div>
<div class="score-footer" id="scoreFooter" style="display:none;">
<button class="btn-clear-board" onclick="clearScoreboard()">Počisti lestvico</button>
</div>
<div class="score-print-footer" >
<button class="btn-print" onclick="printLeaderboard()">🖨️ Natisni lestvico</button>
</div>
</div>
</div>
</div>
<!-- Print-only layout — hidden on screen, shown on print -->
<div id="printLayout" style="display:none;">
<div class="pl-header">
<img src="/static/logo.png" class="pl-logo" alt="Logo" onerror="this.style.display='none'">
<div class="pl-titles">
<div class="pl-title">🏆 Lestvica</div>
<div class="pl-subtitle">Rezultati nabijanja</div>
</div>
<div class="pl-meta" id="plMeta"></div>
</div>
<table class="pl-table">
<thead>
<tr>
<th style="width:50px;">Mesto</th>
<th>Tekmovalec</th>
<th class="right" style="width:80px;">Točke</th>
<th class="right" style="width:70px;">10-ke</th>
</tr>
</thead>
<tbody id="plBody"></tbody>
</table>
</div>
<script>
const STORAGE_KEY = 'kalkulator_lestvica';
let numTargets = 20;
let shotsPerTarget = 2;
let scores = {};
let scoreboard = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
function saveScoreboard() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(scoreboard));
fetch('/api/calculator/leaderboard', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(scoreboard)
}).catch(() => {});
}
// ── Display settings ───────────────────────────────────────────────────
function saveDisplaySettings() {
const enabled = document.getElementById('displayEnabled').checked;
const camera = parseInt(document.getElementById('displayCamera').value);
document.getElementById('displayCamera').disabled = !enabled;
fetch('/api/calculator/display', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ enabled, camera })
}).catch(() => {});
}
function loadDisplaySettings() {
fetch('/api/calculator/display')
.then(r => r.json())
.then(data => {
const enabled = !!data.enabled;
document.getElementById('displayEnabled').checked = enabled;
document.getElementById('displayCamera').value = data.camera || 1;
document.getElementById('displayCamera').disabled = !enabled;
}).catch(() => {});
}
function validateSetup() {
const t = parseInt(document.getElementById('inputTargets').value);
const s = parseInt(document.getElementById('inputShots').value);
document.getElementById('startBtn').disabled = !(t >= 1 && t <= 100 && s >= 1 && s <= 10);
}
function startCalculator() {
numTargets = parseInt(document.getElementById('inputTargets').value);
shotsPerTarget = parseInt(document.getElementById('inputShots').value);
scores = {};
for (let t = 1; t <= numTargets; t++) {
scores[t] = {};
for (let s = 1; s <= shotsPerTarget; s++) scores[t][s] = null;
}
document.getElementById('addBarMeta').textContent =
`${numTargets} tarč × ${shotsPerTarget} strel`;
buildGrid();
document.getElementById('middleEmpty').style.display = 'none';
document.getElementById('targetsWrapper').classList.add('visible');
document.getElementById('leftUtils').classList.add('visible');
document.getElementById('addBar').classList.add('visible');
updateStats();
const first = document.querySelector('.shot-input');
if (first) { first.focus(); first.select(); }
}
function buildGrid() {
const grid = document.getElementById('targetsGrid');
let cols;
if (numTargets <= 4) cols = numTargets;
else if (numTargets <= 10) cols = 5;
else if (numTargets <= 20) cols = 5;
else if (numTargets <= 40) cols = 8;
else cols = 10;
grid.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
let html = '';
for (let t = 1; t <= numTargets; t++) {
html += `<div class="target-group" id="tg-${t}">
<div class="target-num">${t}</div>
<div class="shots-col">`;
for (let s = 1; s <= shotsPerTarget; s++) {
html += `<input type="number" class="shot-input" id="si-${t}-${s}"
min="0" max="10" data-t="${t}" data-s="${s}"
oninput="onShot(${t},${s},this.value)"
onkeydown="onShotKey(event,${t},${s})">`;
}
html += `</div></div>`;
}
grid.innerHTML = html;
}
function onShot(t, s, raw) {
const input = document.getElementById(`si-${t}-${s}`);
let val = null;
if (raw !== '' && raw !== null) {
val = Math.max(0, Math.min(10, parseInt(raw) || 0));
if (input.value !== val.toString()) input.value = val;
}
scores[t][s] = val;
input.classList.remove('valid', 'ten');
if (val !== null) input.classList.add(val === 10 ? 'ten' : 'valid');
updateTargetGroup(t);
updateStats();
}
function onShotKey(e, t, s) {
if (e.key === 'Enter' || e.key === 'Tab') {
e.preventDefault();
const inputs = Array.from(document.querySelectorAll('.shot-input'));
const idx = inputs.indexOf(document.getElementById(`si-${t}-${s}`));
const next = inputs[idx + 1];
if (next) { next.focus(); next.select(); }
}
}
function updateTargetGroup(t) {
const allFilled = Object.values(scores[t]).every(v => v !== null);
document.getElementById(`tg-${t}`).classList.toggle('completed', allFilled);
}
function updateStats() {
let total = 0, tens = 0, filled = 0;
const totalSlots = numTargets * shotsPerTarget;
for (let t = 1; t <= numTargets; t++)
for (let s = 1; s <= shotsPerTarget; s++) {
const v = scores[t][s];
if (v !== null) { total += v; filled++; if (v === 10) tens++; }
}
const pct = totalSlots ? (filled / totalSlots) * 100 : 0;
document.getElementById('addBarPts').textContent = total;
document.getElementById('addBarTens').textContent = `${tens} × 10`;
document.getElementById('addBarProgress').style.width = `${pct}%`;
document.getElementById('addBtn').disabled = (filled < totalSlots);
}
function addToScoreboard() {
const name = document.getElementById('inputName').value.trim() || 'Neznan';
let total = 0, tens = 0;
for (let t = 1; t <= numTargets; t++)
for (let s = 1; s <= shotsPerTarget; s++) {
const v = scores[t][s];
if (v !== null) { total += v; if (v === 10) tens++; }
}
const existing = scoreboard.find(e => e.name.toLowerCase() === name.toLowerCase());
if (existing) {
if (total > existing.score || (total === existing.score && tens > existing.tens)) {
existing.score = total;
existing.tens = tens;
}
} else {
scoreboard.push({ name, score: total, tens, id: Date.now() });
}
scoreboard.sort((a, b) => b.score - a.score || b.tens - a.tens);
saveScoreboard();
renderScoreboard();
document.getElementById('inputName').value = '';
resetScoresQuiet();
document.getElementById('inputName').focus();
}
function removeFromScoreboard(id) {
scoreboard = scoreboard.filter(e => e.id !== id);
saveScoreboard();
renderScoreboard();
}
function clearScoreboard() {
if (!confirm('Počistiti celotno lestvico?')) return;
scoreboard = [];
saveScoreboard();
renderScoreboard();
}
function renderScoreboard() {
const list = document.getElementById('scoreList');
const empty = document.getElementById('scoreEmpty');
const footer = document.getElementById('scoreFooter');
const counter = document.getElementById('scoreCount');
counter.textContent = scoreboard.length;
if (scoreboard.length === 0) {
empty.style.display = '';
list.style.display = 'none';
footer.style.display = 'none';
return;
}
empty.style.display = 'none';
list.style.display = '';
footer.style.display = '';
list.innerHTML = scoreboard.map((entry, idx) => {
const rank = computeRank(idx);
const rankClass = rank <= 3 ? `rank-${rank}` : '';
const medal = rank === 1 ? '🥇' : rank === 2 ? '🥈' : rank === 3 ? '🥉' : rank;
return `
<div class="score-row ${rankClass}">
<div class="rank-badge">${medal}</div>
<div class="score-name" title="Klikni za izbiro" onclick="document.getElementById('inputName').value='${entry.name.replace(/'/g, "\\'")}'">${entry.name}</div>
<div class="score-tens">${entry.tens}×10</div>
<div class="score-pts">${entry.score}</div>
<button class="score-del" onclick="removeFromScoreboard(${entry.id})" title="Odstrani">✕</button>
</div>`;
}).join('');
}
function fillZeros() {
for (let t = 1; t <= numTargets; t++) {
for (let s = 1; s <= shotsPerTarget; s++) {
if (scores[t][s] === null) {
scores[t][s] = 0;
const inp = document.getElementById(`si-${t}-${s}`);
inp.value = 0;
inp.classList.remove('ten');
inp.classList.add('valid');
}
}
updateTargetGroup(t);
}
updateStats();
}
function resetScoresQuiet() {
for (let t = 1; t <= numTargets; t++) {
for (let s = 1; s <= shotsPerTarget; s++) {
scores[t][s] = null;
const inp = document.getElementById(`si-${t}-${s}`);
if (inp) { inp.value = ''; inp.classList.remove('valid', 'ten'); }
}
const tg = document.getElementById(`tg-${t}`);
if (tg) tg.classList.remove('completed');
}
updateStats();
}
function resetScores() {
if (!confirm('Pobrisati vse vnose?')) return;
resetScoresQuiet();
}
function computeRank(idx) {
const e = scoreboard[idx];
return scoreboard.filter(o =>
o.score > e.score || (o.score === e.score && o.tens > e.tens)
).length + 1;
}
function printLeaderboard() {
const medals = ['🥇','🥈','🥉'];
const rows = scoreboard.map((e, idx) => {
const rank = computeRank(idx);
const rc = rank <= 3 ? `r${rank}` : '';
const med = rank <= 3 ? medals[rank - 1] : rank;
return `<tr>
<td><span class="pl-rank ${rc}">${med}</span></td>
<td class="pl-name">${e.name}</td>
<td class="right pl-score">${e.score}</td>
<td class="right pl-tens">${e.tens}</td>
</tr>`;
}).join('');
document.getElementById('plBody').innerHTML = rows || '<tr><td colspan="4" style="text-align:center;color:#aaa;padding:20px;">Ni rezultatov</td></tr>';
const now = new Date();
document.getElementById('plMeta').innerHTML =
`${now.toLocaleDateString('sl-SI')}<br>${now.toLocaleTimeString('sl-SI', {hour:'2-digit',minute:'2-digit'})}<br>${scoreboard.length} tekmovalcev`;
window.print();
}
validateSetup();
renderScoreboard();
loadDisplaySettings();
</script>
</body>
</html>