From bd4f63b99c5935705b33e782e9c437ba3981ea97 Mon Sep 17 00:00:00 2001 From: Yannick Le Duc Date: Tue, 26 Aug 2025 21:49:45 +0200 Subject: [PATCH] =?UTF-8?q?fine=20tux=20=C3=A0=20max=20la=20page=20de=20vo?= =?UTF-8?q?te=20(better=20ux)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[id]/vote/[participantId]/page.tsx | 150 ++++++++++++++---- 1 file changed, 119 insertions(+), 31 deletions(-) diff --git a/src/app/campaigns/[id]/vote/[participantId]/page.tsx b/src/app/campaigns/[id]/vote/[participantId]/page.tsx index 00747e7..e0b0343 100644 --- a/src/app/campaigns/[id]/vote/[participantId]/page.tsx +++ b/src/app/campaigns/[id]/vote/[participantId]/page.tsx @@ -27,6 +27,9 @@ export default function PublicVotePage() { const [localVotes, setLocalVotes] = useState>({}); const [totalVoted, setTotalVoted] = useState(0); const [isRandomOrder, setIsRandomOrder] = useState(false); + const [isCompactView, setIsCompactView] = useState(false); + const [currentVisibleProposition, setCurrentVisibleProposition] = useState(1); + const [isOverBudget, setIsOverBudget] = useState(false); useEffect(() => { if (campaignId && participantId) { @@ -38,7 +41,52 @@ export default function PublicVotePage() { useEffect(() => { const total = Object.values(localVotes).reduce((sum, amount) => sum + amount, 0); setTotalVoted(total); - }, [localVotes]); + + // Vérifier si on dépasse le budget + if (campaign && total > campaign.budget_per_user) { + setIsOverBudget(true); + // Arrêter la vibration après 1 seconde + setTimeout(() => setIsOverBudget(false), 1000); + } else { + setIsOverBudget(false); + } + }, [localVotes, campaign]); + + // Observer les propositions visibles + useEffect(() => { + if (propositions.length === 0) return; + + const observer = new IntersectionObserver( + (entries) => { + let highestVisibleIndex = 1; + + entries.forEach((entry) => { + if (entry.isIntersecting) { + const propositionIndex = parseInt(entry.target.getAttribute('data-proposition-index') || '1'); + if (propositionIndex > highestVisibleIndex) { + highestVisibleIndex = propositionIndex; + } + } + }); + + if (highestVisibleIndex > 1) { + setCurrentVisibleProposition(highestVisibleIndex); + } + }, + { + threshold: 0.3, // La proposition doit être visible à 30% pour être considérée comme active + rootMargin: '-10% 0px -10% 0px' // Zone de détection réduite + } + ); + + // Attendre que le DOM soit mis à jour + setTimeout(() => { + const propositionElements = document.querySelectorAll('[data-proposition-index]'); + propositionElements.forEach((element) => observer.observe(element)); + }, 100); + + return () => observer.disconnect(); + }, [propositions, isCompactView]); const loadData = async () => { try { @@ -253,10 +301,18 @@ export default function PublicVotePage() {
-
+
0 + ? 'text-indigo-600' + : 'text-gray-900' + } ${isOverBudget ? 'animate-bounce' : ''}`}> {totalVoted}€ / {campaign?.budget_per_user}€
-
-
+
{/* Informations de la campagne */}
-

{campaign?.description}

+

{campaign?.description}

{isRandomOrder && ( -
-

- ℹ️ - Les propositions sont affichées dans un ordre aléatoire pour éviter les biais liés à l'ordre de présentation. -

+
+ ℹ️ Les propositions sont affichées dans un ordre aléatoire pour éviter les biais liés à l'ordre de présentation.
)}
@@ -309,35 +362,42 @@ export default function PublicVotePage() {

Aucune proposition n'a été soumise pour cette campagne.

) : ( -
- {propositions.map((proposition) => ( +
+ {propositions.map((proposition, index) => (
0 ? 'border-indigo-400 shadow-lg bg-indigo-100' : 'bg-white border-gray-200' }`} > -
- Proposition -
-
-
-
-

- {proposition.title} -

-

- {proposition.description} -

-
+ {!isCompactView && ( +
+ Proposition
- -
- + )} +
+
+
+

+ {proposition.title} +

+ {!isCompactView && ( +

+ {proposition.description} +

+ )} +
+
+ +
+ {!isCompactView && ( + + )}
{/* Slider */}
@@ -384,7 +444,7 @@ export default function PublicVotePage() {
{/* Valeur sélectionnée */} - {(localVotes[proposition.id] && localVotes[proposition.id] > 0) && ( + {(localVotes[proposition.id] && localVotes[proposition.id] > 0) && !isCompactView && (
Vote sélectionné : {localVotes[proposition.id]}€ @@ -405,6 +465,34 @@ export default function PublicVotePage() {
)}
+ + {/* Barre fixe en bas */} +
+
+
+
+ Proposition {currentVisibleProposition} / {propositions.length} +
+ +
+ Juste les titres + + Avec descriptions +
+
+
+
); }