photos section in moderation portal with slider
This commit is contained in:
@@ -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'] ?>
|
||||||
·
|
·
|
||||||
<i class="fa-solid fa-thumbs-down"></i> <?= $item['dislikes_count'] ?>
|
<i class="fa-solid fa-thumbs-down"></i> <?= $item['dislikes_count'] ?>
|
||||||
|
·
|
||||||
|
<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)
|
||||||
// =============================================================
|
// =============================================================
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
Reference in New Issue
Block a user