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:
parent
6b35d93fd6
commit
4a9ab2b2ad
50
admin.php
50
admin.php
|
|
@ -742,10 +742,12 @@ function nextUniqueCodeName(string $base): string
|
||||||
.topic-node{border:1px solid #e5e7eb;border-radius:10px;padding:10px;background:#fff}
|
.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.level-2{margin-left:20px;border-color:#edf2fb;background:#fbfdff}
|
||||||
.topic-node-head{font-size:12px;color:#667085;margin:0 0 8px}
|
.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-row .btn{height:36px}
|
||||||
|
.topic-delete-btn{height:36px;margin:0}
|
||||||
.topic-children{display:grid;gap:8px;margin-top:8px}
|
.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}
|
.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{position:fixed;inset:0;z-index:90;display:flex;align-items:center;justify-content:center;padding:16px}
|
||||||
.modal[hidden]{display:none}
|
.modal[hidden]{display:none}
|
||||||
|
|
@ -888,32 +890,36 @@ function nextUniqueCodeName(string $base): string
|
||||||
<?php foreach($topicTree as $root): ?>
|
<?php foreach($topicTree as $root): ?>
|
||||||
<div class="topic-node level-1">
|
<div class="topic-node level-1">
|
||||||
<p class="topic-node-head">Уровень 1</p>
|
<p class="topic-node-head">Уровень 1</p>
|
||||||
<form method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=topics" class="topic-row js-topic-form">
|
<div class="topic-line">
|
||||||
<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'] ?>">
|
<form method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=topics" class="topic-row js-topic-form">
|
||||||
<input class="in" type="text" name="name" value="<?= h((string)$root['name']) ?>" required>
|
<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="number" name="sort_order" value="<?= (int)$root['sort_order'] ?>">
|
<input class="in" type="text" name="name" value="<?= h((string)$root['name']) ?>" required>
|
||||||
<div class="small js-save-status" style="grid-column:1 / -1"></div>
|
<input class="in" type="number" name="sort_order" value="<?= (int)$root['sort_order'] ?>">
|
||||||
</form>
|
<div class="small js-save-status" style="grid-column:1 / -1"></div>
|
||||||
<form method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=topics" style="margin-top:8px">
|
</form>
|
||||||
<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'] ?>">
|
<form class="inline-form" method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=topics" onsubmit="return confirm('Удалить тематику? Дочерние тематики и привязки к фото тоже удалятся.')">
|
||||||
<button class="btn btn-danger" type="submit" onclick="return confirm('Удалить тематику? Дочерние тематики и привязки к фото тоже удалятся.')">Удалить</button>
|
<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'] ?>">
|
||||||
</form>
|
<button class="btn btn-danger topic-delete-btn" type="submit">Удалить</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<?php if (!empty($root['children'])): ?>
|
<?php if (!empty($root['children'])): ?>
|
||||||
<div class="topic-children">
|
<div class="topic-children">
|
||||||
<?php foreach($root['children'] as $child): ?>
|
<?php foreach($root['children'] as $child): ?>
|
||||||
<div class="topic-node level-2">
|
<div class="topic-node level-2">
|
||||||
<p class="topic-node-head">Уровень 2 · внутри «<?= h((string)$root['name']) ?>»</p>
|
<p class="topic-node-head">Уровень 2 · внутри «<?= h((string)$root['name']) ?>»</p>
|
||||||
<form method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=topics" class="topic-row js-topic-form">
|
<div class="topic-line">
|
||||||
<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'] ?>">
|
<form method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=topics" class="topic-row js-topic-form">
|
||||||
<input class="in" type="text" name="name" value="<?= h((string)$child['name']) ?>" required>
|
<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="number" name="sort_order" value="<?= (int)$child['sort_order'] ?>">
|
<input class="in" type="text" name="name" value="<?= h((string)$child['name']) ?>" required>
|
||||||
<div class="small js-save-status" style="grid-column:1 / -1"></div>
|
<input class="in" type="number" name="sort_order" value="<?= (int)$child['sort_order'] ?>">
|
||||||
</form>
|
<div class="small js-save-status" style="grid-column:1 / -1"></div>
|
||||||
<form method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=topics" style="margin-top:8px">
|
</form>
|
||||||
<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'] ?>">
|
<form class="inline-form" method="post" action="?token=<?= urlencode($tokenIncoming) ?>&mode=topics" onsubmit="return confirm('Удалить тематику?')">
|
||||||
<button class="btn btn-danger" type="submit" onclick="return confirm('Удалить тематику?')">Удалить</button>
|
<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'] ?>">
|
||||||
</form>
|
<button class="btn btn-danger topic-delete-btn" type="submit">Удалить</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
20
index.php
20
index.php
|
|
@ -94,8 +94,14 @@ $detailIndex = 0;
|
||||||
$prevPhotoId = 0;
|
$prevPhotoId = 0;
|
||||||
$nextPhotoId = 0;
|
$nextPhotoId = 0;
|
||||||
$detailSectionId = 0;
|
$detailSectionId = 0;
|
||||||
|
$photoTopics = [];
|
||||||
if ($photo) {
|
if ($photo) {
|
||||||
$detailSectionId = (int)$photo['section_id'];
|
$detailSectionId = (int)$photo['section_id'];
|
||||||
|
try {
|
||||||
|
$photoTopics = photoTopicsByPhotoId($activePhotoId);
|
||||||
|
} catch (Throwable) {
|
||||||
|
$photoTopics = [];
|
||||||
|
}
|
||||||
$detailPhotos = photosForPublic($detailSectionId, $activeTopicId > 0 ? $activeTopicId : null);
|
$detailPhotos = photosForPublic($detailSectionId, $activeTopicId > 0 ? $activeTopicId : null);
|
||||||
if ($activeTopicId > 0 && $detailPhotos !== []) {
|
if ($activeTopicId > 0 && $detailPhotos !== []) {
|
||||||
$foundInTopic = false;
|
$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{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.ai{background:rgba(31,111,235,.92)}
|
||||||
.card-badge.comments{background:rgba(3,105,161,.9)}
|
.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}
|
.cap{padding:8px;font-size:13px}
|
||||||
.detail img{max-width:100%;border-radius:10px;border:1px solid #e5e7eb}
|
.detail img{max-width:100%;border-radius:10px;border:1px solid #e5e7eb}
|
||||||
.stack{display:grid;gap:12px;grid-template-columns:1fr}
|
.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}
|
.cmt{border-top:1px solid #eee;padding:8px 0}
|
||||||
.muted{color:#6b7280;font-size:13px}
|
.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}
|
.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>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
<?php endif; ?>
|
<?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>
|
</aside>
|
||||||
<main>
|
<main>
|
||||||
<?php if ($activePhotoId > 0 && $photo): ?>
|
<?php if ($activePhotoId > 0 && $photo): ?>
|
||||||
<section class="panel detail">
|
<section class="panel detail">
|
||||||
<h2><?= h((string)$photo['code_name']) ?></h2>
|
<h2><?= h((string)$photo['code_name']) ?></h2>
|
||||||
<p class="muted"><?= h((string)($photo['description'] ?? '')) ?></p>
|
<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">
|
<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['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; ?>
|
<?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; ?>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user