feat: complete UI migration to Tailwind CSS + HTMX

This commit is contained in:
Your Name
2026-07-18 22:20:40 +05:00
parent baf0e858aa
commit 5220c4051b
19 changed files with 1497 additions and 1501 deletions
+54 -235
View File
@@ -1,246 +1,65 @@
<!doctype html>
<html lang="ru">
<html lang="ru" data-theme="light">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="/favi.png">
<title>{{ title or "VK Parser Admin" }}</title>
<!-- Tailwind CSS & DaisyUI -->
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.10.1/dist/full.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.tailwindcss.com"></script>
<!-- HTMX -->
<script src="https://unpkg.com/htmx.org@1.9.11"></script>
<!-- AlpineJS (optional but useful for local state) -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.13.8/dist/cdn.min.js"></script>
<style>
:root { color-scheme: light; --bg:#f4f6f8; --panel:#fff; --panel-soft:#f8fafc; --line:#d8dee8; --line-strong:#c8d0dc; --text:#20242b; --muted:#647086; --accent:#245fd6; --accent-soft:#eef4ff; --danger:#b42318; --ok:#027a48; --shadow:0 1px 2px rgba(16,24,40,.04), 0 10px 30px rgba(16,24,40,.06); }
* { box-sizing: border-box; }
body { margin:0; font:14px/1.45 Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif; color:var(--text); background:var(--bg); }
header { position:sticky; top:0; z-index:40; min-height:58px; display:flex; align-items:center; justify-content:space-between; gap:16px; padding:0 24px; background:rgba(255,255,255,.92); border-bottom:1px solid var(--line); backdrop-filter:blur(12px); }
nav { display:flex; gap:4px; align-items:center; flex-wrap:wrap; }
nav a { color:var(--muted); text-decoration:none; font-weight:700; padding:8px 10px; border-radius:7px; }
nav a:hover { color:var(--text); background:var(--panel-soft); }
main { padding:26px 24px 40px; max-width:1440px; margin:0 auto; }
h1 { font-size:34px; line-height:1.1; margin:0 0 8px; letter-spacing:0; }
h2 { font-size:21px; line-height:1.2; margin:0 0 8px; letter-spacing:0; }
.panel { background:var(--panel); border:1px solid var(--line); border-radius:8px; padding:16px; margin-bottom:16px; box-shadow:var(--shadow); }
details.panel summary { cursor:pointer; list-style:none; font-size:16px; margin:-4px 0 12px; }
details.panel summary::-webkit-details-marker { display:none; }
details.panel summary:before { content:"▸"; display:inline-block; width:18px; color:var(--muted); }
details.panel[open] summary:before { content:"▾"; }
.toolbar { display:flex; gap:10px; align-items:end; justify-content:space-between; flex-wrap:wrap; margin-bottom:14px; }
.row { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
table { width:100%; border-collapse:separate; border-spacing:0; background:var(--panel); }
th, td { padding:10px; border-bottom:1px solid var(--line); text-align:left; vertical-align:top; }
th { color:var(--muted); font-size:12px; text-transform:uppercase; }
input, select, textarea { width:100%; padding:9px 10px; border:1px solid var(--line); border-radius:7px; font:inherit; background:#fff; color:var(--text); outline:none; }
input:focus, select:focus, textarea:focus { border-color:var(--accent); box-shadow:0 0 0 3px rgba(36,95,214,.12); }
label { display:block; color:var(--muted); font-weight:600; margin-bottom:8px; }
label span { display:block; margin-bottom:4px; }
.form-grid { display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:14px; }
.filter-grid { display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)); gap:12px; align-items:end; }
.sources-filter { grid-template-columns:minmax(260px, 1fr) 160px 140px auto; }
.filter-submit { height:38px; margin-bottom:0; }
.btn { display:inline-flex; align-items:center; justify-content:center; gap:6px; min-height:36px; padding:8px 12px; border:1px solid var(--line); border-radius:7px; background:#fff; color:var(--text); text-decoration:none; cursor:pointer; font-weight:700; white-space:nowrap; }
.btn:hover { border-color:var(--line-strong); background:var(--panel-soft); }
.btn.primary { background:var(--accent); border-color:var(--accent); color:#fff; }
.btn.danger { color:var(--danger); }
.pill { display:inline-flex; padding:3px 8px; border-radius:999px; border:1px solid var(--line); color:var(--muted); font-size:12px; }
.pill.ok { color:var(--ok); border-color:#a6f4c5; background:#ecfdf3; }
.pill.bad { color:var(--danger); border-color:#fecdca; background:#fef3f2; }
.pill.warn { color:#92400e; border-color:#fedf89; background:#fffaeb; }
.muted { color:var(--muted); }
.mono { font-family: Consolas, monospace; }
.actions { display:flex; gap:6px; flex-wrap:wrap; }
.compact-table th, .compact-table td { padding:7px 8px; }
.source-state { margin-left:6px; }
.source-url { max-width:300px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.status-msg { max-width:220px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.icon-actions { display:flex; gap:4px; align-items:center; justify-content:flex-end; white-space:nowrap; }
.icon-actions form { margin:0; }
.icon-btn { width:30px; height:30px; display:inline-flex; align-items:center; justify-content:center; border:1px solid var(--line); border-radius:6px; background:#fff; color:var(--text); text-decoration:none; cursor:pointer; font:18px/1 Arial, sans-serif; }
.icon-btn:hover { border-color:#b9c0cc; background:#f8fafc; }
.icon-btn.danger { color:var(--danger); }
.source-add-grid { display:grid; grid-template-columns:minmax(260px, 360px) minmax(360px, 1fr); gap:18px; align-items:start; }
.checkline { display:flex; gap:8px; align-items:center; color:var(--text); font-weight:600; }
.checkline input { width:auto; }
.preview-block { margin-top:16px; border-top:1px solid var(--line); padding-top:14px; }
.pagination { display:flex; justify-content:flex-end; margin-top:12px; }
.pagination-form { display:flex; gap:8px; align-items:center; }
.pagination button[disabled] { opacity:.45; cursor:not-allowed; }
.raw-id { color:var(--text); font-weight:700; text-decoration:none; }
.source-cell { min-width:150px; }
.source-cell a { font-weight:700; }
.stage-cell { min-width:112px; }
.raw-table { table-layout:fixed; }
.raw-table th:nth-child(1), .raw-table td:nth-child(1) { width:82px; }
.raw-table th:nth-child(2), .raw-table td:nth-child(2) { width:170px; }
.raw-table th:nth-child(3), .raw-table td:nth-child(3) { width:120px; }
.raw-table th:nth-child(5), .raw-table td:nth-child(5) { width:96px; }
.raw-content-cell { max-width:none; min-width:0; overflow-wrap:anywhere; }
.raw-text-details { margin-top:8px; }
.raw-text-details summary { cursor:pointer; color:var(--text); white-space:pre-wrap; }
.raw-full-text { margin-top:8px; white-space:pre-wrap; overflow-wrap:anywhere; }
.raw-text-details summary { overflow-wrap:anywhere; }
.raw-content-cell > .raw-full-text:first-child,
.media-strip + .raw-full-text { margin-top:8px; }
.media-strip { display:flex; gap:6px; max-width:100%; overflow-x:auto; padding:1px 0 5px; margin-bottom:2px; }
.media-thumb { flex:0 0 48px; width:48px; height:48px; padding:0; border:1px solid var(--line); border-radius:6px; overflow:hidden; display:flex; align-items:center; justify-content:center; background:#eef1f6; color:var(--text); text-decoration:none; cursor:pointer; }
.media-thumb img { width:100%; height:100%; object-fit:cover; display:block; }
.media-video span { font-size:18px; line-height:1; }
.media-count { font-size:12px; margin-bottom:4px; }
.raw-meta { display:grid; grid-template-columns:1fr; gap:8px; min-width:130px; }
.raw-links { display:flex; flex-direction:column; gap:3px; }
.raw-dates { display:grid; gap:2px; color:var(--text); }
.post-meta { margin-top:8px; color:var(--muted); font-size:12px; }
.ai-cell { width:96px; }
.ai-badge { width:82px; min-height:58px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2px; border:1px solid var(--line); border-radius:8px; color:var(--muted); text-decoration:none; background:#f8fafc; }
.ai-badge span { color:var(--text); font-size:18px; line-height:1; font-weight:800; }
.ai-badge small { max-width:72px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:11px; }
.ai-badge.ok { border-color:#a6f4c5; background:#ecfdf3; color:var(--ok); }
.ai-badge.ok span { color:var(--ok); }
.ai-badge.warn { border-color:#fedf89; background:#fffaeb; color:#92400e; }
.ai-badge.warn span { color:#92400e; }
.ai-badge.bad { border-color:#fecdca; background:#fef3f2; color:var(--danger); }
.ai-badge.bad span { color:var(--danger); }
.ai-model-decision { width:92px; margin-top:5px; font-size:12px; overflow:hidden; text-overflow:ellipsis; }
.detail-grid { display:grid; grid-template-columns:minmax(0, 1fr) 320px; gap:16px; align-items:start; }
.side-panel { position:sticky; top:72px; }
.detail-media { padding-bottom:8px; }
.detail-thumb { flex-basis:72px; width:72px; height:72px; }
.editable-media { position:relative; flex:0 0 auto; }
.editable-media.marked-delete { opacity:.38; filter:grayscale(1); }
.media-remove { position:absolute; top:-6px; right:-6px; width:22px; height:22px; border:1px solid var(--line); border-radius:999px; background:#fff; color:var(--danger); font-weight:900; cursor:pointer; box-shadow:var(--shadow); }
.media-upload-form { display:block; margin:8px 0 8px; }
.detail-text { font-size:15px; line-height:1.55; }
.detail-ai { width:110px; min-height:72px; margin:8px 0 16px; }
.rewrite-text { white-space:pre-wrap; font-size:16px; line-height:1.55; max-width:820px; }
.rewrite-meta { margin-top:14px; }
.kv { display:grid; grid-template-columns:96px minmax(0,1fr); gap:8px 12px; margin:0; }
.kv dt { color:var(--muted); font-weight:700; }
.kv dd { margin:0; overflow-wrap:anywhere; }
.kv-wide { grid-template-columns:180px minmax(0,1fr); margin-bottom:14px; }
.json-box { white-space:pre-wrap; overflow:auto; max-height:420px; padding:12px; border:1px solid var(--line); border-radius:8px; background:#0f172a; color:#e2e8f0; }
.prompt-editor { display:grid; gap:8px; }
.prompt-hint { max-width:720px; border:1px solid var(--line); border-radius:8px; padding:10px 12px; background:#f8fafc; display:grid; gap:8px; color:var(--text); }
.prompt-hint summary { cursor:pointer; font-weight:700; }
.prompt-hint pre, .prompt-box { white-space:pre-wrap; overflow:auto; padding:10px; border:1px solid var(--line); border-radius:8px; background:#f8fafc; color:var(--text); }
.prompt-test-layout { display:grid; grid-template-columns:minmax(0, 1fr) 420px; gap:16px; align-items:start; }
.prompt-test-controls { display:grid; grid-template-columns:150px minmax(260px, 1fr) minmax(220px, .7fr); gap:12px; align-items:end; }
.prompt-test-main { margin-bottom:0; }
.prompt-test-side { display:grid; gap:16px; position:sticky; top:74px; max-height:calc(100vh - 92px); overflow:auto; }
.prompt-test-model { min-height:38px; display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.prompt-test-post { margin:12px 0 14px; border:1px solid var(--line); border-radius:8px; padding:12px; background:var(--panel-soft); }
.prompt-test-post-head { display:flex; justify-content:space-between; gap:12px; align-items:center; margin-bottom:4px; }
.prompt-test-post pre { max-height:260px; overflow:auto; margin:10px 0 0; white-space:pre-wrap; font:inherit; color:var(--text); }
.prompt-test-help p { margin:0; }
.human-answer { white-space:pre-wrap; overflow:auto; max-height:360px; padding:12px; border:1px solid #b7caf8; border-radius:8px; background:#f8fbff; color:var(--text); font:16px/1.55 Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif; }
.hint-grid { display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:10px; }
.hint-label { color:var(--muted); font-size:12px; font-weight:700; text-transform:uppercase; margin-bottom:3px; }
.small-help { font-size:12px; margin-top:4px; }
.secret-editor { display:grid; gap:3px; }
.secret-visible { font-family:Consolas, monospace; }
.category-create { display:grid; grid-template-columns:minmax(180px, 1fr) minmax(180px, 1fr) auto; gap:10px; align-items:end; margin:10px 0 14px; }
.category-create label { margin-bottom:0; }
.category-table input { min-height:34px; padding:7px 8px; }
.category-table th:nth-child(3), .category-table td:nth-child(3) { width:96px; }
.category-table th:nth-child(4), .category-table td:nth-child(4) { width:110px; }
.category-table th:nth-child(5), .category-table td:nth-child(5) { width:120px; }
.advanced-tools { grid-column:1 / -1; border-top:1px solid var(--line); margin-top:4px; padding-top:10px; }
.advanced-tools summary { cursor:pointer; font-weight:700; color:var(--text); }
.advanced-grid { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:16px; margin-top:10px; }
.tool-title { font-weight:700; margin-bottom:6px; }
.tool-row { display:grid; grid-template-columns:minmax(130px, 1.1fr) 120px minmax(130px, 1fr); gap:8px; margin-bottom:8px; }
.tool-row.sort-row { grid-template-columns:minmax(170px, 1fr) 150px; }
.nested-details { margin:12px 0; }
.nested-details summary { cursor:pointer; font-weight:700; }
.gallery-modal { position:fixed; inset:0; z-index:5000; display:none; align-items:center; justify-content:center; padding:48px; background:rgba(15,18,25,.86); border:0; width:100vw; height:100vh; max-width:none; max-height:none; }
.gallery-modal::backdrop { background:rgba(15,18,25,.86); }
.gallery-modal.open { display:flex; }
.gallery-modal img { max-width:calc(100vw - 130px); max-height:calc(100vh - 120px); object-fit:contain; border-radius:8px; box-shadow:0 20px 60px rgba(0,0,0,.45); background:#111; }
.gallery-close, .gallery-nav { position:absolute; border:0; background:rgba(255,255,255,.12); color:#fff; cursor:pointer; }
.gallery-close { top:18px; right:22px; width:38px; height:38px; border-radius:50%; font-size:30px; line-height:1; }
.gallery-nav { top:50%; transform:translateY(-50%); width:44px; height:64px; border-radius:8px; font-size:42px; line-height:1; }
.gallery-prev { left:22px; }
.gallery-next { right:22px; }
.gallery-title { position:absolute; left:24px; bottom:18px; color:#fff; font-weight:600; }
.page-head { display:flex; align-items:flex-end; justify-content:space-between; gap:16px; margin-bottom:16px; }
.status-tabs { display:flex; gap:6px; flex-wrap:wrap; justify-content:flex-end; }
.tab { display:inline-flex; align-items:center; gap:7px; min-height:34px; padding:7px 10px; border:1px solid var(--line); border-radius:999px; color:var(--muted); background:#fff; text-decoration:none; font-weight:700; }
.tab span { color:var(--text); background:var(--panel-soft); border-radius:999px; padding:1px 7px; font-size:12px; }
.tab.active { color:var(--accent); border-color:#b7caf8; background:var(--accent-soft); }
.editor-filter { grid-template-columns:180px 180px 140px auto; }
.catalog-layout { display:grid; grid-template-columns:minmax(0, 1fr) 306px; gap:16px; align-items:start; }
.catalog-main { min-width:0; overflow:hidden; }
.filter-sidebar { position:sticky; top:74px; max-height:calc(100vh - 92px); overflow:auto; overflow-x:hidden; padding:14px; }
.filter-sidebar input, .filter-sidebar select { min-width:0; }
.filter-head { display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:8px; }
.filter-head h2 { margin:0; }
.filter-head a { color:var(--muted); font-weight:700; text-decoration:none; }
.filter-section { border-top:1px solid var(--line); padding-top:12px; margin-top:12px; }
.filter-title { font-weight:800; margin-bottom:8px; }
.range-row { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:8px; }
.facet-search { margin-bottom:8px; }
.facet-list { display:grid; gap:4px; max-height:220px; overflow:auto; padding-right:3px; }
.facet-list.compact { max-height:160px; }
.facet-option { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center; gap:8px; margin:0; padding:6px 7px; border-radius:7px; color:var(--text); font-weight:600; cursor:pointer; }
.facet-option:hover { background:var(--panel-soft); }
.facet-option input { width:16px; height:16px; margin:0; }
.facet-option span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; margin:0; }
.facet-option small { color:var(--muted); font-weight:700; }
.filter-apply { width:100%; margin-top:14px; }
.list-toolbar { display:flex; justify-content:space-between; align-items:end; gap:12px; margin-bottom:12px; }
.list-footer { display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; margin-top:12px; }
.list-footer .pagination { margin-top:0; }
.per-page-form label { width:126px; margin-bottom:0; }
.compact-controls { justify-content:flex-end; }
.compact-controls label { width:170px; margin-bottom:0; }
.sort-head { display:inline-flex; align-items:center; gap:4px; color:inherit; text-decoration:none; }
.sort-head:hover { color:var(--accent); }
.sort-head.active:after { content:"↓"; font-size:11px; color:var(--accent); }
.sort-head.active.asc:after { content:"↑"; }
.raw-select-head, .raw-select-cell { display:flex; align-items:center; gap:8px; margin:0; }
.raw-select-head input, .raw-select-cell input { width:16px; height:16px; margin:0; flex:0 0 auto; }
.editor-list { display:grid; gap:12px; }
.editor-card { display:grid; grid-template-columns:112px minmax(0,1fr) 132px; gap:16px; align-items:start; background:var(--panel); border:1px solid var(--line); border-radius:8px; padding:14px; box-shadow:var(--shadow); }
.editor-media .media-strip { display:grid; grid-template-columns:repeat(2, 48px); overflow:visible; }
.editor-main { min-width:0; }
.editor-meta { display:flex; gap:8px; align-items:center; flex-wrap:wrap; color:var(--muted); font-size:13px; margin-bottom:8px; }
.editor-meta a { color:var(--text); font-weight:800; text-decoration:none; }
.editor-title-row { display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:8px; }
.publication-badges { display:flex; gap:6px; flex-wrap:wrap; margin:0 0 8px; }
.publication-badges .pill { text-decoration:none; }
.editor-text { white-space:pre-wrap; line-height:1.55; overflow-wrap:anywhere; }
.source-compare { margin-top:10px; border-top:1px solid var(--line); padding-top:8px; }
.source-compare summary { cursor:pointer; color:var(--muted); font-weight:700; }
.compare-grid { display:grid; grid-template-columns:1.3fr .7fr; gap:14px; margin-top:8px; }
.editor-actions { display:grid; gap:8px; }
.editor-actions form, .editor-actions button { width:100%; }
.editor-dialog { width:min(1180px, calc(100vw - 36px)); max-height:calc(100vh - 36px); border:1px solid var(--line); border-radius:8px; padding:0; box-shadow:0 24px 80px rgba(16,24,40,.28); }
.editor-dialog::backdrop { background:rgba(15,23,42,.56); backdrop-filter:blur(4px); }
.dialog-head { position:sticky; top:0; z-index:2; display:flex; align-items:center; justify-content:space-between; gap:12px; padding:16px 18px; border-bottom:1px solid var(--line); background:#fff; }
.dialog-grid { display:grid; grid-template-columns:420px minmax(0,1fr); gap:0; min-height:620px; }
.context-pane { border-right:1px solid var(--line); background:var(--panel-soft); padding:16px; overflow:auto; max-height:calc(100vh - 112px); }
.context-pane details { border-top:1px solid var(--line); padding-top:10px; margin-top:12px; }
.context-pane summary { cursor:pointer; font-weight:800; }
.edit-pane { padding:16px; overflow:auto; max-height:calc(100vh - 112px); }
.edit-form textarea[name="final_text"] { min-height:360px; resize:vertical; line-height:1.55; }
.hashtag-preview { margin:-2px 0 10px; color:var(--muted); font-family:Consolas, monospace; }
.dialog-actions { display:flex; gap:8px; justify-content:flex-end; flex-wrap:wrap; }
.regen-form { margin-top:16px; border-top:1px solid var(--line); padding-top:14px; }
.compact-kv { margin-top:12px; grid-template-columns:90px minmax(0,1fr); }
.empty-state { text-align:center; color:var(--muted); padding:34px; }
@media (max-width: 900px) { header { padding:8px 12px; align-items:flex-start; } .filter-grid, .sources-filter, .source-add-grid, .hint-grid, .advanced-grid, .tool-row, .tool-row.sort-row, .editor-filter, .page-head, .compare-grid, .dialog-grid, .catalog-layout, .media-upload-form, .category-create, .prompt-test-layout, .prompt-test-controls { grid-template-columns:1fr; } .page-head { display:grid; } .form-grid { grid-template-columns:1fr; } .detail-grid { grid-template-columns:1fr; } .editor-card { grid-template-columns:1fr; } .editor-actions { grid-template-columns:repeat(3, minmax(0,1fr)); } .context-pane { border-right:0; border-bottom:1px solid var(--line); max-height:none; } .edit-pane { max-height:none; } .side-panel, .filter-sidebar, .prompt-test-side { position:static; max-height:none; } .list-toolbar { display:grid; align-items:start; } .compact-controls { justify-content:start; } main { padding:14px; } }
/* Custom tweaks to match Tailwind typography nicely */
body { font-family: 'Inter', system-ui, sans-serif; }
.htmx-indicator { display:none; }
.htmx-request .htmx-indicator { display:inline-block; }
.htmx-request.htmx-indicator { display:inline-block; }
</style>
</head>
<body>
{% if user %}
<header>
<nav>
<a href="/sources">Источники</a>
<a href="/raw">Raw</a>
<a href="/editor">Редакторская</a>
<a href="/prompt-test">Тест промптов</a>
<a href="/workers">Воркеры</a>
<a href="/logs">Логи</a>
<a href="/users">Пользователи</a>
</nav>
<form method="post" action="/logout"><button class="btn" type="submit">{{ user.login }} · выйти</button></form>
</header>
{% endif %}
<main>{% block body %}{% endblock %}</main>
<body class="bg-base-200 min-h-screen">
{% if user %}
<div class="navbar bg-base-100 shadow-sm sticky top-0 z-50">
<div class="flex-1 px-2 mx-2">
<span class="text-lg font-bold">Parser Admin</span>
</div>
<div class="flex-none">
<ul class="menu menu-horizontal px-1">
<li><a href="/raw" class="{{ 'active' if 'raw' in request.url.path }}">Сырые посты</a></li>
<li><a href="/editor" class="{{ 'active' if 'editor' in request.url.path }}">Редактор</a></li>
<li><a href="/sources" class="{{ 'active' if 'sources' in request.url.path }}">Источники</a></li>
<li><a href="/workers" class="{{ 'active' if 'workers' in request.url.path }}">Воркеры</a></li>
</ul>
</div>
<div class="flex-none hidden lg:block">
<ul class="menu menu-horizontal gap-2">
<li>
<form method="post" action="/logout" class="m-0">
<input type="hidden" name="csrf_token" value="{{ user.csrf_token }}">
<button class="btn btn-ghost btn-sm text-error">Выйти</button>
</form>
</li>
</ul>
</div>
</div>
{% endif %}
<main class="container mx-auto p-4 md:p-6 max-w-7xl">
{% block body %}{% endblock %}
</main>
<script>
// Handle HTMX errors
document.body.addEventListener('htmx:responseError', function(evt) {
alert("Ошибка при загрузке: " + evt.detail.xhr.status);
});
</script>
</body>
</html>