Files
WFL/templates/admin_db_index.html

505 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="ru">
<head>
<meta charset="UTF-8">
<title>База</title>
<style>
:root {
--bg: #0f1115;
--panel: #171a21;
--panel-2: #1d222b;
--border: #2b3240;
--text: #e8ecf3;
--muted: #9aa4b2;
--accent: #00ff88;
--accent-hover: #00d976;
--success-bg: rgba(46, 204, 113, 0.12);
--success-border: rgba(46, 204, 113, 0.35);
--danger-bg: rgba(255, 99, 99, 0.12);
--danger-border: rgba(255, 99, 99, 0.35);
--shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
--radius: 16px;
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--bg);
color: var(--text);
font-family: Arial, sans-serif;
}
.page {
max-width: 1200px;
margin: 0 auto;
padding: 24px;
}
.panel {
background: var(--panel);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 20px;
}
.title {
font-size: 24px;
font-weight: 700;
margin-bottom: 18px;
}
.tabs,
.parser-actions,
.account-actions {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.parser-actions,
.account-actions {
margin-top: 18px;
padding-top: 18px;
border-top: 1px solid var(--border);
}
.tab-link,
.action-btn,
.account-toggle-btn,
.submit-btn,
.cancel-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 42px;
padding: 0 16px;
border-radius: 999px;
background: var(--panel-2);
border: 1px solid var(--border);
color: var(--text);
text-decoration: none;
font-weight: 600;
cursor: pointer;
font-size: 14px;
transition: 0.2s ease;
}
.tab-link:hover,
.action-btn:hover,
.account-toggle-btn:hover,
.submit-btn:hover {
background: var(--accent);
border-color: var(--accent);
color: #04110b;
}
.cancel-btn:hover {
background: #293140;
border-color: #3b4658;
}
.action-form,
.account-create-form {
margin: 0;
}
.log-block {
margin-top: 20px;
border: 1px solid var(--border);
border-radius: 14px;
padding: 16px;
background: var(--panel-2);
}
.log-block.success {
background: var(--success-bg);
border-color: var(--success-border);
}
.log-block.error {
background: var(--danger-bg);
border-color: var(--danger-border);
}
.log-title {
font-size: 18px;
font-weight: 700;
margin-bottom: 10px;
}
.log-status {
color: var(--muted);
margin-bottom: 10px;
}
.log-output {
margin: 0;
white-space: pre-wrap;
word-break: break-word;
font-family: Consolas, Monaco, monospace;
font-size: 13px;
line-height: 1.5;
}
.action-form.is-loading .action-btn {
opacity: 0.75;
pointer-events: none;
}
.account-modal {
position: fixed;
inset: 0;
display: none;
align-items: center;
justify-content: center;
background: rgba(3, 8, 15, 0.84);
z-index: 9998;
padding: 20px;
}
.account-modal.active {
display: flex;
}
.account-modal-card {
width: min(100%, 460px);
background: rgba(12, 17, 26, 0.98);
border: 1px solid rgba(0, 255, 136, 0.28);
border-radius: 18px;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.45);
padding: 24px;
}
.account-modal-title {
font-size: 22px;
font-weight: 700;
margin-bottom: 8px;
}
.account-modal-subtitle {
color: var(--muted);
margin-bottom: 18px;
line-height: 1.5;
}
.form-grid {
display: grid;
gap: 14px;
}
.field-label {
display: block;
font-size: 13px;
font-weight: 700;
color: var(--muted);
margin-bottom: 6px;
}
.field-input,
.field-select {
width: 100%;
min-height: 44px;
border-radius: 12px;
border: 1px solid var(--border);
background: #0f141d;
color: var(--text);
padding: 0 14px;
font-size: 14px;
outline: none;
}
.field-input:focus,
.field-select:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(0, 255, 136, 0.12);
}
.modal-actions {
display: flex;
gap: 12px;
justify-content: flex-end;
margin-top: 18px;
flex-wrap: wrap;
}
.matrix-loader {
position: fixed;
inset: 0;
display: none;
align-items: center;
justify-content: center;
background: rgba(3, 8, 15, 0.94);
z-index: 9999;
overflow: hidden;
}
.matrix-loader.active {
display: flex;
}
.matrix-canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.matrix-content {
position: relative;
z-index: 1;
width: min(90vw, 560px);
padding: 28px 24px;
border-radius: 18px;
border: 1px solid rgba(0, 255, 136, 0.35);
background: rgba(12, 17, 26, 0.88);
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.45);
text-align: center;
backdrop-filter: blur(6px);
}
.matrix-title {
font-size: 28px;
font-weight: 700;
color: #dcffef;
margin-bottom: 12px;
}
.matrix-status {
font-size: 16px;
color: #9cf3c8;
line-height: 1.6;
}
</style>
</head>
<body>
<div class="page">
<div class="panel">
<div class="title">Редактирование базы</div>
<div class="tabs">
<a class="tab-link" href="/admin/db/players">Игроки</a>
<a class="tab-link" href="/admin/db/referees">Судьи</a>
<a class="tab-link" href="/admin/db/teams">Команды</a>
<a class="tab-link" href="/admin/db/coaches">Тренеры</a>
<a class="tab-link" href="/admin/db/stadiums">Стадионы</a>
<a class="tab-link" href="/admin/matches">Назад к матчам</a>
</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" data-parser-title="Расписание" data-parser-status="Загрузка расписания матчей с сайта...">
<input type="hidden" name="parser_name" value="schedule">
<button type="submit" class="action-btn">🗄️ Заграбить расписание</button>
</form>
<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>
</form>
</div>
<div class="account-actions">
<button type="button" class="account-toggle-btn" id="openCreateAccountBtn"> Создать аккаунт</button>
</div>
{% if account_message %}
<div class="log-block {% if account_success %}success{% else %}error{% endif %}">
<div class="log-title">Создание аккаунта</div>
<div class="log-status">
Статус:
{% if account_success %}
успешно
{% else %}
ошибка
{% endif %}
</div>
<pre class="log-output">{{ account_message }}</pre>
</div>
{% endif %}
{% if parser_output %}
<div class="log-block {% if parser_success %}success{% else %}error{% endif %}">
<div class="log-title">Итог парсинга{% if parser_name %}: {{ parser_name }}{% endif %}</div>
<div class="log-status">
Статус:
{% if parser_success %}
успешно
{% else %}
ошибка
{% endif %}
</div>
<pre class="log-output">{{ parser_output }}</pre>
</div>
{% endif %}
</div>
</div>
<div id="accountModal" class="account-modal" aria-hidden="true">
<div class="account-modal-card">
<div class="account-modal-title">Создать аккаунт</div>
<div class="account-modal-subtitle">Заполните данные нового пользователя и выберите роль.</div>
<form method="post" action="/admin/db/create-account" class="account-create-form" id="createAccountForm">
<div class="form-grid">
<div>
<label class="field-label" for="accountLogin">Логин</label>
<input class="field-input" id="accountLogin" type="text" name="login" required>
</div>
<div>
<label class="field-label" for="accountPassword">Пароль</label>
<input class="field-input" id="accountPassword" type="password" name="password" required>
</div>
<div>
<label class="field-label" for="accountRole">Роль</label>
<select class="field-select" id="accountRole" name="role" required>
<option value="admin">admin</option>
<option value="operator">operator</option>
</select>
</div>
</div>
<div class="modal-actions">
<button type="button" class="cancel-btn" id="closeCreateAccountBtn">Отмена</button>
<button type="submit" class="submit-btn">Создать</button>
</div>
</form>
</div>
</div>
<div id="matrixLoader" class="matrix-loader" aria-hidden="true">
<canvas id="matrixCanvas" class="matrix-canvas"></canvas>
<div class="matrix-content">
<div class="matrix-title" id="matrixTitle">Запуск парсера</div>
<div class="matrix-status" id="matrixStatusText">Подготовка...</div>
</div>
</div>
<script>
const loader = document.getElementById("matrixLoader");
const titleEl = document.getElementById("matrixTitle");
const statusText = document.getElementById("matrixStatusText");
const canvas = document.getElementById("matrixCanvas");
const ctx = canvas ? canvas.getContext("2d") : null;
const parserForms = document.querySelectorAll(".action-form");
const openCreateAccountBtn = document.getElementById("openCreateAccountBtn");
const closeCreateAccountBtn = document.getElementById("closeCreateAccountBtn");
const accountModal = document.getElementById("accountModal");
let drops = [];
let fontSize = 18;
let columns = 0;
let matrixAnimationId = null;
let lastFrameTime = 0;
const frameInterval = 70;
const chars = "01アイウエオカキクケコサシスセソタチツテトABCDEFGHIJKLMNOPQRSTUVWXYZ";
function resizeCanvas() {
if (!canvas || !ctx) return;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
columns = Math.max(1, Math.floor(canvas.width / fontSize));
drops = Array.from({ length: columns }, () => Math.random() * canvas.height / fontSize);
}
function drawMatrix(timestamp = 0) {
if (!canvas || !ctx) return;
if (timestamp - lastFrameTime < frameInterval) {
matrixAnimationId = requestAnimationFrame(drawMatrix);
return;
}
lastFrameTime = timestamp;
ctx.fillStyle = "rgba(3, 8, 15, 0.08)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#00ff88";
ctx.font = `${fontSize}px monospace`;
for (let i = 0; i < drops.length; i++) {
const text = chars[Math.floor(Math.random() * chars.length)];
const x = i * fontSize;
const y = drops[i] * fontSize;
ctx.fillText(text, x, y);
if (y > canvas.height && Math.random() > 0.985) {
drops[i] = 0;
}
drops[i] += 0.65;
}
matrixAnimationId = requestAnimationFrame(drawMatrix);
}
function startLoader(title, message) {
if (!loader) return;
titleEl.textContent = title;
statusText.textContent = message;
loader.classList.add("active");
loader.setAttribute("aria-hidden", "false");
if (!matrixAnimationId) {
resizeCanvas();
lastFrameTime = 0;
drawMatrix();
}
}
function openAccountModal() {
if (!accountModal) return;
accountModal.classList.add("active");
accountModal.setAttribute("aria-hidden", "false");
}
function closeAccountModal() {
if (!accountModal) return;
accountModal.classList.remove("active");
accountModal.setAttribute("aria-hidden", "true");
}
parserForms.forEach((form) => {
form.addEventListener("submit", () => {
const parserTitle = form.dataset.parserTitle || "Запуск парсера";
const parserStatus = form.dataset.parserStatus || "Обработка данных...";
document.querySelectorAll(".action-btn").forEach((btn) => {
btn.disabled = true;
});
form.classList.add("is-loading");
startLoader(`Парсинг: ${parserTitle}`, parserStatus);
});
});
if (openCreateAccountBtn) {
openCreateAccountBtn.addEventListener("click", openAccountModal);
}
if (closeCreateAccountBtn) {
closeCreateAccountBtn.addEventListener("click", closeAccountModal);
}
if (accountModal) {
accountModal.addEventListener("click", (event) => {
if (event.target === accountModal) {
closeAccountModal();
}
});
}
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
closeAccountModal();
}
});
resizeCanvas();
window.addEventListener("resize", resizeCanvas);
</script>
</body>
</html>