обновление для Кубка России

переделаны все парсеры на ссылки из базы
This commit is contained in:
2026-07-02 12:46:29 +03:00
parent b0fe3ad5a4
commit 57907fd86b
29 changed files with 1838 additions and 240 deletions

View File

@@ -107,6 +107,82 @@
margin: 0;
}
.parser-import-card {
width: 100%;
padding: 18px;
border: 1px solid rgba(0, 255, 136, 0.18);
border-radius: 16px;
background: rgba(255, 255, 255, 0.025);
}
.parser-import-title {
font-size: 18px;
font-weight: 700;
margin-bottom: 6px;
}
.parser-import-subtitle {
color: var(--muted);
font-size: 13px;
line-height: 1.45;
margin-bottom: 16px;
}
.parser-import-grid {
display: grid;
grid-template-columns: minmax(260px, 360px) minmax(0, 1fr);
gap: 16px;
align-items: start;
}
.parser-checkboxes {
display: grid;
grid-template-columns: repeat(4, minmax(120px, 1fr));
gap: 10px;
}
.parser-checkbox {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
padding: 0 12px;
border-radius: 12px;
border: 1px solid var(--border);
background: #0f141d;
color: var(--text);
font-size: 14px;
font-weight: 600;
cursor: pointer;
}
.parser-checkbox input {
accent-color: var(--accent);
}
.parser-submit-row {
margin-top: 16px;
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.parser-mode-note {
color: var(--muted);
font-size: 12px;
line-height: 1.45;
}
@media (max-width: 900px) {
.parser-import-grid,
.parser-checkboxes {
grid-template-columns: 1fr;
}
}
.log-block {
margin-top: 20px;
border: 1px solid var(--border);
@@ -151,7 +227,8 @@
}
.account-modal,
.update-modal {
.update-modal,
.settings-modal {
position: fixed;
inset: 0;
display: none;
@@ -163,12 +240,14 @@
}
.account-modal.active,
.update-modal.active {
.update-modal.active,
.settings-modal.active {
display: flex;
}
.account-modal-card,
.update-modal-card {
.update-modal-card,
.settings-modal-card {
width: min(100%, 460px);
background: rgba(12, 17, 26, 0.98);
border: 1px solid rgba(0, 255, 136, 0.28);
@@ -177,21 +256,24 @@
padding: 24px;
}
.update-modal-card {
.update-modal-card,
.settings-modal-card {
width: min(100%, 760px);
max-height: 92vh;
overflow-y: auto;
}
.account-modal-title,
.update-modal-title {
.update-modal-title,
.settings-modal-title {
font-size: 22px;
font-weight: 700;
margin-bottom: 8px;
}
.account-modal-subtitle,
.update-modal-subtitle {
.update-modal-subtitle,
.settings-modal-subtitle {
color: var(--muted);
margin-bottom: 18px;
line-height: 1.5;
@@ -202,6 +284,46 @@
gap: 14px;
}
.env-groups {
display: grid;
gap: 16px;
}
.env-group {
border: 1px solid var(--border);
border-radius: 14px;
padding: 14px;
background: rgba(255, 255, 255, 0.025);
}
.env-group-title {
font-size: 16px;
font-weight: 700;
margin-bottom: 4px;
}
.env-group-subtitle {
color: var(--muted);
font-size: 12px;
margin-bottom: 12px;
}
.env-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.env-field-wide {
grid-column: 1 / -1;
}
@media (max-width: 720px) {
.env-grid {
grid-template-columns: 1fr;
}
}
.field-label {
display: block;
font-size: 13px;
@@ -407,24 +529,66 @@
</div>
<div class="parser-actions">
<form method="post" action="/admin/db/run-parser" class="action-form" data-parser-title="Игроки" data-parser-status="Сбор и сохранение базы игроков...">
<input type="hidden" name="parser_name" value="players">
<button type="submit" class="action-btn">🗄️ Заграбить игроков</button>
</form>
<form method="post" action="/admin/db/run-parser" class="action-form parser-import-card" data-parser-title="Выбранные данные" data-parser-status="Загрузка выбранных данных из выбранного турнира...">
<div class="parser-import-title">Импорт данных</div>
<div class="parser-import-subtitle">
Выберите источник турнира и отметьте, какие данные нужно заграбить. Существующие команды, игроки и матчи будут обновлены через UPSERT, без полной очистки таблиц.
</div>
<form method="post" action="/admin/db/run-parser" class="action-form" data-parser-title="Расписание" data-parser-status="Загрузка расписания матчей с сайта...">
<input type="hidden" name="parser_name" value="schedule">
<button type="submit" class="action-btn">🗄️ Заграбить расписание</button>
</form>
<div class="parser-import-grid">
<div>
<label class="field-label" for="parserSource">Источник / турнир</label>
{% set sources = parser_sources.values() if parser_sources is mapping else parser_sources|default([]) %}
<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 '' }}" {% 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" 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>
<form method="post" action="/admin/db/run-parser" class="action-form" data-parser-title="Турнирка" data-parser-status="Обновление турнирной таблицы...">
<input type="hidden" name="parser_name" value="standings">
<button type="submit" class="action-btn">🗄️ Заграбить турнирку</button>
<div>
<label class="field-label">Что парсить</label>
<div class="parser-checkboxes">
<label class="parser-checkbox">
<input type="checkbox" name="parser_names" value="teams" checked>
<span>Команды</span>
</label>
<label class="parser-checkbox">
<input type="checkbox" name="parser_names" value="players" checked>
<span>Игроки</span>
</label>
<label class="parser-checkbox">
<input type="checkbox" name="parser_names" value="schedule" checked>
<span>Расписание</span>
</label>
<label class="parser-checkbox">
<input type="checkbox" name="parser_names" value="standings">
<span>Турнирка</span>
</label>
</div>
</div>
</div>
<div class="parser-submit-row">
<button type="submit" class="action-btn">🗄️ Заграбить выбранное</button>
<div class="parser-mode-note">Безопасный режим: добавляет новые записи и обновляет существующие. Турнирка по-прежнему пересобирается только за выбранный сезон.</div>
</div>
</form>
</div>
<div class="account-actions">
<button type="button" class="account-toggle-btn" id="openCreateAccountBtn"> Создать аккаунт</button>
<button type="button" class="account-toggle-btn" id="openSettingsBtn">⚙️ Настройки проекта</button>
<button type="button" class="account-toggle-btn" id="openAddUpdateBtn">📝 Добавить обновление</button>
</div>
@@ -458,6 +622,21 @@
</div>
{% endif %}
{% if settings_output %}
<div class="log-block {% if settings_success %}success{% else %}error{% endif %}">
<div class="log-title">Сохранение настроек проекта</div>
<div class="log-status">
Статус:
{% if settings_success %}
успешно
{% else %}
ошибка
{% endif %}
</div>
<pre class="log-output">{{ settings_output }}</pre>
</div>
{% endif %}
{% if update_output %}
<div class="log-block {% if update_success %}success{% else %}error{% endif %}">
<div class="log-title">Добавление обновления</div>
@@ -510,6 +689,58 @@
</div>
<div id="settingsModal" class="settings-modal" aria-hidden="true">
<div class="settings-modal-card">
<div class="settings-modal-title">Настройки проекта</div>
<div class="settings-modal-subtitle">
Здесь редактируются источники парсинга: ссылки, сезон, тип календаря, папки логотипов и фотографий. Эти данные хранятся в базе данных.
<br>.env оставляем только для NAS и подключения к базе.
</div>
<form method="post" action="/admin/db/project-settings" id="settingsForm">
{% if project_settings and project_settings.groups %}
<div class="env-groups">
{% for group in project_settings.groups %}
<div class="env-group">
<div class="env-group-title">{{ group.title }}</div>
{% if group.subtitle %}
<div class="env-group-subtitle">{{ group.subtitle }}</div>
{% endif %}
<div class="env-grid">
{% for field in group.fields %}
<div class="{% if field.wide %}env-field-wide{% endif %}">
<label class="field-label" for="env_{{ field.key }}">{{ field.label }}</label>
{% if field.type == 'select' %}
<select class="field-select" id="env_{{ field.key }}" name="{{ field.key }}">
{% for option in field.options %}
<option value="{{ option.value }}" {% if option.value == field.value %}selected{% endif %}>{{ option.label }}</option>
{% endfor %}
</select>
{% else %}
<input class="field-input" id="env_{{ field.key }}" type="text" name="{{ field.key }}" value="{{ field.value }}">
{% endif %}
{% if field.help %}
<div class="helper-text">{{ field.help }}</div>
{% endif %}
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="helper-text">Не удалось загрузить список настроек проекта из базы данных.</div>
{% endif %}
<div class="modal-actions">
<button type="button" class="cancel-btn" id="closeSettingsBtn">Отмена</button>
<button type="submit" class="submit-btn">Сохранить настройки</button>
</div>
</form>
</div>
</div>
<div id="updateModal" class="update-modal" aria-hidden="true">
<div class="update-modal-card">
<div class="update-modal-title">Добавить обновление</div>
@@ -617,9 +848,15 @@
const canvas = document.getElementById("matrixCanvas");
const ctx = canvas ? canvas.getContext("2d") : null;
const parserForms = document.querySelectorAll(".action-form");
const parserSourceSelect = document.getElementById("parserSource");
const selectedLogoBasePath = document.getElementById("selectedLogoBasePath");
const selectedPhotoBasePath = document.getElementById("selectedPhotoBasePath");
const openCreateAccountBtn = document.getElementById("openCreateAccountBtn");
const closeCreateAccountBtn = document.getElementById("closeCreateAccountBtn");
const accountModal = document.getElementById("accountModal");
const openSettingsBtn = document.getElementById("openSettingsBtn");
const closeSettingsBtn = document.getElementById("closeSettingsBtn");
const settingsModal = document.getElementById("settingsModal");
const openAddUpdateBtn = document.getElementById("openAddUpdateBtn");
const closeAddUpdateBtn = document.getElementById("closeAddUpdateBtn");
const updateModal = document.getElementById("updateModal");
@@ -695,6 +932,18 @@
accountModal.setAttribute("aria-hidden", "true");
}
function openSettingsModal() {
if (!settingsModal) return;
settingsModal.classList.add("active");
settingsModal.setAttribute("aria-hidden", "false");
}
function closeSettingsModal() {
if (!settingsModal) return;
settingsModal.classList.remove("active");
settingsModal.setAttribute("aria-hidden", "true");
}
function openUpdateModal() {
if (!updateModal) return;
if (updateDate && !updateDate.value) {
@@ -742,6 +991,17 @@
textarea.setSelectionRange(cursorPosition, cursorPosition);
}
function updateSelectedSourcePaths() {
if (!parserSourceSelect) return;
const option = parserSourceSelect.options[parserSourceSelect.selectedIndex];
if (selectedLogoBasePath) {
selectedLogoBasePath.textContent = option?.dataset?.logoBasePath || "—";
}
if (selectedPhotoBasePath) {
selectedPhotoBasePath.textContent = option?.dataset?.photoBasePath || "—";
}
}
document.addEventListener("click", (event) => {
const button = event.target.closest(".format-btn");
if (!button) return;
@@ -757,6 +1017,9 @@
insertSnippet(textarea, snippet);
});
updateSelectedSourcePaths();
parserSourceSelect?.addEventListener("change", updateSelectedSourcePaths);
parserForms.forEach((form) => {
form.addEventListener("submit", () => {
const parserTitle = form.dataset.parserTitle || "Запуск парсера";
@@ -786,6 +1049,22 @@
});
}
if (openSettingsBtn) {
openSettingsBtn.addEventListener("click", openSettingsModal);
}
if (closeSettingsBtn) {
closeSettingsBtn.addEventListener("click", closeSettingsModal);
}
if (settingsModal) {
settingsModal.addEventListener("click", (event) => {
if (event.target === settingsModal) {
closeSettingsModal();
}
});
}
if (openAddUpdateBtn) {
openAddUpdateBtn.addEventListener("click", openUpdateModal);
}
@@ -809,6 +1088,7 @@
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
closeAccountModal();
closeSettingsModal();
closeUpdateModal();
}
});