Files
WhatIDo/public/worker.html
T

193 lines
9.8 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">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Воркер ИИ — Админ-панель</title>
<link rel="stylesheet" href="admin.css">
<style>
.enhance-compare { position: relative; border-radius: 10px; overflow: hidden; background: var(--bg); border: 1px solid var(--border); user-select: none; touch-action: none; cursor: ew-resize; }
.enhance-stage { transform-origin: 0 0; }
.enhance-stage > img { display: block; width: 100%; height: auto; }
.enhance-top { position: absolute; inset: 0; overflow: hidden; }
.enhance-top .enhance-stage { position: absolute; inset: 0; }
.enhance-top .enhance-stage > img { height: 100%; object-fit: fill; }
.enhance-divider { position: absolute; top: 0; bottom: 0; left: 50%; width: 32px; transform: translateX(-50%); cursor: ew-resize; }
.enhance-divider::before { content: ''; position: absolute; top: 0; bottom: 0; left: 50%; width: 2px; transform: translateX(-50%); background: #fff; box-shadow: 0 0 6px rgba(0,0,0,.6); pointer-events: none; }
.enhance-handle { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 34px; height: 34px; border-radius: 50%; background: #fff; color: #333; display: flex; align-items: center; justify-content: center; font-size: 1rem; box-shadow: 0 2px 8px rgba(0,0,0,.4); pointer-events: none; }
.enhance-badge { position: absolute; top: 10px; padding: 3px 10px; border-radius: 999px; background: rgba(0,0,0,.55); color: #fff; font-size: .72rem; pointer-events: none; }
.enhance-badge.left { left: 10px; }
.enhance-badge.right { right: 10px; }
.photo-thumb { width: 56px; height: 56px; object-fit: cover; border-radius: 6px; border: 1px solid var(--border); display: block; }
</style>
</head>
<body data-page="worker">
<div class="layout">
<div class="sidebar" id="sidebar"></div>
<div class="main">
<div class="page-head">
<h2>Воркер ИИ</h2>
<p class="page-sub">Отслеживание автоматической проверки сообщений записей</p>
</div>
<div class="actions-row">
<span class="worker-state" id="stateLine"><span class="worker-dot"></span> Загрузка…</span>
<span style="flex:1"></span>
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:.85rem">
<input type="checkbox" id="autoRefresh"> Автообновление
</label>
<button class="btn-link" id="refreshBtn" type="button">Обновить</button>
<button class="btn-link" id="wakeBtn" type="button" title="Запустить обработку очереди немедленно">Разбудить</button>
<button class="btn-link" id="requeueBtn" type="button" title="Вернуть все записи с ошибкой в очередь">Перепроверить ошибки</button>
<button class="btn-link" id="photoWakeBtn" type="button" title="Разбудить фото-воркер">📷 Разбудить фото</button>
<button class="btn-link" id="photoToggleBtn" type="button">📷 …</button>
<button class="btn-link" id="toggleBtn" type="button">…</button>
</div>
<div class="sys-grid" id="workerCards" style="margin-top:16px">
<div class="sys-card loading">Загрузка…</div>
</div>
<div class="worker-counts" id="workerCounts"></div>
<div class="settings-stack settings-stack-grid" style="margin-top:20px">
<div class="settings-card wide">
<div class="card-head">
<div class="ic"><i data-lucide="hourglass"></i></div>
<h3>Очередь (<span id="pendingCount">0</span>)</h3>
</div>
<p class="hint">Записи, ожидающие проверки моделью. Обновляются автоматически.</p>
<div class="audit-wrap">
<table class="audit-table" id="pendingTable">
<thead>
<tr><th>ID</th><th>Ученик</th><th>Группа</th><th>Создана</th></tr>
</thead>
<tbody></tbody>
</table>
<div class="empty" id="pendingEmpty" style="display:none">Очередь пуста.</div>
</div>
</div>
<div class="settings-card wide">
<div class="card-head">
<div class="ic"><i data-lucide="history"></i></div>
<h3>Последние проверки</h3>
</div>
<div class="actions-row" style="margin-bottom:10px">
<select id="recentFilter" class="settings-input" style="max-width:220px">
<option value="">Все статусы</option>
<option value="done">Проверено</option>
<option value="skipped">Пропущено</option>
<option value="error">Ошибка</option>
<option value="reverted">Возвращён оригинал</option>
</select>
<span class="hint" id="recentCount"></span>
</div>
<div class="audit-wrap">
<table class="audit-table" id="recentTable">
<thead>
<tr><th>Проверено</th><th>Ученик</th><th>Группа</th><th>Статус</th><th>Изменения</th><th>Детали</th></tr>
</thead>
<tbody></tbody>
</table>
<div class="empty" id="recentEmpty" style="display:none">Проверок пока нет.</div>
</div>
<div class="pager" id="recentPager"></div>
</div>
<div class="settings-card wide">
<div class="card-head">
<div class="ic"><i data-lucide="image"></i></div>
<h3>Обработка фотографий</h3>
</div>
<div class="actions-row" style="margin-bottom:10px">
<span class="hint" id="photoStateHint"></span>
<span style="flex:1"></span>
<button class="btn-link" id="photoRequeueBtn" type="button" title="Вернуть задания с ошибкой в очередь">Повторить ошибки фото</button>
</div>
<div class="worker-counts" id="photoCounts" style="margin-bottom:10px"></div>
<p class="hint">В очереди (<span id="photoPendingCount">0</span>)</p>
<div class="audit-wrap" style="margin-bottom:14px">
<table class="audit-table" id="photoPendingTable">
<thead>
<tr><th>Задание</th><th>Запись</th><th>Действие</th><th>Поставлено</th></tr>
</thead>
<tbody></tbody>
</table>
<div class="empty" id="photoPendingEmpty" style="display:none">Очередь пуста.</div>
</div>
<div class="audit-wrap">
<table class="audit-table" id="photoTable">
<thead>
<tr><th>Время</th><th>Запись</th><th>Действие</th><th>Статус</th><th>Было</th><th>Стало</th><th>Ошибка</th><th></th></tr>
</thead>
<tbody></tbody>
</table>
<div class="empty" id="photoEmpty" style="display:none">Обработок фотографий пока нет.</div>
</div>
<div class="pager" id="photoRecentPager"></div>
</div>
<div class="settings-card wide" id="errorsCard" style="display:none">
<div class="card-head">
<div class="ic"><i data-lucide="triangle-alert"></i></div>
<h3>Ошибки (<span id="errorsCount">0</span>)</h3>
</div>
<p class="hint">Записи, которые не удалось обработать после всех попыток. Нажмите «Перепроверить ошибки», чтобы запустить их снова.</p>
<div class="audit-wrap">
<table class="audit-table" id="errorsTable">
<thead>
<tr><th>Время</th><th>ID</th><th>Ученик</th><th>Ошибка</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="modal-overlay" id="aiDetailModal">
<div class="edit-modal" style="max-width:680px">
<h3 id="aiDetailTitle">Детали ИИ-проверки</h3>
<div class="ai-detail-block">
<div class="ai-detail-label">Отправлено в модель</div>
<pre id="aiDetailInput"></pre>
</div>
<div class="ai-detail-block">
<div class="ai-detail-label">Ответ модели</div>
<pre id="aiDetailOutput"></pre>
</div>
<div class="card-foot" style="justify-content:flex-end;gap:8px">
<button type="button" class="cancel" id="aiDetailCloseBtn">Закрыть</button>
</div>
</div>
</div>
<div class="modal-overlay" id="photoJobModal">
<div class="edit-modal" style="max-width:900px">
<h3 id="photoJobTitle">Обработка фотографии</h3>
<div class="enhance-compare" id="photoJobCompare">
<div class="enhance-stage">
<img id="photoJobBefore" alt="Было">
</div>
<div class="enhance-top" id="photoJobTop" style="clip-path:inset(0 0 0 50%)">
<div class="enhance-stage enhance-stage-fill">
<img id="photoJobAfter" alt="Стало">
</div>
</div>
<div class="enhance-divider" id="photoJobDivider"><div class="enhance-handle">⇔</div></div>
<div class="enhance-badge left">Было</div>
<div class="enhance-badge right">Стало</div>
</div>
<div class="card-foot" style="justify-content:flex-end;gap:8px;margin-top:14px">
<button type="button" class="btn-primary ghost" id="photoJobCloseBtn">Закрыть</button>
<button type="button" class="btn-primary" id="photoJobRollbackBtn">↩ Откатить к версии «Было»</button>
</div>
</div>
</div>
<div class="toast" id="toast"></div>
<script src="vendor/lucide.min.js"></script>
<script src="admin.js"></script>
<script src="js/worker.js"></script>
</body>
</html>