photos section in moderation portal with slider

This commit is contained in:
2026-04-27 15:30:33 +02:00
parent be7bbfc28b
commit 879d7c5858
2 changed files with 116 additions and 4 deletions

View File

@@ -99,7 +99,7 @@ $categories = get_categories();
// Loads all Contributions for Municipality // Loads all Contributions for Municipality
$stmt = $pdo->prepare(" $stmt = $pdo->prepare("
SELECT contribution_id, title, category, description, author_name, SELECT contribution_id, title, category, description, author_name, photo_path,
geom_type, status, likes_count, dislikes_count, created_at, updated_at geom_type, status, likes_count, dislikes_count, created_at, updated_at
FROM contributions FROM contributions
WHERE municipality_id = :mid WHERE municipality_id = :mid
@@ -275,9 +275,28 @@ $counts['total'] = count($all_contributions);
<!-- Expanded Detail --> <!-- Expanded Detail -->
<div class="contribution-row-detail"> <div class="contribution-row-detail">
<div class="detail-layout"> <div class="detail-layout">
<!-- Map Preview --> <!-- Map and Photo Slider -->
<div class="detail-map" id="map-<?= $item['contribution_id'] ?>" <div class="detail-slider" id="slider-<?= $item['contribution_id'] ?>">
data-contribution-id="<?= $item['contribution_id'] ?>"> <!-- Slide 1: Map -->
<div class="detail-slide active" data-slide="map">
<div class="detail-map" id="map-<?= $item['contribution_id'] ?>"
data-contribution-id="<?= $item['contribution_id'] ?>">
</div>
</div>
<?php if (!empty($item['photo_path'])): ?>
<!-- Slide 2: Photo -->
<div class="detail-slide" data-slide="photo" style="display:none;">
<img src="<?= htmlspecialchars($item['photo_path']) ?>" alt="Foto"
class="detail-slide-photo" onclick="window.open('<?= htmlspecialchars($item['photo_path']) ?>', '_blank')">
</div>
<!-- Slider Arrows -->
<button class="slider-arrow slider-arrow-left" onclick="slideDetail(<?= $item['contribution_id'] ?>, -1)">
<i class="fa-solid fa-chevron-left"></i>
</button>
<button class="slider-arrow slider-arrow-right" onclick="slideDetail(<?= $item['contribution_id'] ?>, 1)">
<i class="fa-solid fa-chevron-right"></i>
</button>
<?php endif; ?>
</div> </div>
<!-- Content --> <!-- Content -->
@@ -295,6 +314,10 @@ $counts['total'] = count($all_contributions);
<i class="fa-solid fa-thumbs-up"></i> <?= $item['likes_count'] ?> <i class="fa-solid fa-thumbs-up"></i> <?= $item['likes_count'] ?>
&middot; &middot;
<i class="fa-solid fa-thumbs-down"></i> <?= $item['dislikes_count'] ?> <i class="fa-solid fa-thumbs-down"></i> <?= $item['dislikes_count'] ?>
&middot;
<i class="fa-solid fa-comment"></i> <?= $item['comment_count'] ?? 0 ?>
</span> </span>
</div> </div>
</div> </div>
@@ -537,6 +560,39 @@ $counts['total'] = count($all_contributions);
} }
// =============================================================
// Detail Slider for Maps and Photos
// =============================================================
function slideDetail(contributionId, direction) {
const slider = document.getElementById('slider-' + contributionId);
if (!slider) return;
const slides = slider.querySelectorAll('.detail-slide');
let activeIndex = -1;
// Finds currently active Slide
slides.forEach(function (slide, i) {
if (slide.style.display !== 'none') activeIndex = i;
});
// Calculates next Slide Index (wraps around)
const nextIndex = (activeIndex + direction + slides.length) % slides.length;
// Switches Slides
slides.forEach(function (slide) { slide.style.display = 'none'; });
slides[nextIndex].style.display = 'block';
// Loads Map if switching to Map Slide and not yet loaded
if (slides[nextIndex].dataset.slide === 'map') {
const mapDiv = slides[nextIndex].querySelector('.detail-map');
if (mapDiv && !mapDiv.dataset.loaded) {
loadMapPreview(mapDiv);
}
}
}
// ============================================================= // =============================================================
// Map Preview (Leaflet Mini Map per Contribution) // Map Preview (Leaflet Mini Map per Contribution)
// ============================================================= // =============================================================

View File

@@ -1036,6 +1036,60 @@ select.form-input { cursor: pointer; }
.back-link a { color: var(--color-text-secondary); } .back-link a { color: var(--color-text-secondary); }
/* -----------------------------------------------------------------
5.8 Detail Slider (Map/Photo in Admin)
----------------------------------------------------------------- */
.detail-slider {
width: 220px;
height: 170px;
flex-shrink: 0;
position: relative;
border-radius: 6px;
overflow: hidden;
border: 1px solid var(--color-border);
background: #f0f0f0;
}
.detail-slide { width: 100%; height: 100%; }
.detail-slide-photo {
width: 100%;
height: 100%;
object-fit: cover;
cursor: pointer;
}
.detail-slider .detail-map {
width: 100%;
height: 100%;
border: none;
border-radius: 0;
}
.slider-arrow {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: rgba(0, 0, 0, 0.5);
color: white;
border: none;
width: 28px;
height: 28px;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.7rem;
z-index: 1000;
transition: background var(--transition-fast);
}
.slider-arrow:hover { background: rgba(0, 0, 0, 0.7); }
.slider-arrow-left { left: 4px; }
.slider-arrow-right { right: 4px; }
/* ================================================================= /* =================================================================
SECTION 6: Responsive Overrides SECTION 6: Responsive Overrides
================================================================= */ ================================================================= */
@@ -1075,6 +1129,8 @@ select.form-input { cursor: pointer; }
.action-buttons .btn { justify-content: center; } .action-buttons .btn { justify-content: center; }
.filter-tabs { overflow-x: auto; } .filter-tabs { overflow-x: auto; }
.page-tabs { overflow-x: auto; } .page-tabs { overflow-x: auto; }
.detail-slider { width: 100%; height: 200px; }
/* Legal */ /* Legal */
.page-content-box { padding: 20px; } .page-content-box { padding: 20px; }