Files
remna/app/templates/admin/dashboard.html
T

212 lines
9.0 KiB
HTML

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Админка — {{ brand }}</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,500..800&family=Sora:wght@400;500;600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="/static/css/style.css" />
</head>
<body class="panel-body">
<div class="panel-wrap">
<header class="panel-header">
<div>
<h1>Гостевые ссылки</h1>
<p class="muted">Продление фиксирует трафик тарифа; докупка даёт временные ГБ</p>
</div>
<div class="row-actions">
<a class="btn btn-ghost" href="/">На сайт</a>
<form method="post" action="/admin/logout">
<button class="btn btn-danger" type="submit">Выйти</button>
</form>
</div>
</header>
{% if flash == "created" %}
<div class="flash">Ссылка создана</div>
{% elif flash == "deleted" %}
<div class="flash">Ссылка удалена</div>
{% elif flash == "toggled" %}
<div class="flash">Статус ссылки обновлён</div>
{% elif flash == "imported" %}
<div class="flash">База восстановлена из бэкапа</div>
{% endif %}
{% if flash_error %}
<div class="alert-error">Импорт не удался: {{ flash_error }}</div>
{% endif %}
<section class="backup-box">
<h2 style="font-family:var(--font-display);font-size:1.15rem;margin:0 0 0.5rem;">Бэкап базы</h2>
<p class="muted" style="margin:0 0 1rem;font-size:0.9rem;">
Экспорт / импорт всех ссылок, активаций и временного трафика (JSON).
Импорт <strong>полностью заменяет</strong> текущие данные.
</p>
<div class="row-actions">
<a class="btn btn-signal" href="/admin/backup/export">Скачать бэкап</a>
</div>
<form class="backup-import" method="post" action="/admin/backup/import" enctype="multipart/form-data" onsubmit="return confirm('Импорт заменит ВСЕ текущие ссылки и историю. Продолжить?')">
<label>
Восстановить из файла
<input type="file" name="file" accept=".json,application/json" required />
</label>
<button class="btn btn-ghost" type="submit">Импортировать</button>
</form>
</section>
<h2 style="font-family:var(--font-display);font-size:1.25rem;margin:0 0 1rem;">Создать ссылку</h2>
<form class="form-grid" method="post" action="/admin/links">
<label>
Направление
<select name="link_type" required>
<option value="extend" {% if tab == "extend" %}selected{% endif %}>Продление VPN (дни)</option>
<option value="traffic" {% if tab == "traffic" %}selected{% endif %}>Докупка трафика (ГБ)</option>
</select>
</label>
<label>
Значение
<input type="number" name="value" min="1" value="30" required />
<span class="muted" style="font-size:0.75rem;">Продление = дни · Трафик = ГБ бонуса</span>
</label>
<label>
Трафик тарифа (ГБ)
<input type="number" name="plan_traffic_gb" min="0" value="100" />
<span class="muted" style="font-size:0.75rem;">Только продление: лимит станет ровно столько (и сбросится счётчик)</span>
</label>
<label>
Срок бонус-трафика (дней)
<input type="number" name="duration_days" min="0" value="7" />
<span class="muted" style="font-size:0.75rem;">Только докупка: через N дней бонус-ГБ снимутся</span>
</label>
<label>
Заголовок
<input type="text" name="title" placeholder="Напр. +50 ГБ на 7 дней" maxlength="120" />
</label>
<label>
Макс. активаций
<input type="number" name="max_uses" min="1" value="1" required />
</label>
<label>
Срок жизни ссылки (опционально)
<input type="datetime-local" name="expires_at" />
</label>
<label class="full">
Заметка
<textarea name="note" rows="2" placeholder="Для себя"></textarea>
</label>
<div class="full">
<button class="btn btn-signal" type="submit">Создать гостевую ссылку</button>
</div>
</form>
<div class="admin-tabs">
<a class="admin-tab {% if tab == 'extend' %}is-active{% endif %}" href="/admin?type=extend">
Продление <span class="tab-count">{{ counts.extend }}</span>
</a>
<a class="admin-tab {% if tab == 'traffic' %}is-active{% endif %}" href="/admin?type=traffic">
Трафик <span class="tab-count">{{ counts.traffic }}</span>
</a>
<a class="admin-tab {% if tab == 'all' %}is-active{% endif %}" href="/admin?type=all">
Все <span class="tab-count">{{ counts.all }}</span>
</a>
</div>
<p class="muted list-meta">
Показано {{ links|length }} из {{ total }} · страница {{ page }} / {{ total_pages }}
</p>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>ID</th>
<th>Тип</th>
<th>Заголовок</th>
<th>Значение</th>
<th>Использовано</th>
<th>Ссылка</th>
<th>Статус</th>
<th></th>
</tr>
</thead>
<tbody>
{% for link in links %}
<tr>
<td><span class="id-pill">#{{ link.id }}</span></td>
<td>
{% if link.link_type == "extend" %}
<span class="badge badge-extend">Продление</span>
{% else %}
<span class="badge badge-traffic">Трафик</span>
{% endif %}
</td>
<td>{{ link.title }}</td>
<td>
{% if link.link_type == "extend" %}
+{{ link.value }} дн. / {{ link.plan_traffic_gb or "?" }} ГБ
{% else %}
+{{ link.value }} ГБ / {{ link.duration_days or "?" }} дн.
{% endif %}
</td>
<td>{{ link.used_count }} / {{ link.max_uses }}</td>
<td>
<button
type="button"
class="btn btn-ghost btn-copy"
data-copy="{{ site_base }}/g/{{ link.token }}"
title="{{ site_base }}/g/{{ link.token }}"
>Копировать</button>
</td>
<td>
{% if link.is_active %}
активна
{% else %}
<span class="badge badge-off">выкл</span>
{% endif %}
</td>
<td>
<div class="row-actions">
<a class="btn btn-ghost" href="/admin/links/{{ link.id }}">История</a>
<form method="post" action="/admin/links/{{ link.id }}/toggle?type={{ tab }}&page={{ page }}">
<button class="btn btn-ghost" type="submit">{% if link.is_active %}Выкл{% else %}Вкл{% endif %}</button>
</form>
<form method="post" action="/admin/links/{{ link.id }}/delete?type={{ tab }}&page={{ page }}" onsubmit="return confirm('Удалить ссылку #{{ link.id }}?')">
<button class="btn btn-danger" type="submit">Удалить</button>
</form>
</div>
</td>
</tr>
{% else %}
<tr>
<td colspan="8" class="muted">В этом разделе пока нет ссылок.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if total_pages > 1 %}
<nav class="pager">
{% if page > 1 %}
<a class="btn btn-ghost" href="/admin?type={{ tab }}&page={{ page - 1 }}">← Назад</a>
{% endif %}
{% for p in range(1, total_pages + 1) %}
{% if p == page %}
<span class="pager-current">{{ p }}</span>
{% elif p == 1 or p == total_pages or (p >= page - 2 and p <= page + 2) %}
<a class="btn btn-ghost" href="/admin?type={{ tab }}&page={{ p }}">{{ p }}</a>
{% elif p == page - 3 or p == page + 3 %}
<span class="muted"></span>
{% endif %}
{% endfor %}
{% if page < total_pages %}
<a class="btn btn-ghost" href="/admin?type={{ tab }}&page={{ page + 1 }}">Вперёд →</a>
{% endif %}
</nav>
{% endif %}
</div>
<script src="/static/js/admin.js"></script>
</body>
</html>