From adb5f08628bbe7e0b380339d4f3e9a19cbb270d2 Mon Sep 17 00:00:00 2001 From: kdeterme Date: Mon, 17 Aug 2026 10:49:01 +0200 Subject: [PATCH] feat: add photo carousel for controls and improve mobile touch gestures for remark images --- loko/controls/locale/en/LC_MESSAGES/django.mo | Bin 13137 -> 13313 bytes loko/controls/locale/en/LC_MESSAGES/django.po | 6 ++ .../locale/en/LC_MESSAGES/djangojs.mo | Bin 2397 -> 2500 bytes .../locale/en/LC_MESSAGES/djangojs.po | 7 ++ loko/controls/locale/nl/LC_MESSAGES/django.mo | Bin 13601 -> 13786 bytes loko/controls/locale/nl/LC_MESSAGES/django.po | 7 ++ .../locale/nl/LC_MESSAGES/djangojs.mo | Bin 2528 -> 2634 bytes .../locale/nl/LC_MESSAGES/djangojs.po | 7 ++ .../static/controls/controls_detail.css | 43 ++++++++ .../static/controls/controls_detail.js | 101 +++++++++++++++++- .../controls/controls_detail_content.html | 18 +++- .../controls/controls_detail_mobile.html | 21 +++- loko/controls/views.py | 6 +- 13 files changed, 201 insertions(+), 15 deletions(-) diff --git a/loko/controls/locale/en/LC_MESSAGES/django.mo b/loko/controls/locale/en/LC_MESSAGES/django.mo index cf52c51b7742f579377d85e44804f877b78e47db..2241856b431d9b6723f95be4e0a356787b598024 100644 GIT binary patch delta 4737 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 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 427a639c9948b32c6c8933ee80d14f0fa6372df5..ffc1826b7909ded86360251fe1b5b75a6b7e99e6 100644 GIT binary patch delta 592 zcmXxhze_?<6u|MD`qH2usHJ8nkwZfb(Uf4&)Y1}!5G{RA+J*Bz_Xp?Rd+sZ{itgO2%T`dd4x*FzB0|JN9Ut0? zMX5oIVLcvT1dq{yPbj%1^kW4F&}mg_7$ew?No>S@?88%>Q!1yf=>(Xt+mxEbe(c3K zPT?NPK}BrAN9@8fw&4%D(Wxn=V+fmY7Kd;NeYlGgc!p7md&VH=E3ds;*(6HBOl@qU zm+>~rL5C1%Z^EiqLl)Mv^m$^VKLFbK5Grps2P(i7{ z-B6wHL3zRvWJxVx5SLLdu!CcGg!0$qvT{%>ATz>DlfJu38J8v7r`6ATQs4N?QBhekQ?+6K-ck4k D8{JJ% delta 491 zcmXZZ&npCB9LMp`&g@t#KWdo8T2`16lOmxI+P(O3aP|jC%87E^vy(E(g^L_lVsFaL zMR9czr4&bVb8ztfYMR0cSfZE%9OV6pDy)!%E@FTE)F;A>)X74G0hZBzw1HOP zF8X+gR>?WqiCy6oK4K1CvK_}f+IMScC$WX(pc8DxGc1%DR2kS2Js^+1=q%F&ZNnfd qP2w=tZ{`2GI+(K-#P6vE&r;3Bp;Pp%2-TLI8>bd}-)=mSJ^BOhqAyec 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 bcf21dcb04e2f2717f0e97c48d7a854d2e4bf1f0..d0fdee931b47afe304cd7617ca787c19c022b657 100644 GIT binary patch delta 4695 zcmY+`2~bs40LJkn=);9Y6mmnoZh(SMQOGDFL(PaVlX9udP)L@i*aIp`y((53E~Hdi zT47q2nHp%TX;LngQCiL9G)UV>)0Xw!>}c;4X~7V|Wpsz&>~up93r zrV#acCGvgOEb~rGz5QSvw&#ZJ);*{W?8BaT1Y__N4#D4$KAXN>y^bYfZ|dVw*Nagd z4WQbsLJedAcH;S_mXlFjSd9bl5Vpl1Q9V9`-7$=Acg9|*22)WTOUI6wZR@$npDEy= zc1o}l&cQ)ggZ=SIjOO{~04M74N0^JJQ4h|H_a2mo`n&)&f;+7Dpc<~lbX<*^>bJ2M zevKN?zo-WfV4V^%7gKQ_y82)vCxfxYe(*EqQ198z8{u@+2xp_FG>Gb84fen~)QoIF zwYME3@i?l3Um|@lKcU(QV>ccP~76;wlekv7Z`Ou>_= z`=V%#ZD8V24^Bqy-wagyV=x(~;uW|UHIr}kVE(f>d6x^Cst9(N)+Pn@;0f3c3s6%x z4fTLUs7<&MwFfq$I`}NIT}+d$AHrzr$E{zXzJC_Am(IDIs3$Qjn5L+^)sO1=SnP|_ zFdS=84_bg~csZ&g>rf4EMJ>&4>mloBs2TbZ!|)vHezy%Ty=x)n9`Z(zLM|D&8}q+g>N_zC$l zKIUE52cbIXM>RMR2Vya5?H@$`Oali^eKTri4x&2#IqJSsr~&?h>cH<9?{eaEye}kJ zN1(oNwXILWB)xZwa2wz3b&^z|}3DgvyvYth?a}L!`w?yyvA_?14pMcsE z1<10SQuN_+*Phg)X5dk5hnr9j*ov9B8}-H0s0L%$5c)h0^|tgwt!bvco`+h>si=Vz zqGqZbHIN$AbKF&&sG$a=ZPSRndFC+c2j&~pgMY^?j8FDPI0=2!XQHOE6m@^Cy}kw` zsISN2*oaz!W2oPU)5xD|It}(-uXNPfO-6O38rdgi1L_BAAM$5@;Gh|aq7fa>Ue?4b9*l@o1}&rl7XK{fCvs;3=OyctTwDbz<|d#pn( zMFV!k^{BPqj1D%VX6O^t`~Eq0!k3-&4J(*)~T!fS z$f}s5sNMS~>i%=6`@@HMBa1<8Vh5u!8~KOUh=7FbP}j=YP1IsOJ%BUISfGQ#Jtg zpfuD2u0eI|2GkEpiM_rM^_DC{?e=ZxU<<0fSO3~KG8_+8e39n|M;1}A#Jm8cQs zp*k=L^;X=5`odkP5v)Lc@paBPTFT*#HUt#k%svRfY+e3cT)K5WH)wnoZ z@Ba=?F6P3Qn1S6maBA{VYq|v0fit)xc^I!EW#nD5 zo~Z02@p``2c%ZIOw3FwM$H-cuQbJaf8RQt*OH{N6t|m9ALV1~NCY@Eepj>Cq^^er~ zvW}A*LlwqfW3Rq#y&kpE8b~6!mGG9DZ1Nh>rkYOJ;Gyz3$3vu_S2eY`k+hJd zbS2Ldm96A*vRw232GNdvj*KQ_Nh?u#Ds<$(*|^i5UxI_lHe2^&ggqaE_mUual_U|B z+ekYyjueo3vY)u`b5L0nI(j>N6z3DkRH6>PLsZ6lm^<(Z(%aTwv4&$ad6GP1ujw~r zC<)kl23||nkO$QKJ)A_6A~Kh#9Pu!_a0TgZ>v!S-@-#^xDMY1`q>=kop`?45yD*bD zWF%3U=Mg&g(eO$+JxX3D?}aY^|8O(aVv=kx-)-%MZOKY9(_Wi|FOmx4BTL8vqH>tL zM8>E>DJOeIb67>LAu4@6LdQ6c(Y79jQ|$RLOeK?5Z<-Ukx#Or=71hDgpi@;*T~%D^ z1jxLSimHlAr>NSQRWY|J*wP#*c8Ut;>O*H%VK7)xU0EDh8Qy9>_`gTR2_;#90s&q={Iz<;YknbPAHhXw`pZ9UoTK_8Fe=?uZB>(^b delta 4524 zcmX}t3v||H0mt!2+PH0OY%p*LIGJ1q46wL~fD8y43SpoKG_fUd69E%MfnEufnNGQi zfKY(RB@JO{1>R7h5Mm;AI8-9(v4h~LLl|;Os_*yxKl3^Jy`Sg*f8Y1H|KI&-#-{11 zQ~4RI1LZx^nmk)Sgn}!mH}P%=v-3h|gmW+x7hn-C#s;_y<^u;V3fbFpy+uIGF3R=!{BRCNu1VF7;q^faO?-6VVl! zi?+82vvCVLz?YD|!>i~@y^ntHgO;p6lMP>R!-lh2=ew~P+TcUz5>}xNRU;n_Yq1ON zLO=HbvhCp$wBt+Y`OTyk+i!-QuoK>hx1cMzypZ+pPh}l9T&ff3-uwscxGg6x6N}NM zD?vLLjUK`ZbcJT31H2zO7GX)eUV}MYZ;9?kzrPb&!oa%SQ_7W9v$IM zw83L|E&dJN>n!%eEh*r|rS63`JRBW(1^T(k=%Jp44qzVoxuww!n&L(c6~AyOzHuBo za{VcCkV9TkvNfI2k#~>wMQ2)u?&YoMmQ|tynTl@dY;>S=(G^{aPHZ)%Y;Xq^zfcq1 zhtBwI?1yKNzfjDwc=(25R~(JqaS?tUU&Nkx8v9@YkJ2}ABzk`_x&p7DTX>`$>u==* zH|pU>=qdjMo!O_+%Q*N7X2CP+48}4#m$nkwl`sum!7B9dtw7FM*p8n1gXmc~iLU4c z^iXHFPbD3-=4Z^u z^C8VcCw^s$N+y+Jw1X179($u-oQ*cPEWW=QeOfl7d%8E?KZ0)MNpvC~p)2(nI+1^) z{p516?56;^6;kb}@Z^MY^ao`M+VMQ>k58jBJdXA8V{|FcqMy&`l-zHI4Y+QFJ+K|R z1!K_PhuO$qc!C#>M|c_8x>Ptp#gSY@wmGyZNq&$9A%9^8FRsXHw1Zl7i%!JrkI|Jl zhpx=!c)y@?@{`^M9cURg!qMm%nSc#^{^wA!fd|l$K8`NYRvd?Wupwr5Nw%T@8*|+X zU77Y+guT!ex($8aD=-W1LEC!}9pF-Q!av2X^dAn!4_rVWH=ivBaszVU!#H&23(&K$ z1es*0iuZS;1KNvOcmT8U7<&3op#wUPu0Xxg6xH2?S-z$!06~`C3@=b zLm$)Xcz-$i{Vmu8YtRAgkN1y8PoV?(J2t`pm9qZMs7beEo8GIYte zqf536b8%nvaP-e;N9WLXE@4M((Iff%Ky<*P;`Ib{=F`!B=BKFG!E$tD>(L*Mo$>ww z^r@&t56?fb2peCQG~5M!%=(~vJrRLOTQ7>g>VpAtZ)%~W5=ExY`g>c3#)j^ z_4z+U#Q~f|J3NE+@N@LgeS!J-CFWr+t?<7nU7Yb`vLv}oH|(#b1F-m%xX*QEd|d_i zkw<*~Ez`*3@c3!@GvqyT zw-=OeC!}AGy40Rp%Up5|`5y74KTJG$H<1U4rHVY4uJI29n-hP4Ed59ZnMsBa%OpkSB>{I~hoxAor4cNGoDFkPu$Qmtwsf xA0-3gwOk$R%W!a0Ue?DO)A7Zc4SCNsUb(RK%{6~%eLipHrEbG&Ms}~R|9=rvpb7v0 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 a32a8a66481acf9144f51c10ba62a1aac262d55f..b4d6b9eb3c53cd63f7c6184b40f8ab85babba3c1 100644 GIT binary patch delta 597 zcmXxh$u9#@9Ki9XW^A!lt+jL_q^b2LLJ=Vj95_gvB&r=tI_)qx3kO8vR6^V&!d;!j zMJxy5AZ|^F_#4ECdeE&aWneQF;Rv?jBv#`FcH=P)a=2IQV1HGSn?Bhvb}?WKsi{qr zi8~oj(9io72JjQ*WQBZPF}9-AdvFM6QR-*dkM}4yrRAshF;I_?n4%*KOru<(g{8Qi z$seICbdCl1fbIB#GX57k(MwVOn%s(PR8B~%O59`(=rXc8p}Tx~4ZX@_z;)pYOf1-| z_KFcP4J%|gW-R0^ubJvUyJm##_^NYzX_?8C7S?>@OEJ@lF2?7LIp+CS)R5F|%S^u} Hg88R^vhq!E delta 491 zcmXZZJxD@P6u|NGUhic}7O0h(Wk?UP58G78Pi(1{wic&`f*@!J92%UPAA&<$K}$jA z7PK_l?jlHn&{}IW^grf>bAR`q`!4sMcRQhvV7(u(8bVtnqQsDJhzEm?Rx(94u#A&< zjyb%;gYIw^ zA8{F9aTR;$$6wq;pDhx`3}*2V%M^2sdZBl!&_aIb(wr}bM}#%WqHZjVJ;DU@3ru1Y zwURfS!2u@l3o97rmbzZW0$$zFZnL6hx0^Vw`W_Oa#OdvE^$&gCw{ 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 = `
- ${photo.caption} + ${photo.caption}
`; carouselInner.appendChild(item); @@ -1901,9 +1987,11 @@ function showRemarkCarouselModal(startIndex) { if (bsCarousel) { bsCarousel.dispose(); } - bsCarousel = new bootstrap.Carousel(carouselEl, { ride: false, interval: false }); + bsCarousel = new bootstrap.Carousel(carouselEl, { ride: false, interval: false, touch: true }); bsCarousel.to(startIndex); + setupCarouselTouchGestures(carouselEl); + carouselEl.addEventListener('slid.bs.carousel', function (e) { updateRemarkCarouselActiveSlide(e.to); }, { once: false }); @@ -1975,6 +2063,11 @@ document.addEventListener('DOMContentLoaded', function() { bsCarousel.next(); } }); + + const carouselEl = document.getElementById('remarkPhotoCarousel'); + if (carouselEl) { + setupCarouselTouchGestures(carouselEl); + } } }); diff --git a/loko/controls/templates/controls/controls_detail_content.html b/loko/controls/templates/controls/controls_detail_content.html index d9d0bc5..a71e0e9 100644 --- a/loko/controls/templates/controls/controls_detail_content.html +++ b/loko/controls/templates/controls/controls_detail_content.html @@ -91,6 +91,16 @@

{% translate 'Lieu' %} : {{ control.location }}

{% endif %} + + {% if control_photos_count > 0 %} +
+ +
+ {% endif %} @@ -776,20 +786,20 @@ - +