diff --git a/loko/controls/locale/en/LC_MESSAGES/django.mo b/loko/controls/locale/en/LC_MESSAGES/django.mo index cf52c51..2241856 100644 Binary files a/loko/controls/locale/en/LC_MESSAGES/django.mo and b/loko/controls/locale/en/LC_MESSAGES/django.mo differ diff --git a/loko/controls/locale/en/LC_MESSAGES/django.po b/loko/controls/locale/en/LC_MESSAGES/django.po index 85c7f88..7f622e1 100644 --- a/loko/controls/locale/en/LC_MESSAGES/django.po +++ b/loko/controls/locale/en/LC_MESSAGES/django.po @@ -1097,3 +1097,9 @@ msgstr "PDF generated and saved successfully." #: streetup/controls/views_reports_pdf.py:845 msgid "Erreur lors de la génération du PDF" msgstr "Error generating the PDF" + +msgid "Photos du contrôle" +msgstr "Control photos" + +msgid "Parcourir toutes les photos du contrôle dans le carrousel" +msgstr "Browse all control photos in the carousel" diff --git a/loko/controls/locale/en/LC_MESSAGES/djangojs.mo b/loko/controls/locale/en/LC_MESSAGES/djangojs.mo index 427a639..ffc1826 100644 Binary files a/loko/controls/locale/en/LC_MESSAGES/djangojs.mo and b/loko/controls/locale/en/LC_MESSAGES/djangojs.mo differ diff --git a/loko/controls/locale/en/LC_MESSAGES/djangojs.po b/loko/controls/locale/en/LC_MESSAGES/djangojs.po index 3689a6f..9f00d1c 100644 --- a/loko/controls/locale/en/LC_MESSAGES/djangojs.po +++ b/loko/controls/locale/en/LC_MESSAGES/djangojs.po @@ -144,3 +144,10 @@ msgstr "" #: controls/static/controls/remarks_form.js:222 msgid "Aucune caméra disponible sur cet appareil." msgstr "No camera available on this device." + +msgid "Photos du contrôle" +msgstr "Control photos" + +msgid "Photos de la remarque" +msgstr "Remark photos" + diff --git a/loko/controls/locale/nl/LC_MESSAGES/django.mo b/loko/controls/locale/nl/LC_MESSAGES/django.mo index bcf21dc..d0fdee9 100644 Binary files a/loko/controls/locale/nl/LC_MESSAGES/django.mo and b/loko/controls/locale/nl/LC_MESSAGES/django.mo differ diff --git a/loko/controls/locale/nl/LC_MESSAGES/django.po b/loko/controls/locale/nl/LC_MESSAGES/django.po index a3ac110..266abac 100644 --- a/loko/controls/locale/nl/LC_MESSAGES/django.po +++ b/loko/controls/locale/nl/LC_MESSAGES/django.po @@ -1092,3 +1092,10 @@ msgstr "PDF succesvol gegenereerd en opgeslagen." #: controls/views_reports_pdf.py:845 msgid "Erreur lors de la génération du PDF" msgstr "Fout bij het genereren van de PDF" + +msgid "Photos du contrôle" +msgstr "Controlefoto's" + +msgid "Parcourir toutes les photos du contrôle dans le carrousel" +msgstr "Bekijk alle controlefoto's in de carrousel" + diff --git a/loko/controls/locale/nl/LC_MESSAGES/djangojs.mo b/loko/controls/locale/nl/LC_MESSAGES/djangojs.mo index a32a8a6..b4d6b9e 100644 Binary files a/loko/controls/locale/nl/LC_MESSAGES/djangojs.mo and b/loko/controls/locale/nl/LC_MESSAGES/djangojs.mo differ diff --git a/loko/controls/locale/nl/LC_MESSAGES/djangojs.po b/loko/controls/locale/nl/LC_MESSAGES/djangojs.po index ee58b1e..124c6b3 100644 --- a/loko/controls/locale/nl/LC_MESSAGES/djangojs.po +++ b/loko/controls/locale/nl/LC_MESSAGES/djangojs.po @@ -144,3 +144,10 @@ msgstr "" #: controls/static/controls/remarks_form.js:222 msgid "Aucune caméra disponible sur cet appareil." msgstr "Er is geen camera beschikbaar op dit apparaat." + +msgid "Photos du contrôle" +msgstr "Controlefoto's" + +msgid "Photos de la remarque" +msgstr "Opmerking foto's" + diff --git a/loko/controls/static/controls/controls_detail.css b/loko/controls/static/controls/controls_detail.css index 6e51add..1d4f057 100644 --- a/loko/controls/static/controls/controls_detail.css +++ b/loko/controls/static/controls/controls_detail.css @@ -49,10 +49,53 @@ body { background-color: #000; } +#remarkPhotoCarousel { + touch-action: pan-y; + user-select: none; + -webkit-user-select: none; +} + +#remarkPhotoCarousel .carousel-control-prev, +#remarkPhotoCarousel .carousel-control-next { + width: 15%; + opacity: 0.85; +} + #remarkPhotoCarousel .carousel-control-prev-icon, #remarkPhotoCarousel .carousel-control-next-icon { width: 2.5rem; height: 2.5rem; + background-color: rgba(0, 0, 0, 0.65); + border-radius: 50%; + background-size: 60%; +} + +#remarkPhotoCarousel .carousel-indicators [data-bs-target] { + width: 8px; + height: 8px; + border-radius: 50%; + margin: 0 4px; + background-color: rgba(255, 255, 255, 0.6); +} + +#remarkPhotoCarousel .carousel-indicators .active { + background-color: #0d6efd; + transform: scale(1.3); +} + +@media (max-width: 576px) { + #remarkPhotoCarouselModal .modal-content { + height: 100dvh; + border-radius: 0; + } + #remarkPhotoCarouselModal .modal-body { + flex: 1 1 auto; + min-height: 0; + max-height: none !important; + } + #remarkPhotoCarousel .carousel-inner img { + max-height: 70dvh !important; + } } #remark-form-container { diff --git a/loko/controls/static/controls/controls_detail.js b/loko/controls/static/controls/controls_detail.js index e5e32e8..3ec6319 100644 --- a/loko/controls/static/controls/controls_detail.js +++ b/loko/controls/static/controls/controls_detail.js @@ -1795,15 +1795,54 @@ function bindAutocompleteForRow(card) { } })(); -// Global variables for remark photo carousel +// Global variables for remark / control photo carousel let currentRemarkCarouselPhotos = []; let currentRemarkCarouselIndex = 0; +function setCarouselModalTitle(titleText) { + const titleEl = document.getElementById('remarkPhotoCarouselModalLabel'); + if (titleEl) { + titleEl.textContent = titleText; + } +} + +// Ouvrir toutes les photos du contrôle dans le carrousel +function openAllControlPhotosCarousel(startIndex = 0) { + const thumbs = Array.from(document.querySelectorAll('#remarks-list .remark-photo-thumb, #archives-list .remark-photo-thumb')); + if (thumbs.length === 0) { + const fallbackThumbs = Array.from(document.querySelectorAll('.remark-photo-thumb')); + if (fallbackThumbs.length === 0) return; + thumbs.push(...fallbackThumbs); + } + + 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 || null, + element: t + })); + + let targetIndex = 0; + if (typeof startIndex === 'number') { + targetIndex = Math.max(0, Math.min(startIndex, currentRemarkCarouselPhotos.length - 1)); + } else if (startIndex instanceof HTMLElement) { + targetIndex = thumbs.indexOf(startIndex); + if (targetIndex < 0) targetIndex = 0; + } + + const controlTitle = typeof gettext !== 'undefined' ? gettext('Photos du contrôle') : 'Photos du contrôle'; + setCarouselModalTitle(controlTitle); + showRemarkCarouselModal(targetIndex); +} + +// Ouvrir les photos d'une remarque spécifique dans le carrousel function openRemarkPhotoCarousel(thumbElement) { if (!thumbElement) return; const remarkId = thumbElement.dataset.remarkId; - // Find all photo thumbnails belonging to this remark + // Trouver toutes les photos appartenant à cette remarque let thumbs = []; const remarkCard = document.getElementById(`remark-${remarkId}`); if (remarkCard) { @@ -1828,6 +1867,8 @@ function openRemarkPhotoCarousel(thumbElement) { let startIndex = thumbs.indexOf(thumbElement); if (startIndex < 0) startIndex = 0; + const remarkTitle = typeof gettext !== 'undefined' ? gettext('Photos de la remarque') : 'Photos de la remarque'; + setCarouselModalTitle(remarkTitle); showRemarkCarouselModal(startIndex); } @@ -1844,6 +1885,51 @@ function openRemarkPhotoCarouselByRemarkId(remarkId) { } } +// Gestion des gestes tactiles (swipe mobile) +function setupCarouselTouchGestures(carouselEl) { + if (!carouselEl || carouselEl.dataset.touchAttached === 'true') return; + carouselEl.dataset.touchAttached = 'true'; + + let touchStartX = 0; + let touchStartY = 0; + let touchEndX = 0; + let touchEndY = 0; + + carouselEl.addEventListener('touchstart', function(e) { + if (e.touches.length === 1) { + touchStartX = e.touches[0].clientX; + touchStartY = e.touches[0].clientY; + touchEndX = touchStartX; + touchEndY = touchStartY; + } + }, { passive: true }); + + carouselEl.addEventListener('touchmove', function(e) { + if (e.touches.length === 1) { + touchEndX = e.touches[0].clientX; + touchEndY = e.touches[0].clientY; + } + }, { passive: true }); + + carouselEl.addEventListener('touchend', function(e) { + const diffX = touchEndX - touchStartX; + const diffY = touchEndY - touchStartY; + // Détecter un balayage horizontal net (distance > 35px et plus horizontal que vertical) + if (Math.abs(diffX) > 35 && Math.abs(diffX) > Math.abs(diffY) * 1.2) { + if (typeof bootstrap !== 'undefined' && bootstrap.Carousel) { + const bsCarousel = bootstrap.Carousel.getInstance(carouselEl); + if (bsCarousel) { + if (diffX < 0) { + bsCarousel.next(); // Balayage vers la gauche -> Photo suivante + } else { + bsCarousel.prev(); // Balayage vers la droite -> Photo précédente + } + } + } + } + }, { passive: true }); +} + function showRemarkCarouselModal(startIndex) { const modalEl = document.getElementById('remarkPhotoCarouselModal'); if (!modalEl) { @@ -1868,7 +1954,7 @@ function showRemarkCarouselModal(startIndex) { item.className = `carousel-item ${idx === startIndex ? 'active' : ''} h-100`; item.innerHTML = `
{% translate 'Lieu' %} : {{ control.location }}
{% endif %} + + {% if control_photos_count > 0 %} +