Часовой пояс и формат времени (24h/12h) перенесены из браузера в настройки приложения: валидация IANA-зоны, сид в db/init.sql + db/migration.sql, отдача в GET /api/public-settings, раздел #sec-datetime в настройках с живым превью. Границы суток в SQL переведены на tzDayStart/tzDayEnd + bindTz ($TZ$) — раньше $n::date по TIMESTAMPTZ считал дни в UTC (у контейнера TimeZone=UTC) и молча сдвигал выборку на день; хардкод Europe/Moscow вычищен, now() заменён на tzWall(). Новый public/js/datetime.js: три семейства хелперов (instant / чистая DATE-строка без Date() / чистая TIME-строка), подключён на всех страницах включая публичные share/report/index, initDateTime() встроен в checkAuth(). Прямые toLocale*/getFullYear/toISOString().slice(0,10) в public/ выпилены. Попутно: bindTz добавляет параметр только при наличии $TZ$ в SQL (иначе запрос виснет вечно без global error handler) — регрессия закрыта в api.smoketest.js; починены off-by-one месяца в report.js и группировка по дате в share.js.
45 lines
2.0 KiB
HTML
45 lines
2.0 KiB
HTML
<!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">
|
|
</head>
|
|
<body data-page="notifications">
|
|
<div class="layout">
|
|
<div class="sidebar" id="sidebar"></div>
|
|
<div class="main">
|
|
<div class="page-head">
|
|
<h2>Уведомления</h2>
|
|
<p class="page-sub">События системы: новые записи в журнале, обработка фото нейросетью, блокировки IP и другое</p>
|
|
<span class="page-status" id="authStatus"><span class="spinner dark"></span> Загрузка…</span>
|
|
<button class="head-btn" id="nfReloadBtn" type="button" title="Обновить список"><i data-lucide="rotate-ccw"></i> Обновить</button>
|
|
</div>
|
|
|
|
<div class="notify-page-head">
|
|
<div class="view-toggle" id="notifyFilter">
|
|
<button type="button" data-filter="all" class="active">Все</button>
|
|
<button type="button" data-filter="unread">Непрочитанные</button>
|
|
</div>
|
|
<span class="grow"></span>
|
|
<button class="btn-primary ghost" id="nfReadAllBtn" type="button"><i data-lucide="check"></i> Прочитать все</button>
|
|
<button class="btn-primary danger-ghost" id="nfClearBtn" type="button" hidden><i data-lucide="trash-2"></i> Очистить всё</button>
|
|
</div>
|
|
<div class="hint" id="nfState"></div>
|
|
|
|
<div class="notify-page-list" id="nfList"></div>
|
|
<div style="display:flex;justify-content:center;margin-top:16px">
|
|
<button class="btn-primary ghost" id="nfMoreBtn" type="button" hidden>Показать ещё</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="toast" id="toast"></div>
|
|
<script src="vendor/lucide.min.js"></script>
|
|
<script src="js/datetime.js"></script>
|
|
<script src="admin.js"></script>
|
|
<script src="js/notifications.js"></script>
|
|
</body>
|
|
</html>
|