Admin/Public: tighten topic rows and improve photo detail metadata

In admin topics, align name, sort index, and delete action on one line while keeping autosave updates in place. On public pages, present viewer mode with a divider block, prevent catalog thumbnail cropping by using contain fit, and show section/topic quick links above photo previews for direct navigation.
This commit is contained in:
Alexander Andreev 2026-02-21 12:57:17 +03:00
parent 6b35d93fd6
commit 4a9ab2b2ad
2 changed files with 46 additions and 24 deletions

View File

@ -742,10 +742,12 @@ function nextUniqueCodeName(string $base): string
.topic-node{border:1px solid #e5e7eb;border-radius:10px;padding:10px;background:#fff}
.topic-node.level-2{margin-left:20px;border-color:#edf2fb;background:#fbfdff}
.topic-node-head{font-size:12px;color:#667085;margin:0 0 8px}
.topic-row{display:grid;grid-template-columns:minmax(180px,1fr) 110px;gap:8px;align-items:center}
.topic-line{display:flex;align-items:flex-start;gap:8px}
.topic-row{display:grid;grid-template-columns:minmax(180px,1fr) 110px;gap:8px;align-items:center;flex:1}
.topic-row .btn{height:36px}
.topic-delete-btn{height:36px;margin:0}
.topic-children{display:grid;gap:8px;margin-top:8px}
@media (max-width:900px){.topic-row{grid-template-columns:1fr 110px}.topic-row .btn{width:100%}}
@media (max-width:900px){.topic-line{flex-direction:column}.topic-row{grid-template-columns:1fr 110px;width:100%}.topic-row .btn,.topic-delete-btn{width:100%}}
.row-actions{display:flex;flex-direction:column;align-items:flex-start;gap:8px}
.modal{position:fixed;inset:0;z-index:90;display:flex;align-items:center;justify-content:center;padding:16px}
.modal[hidden]{display:none}
@ -888,33 +890,37 @@ function nextUniqueCodeName(string $base): string
<?php foreach($topicTree as $root): ?>
<div class="topic-node level-1">
<p class="topic-node-head">Уровень 1</p>
<div class="topic-line">
<form method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=topics" class="topic-row js-topic-form">
<input type="hidden" name="action" value="update_topic"><input type="hidden" name="ajax" value="1"><input type="hidden" name="token" value="<?= h($tokenIncoming) ?>"><input type="hidden" name="topic_id" value="<?= (int)$root['id'] ?>">
<input class="in" type="text" name="name" value="<?= h((string)$root['name']) ?>" required>
<input class="in" type="number" name="sort_order" value="<?= (int)$root['sort_order'] ?>">
<div class="small js-save-status" style="grid-column:1 / -1"></div>
</form>
<form method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=topics" style="margin-top:8px">
<form class="inline-form" method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=topics" onsubmit="return confirm('Удалить тематику? Дочерние тематики и привязки к фото тоже удалятся.')">
<input type="hidden" name="action" value="delete_topic"><input type="hidden" name="token" value="<?= h($tokenIncoming) ?>"><input type="hidden" name="topic_id" value="<?= (int)$root['id'] ?>">
<button class="btn btn-danger" type="submit" onclick="return confirm('Удалить тематику? Дочерние тематики и привязки к фото тоже удалятся.')">Удалить</button>
<button class="btn btn-danger topic-delete-btn" type="submit">Удалить</button>
</form>
</div>
<?php if (!empty($root['children'])): ?>
<div class="topic-children">
<?php foreach($root['children'] as $child): ?>
<div class="topic-node level-2">
<p class="topic-node-head">Уровень 2 · внутри «<?= h((string)$root['name']) ?>»</p>
<div class="topic-line">
<form method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=topics" class="topic-row js-topic-form">
<input type="hidden" name="action" value="update_topic"><input type="hidden" name="ajax" value="1"><input type="hidden" name="token" value="<?= h($tokenIncoming) ?>"><input type="hidden" name="topic_id" value="<?= (int)$child['id'] ?>">
<input class="in" type="text" name="name" value="<?= h((string)$child['name']) ?>" required>
<input class="in" type="number" name="sort_order" value="<?= (int)$child['sort_order'] ?>">
<div class="small js-save-status" style="grid-column:1 / -1"></div>
</form>
<form method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=topics" style="margin-top:8px">
<form class="inline-form" method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=topics" onsubmit="return confirm('Удалить тематику?')">
<input type="hidden" name="action" value="delete_topic"><input type="hidden" name="token" value="<?= h($tokenIncoming) ?>"><input type="hidden" name="topic_id" value="<?= (int)$child['id'] ?>">
<button class="btn btn-danger" type="submit" onclick="return confirm('Удалить тематику?')">Удалить</button>
<button class="btn btn-danger topic-delete-btn" type="submit">Удалить</button>
</form>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>

View File

@ -94,8 +94,14 @@ $detailIndex = 0;
$prevPhotoId = 0;
$nextPhotoId = 0;
$detailSectionId = 0;
$photoTopics = [];
if ($photo) {
$detailSectionId = (int)$photo['section_id'];
try {
$photoTopics = photoTopicsByPhotoId($activePhotoId);
} catch (Throwable) {
$photoTopics = [];
}
$detailPhotos = photosForPublic($detailSectionId, $activeTopicId > 0 ? $activeTopicId : null);
if ($activeTopicId > 0 && $detailPhotos !== []) {
$foundInTopic = false;
@ -294,10 +300,12 @@ function outputWatermarked(string $path, string $mime): never
.card-badge{display:inline-flex;align-items:center;justify-content:center;background:rgba(17,24,39,.78);color:#fff;font-size:11px;line-height:1;padding:6px 7px;border-radius:999px}
.card-badge.ai{background:rgba(31,111,235,.92)}
.card-badge.comments{background:rgba(3,105,161,.9)}
.card img{width:100%;height:130px;object-fit:cover}
.card img{width:100%;height:130px;object-fit:contain;object-position:center;background:#f8fafc}
.cap{padding:8px;font-size:13px}
.detail img{max-width:100%;border-radius:10px;border:1px solid #e5e7eb}
.stack{display:grid;gap:12px;grid-template-columns:1fr}
.detail-meta{display:flex;flex-wrap:wrap;gap:8px;margin:10px 0 12px}
.detail-meta-link{display:inline-flex;align-items:center;padding:6px 10px;border-radius:999px;border:1px solid #dbe3ef;background:#f8fbff;color:#1f3b7a;text-decoration:none;font-size:12px;line-height:1.25}
.cmt{border-top:1px solid #eee;padding:8px 0}
.muted{color:#6b7280;font-size:13px}
.pager{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;margin-top:16px;padding-top:12px;border-top:1px solid #e5e7eb}
@ -394,13 +402,21 @@ function outputWatermarked(string $path, string $mime): never
</div>
</details>
<?php endif; ?>
<p class="note" style="margin-top:12px"><?= $viewer ? 'Вы авторизованы для комментариев: ' . h((string)$viewer['display_name']) : 'Режим просмотра' ?></p>
<div class="nav-group">
<p class="note" style="margin:0"><?= $viewer ? 'Вы авторизованы для комментариев: ' . h((string)$viewer['display_name']) : 'Режим просмотра' ?></p>
</div>
</aside>
<main>
<?php if ($activePhotoId > 0 && $photo): ?>
<section class="panel detail">
<h2><?= h((string)$photo['code_name']) ?></h2>
<p class="muted"><?= h((string)($photo['description'] ?? '')) ?></p>
<div class="detail-meta">
<a class="detail-meta-link" href="?section_id=<?= (int)$detailSectionId ?><?= $viewerToken!=='' ? '&viewer=' . urlencode($viewerToken) : '' ?>">Раздел: <?= h($sectionNames[$detailSectionId] ?? ('#' . (string)$detailSectionId)) ?></a>
<?php foreach($photoTopics as $topic): ?>
<a class="detail-meta-link" href="?section_id=<?= (int)$detailSectionId ?>&topic_id=<?= (int)$topic['id'] ?><?= $viewerToken!=='' ? '&viewer=' . urlencode($viewerToken) : '' ?>">Тематика: <?= h((string)$topic['full_name']) ?></a>
<?php endforeach; ?>
</div>
<div class="stack">
<?php if (!empty($photo['before_file_id'])): ?><div><div class="muted">До обработки</div><div class="img-box"><img src="?action=image&file_id=<?= (int)$photo['before_file_id'] ?>" alt="" decoding="async" fetchpriority="high"></div></div><?php endif; ?>
<?php if (!empty($photo['after_file_id'])): ?><div><div class="muted">После обработки (watermark)</div><div class="img-box"><img src="?action=image&file_id=<?= (int)$photo['after_file_id'] ?>" alt="" decoding="async" fetchpriority="high"></div></div><?php endif; ?>