feat(photos): раздел «Фото» — единая галерея всех загруженных фотографий

Новый read-only раздел для просмотра всех фотографий системы:
GET /api/photos собирает через UNION ALL пять источников — главное фото
записи, фото записи (entry_photos), фотохронику групп, фото учеников и фото
тем модулей. Фильтры search/student_name/group_id/date_from/date_to и
пагинация limit/offset, ответ { photos, total }. Не-admin ограничен
своими филиалами (branch_id), чужой group_id отдаёт 403.

Фронтенд: public/photos.html + public/js/photos.js — сетка превью
(/uploads/thumb/...), бейдж источника, описание, ученик/группа, дата,
ссылка на источник, lightbox по клику, поиск с debounce и пагинация.
Навигация: пункт «Фото» в сайдбаре и плитка в быстрых действиях дашборда.
This commit is contained in:
dev
2026-09-28 00:15:32 +03:00
parent 2afe676969
commit 31542de33b
6 changed files with 383 additions and 31 deletions
+49
View File
@@ -0,0 +1,49 @@
<!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>
.ph-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:14px}
.ph-card{background:var(--card);border:1px solid var(--border);border-radius:12px;overflow:hidden;display:flex;flex-direction:column;min-width:0}
.ph-card img{width:100%;aspect-ratio:4/3;object-fit:cover;display:block;cursor:zoom-in;background:var(--bg);transition:opacity .15s}
.ph-card:hover img{opacity:.9}
.ph-card .ph-body{padding:10px 12px;display:flex;flex-direction:column;gap:4px;min-width:0}
.ph-badge{align-self:flex-start;background:var(--bg);border:1px solid var(--border);color:var(--muted);border-radius:999px;padding:2px 8px;font-size:.68rem;font-weight:700}
.ph-title{font-size:.82rem;font-weight:600;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;word-break:break-word}
.ph-meta{font-size:.72rem;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ph-time{font-size:.7rem;color:var(--muted)}
.ph-link{font-size:.72rem;color:var(--accent);text-decoration:none}
.ph-link:hover{text-decoration:underline}
</style>
</head>
<body data-page="photos">
<div class="layout">
<div class="sidebar" id="sidebar"></div>
<div class="main">
<h2>Фото</h2>
<div class="filters">
<div><label>Поиск</label><input type="text" id="searchInput" class="search-input" placeholder="Описание или имя"></div>
<div><label>Ученик</label><input type="text" id="studentFilter" list="studentFilterList" placeholder="Все / введите имя..."><datalist id="studentFilterList"></datalist></div>
<div><label>Группа</label><select id="groupFilter"><option value="">Все группы</option></select></div>
<div><label>Источник</label><select id="sourceFilter"><option value="">Все источники</option></select></div>
<div><label>С</label><input type="date" id="dateFrom"></div>
<div><label>По</label><input type="date" id="dateTo"></div>
</div>
<div class="ph-grid" id="grid"><div class="empty" style="grid-column:1/-1">Загрузка...</div></div>
<div class="pager" id="pager"></div>
</div>
</div>
<div class="modal-overlay" id="imgModal">
<img id="imgModalSrc">
</div>
<div class="toast" id="toast"></div>
<script src="vendor/lucide.min.js"></script>
<script src="admin.js"></script>
<script src="js/photos.js"></script>
</body>
</html>