added new simple calculator feature.
This commit is contained in:
@@ -1103,6 +1103,13 @@ def results_calculator():
|
|||||||
translations=get_translations(),
|
translations=get_translations(),
|
||||||
current_language=get_current_language())
|
current_language=get_current_language())
|
||||||
|
|
||||||
|
@app.route('/calculator')
|
||||||
|
def standalone_calculator():
|
||||||
|
"""Standalone calculator — no tournament required"""
|
||||||
|
return render_template('calculator.html',
|
||||||
|
translations=get_translations(),
|
||||||
|
current_language=get_current_language())
|
||||||
|
|
||||||
@app.route('/results')
|
@app.route('/results')
|
||||||
def results_display():
|
def results_display():
|
||||||
"""Results display page"""
|
"""Results display page"""
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,318 +0,0 @@
|
|||||||
{
|
|
||||||
"rounds": [
|
|
||||||
{
|
|
||||||
"round_number": 1,
|
|
||||||
"players": [
|
|
||||||
{
|
|
||||||
"id": 14,
|
|
||||||
"name": "Karli Proje",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 23,
|
|
||||||
"name": "Robi Krautberger",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 11,
|
|
||||||
"name": "Rado Kefer",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 43,
|
|
||||||
"name": "Marko Pokržnik",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 7,
|
|
||||||
"name": "Branko Pokeržnik",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 9,
|
|
||||||
"name": "Janez Božič",
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"status": "completed"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"round_number": 2,
|
|
||||||
"players": [
|
|
||||||
{
|
|
||||||
"id": 39,
|
|
||||||
"name": "Tia Sudar",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 16,
|
|
||||||
"name": "Silvo Poročnik",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 31,
|
|
||||||
"name": "Dejan Kučnik",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 10,
|
|
||||||
"name": "Mitja Čeh",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 48,
|
|
||||||
"name": "Janja Salcman",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 50,
|
|
||||||
"name": "Vid Ravnjak",
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"status": "completed"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"round_number": 3,
|
|
||||||
"players": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"name": "Domen Pleterski",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 35,
|
|
||||||
"name": "Vanja Kolar",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 36,
|
|
||||||
"name": "Klara Wankmuller",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 46,
|
|
||||||
"name": "Tijana Štumpfl",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 34,
|
|
||||||
"name": "Jože Planinšec",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"name": "Ivan Tandler",
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"status": "completed"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"round_number": 4,
|
|
||||||
"players": [
|
|
||||||
{
|
|
||||||
"id": 26,
|
|
||||||
"name": "Jakob Herman",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 25,
|
|
||||||
"name": "Andrej Herman",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 30,
|
|
||||||
"name": "Maja Hirtl",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 4,
|
|
||||||
"name": "Mateja Pleterski",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 29,
|
|
||||||
"name": "Alen Kolar",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 5,
|
|
||||||
"name": "Jože Verhnjak",
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"status": "completed"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"round_number": 5,
|
|
||||||
"players": [
|
|
||||||
{
|
|
||||||
"id": 44,
|
|
||||||
"name": "Anka Kačnik",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 42,
|
|
||||||
"name": "Jure Glaser",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 45,
|
|
||||||
"name": "Lidija Blimen",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 32,
|
|
||||||
"name": "David Strniša",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 51,
|
|
||||||
"name": "Robi Ovčar",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 41,
|
|
||||||
"name": "Tadej Štruc",
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"status": "completed"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"round_number": 6,
|
|
||||||
"players": [
|
|
||||||
{
|
|
||||||
"id": 13,
|
|
||||||
"name": "Angelca Mrak",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 24,
|
|
||||||
"name": "Jože Verdinek",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 49,
|
|
||||||
"name": "Jolanda Verhnjak",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 18,
|
|
||||||
"name": "Matjaž Pleterski",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 40,
|
|
||||||
"name": "Jaka Cvar",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 19,
|
|
||||||
"name": "Franc Rizmal",
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"status": "pending"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"round_number": 7,
|
|
||||||
"players": [
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"name": "Nik Pleterski",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 17,
|
|
||||||
"name": "Dušan Onuk",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 28,
|
|
||||||
"name": "Anže Kolar",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 21,
|
|
||||||
"name": "Marko Blimen",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 20,
|
|
||||||
"name": "Jože Preglav",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 22,
|
|
||||||
"name": "Doris Fesel",
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"status": "waiting"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"round_number": 8,
|
|
||||||
"players": [
|
|
||||||
{
|
|
||||||
"id": 15,
|
|
||||||
"name": "Jan Pleterski",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 6,
|
|
||||||
"name": "Mateja Senica",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 8,
|
|
||||||
"name": "Franc Žigart",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 12,
|
|
||||||
"name": "Matej Kvasnik",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 33,
|
|
||||||
"name": "Namir Uzunović",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 38,
|
|
||||||
"name": "Bojan Sudar",
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"status": "waiting"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"round_number": 9,
|
|
||||||
"players": [
|
|
||||||
{
|
|
||||||
"id": 37,
|
|
||||||
"name": "Milan Stramec",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 27,
|
|
||||||
"name": "Janez Mrak",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 47,
|
|
||||||
"name": "Ljuba Mršak",
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"status": "waiting"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"created_at": "2026-04-22T17:12:17.015870",
|
|
||||||
"total_players": 51,
|
|
||||||
"total_rounds": 9,
|
|
||||||
"current_round": 6,
|
|
||||||
"tournament_type": "20_targets"
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,503 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Kalkulator</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">
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
background: #f0f2f5;
|
||||||
|
}
|
||||||
|
.navbar { flex-shrink: 0; z-index: 100; }
|
||||||
|
|
||||||
|
/* ── PAGE WRAPPER ── */
|
||||||
|
.page {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── MAIN CARD ── */
|
||||||
|
.main-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0,0,0,0.12);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1100px;
|
||||||
|
max-height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── LEFT PANEL: config ── */
|
||||||
|
.left-panel {
|
||||||
|
width: 260px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-right: 1px solid #e9ecef;
|
||||||
|
background: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-header {
|
||||||
|
padding: 22px 24px 16px;
|
||||||
|
border-bottom: 1px solid #e9ecef;
|
||||||
|
}
|
||||||
|
.left-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-body {
|
||||||
|
padding: 20px 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.setup-field label {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #6c757d;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.4px;
|
||||||
|
}
|
||||||
|
.setup-input {
|
||||||
|
padding: 13px 14px;
|
||||||
|
border: 2px solid #dee2e6;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 1.15rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2c3e50;
|
||||||
|
text-align: center;
|
||||||
|
background: white;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
outline: none;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.setup-input:focus {
|
||||||
|
border-color: #28a745;
|
||||||
|
box-shadow: 0 0 0 3px rgba(40,167,69,0.12);
|
||||||
|
}
|
||||||
|
.setup-hint {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
color: #adb5bd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gen-btn {
|
||||||
|
background: #28a745;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 14px;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s, transform 0.1s;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.gen-btn:hover:not(:disabled) { background: #1e7e34; transform: translateY(-1px); }
|
||||||
|
.gen-btn:disabled { background: #ced4da; cursor: not-allowed; }
|
||||||
|
|
||||||
|
/* stats + actions below the fold */
|
||||||
|
.left-stats {
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
border-top: 1px solid #e9ecef;
|
||||||
|
}
|
||||||
|
.left-stats.visible { display: flex; }
|
||||||
|
|
||||||
|
.stat-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
.stat-label {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #6c757d;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
}
|
||||||
|
.stat-value {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
#totalScore { color: #28a745; }
|
||||||
|
#tensCount { color: #ffc107; }
|
||||||
|
|
||||||
|
.progress-bar-wrap {
|
||||||
|
height: 6px;
|
||||||
|
background: #e9ecef;
|
||||||
|
border-radius: 3px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.progress-bar-fill {
|
||||||
|
height: 100%;
|
||||||
|
background: #28a745;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: width 0.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-actions {
|
||||||
|
padding: 0 24px 24px;
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.left-actions.visible { display: flex; }
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
padding: 11px 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.btn-zeros {
|
||||||
|
background: white;
|
||||||
|
border: 2px solid #6c757d;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
.btn-zeros:hover { background: #6c757d; color: white; }
|
||||||
|
.btn-reset {
|
||||||
|
background: white;
|
||||||
|
border: 2px solid #dc3545;
|
||||||
|
color: #dc3545;
|
||||||
|
}
|
||||||
|
.btn-reset:hover { background: #dc3545; color: white; }
|
||||||
|
|
||||||
|
/* ── RIGHT PANEL: grid ── */
|
||||||
|
.right-panel {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-empty {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #adb5bd;
|
||||||
|
gap: 10px;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
.right-empty .empty-icon { font-size: 3rem; }
|
||||||
|
|
||||||
|
.targets-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding: 18px 20px 20px;
|
||||||
|
min-height: 0;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.targets-wrapper.visible { display: block; }
|
||||||
|
.targets-wrapper::-webkit-scrollbar { width: 7px; }
|
||||||
|
.targets-wrapper::-webkit-scrollbar-track { background: transparent; }
|
||||||
|
.targets-wrapper::-webkit-scrollbar-thumb { background: #ced4da; border-radius: 4px; }
|
||||||
|
|
||||||
|
.targets-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.target-group {
|
||||||
|
border: 2px solid #e9ecef;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f8f9fa;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: border-color 0.15s, background 0.15s;
|
||||||
|
}
|
||||||
|
.target-group.completed {
|
||||||
|
border-color: #28a745;
|
||||||
|
background: #f0f8f5;
|
||||||
|
}
|
||||||
|
.target-num {
|
||||||
|
background: #e9ecef;
|
||||||
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #495057;
|
||||||
|
text-align: center;
|
||||||
|
padding: 5px 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: background 0.15s, color 0.15s;
|
||||||
|
}
|
||||||
|
.target-group.completed .target-num {
|
||||||
|
background: linear-gradient(135deg, #e8f5e8, #c8e6c9);
|
||||||
|
border-bottom-color: #28a745;
|
||||||
|
color: #28a745;
|
||||||
|
}
|
||||||
|
.shots-col {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
.shot-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 6px 2px;
|
||||||
|
border: 1.5px solid #dee2e6;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #333;
|
||||||
|
background: white;
|
||||||
|
min-height: 32px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: border-color 0.15s, color 0.15s;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.shot-input:focus {
|
||||||
|
border-color: #28a745;
|
||||||
|
box-shadow: 0 0 0 2px rgba(40,167,69,0.2);
|
||||||
|
}
|
||||||
|
.shot-input.valid { border-color: #28a745; }
|
||||||
|
.shot-input.ten { border-color: #ffc107; color: #ffc107; }
|
||||||
|
</style>
|
||||||
|
<script src="/static/js/i18n.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="navbar">
|
||||||
|
<div class="navbar-title">🎯 Kalkulator</div>
|
||||||
|
<div class="navbar-controls">
|
||||||
|
<a href="/" class="nav-btn">✕</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
<div class="main-card">
|
||||||
|
|
||||||
|
<!-- LEFT: config + stats -->
|
||||||
|
<div class="left-panel">
|
||||||
|
<div class="left-header">
|
||||||
|
<h3>🎯 Konfiguracija</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-stats" id="leftStats">
|
||||||
|
<div class="stat-row">
|
||||||
|
<span class="stat-label">Točke</span>
|
||||||
|
<span class="stat-value" id="totalScore">0</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-row">
|
||||||
|
<span class="stat-label">10-ke</span>
|
||||||
|
<span class="stat-value" id="tensCount">0</span>
|
||||||
|
</div>
|
||||||
|
<div class="progress-bar-wrap">
|
||||||
|
<div class="progress-bar-fill" id="progressBar" style="width:0%"></div>
|
||||||
|
</div>
|
||||||
|
<span class="setup-hint" id="calcMeta" style="text-align:center;"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="left-actions" id="leftActions">
|
||||||
|
<button class="action-btn btn-zeros" onclick="fillZeros()">Zapolni 0</button>
|
||||||
|
<button class="action-btn btn-reset" onclick="resetScores()">Pobriši vse</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- RIGHT: grid -->
|
||||||
|
<div class="right-panel">
|
||||||
|
<div class="right-empty" id="rightEmpty">
|
||||||
|
<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>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let numTargets = 20;
|
||||||
|
let shotsPerTarget = 2;
|
||||||
|
let scores = {};
|
||||||
|
|
||||||
|
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('calcMeta').textContent =
|
||||||
|
`${numTargets} × ${shotsPerTarget} = maks. ${numTargets * shotsPerTarget * 10} točk`;
|
||||||
|
|
||||||
|
buildGrid();
|
||||||
|
|
||||||
|
document.getElementById('rightEmpty').style.display = 'none';
|
||||||
|
document.getElementById('targetsWrapper').classList.add('visible');
|
||||||
|
document.getElementById('leftStats').classList.add('visible');
|
||||||
|
document.getElementById('leftActions').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++; }
|
||||||
|
}
|
||||||
|
document.getElementById('totalScore').textContent = total;
|
||||||
|
document.getElementById('tensCount').textContent = tens;
|
||||||
|
document.getElementById('progressBar').style.width = `${(filled / totalSlots) * 100}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 resetScores() {
|
||||||
|
if (!confirm('Pobrisati vse vnose?')) return;
|
||||||
|
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}`);
|
||||||
|
inp.value = '';
|
||||||
|
inp.classList.remove('valid', 'ten');
|
||||||
|
}
|
||||||
|
updateTargetGroup(t);
|
||||||
|
}
|
||||||
|
updateStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
validateSetup();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -803,6 +803,7 @@
|
|||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<h4 data-i18n="league.tournaments">Turnirji</h4>
|
<h4 data-i18n="league.tournaments">Turnirji</h4>
|
||||||
<a href="/tournament" class="nav-link" id="tournamentModeLink">🏆 <span data-i18n="tournament.tournament_mode">Način Turnirja</span></a>
|
<a href="/tournament" class="nav-link" id="tournamentModeLink">🏆 <span data-i18n="tournament.tournament_mode">Način Turnirja</span></a>
|
||||||
|
<a href="/calculator" class="nav-link">🎯 Kalkulator</a>
|
||||||
<a href="/archive/player-analysis" class="nav-link">👤 <span data-i18n="players.player_analysis">Analiza Igralcev</span></a>
|
<a href="/archive/player-analysis" class="nav-link">👤 <span data-i18n="players.player_analysis">Analiza Igralcev</span></a>
|
||||||
<a href="/archive" class="nav-link">📚 <span data-i18n="navigation.archive">Arhiv</span></a>
|
<a href="/archive" class="nav-link">📚 <span data-i18n="navigation.archive">Arhiv</span></a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user