обновление для Кубка России
переделаны все парсеры на ссылки из базы
This commit is contained in:
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user