liens publics pour voter pour les participants

This commit is contained in:
Yannick Le Duc
2025-08-25 15:04:27 +02:00
parent 30a228e14f
commit 06bfe11dcc
8 changed files with 583 additions and 17 deletions

View File

@@ -3,8 +3,8 @@
import { useState, useEffect } from 'react';
import Link from 'next/link';
import { useParams } from 'next/navigation';
import { Campaign, Participant } from '@/types';
import { campaignService, participantService } from '@/lib/services';
import { Campaign, Participant, ParticipantWithVoteStatus } from '@/types';
import { campaignService, participantService, voteService } from '@/lib/services';
import AddParticipantModal from '@/components/AddParticipantModal';
import EditParticipantModal from '@/components/EditParticipantModal';
import DeleteParticipantModal from '@/components/DeleteParticipantModal';
@@ -17,12 +17,13 @@ export default function CampaignParticipantsPage() {
const campaignId = params.id as string;
const [campaign, setCampaign] = useState<Campaign | null>(null);
const [participants, setParticipants] = useState<Participant[]>([]);
const [participants, setParticipants] = useState<ParticipantWithVoteStatus[]>([]);
const [loading, setLoading] = useState(true);
const [showAddModal, setShowAddModal] = useState(false);
const [showEditModal, setShowEditModal] = useState(false);
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [selectedParticipant, setSelectedParticipant] = useState<Participant | null>(null);
const [copiedParticipantId, setCopiedParticipantId] = useState<string | null>(null);
useEffect(() => {
if (campaignId) {
@@ -33,13 +34,13 @@ export default function CampaignParticipantsPage() {
const loadData = async () => {
try {
setLoading(true);
const [campaignData, participantsData] = await Promise.all([
const [campaigns, participantsWithVoteStatus] = await Promise.all([
campaignService.getAll().then(campaigns => campaigns.find(c => c.id === campaignId)),
participantService.getByCampaign(campaignId)
voteService.getParticipantVoteStatus(campaignId)
]);
setCampaign(campaignData || null);
setParticipants(participantsData);
setCampaign(campaigns || null);
setParticipants(participantsWithVoteStatus);
} catch (error) {
console.error('Erreur lors du chargement des données:', error);
} finally {
@@ -181,9 +182,65 @@ export default function CampaignParticipantsPage() {
</p>
</div>
</div>
<p className="text-xs text-gray-500">
Inscrit le {new Date(participant.created_at).toLocaleDateString('fr-FR')}
</p>
<div className="flex items-center space-x-4 text-xs text-gray-500">
<span>
<strong>Inscrit le :</strong> {new Date(participant.created_at).toLocaleDateString('fr-FR')}
</span>
<span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${
participant.has_voted
? 'bg-green-100 text-green-800'
: 'bg-yellow-100 text-yellow-800'
}`}>
{participant.has_voted ? 'A voté' : 'N\'a pas voté'}
</span>
{participant.has_voted && participant.total_voted_amount && (
<span>
<strong>Total voté :</strong> {participant.total_voted_amount}
</span>
)}
</div>
{campaign?.status === 'voting' && (
<div className="mt-4 p-3 bg-blue-50 rounded-lg border border-blue-200">
<div className="flex items-center justify-between">
<div className="flex-1">
<h4 className="text-sm font-medium text-blue-900 mb-1">Lien de vote personnel</h4>
<div className="flex items-center space-x-2">
<input
type="text"
readOnly
value={`${window.location.origin}/campaigns/${campaignId}/vote/${participant.id}`}
className="flex-1 text-xs bg-white border border-blue-300 rounded px-2 py-1 text-blue-700 font-mono"
/>
<button
onClick={() => {
navigator.clipboard.writeText(`${window.location.origin}/campaigns/${campaignId}/vote/${participant.id}`);
setCopiedParticipantId(participant.id);
setTimeout(() => setCopiedParticipantId(null), 2000);
}}
className="inline-flex items-center px-2 py-1 border border-blue-300 rounded text-xs font-medium text-blue-700 bg-white hover:bg-blue-50"
title="Copier le lien"
>
{copiedParticipantId === participant.id ? (
<>
<svg className="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
Copié !
</>
) : (
<>
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
</>
)}
</button>
</div>
</div>
</div>
</div>
)}
</div>
<div className="flex items-center space-x-2 ml-6">
<button