все остальные апдейты на Кубок России

This commit is contained in:
2026-07-02 17:00:15 +03:00
parent 03a68ee8ca
commit e7b215af5e
11 changed files with 1644 additions and 180 deletions

View File

@@ -542,19 +542,18 @@
<select class="field-select" id="parserSource" name="parser_source" required>
{% if sources %}
{% for source in sources %}
<option value="{{ source.key }}" data-logo-base-path="{{ source.logo_base_path or '' }}" data-photo-base-path="{{ source.photo_base_path or '' }}" data-channel-logo-base-path="{{ source.channel_logo_base_path or '' }}" {% if source.key == default_parser_source_key %}selected{% endif %}>
<option value="{{ source.key }}" data-logo-base-path="{{ source.logo_base_path or '' }}" data-photo-base-path="{{ source.photo_base_path or '' }}" {% if source.key == default_parser_source_key %}selected{% endif %}>
{{ source.title }}
</option>
{% endfor %}
{% else %}
<option value="SUPERLEAGUE" data-logo-base-path="D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Teams Logos" data-photo-base-path="D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Photo" data-channel-logo-base-path="D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Лого каналов" selected>Суперлига 2026</option>
<option value="RUSSIAN_CUP" data-logo-base-path="D:\Графика\ФУТБОЛ\Кубок России 2026\Teams Logos" data-photo-base-path="D:\Графика\ФУТБОЛ\Кубок России 2026\Photo" data-channel-logo-base-path="D:\Графика\ФУТБОЛ\ЖФЛ Кубок России 2026\Лого каналов">Кубок России 2026</option>
<option value="SUPERLEAGUE" data-logo-base-path="D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Teams Logos" data-photo-base-path="D:\Графика\ФУТБОЛ\Женская Суперлига 2026\Photo" selected>Суперлига 2026</option>
<option value="RUSSIAN_CUP" data-logo-base-path="D:\Графика\ФУТБОЛ\Кубок России 2026\Teams Logos" data-photo-base-path="D:\Графика\ФУТБОЛ\Кубок России 2026\Photo">Кубок России 2026</option>
{% endif %}
</select>
<div class="helper-text">Список источников хранится в <b>базе данных</b> и редактируется в настройках проекта.</div>
<div class="helper-text">Путь к логотипам: <b id="selectedLogoBasePath"></b></div>
<div class="helper-text">Путь к фотографиям: <b id="selectedPhotoBasePath"></b></div>
<div class="helper-text">Путь к логотипам каналов: <b id="selectedChannelLogoBasePath"></b></div>
</div>
<div>
@@ -852,7 +851,6 @@
const parserSourceSelect = document.getElementById("parserSource");
const selectedLogoBasePath = document.getElementById("selectedLogoBasePath");
const selectedPhotoBasePath = document.getElementById("selectedPhotoBasePath");
const selectedChannelLogoBasePath = document.getElementById("selectedChannelLogoBasePath");
const openCreateAccountBtn = document.getElementById("openCreateAccountBtn");
const closeCreateAccountBtn = document.getElementById("closeCreateAccountBtn");
const accountModal = document.getElementById("accountModal");
@@ -1002,9 +1000,6 @@
if (selectedPhotoBasePath) {
selectedPhotoBasePath.textContent = option?.dataset?.photoBasePath || "—";
}
if (selectedChannelLogoBasePath) {
selectedChannelLogoBasePath.textContent = option?.dataset?.channelLogoBasePath || "—";
}
}
document.addEventListener("click", (event) => {

View File

@@ -245,6 +245,15 @@
Расписание тура
</a>
{% if is_russian_cup %}
<a
class="tab-link {% if tab == 'penalties' %}active{% endif %}"
href="/admin/session/{{ session[3] }}?tab=penalties"
data-vmix-title="ПЕНАЛЬТИ"
>
Пенальти
</a>
{% else %}
<a
class="tab-link {% if tab == 'standings' %}active{% endif %}"
href="/admin/session/{{ session[3] }}?tab=standings"
@@ -252,6 +261,7 @@
>
Турнирная таблица
</a>
{% endif %}
</div>
{% if tab == 'game' %}
@@ -969,11 +979,15 @@ data-role="{{ p.position or '' }}"
<th class="tour-meta-col">Счёт / статус</th>
<th class="tour-stadium-col">Стадион</th>
<th class="tour-channel-col">Канал</th>
<th class="tour-actions-col tour-edit-only">Правка</th>
</tr>
</thead>
<tbody>
{% for m in tour_schedule %}
<tr>
{% set row_key = m.match_external_id %}
{% set home_score_value = '' if m.home_score is none else m.home_score %}
{% set away_score_value = '' if m.away_score is none else m.away_score %}
<tr class="tour-schedule-row" data-schedule-row="{{ row_key }}">
<td class="tour-date-cell">
{% if m.match_date %}
{{ m.match_date.day }} {{ month_names[m.match_date.month] }}
@@ -991,27 +1005,61 @@ data-role="{{ p.position or '' }}"
</td>
<td class="tour-meta-cell">
<div class="tour-meta-wrap">
{% if m.status == 'finished' and m.home_score is not none
and m.away_score is not none %}
<span class="score-badge"
>{{ m.home_score }} : {{ m.away_score }}</span
>
{% endif %} {% if m.status == 'finished' %}
<div class="tour-meta-wrap tour-meta-display" data-schedule-display="{{ row_key }}">
{% if m.home_score is not none and m.away_score is not none %}
<span class="score-badge">{{ m.home_score }} : {{ m.away_score }}</span>
{% endif %}
{% if m.status == 'finished' %}
<span class="status-badge status-finished">Завершён</span>
{% elif m.status == 'live' %}
<span class="status-badge status-live">Идёт</span>
<span class="status-badge status-live">Live</span>
{% elif m.status == 'scheduled' %}
<span class="status-badge status-scheduled"
>Не начался</span
>
<span class="status-badge status-scheduled">Не начался</span>
{% else %}
<span
class="status-badge status-scheduled"
>{{ m.status or "—" }}</span
>
<span class="status-badge status-scheduled">{{ m.status or "—" }}</span>
{% endif %}
</div>
<div class="tour-score-edit" data-schedule-edit="{{ row_key }}" hidden>
<div class="tour-edit-line">
<div class="tour-score-inputs" aria-label="Счёт">
<input
type="number"
min="0"
class="tour-score-input"
data-match-id="{{ row_key }}"
data-score-side="home"
value="{{ home_score_value }}"
data-initial-value="{{ home_score_value }}"
placeholder="—"
disabled
>
<span class="tour-score-separator">:</span>
<input
type="number"
min="0"
class="tour-score-input"
data-match-id="{{ row_key }}"
data-score-side="away"
value="{{ away_score_value }}"
data-initial-value="{{ away_score_value }}"
placeholder="—"
disabled
>
</div>
<select
class="tour-status-select"
data-match-id="{{ row_key }}"
data-initial-status="{{ m.status or 'scheduled' }}"
disabled
>
<option value="scheduled" {% if m.status == 'scheduled' or not m.status %}selected{% endif %}>Не начался</option>
<option value="live" {% if m.status == 'live' %}selected{% endif %}>Live</option>
<option value="finished" {% if m.status == 'finished' %}selected{% endif %}>Завершён</option>
</select>
</div>
</div>
</td>
<td class="stadium-cell">
@@ -1046,7 +1094,22 @@ data-role="{{ p.position or '' }}"
</label>
</td>
<td class="tour-actions-cell tour-edit-only">
<div class="tour-save-actions" data-schedule-save-actions="{{ row_key }}" hidden>
<button
type="button"
class="btn btn-primary tour-save-btn"
onclick="saveTourScheduleMatch('{{ row_key }}')"
title="Сохранить счёт и статус"
></button>
<button
type="button"
class="btn btn-secondary tour-cancel-btn"
onclick="cancelTourScheduleEdit('{{ row_key }}')"
title="Отмена"
>×</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
@@ -1108,6 +1171,64 @@ data-role="{{ p.position or '' }}"
{% endif %}
</div>
{% elif tab == 'penalties' and is_russian_cup %}
<div class="panel penalty-panel" id="penaltyPanel">
<div class="penalty-header">
<div>
<div class="team-panel-title">Серия пенальти</div>
<div class="penalty-subtitle">Быстро отмечай удары для Кубка России. В vMix всегда уходят 5 строк: 15, затем 610 снова в строки 15.</div>
</div>
<div class="penalty-header-actions">
<button type="button" class="btn btn-secondary" onclick="addPenaltyRound()">+ Добавить серию</button>
<button type="button" class="btn btn-secondary" onclick="deleteLastPenaltyRound()">Удалить последнюю серию</button>
<button type="button" class="btn btn-danger" onclick="clearPenaltyShootout()">Очистить</button>
</div>
</div>
<div class="penalty-scoreboard">
<div class="penalty-team-card home">
<div class="penalty-team-name">{{ session[14] }}</div>
<div class="penalty-team-score" id="penaltyHomeScore">0</div>
<div class="penalty-next-label" id="penaltyHomeNext">Следующий удар: 1</div>
<div class="penalty-fast-actions">
<button type="button" class="penalty-fast-btn scored" onclick="setNextPenaltyShot('home', 'scored')">Забил</button>
<button type="button" class="penalty-fast-btn missed" onclick="setNextPenaltyShot('home', 'missed')">Не забил</button>
</div>
</div>
<div class="penalty-team-card away">
<div class="penalty-team-name">{{ session[18] }}</div>
<div class="penalty-team-score" id="penaltyAwayScore">0</div>
<div class="penalty-next-label" id="penaltyAwayNext">Следующий удар: 1</div>
<div class="penalty-fast-actions">
<button type="button" class="penalty-fast-btn scored" onclick="setNextPenaltyShot('away', 'scored')">Забил</button>
<button type="button" class="penalty-fast-btn missed" onclick="setNextPenaltyShot('away', 'missed')">Не забил</button>
</div>
</div>
</div>
<div class="penalty-table-wrap">
<table class="penalty-table">
<thead>
<tr>
<th>Серия</th>
<th>{{ session[14] }}</th>
<th>{{ session[18] }}</th>
</tr>
</thead>
<tbody id="penaltyRoundsBody">
<tr>
<td colspan="3" class="empty-box">Загрузка серии пенальти…</td>
</tr>
</tbody>
</table>
</div>
<div class="penalty-footer">
<div class="penalty-help">Любой удар можно исправить прямо в таблице: нажми “Забил”, “Не забил” или “Очистить” в нужной ячейке. После 5 ударов продолжай 6-й, 7-й и дальше — для vMix они будут записываться в первые 5 строк нового блока.</div>
</div>
</div>
{% else %}
<div class="panel">
<div class="empty-box">Эта вкладка пока в разработке.</div>
@@ -1132,6 +1253,8 @@ data-role="{{ p.position or '' }}"
},
sessionToken: {{ session[3] | tojson }},
matchId: {{ session[1] | tojson }},
sourceKey: {{ source_key | tojson }},
isRussianCup: {{ is_russian_cup | tojson }},
homeFormations: {{ home_formations | tojson }},
awayFormations: {{ away_formations | tojson }},