182 lines
8.0 KiB
HTML
182 lines
8.0 KiB
HTML
<!doctype html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>{% block title %}Админка{% endblock %} — DispelEvilBot</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<script src="https://unpkg.com/htmx.org@2.0.4" defer></script>
|
|
</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 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>
|
|
<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-1 sm:border-0 sm:pt-0"
|
|
>
|
|
<a href="/" class="rounded px-2 py-1.5 text-slate-600 hover:bg-slate-100 hover:text-slate-900">Главная</a>
|
|
|
|
<div class="relative" data-group>
|
|
<button
|
|
type="button"
|
|
class="flex w-full items-center justify-between gap-1 rounded px-2 py-1.5 text-slate-600 hover:bg-slate-100 hover:text-slate-900 sm:w-auto"
|
|
data-group-toggle
|
|
aria-expanded="false"
|
|
>
|
|
Сообщения <span class="text-xs">▾</span>
|
|
</button>
|
|
<div
|
|
class="mt-1 hidden flex-col gap-1 rounded-lg border border-slate-200 bg-white p-2 sm:absolute sm:left-0 sm:top-full sm:z-20 sm:mt-1 sm:min-w-48 sm:shadow-lg"
|
|
data-group-dropdown
|
|
>
|
|
<a href="/sequences" class="rounded px-3 py-1.5 text-slate-600 hover:bg-slate-100 hover:text-slate-900">Шаблоны</a>
|
|
<a href="/schedules" class="rounded px-3 py-1.5 text-slate-600 hover:bg-slate-100 hover:text-slate-900">Расписания</a>
|
|
<a href="/send" class="rounded px-3 py-1.5 text-slate-600 hover:bg-slate-100 hover:text-slate-900">Отправить</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="relative" data-group>
|
|
<button
|
|
type="button"
|
|
class="flex w-full items-center justify-between gap-1 rounded px-2 py-1.5 text-slate-600 hover:bg-slate-100 hover:text-slate-900 sm:w-auto"
|
|
data-group-toggle
|
|
aria-expanded="false"
|
|
>
|
|
Пользователи <span class="text-xs">▾</span>
|
|
</button>
|
|
<div
|
|
class="mt-1 hidden flex-col gap-1 rounded-lg border border-slate-200 bg-white p-2 sm:absolute sm:left-0 sm:top-full sm:z-20 sm:mt-1 sm:min-w-48 sm:shadow-lg"
|
|
data-group-dropdown
|
|
>
|
|
<a href="/users" class="rounded px-3 py-1.5 text-slate-600 hover:bg-slate-100 hover:text-slate-900">Список</a>
|
|
<a href="/admins" class="rounded px-3 py-1.5 text-slate-600 hover:bg-slate-100 hover:text-slate-900">Админы</a>
|
|
<a href="/titles" class="rounded px-3 py-1.5 text-slate-600 hover:bg-slate-100 hover:text-slate-900">Звания</a>
|
|
</div>
|
|
</div>
|
|
|
|
<a href="/logs" class="rounded px-2 py-1.5 text-slate-600 hover:bg-slate-100 hover:text-slate-900">Логи</a>
|
|
|
|
{% if request.session.get('admin_id') %}
|
|
<form method="post" action="/logout" class="py-1 sm:ml-1">
|
|
<input type="hidden" name="csrf_token" value="{{ request.session.get('csrf', '') }}" />
|
|
<button type="submit" class="rounded px-2 py-1.5 text-slate-600 hover:bg-slate-100 hover:text-slate-900">
|
|
Выйти
|
|
</button>
|
|
</form>
|
|
{% endif %}
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main class="mx-auto max-w-5xl px-4 py-8">
|
|
{% block content %}{% endblock %}
|
|
</main>
|
|
|
|
<footer class="mx-auto max-w-5xl px-4 pb-8 text-xs text-slate-400">DispelEvilBot v{{ version }}</footer>
|
|
|
|
<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 groups = document.querySelectorAll("[data-group]");
|
|
groups.forEach(function (group) {
|
|
var toggle = group.querySelector("[data-group-toggle]");
|
|
var dropdown = group.querySelector("[data-group-dropdown]");
|
|
if (!toggle || !dropdown) return;
|
|
|
|
function setOpen(open) {
|
|
dropdown.classList.toggle("hidden", !open);
|
|
toggle.setAttribute("aria-expanded", String(open));
|
|
}
|
|
|
|
toggle.addEventListener("click", function (event) {
|
|
event.stopPropagation();
|
|
setOpen(dropdown.classList.contains("hidden"));
|
|
});
|
|
|
|
if (window.matchMedia("(min-width: 640px)").matches) {
|
|
group.addEventListener("mouseenter", function () { setOpen(true); });
|
|
group.addEventListener("mouseleave", function () { setOpen(false); });
|
|
}
|
|
});
|
|
|
|
document.addEventListener("click", function () {
|
|
groups.forEach(function (group) {
|
|
var dropdown = group.querySelector("[data-group-dropdown]");
|
|
if (dropdown) dropdown.classList.add("hidden");
|
|
});
|
|
});
|
|
|
|
var tags = {
|
|
b: ["<b>", "</b>"],
|
|
i: ["<i>", "</i>"],
|
|
u: ["<u>", "</u>"],
|
|
s: ["<s>", "</s>"],
|
|
code: ["<code>", "</code>"],
|
|
pre: ["<pre>", "</pre>"],
|
|
quote: ["<blockquote>", "</blockquote>"],
|
|
spoiler: ["<tg-spoiler>", "</tg-spoiler>"],
|
|
a: ['<a href="">', "</a>"],
|
|
};
|
|
|
|
document.querySelectorAll("[data-editor]").forEach(function (root) {
|
|
var textarea = root.querySelector("textarea");
|
|
var counter = root.querySelector("[data-counter]");
|
|
var max = parseInt(root.dataset.max || "0", 10);
|
|
|
|
function updateCounter() {
|
|
if (counter) counter.textContent = textarea.value.length + (max ? " / " + max : "");
|
|
}
|
|
textarea.addEventListener("input", updateCounter);
|
|
updateCounter();
|
|
|
|
root.querySelectorAll("button[data-tag]").forEach(function (button) {
|
|
button.addEventListener("click", function () {
|
|
var pair = tags[button.dataset.tag];
|
|
if (!pair) return;
|
|
var start = textarea.selectionStart;
|
|
var end = textarea.selectionEnd;
|
|
var selected = textarea.value.slice(start, end);
|
|
var inserted = pair[0] + selected + pair[1];
|
|
textarea.value = textarea.value.slice(0, start) + inserted + textarea.value.slice(end);
|
|
textarea.focus();
|
|
textarea.selectionStart = start + pair[0].length;
|
|
textarea.selectionEnd = start + pair[0].length + selected.length;
|
|
updateCounter();
|
|
});
|
|
});
|
|
|
|
textarea.addEventListener("keydown", function (event) {
|
|
if (!(event.ctrlKey || event.metaKey)) return;
|
|
var key = event.key.toLowerCase();
|
|
var tag = key === "b" ? "b" : key === "i" ? "i" : key === "u" ? "u" : null;
|
|
if (!tag) return;
|
|
event.preventDefault();
|
|
var button = root.querySelector('button[data-tag="' + tag + '"]');
|
|
if (button) button.click();
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|