diff --git a/streetup/controls/static/controls/controls_detail.css b/streetup/controls/static/controls/controls_detail.css
index e38d927..6e51add 100644
--- a/streetup/controls/static/controls/controls_detail.css
+++ b/streetup/controls/static/controls/controls_detail.css
@@ -19,6 +19,16 @@ body {
object-fit: cover; /* Garde le ratio et remplit */
border-radius: 8px; /* Coins arrondis */
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1); /* Ombre légère */
+ transition: transform 0.15s ease-in-out, opacity 0.15s ease-in-out;
+}
+
+.remark-photo-thumb {
+ cursor: pointer;
+}
+
+.remark-photo-thumb:hover {
+ transform: scale(1.05);
+ opacity: 0.9;
}
.remark-images a {
@@ -35,6 +45,16 @@ body {
z-index: 5;
}
+#remarkPhotoCarouselModal .modal-body {
+ background-color: #000;
+}
+
+#remarkPhotoCarousel .carousel-control-prev-icon,
+#remarkPhotoCarousel .carousel-control-next-icon {
+ width: 2.5rem;
+ height: 2.5rem;
+}
+
#remark-form-container {
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
diff --git a/streetup/controls/static/controls/controls_detail.js b/streetup/controls/static/controls/controls_detail.js
index 1b789dd..e5e32e8 100644
--- a/streetup/controls/static/controls/controls_detail.js
+++ b/streetup/controls/static/controls/controls_detail.js
@@ -1795,44 +1795,198 @@ function bindAutocompleteForRow(card) {
}
})();
-// Fonction pour afficher une image dans une modal
-function showImageModal(imageUrl) {
- // Créer une modal pour afficher l'image en plein écran
- let modal = document.getElementById('image-preview-modal');
+// Global variables for remark photo carousel
+let currentRemarkCarouselPhotos = [];
+let currentRemarkCarouselIndex = 0;
+
+function openRemarkPhotoCarousel(thumbElement) {
+ if (!thumbElement) return;
+ const remarkId = thumbElement.dataset.remarkId;
- if (!modal) {
- // Créer la modal si elle n'existe pas
- modal = document.createElement('div');
- modal.id = 'image-preview-modal';
- modal.className = 'modal fade';
- modal.tabIndex = -1;
- modal.setAttribute('aria-hidden', 'true');
-
- modal.innerHTML = `
-
-
-
-
-
![]()
-
-
+ // Find all photo thumbnails belonging to this remark
+ let thumbs = [];
+ const remarkCard = document.getElementById(`remark-${remarkId}`);
+ if (remarkCard) {
+ thumbs = Array.from(remarkCard.querySelectorAll('.remark-photo-thumb'));
+ } else if (remarkId) {
+ thumbs = Array.from(document.querySelectorAll(`.remark-photo-thumb[data-remark-id="${remarkId}"]`));
+ }
+
+ if (thumbs.length === 0) {
+ thumbs = [thumbElement];
+ }
+
+ currentRemarkCarouselPhotos = thumbs.map(t => ({
+ fullUrl: t.dataset.fullUrl || t.src,
+ thumbUrl: t.dataset.thumbUrl || t.src,
+ imageId: t.dataset.imageId || null,
+ caption: t.dataset.caption || '',
+ remarkId: t.dataset.remarkId || remarkId,
+ element: t
+ }));
+
+ let startIndex = thumbs.indexOf(thumbElement);
+ if (startIndex < 0) startIndex = 0;
+
+ showRemarkCarouselModal(startIndex);
+}
+
+function openRemarkPhotoCarouselByRemarkId(remarkId) {
+ const remarkCard = document.getElementById(`remark-${remarkId}`);
+ let firstThumb = null;
+ if (remarkCard) {
+ firstThumb = remarkCard.querySelector('.remark-photo-thumb');
+ } else {
+ firstThumb = document.querySelector(`.remark-photo-thumb[data-remark-id="${remarkId}"]`);
+ }
+ if (firstThumb) {
+ openRemarkPhotoCarousel(firstThumb);
+ }
+}
+
+function showRemarkCarouselModal(startIndex) {
+ const modalEl = document.getElementById('remarkPhotoCarouselModal');
+ if (!modalEl) {
+ if (currentRemarkCarouselPhotos[startIndex]) {
+ window.open(currentRemarkCarouselPhotos[startIndex].fullUrl, '_blank');
+ }
+ return;
+ }
+
+ const carouselInner = document.getElementById('remark-carousel-inner');
+ const carouselIndicators = document.getElementById('remark-carousel-indicators');
+ const total = currentRemarkCarouselPhotos.length;
+ currentRemarkCarouselIndex = startIndex;
+
+ if (!carouselInner) return;
+
+ carouselInner.innerHTML = '';
+ if (carouselIndicators) carouselIndicators.innerHTML = '';
+
+ currentRemarkCarouselPhotos.forEach((photo, idx) => {
+ const item = document.createElement('div');
+ item.className = `carousel-item ${idx === startIndex ? 'active' : ''} h-100`;
+ item.innerHTML = `
+
+
`;
+ carouselInner.appendChild(item);
- document.body.appendChild(modal);
+ if (carouselIndicators && total > 1) {
+ const ind = document.createElement('button');
+ ind.type = 'button';
+ ind.setAttribute('data-bs-target', '#remarkPhotoCarousel');
+ ind.setAttribute('data-bs-slide-to', idx.toString());
+ if (idx === startIndex) {
+ ind.className = 'active';
+ ind.setAttribute('aria-current', 'true');
+ }
+ ind.setAttribute('aria-label', `Photo ${idx + 1}`);
+ carouselIndicators.appendChild(ind);
+ }
+ });
+
+ const prevBtn = modalEl.querySelector('.carousel-control-prev');
+ const nextBtn = modalEl.querySelector('.carousel-control-next');
+ if (prevBtn) prevBtn.style.display = total > 1 ? 'flex' : 'none';
+ if (nextBtn) nextBtn.style.display = total > 1 ? 'flex' : 'none';
+ if (carouselIndicators) carouselIndicators.style.display = total > 1 ? 'flex' : 'none';
+
+ updateRemarkCarouselActiveSlide(startIndex);
+
+ const carouselEl = document.getElementById('remarkPhotoCarousel');
+ if (carouselEl && typeof bootstrap !== 'undefined' && bootstrap.Carousel) {
+ let bsCarousel = bootstrap.Carousel.getInstance(carouselEl);
+ if (bsCarousel) {
+ bsCarousel.dispose();
+ }
+ bsCarousel = new bootstrap.Carousel(carouselEl, { ride: false, interval: false });
+ bsCarousel.to(startIndex);
+
+ carouselEl.addEventListener('slid.bs.carousel', function (e) {
+ updateRemarkCarouselActiveSlide(e.to);
+ }, { once: false });
}
- // Mettre à jour l'image
- const img = modal.querySelector('#modal-preview-image');
- if (img) {
- img.src = imageUrl;
- }
-
- // Afficher la modal
if (typeof bootstrap !== 'undefined' && bootstrap.Modal) {
- const modalInstance = new bootstrap.Modal(modal);
- modalInstance.show();
+ const bsModal = bootstrap.Modal.getOrCreateInstance(modalEl);
+ bsModal.show();
}
+}
+
+function updateRemarkCarouselActiveSlide(idx) {
+ currentRemarkCarouselIndex = idx;
+ const photo = currentRemarkCarouselPhotos[idx];
+ const total = currentRemarkCarouselPhotos.length;
+
+ const counterBadge = document.getElementById('remark-carousel-counter');
+ const captionEl = document.getElementById('remark-carousel-caption');
+ const hdBtn = document.getElementById('remark-carousel-hd-btn');
+ const deleteBtn = document.getElementById('remark-carousel-delete-btn');
+
+ if (counterBadge) {
+ counterBadge.textContent = `${idx + 1} / ${total}`;
+ }
+ if (captionEl && photo) {
+ captionEl.textContent = photo.caption || '';
+ }
+ if (hdBtn && photo) {
+ hdBtn.href = photo.fullUrl;
+ }
+ if (deleteBtn) {
+ if (photo && photo.imageId) {
+ deleteBtn.style.display = 'inline-flex';
+ deleteBtn.dataset.imageId = photo.imageId;
+ deleteBtn.dataset.remarkId = photo.remarkId;
+ deleteBtn.onclick = function () {
+ if (photo.element) {
+ const trashBtn = photo.element.parentElement ? photo.element.parentElement.querySelector('.remark-image-delete') : null;
+ if (trashBtn) {
+ trashBtn.click();
+ } else {
+ deleteRemarkImage(photo.imageId, photo.element);
+ }
+ } else {
+ deleteRemarkImage(photo.imageId, deleteBtn);
+ }
+ };
+ } else {
+ deleteBtn.style.display = 'none';
+ }
+ }
+}
+
+// Support navigation au clavier (flèche gauche / droite) pour le carrousel
+document.addEventListener('DOMContentLoaded', function() {
+ const modalEl = document.getElementById('remarkPhotoCarouselModal');
+ if (modalEl) {
+ modalEl.addEventListener('keydown', function(e) {
+ const carouselEl = document.getElementById('remarkPhotoCarousel');
+ if (!carouselEl || typeof bootstrap === 'undefined' || !bootstrap.Carousel) return;
+ const bsCarousel = bootstrap.Carousel.getInstance(carouselEl);
+ if (!bsCarousel) return;
+
+ if (e.key === 'ArrowLeft') {
+ e.preventDefault();
+ bsCarousel.prev();
+ } else if (e.key === 'ArrowRight') {
+ e.preventDefault();
+ bsCarousel.next();
+ }
+ });
+ }
+});
+
+// Backward compatibility for single image preview
+function showImageModal(imageUrl) {
+ currentRemarkCarouselPhotos = [{
+ fullUrl: imageUrl,
+ thumbUrl: imageUrl,
+ imageId: null,
+ caption: '',
+ remarkId: null,
+ element: null
+ }];
+ showRemarkCarouselModal(0);
}
\ No newline at end of file
diff --git a/streetup/controls/templates/controls/controls_detail_content.html b/streetup/controls/templates/controls/controls_detail_content.html
index 7b7be27..d9d0bc5 100644
--- a/streetup/controls/templates/controls/controls_detail_content.html
+++ b/streetup/controls/templates/controls/controls_detail_content.html
@@ -365,8 +365,15 @@
-