Админка стала удобной с телефона
Навигация сворачивается в бургер-меню, второстепенные колонки таблиц скрываются на узких экранах, формы и тулбар редактора нормально складываются в одну колонку. Основные действия и ссылки остаются доступны на ширине 360–390 px.
This commit is contained in:
parent
1c85590f6f
commit
cfa0e29bbf
|
|
@ -96,6 +96,9 @@ ID пользователя ведёт в его карточку. ClickHouse ч
|
|||
код, блок кода, цитата, спойлер и ссылка; есть предпросмотр и счётчик символов
|
||||
(1024 для подписи к фото, 4096 для обычного сообщения).
|
||||
|
||||
Интерфейс адаптирован под мобильные: бургер-меню, второстепенные колонки таблиц
|
||||
скрываются на узких экранах, формы складываются в одну колонку.
|
||||
|
||||
## Аналитика (ClickHouse)
|
||||
|
||||
Все значимые действия бота пишутся в ClickHouse в единую таблицу `analytics.events`.
|
||||
|
|
@ -158,7 +161,7 @@ make backup E=test # на домашнем сервере: ./scripts/clickho
|
|||
|
||||
## Версионирование
|
||||
|
||||
- Единый источник версии — `common/version.py` (`__version__`), текущая — `0.5.2`.
|
||||
- Единый источник версии — `common/version.py` (`__version__`), текущая — `0.5.3`.
|
||||
- Схема — SemVer-подобная (`MAJOR.MINOR.PATCH`); git-тег = `v` + версия (`v0.1`).
|
||||
- Docker-образы тегируются версией (Блок 2), события ClickHouse получают `bot_version` (Блок 3).
|
||||
- Выпуск: изменить `common/version.py` → коммит → аннотированный тег `vX.Y` → `git push --tags`.
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@
|
|||
<th class="px-4 py-3 font-medium">Username</th>
|
||||
<th class="px-4 py-3 font-medium">Роль</th>
|
||||
<th class="px-4 py-3 font-medium">Доступ</th>
|
||||
<th class="px-4 py-3 font-medium">Последний вход</th>
|
||||
<th class="hidden px-4 py-3 font-medium lg:table-cell">Последний вход</th>
|
||||
<th class="px-4 py-3 font-medium"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -116,7 +116,7 @@
|
|||
<span class="rounded bg-slate-100 px-2 py-0.5 text-xs text-slate-500">выключен</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="px-4 py-3 text-slate-600">
|
||||
<td class="hidden px-4 py-3 text-slate-600 lg:table-cell">
|
||||
{{ admin.last_login_at.strftime("%d.%m.%Y %H:%M") if admin.last_login_at else "—" }}
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
|
|
|
|||
|
|
@ -9,26 +9,39 @@
|
|||
</head>
|
||||
<body class="min-h-screen bg-slate-100 text-slate-900">
|
||||
<header class="border-b border-slate-200 bg-white">
|
||||
<div class="mx-auto flex max-w-5xl items-center justify-between px-4 py-4">
|
||||
<div class="mx-auto max-w-5xl px-4 py-3">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<a href="/" class="text-lg font-semibold">DispelEvilBot</a>
|
||||
<nav class="flex items-center gap-4 text-sm">
|
||||
<a href="/" class="text-slate-600 hover:text-slate-900">Главная</a>
|
||||
<a href="/messages" class="text-slate-600 hover:text-slate-900">Сообщения</a>
|
||||
<a href="/sequences" class="text-slate-600 hover:text-slate-900">Последовательности</a>
|
||||
<a href="/schedules" class="text-slate-600 hover:text-slate-900">Расписания</a>
|
||||
<a href="/users" class="text-slate-600 hover:text-slate-900">Пользователи</a>
|
||||
<a href="/admins" class="text-slate-600 hover:text-slate-900">Админы</a>
|
||||
<a href="/logs" class="text-slate-600 hover:text-slate-900">Логи</a>
|
||||
<button
|
||||
id="nav-toggle"
|
||||
type="button"
|
||||
class="rounded-lg border border-slate-300 px-3 py-1.5 text-sm text-slate-600 sm:hidden"
|
||||
aria-expanded="false"
|
||||
>
|
||||
Меню
|
||||
</button>
|
||||
<nav
|
||||
id="nav-menu"
|
||||
class="hidden w-full flex-col gap-2 border-t border-slate-100 pt-2 text-sm sm:flex sm:w-auto sm:flex-row sm:items-center sm:gap-4 sm:border-0 sm:pt-0"
|
||||
>
|
||||
<a href="/" class="py-1 text-slate-600 hover:text-slate-900">Главная</a>
|
||||
<a href="/messages" class="py-1 text-slate-600 hover:text-slate-900">Сообщения</a>
|
||||
<a href="/sequences" class="py-1 text-slate-600 hover:text-slate-900">Последовательности</a>
|
||||
<a href="/schedules" class="py-1 text-slate-600 hover:text-slate-900">Расписания</a>
|
||||
<a href="/users" class="py-1 text-slate-600 hover:text-slate-900">Пользователи</a>
|
||||
<a href="/admins" class="py-1 text-slate-600 hover:text-slate-900">Админы</a>
|
||||
<a href="/logs" class="py-1 text-slate-600 hover:text-slate-900">Логи</a>
|
||||
{% if request.session.get('admin_id') %}
|
||||
<span class="text-slate-500">{{ request.session.get('admin_username') or 'админ' }}</span>
|
||||
<form method="post" action="/logout">
|
||||
<span class="py-1 text-slate-500">{{ request.session.get('admin_username') or 'админ' }}</span>
|
||||
<form method="post" action="/logout" class="py-1">
|
||||
<input type="hidden" name="csrf_token" value="{{ request.session.get('csrf', '') }}" />
|
||||
<button type="submit" class="text-slate-600 hover:text-slate-900">Выйти</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<span class="text-slate-500">админка · v{{ version }}</span>
|
||||
<span class="py-1 text-slate-500">админка · v{{ version }}</span>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="mx-auto max-w-5xl px-4 py-8">
|
||||
|
|
@ -39,6 +52,16 @@
|
|||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
var navToggle = document.getElementById("nav-toggle");
|
||||
var navMenu = document.getElementById("nav-menu");
|
||||
if (navToggle && navMenu) {
|
||||
navToggle.addEventListener("click", function () {
|
||||
var hidden = navMenu.classList.toggle("hidden");
|
||||
navMenu.classList.toggle("flex", !hidden);
|
||||
navToggle.setAttribute("aria-expanded", String(!hidden));
|
||||
});
|
||||
}
|
||||
|
||||
var tags = {
|
||||
b: ["<b>", "</b>"],
|
||||
i: ["<i>", "</i>"],
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@
|
|||
<th class="px-4 py-3 font-medium">Время</th>
|
||||
<th class="px-4 py-3 font-medium">Событие</th>
|
||||
<th class="px-4 py-3 font-medium">Пользователь</th>
|
||||
<th class="px-4 py-3 font-medium">Чат</th>
|
||||
<th class="px-4 py-3 font-medium">Версия</th>
|
||||
<th class="hidden px-4 py-3 font-medium lg:table-cell">Чат</th>
|
||||
<th class="hidden px-4 py-3 font-medium xl:table-cell">Версия</th>
|
||||
<th class="px-4 py-3 font-medium">Payload</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -93,8 +93,8 @@
|
|||
<span class="text-slate-400">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="px-4 py-3 font-mono text-xs text-slate-500">{{ event.chat_id or "—" }}</td>
|
||||
<td class="px-4 py-3 text-xs text-slate-500">{{ event.bot_version }}</td>
|
||||
<td class="hidden px-4 py-3 font-mono text-xs text-slate-500 lg:table-cell">{{ event.chat_id or "—" }}</td>
|
||||
<td class="hidden px-4 py-3 text-xs text-slate-500 xl:table-cell">{{ event.bot_version }}</td>
|
||||
<td class="px-4 py-3">
|
||||
{% if event.payload_pretty %}
|
||||
<details>
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@
|
|||
<tr>
|
||||
<th class="px-4 py-3 font-medium">Ключ</th>
|
||||
<th class="px-4 py-3 font-medium">Описание</th>
|
||||
<th class="px-4 py-3 font-medium">Длина</th>
|
||||
<th class="px-4 py-3 font-medium">Изменён</th>
|
||||
<th class="hidden px-4 py-3 font-medium sm:table-cell">Длина</th>
|
||||
<th class="hidden px-4 py-3 font-medium md:table-cell">Изменён</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
|
|
@ -40,8 +40,8 @@
|
|||
<a href="/messages/{{ template.key }}" class="font-mono text-sky-700 hover:underline">{{ template.key }}</a>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-slate-700">{{ template.description }}</td>
|
||||
<td class="px-4 py-3 text-slate-500">{{ template.text_length }}</td>
|
||||
<td class="px-4 py-3 text-slate-500">{{ template.updated_at.strftime("%d.%m.%Y %H:%M") }}</td>
|
||||
<td class="hidden px-4 py-3 text-slate-500 sm:table-cell">{{ template.text_length }}</td>
|
||||
<td class="hidden px-4 py-3 text-slate-500 md:table-cell">{{ template.updated_at.strftime("%d.%m.%Y %H:%M") }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@
|
|||
<thead class="bg-slate-50 text-left text-slate-600">
|
||||
<tr>
|
||||
<th class="px-4 py-3 font-medium">Время</th>
|
||||
<th class="px-4 py-3 font-medium">Дни</th>
|
||||
<th class="hidden px-4 py-3 font-medium sm:table-cell">Дни</th>
|
||||
<th class="px-4 py-3 font-medium">Шаблон</th>
|
||||
<th class="px-4 py-3 font-medium">Таймзона</th>
|
||||
<th class="hidden px-4 py-3 font-medium lg:table-cell">Таймзона</th>
|
||||
<th class="px-4 py-3 font-medium">Статус</th>
|
||||
<th class="px-4 py-3 font-medium"></th>
|
||||
</tr>
|
||||
|
|
@ -31,12 +31,12 @@
|
|||
{% for schedule in schedules %}
|
||||
<tr class="hover:bg-slate-50">
|
||||
<td class="px-4 py-3 font-mono">{{ "%02d:%02d" | format(schedule.hour, schedule.minute) }}</td>
|
||||
<td class="px-4 py-3">{{ format_weekdays(schedule.weekdays) }}</td>
|
||||
<td class="hidden px-4 py-3 sm:table-cell">{{ format_weekdays(schedule.weekdays) }}</td>
|
||||
<td class="px-4 py-3">
|
||||
<div class="font-mono text-sky-700">{{ schedule.template_key }}</div>
|
||||
<div class="text-xs text-slate-500">{{ schedule.description or schedule.template_description }}</div>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-slate-600">{{ schedule.timezone }}</td>
|
||||
<td class="hidden px-4 py-3 text-slate-600 lg:table-cell">{{ schedule.timezone }}</td>
|
||||
<td class="px-4 py-3">
|
||||
{% if schedule.enabled %}
|
||||
<span class="rounded bg-emerald-100 px-2 py-0.5 text-xs text-emerald-800">включено</span>
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@
|
|||
<tr>
|
||||
<th class="px-4 py-3 font-medium">Ник</th>
|
||||
<th class="px-4 py-3 font-medium">Telegram</th>
|
||||
<th class="px-4 py-3 font-medium">Уровень</th>
|
||||
<th class="hidden px-4 py-3 font-medium sm:table-cell">Уровень</th>
|
||||
<th class="px-4 py-3 font-medium">Баланс</th>
|
||||
<th class="px-4 py-3 font-medium">Сообщения</th>
|
||||
<th class="hidden px-4 py-3 font-medium md:table-cell">Сообщения</th>
|
||||
<th class="px-4 py-3 font-medium">Подписка</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -54,9 +54,9 @@
|
|||
{% if user.username %}<div>@{{ user.username }}</div>{% endif %}
|
||||
<div class="font-mono text-xs text-slate-500">{{ user.id_user }}</div>
|
||||
</td>
|
||||
<td class="px-4 py-3">{{ user.lvl }}</td>
|
||||
<td class="hidden px-4 py-3 sm:table-cell">{{ user.lvl }}</td>
|
||||
<td class="px-4 py-3">{{ user.balance }}</td>
|
||||
<td class="px-4 py-3">{{ user.msg_count }}</td>
|
||||
<td class="hidden px-4 py-3 md:table-cell">{{ user.msg_count }}</td>
|
||||
<td class="px-4 py-3">
|
||||
{% if user.subscribed %}
|
||||
<span class="rounded bg-emerald-100 px-2 py-0.5 text-xs text-emerald-800">да</span>
|
||||
|
|
|
|||
|
|
@ -7,4 +7,4 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
__version__ = "0.5.2"
|
||||
__version__ = "0.5.3"
|
||||
|
|
|
|||
|
|
@ -7,4 +7,4 @@ from common import __version__
|
|||
|
||||
def test_version_is_set():
|
||||
assert __version__
|
||||
assert __version__ == "0.5.2"
|
||||
assert __version__ == "0.5.3"
|
||||
|
|
|
|||
Loading…
Reference in New Issue