diff --git a/database/supabase-schema.sql b/database/supabase-schema.sql index 52c3b55..e9eb8c3 100644 --- a/database/supabase-schema.sql +++ b/database/supabase-schema.sql @@ -218,9 +218,11 @@ DECLARE counter INTEGER := 0; max_attempts INTEGER := 10; BEGIN - -- Convertir le titre en slug (minuscules, remplacer espaces par tirets, supprimer caractères spéciaux) - base_slug := lower(regexp_replace(title, '[^a-zA-Z0-9\s]', '', 'g')); + -- Convertir le titre en slug (minuscules, supprimer accents, remplacer espaces par tirets, supprimer caractères spéciaux) + base_slug := lower(unaccent(title)); + base_slug := regexp_replace(base_slug, '[^a-z0-9\s-]', '', 'g'); base_slug := regexp_replace(base_slug, '\s+', '-', 'g'); + base_slug := regexp_replace(base_slug, '-+', '-', 'g'); base_slug := trim(both '-' from base_slug); -- Si le slug est vide, utiliser un slug par défaut diff --git a/package.json b/package.json index c1fa8d1..08d7492 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mes-budgets-participatifs", - "version": "0.1.0", + "version": "0.2.0", "private": true, "scripts": { "dev": "next dev --turbopack", diff --git a/src/app/admin/campaigns/[id]/participants/page.tsx b/src/app/admin/campaigns/[id]/participants/page.tsx index ee5f9da..1939089 100644 --- a/src/app/admin/campaigns/[id]/participants/page.tsx +++ b/src/app/admin/campaigns/[id]/participants/page.tsx @@ -87,6 +87,10 @@ function CampaignParticipantsPageContent() { const handleImportParticipants = async (data: any[]) => { try { + // Récupérer les participants existants pour vérifier les emails + const existingParticipants = await participantService.getByCampaign(campaignId); + const existingEmails = new Set(existingParticipants.map(p => p.email.toLowerCase())); + const participantsToCreate = data.map(row => ({ campaign_id: campaignId, first_name: row.Prénom || '', @@ -94,11 +98,24 @@ function CampaignParticipantsPageContent() { email: row.Email || '' })); - // Créer les participants un par un - for (const participant of participantsToCreate) { + // Filtrer les participants pour éviter les doublons d'email + const newParticipants = participantsToCreate.filter(participant => { + const email = participant.email.toLowerCase(); + return email && !existingEmails.has(email); + }); + + const skippedCount = participantsToCreate.length - newParticipants.length; + + // Créer les nouveaux participants un par un + for (const participant of newParticipants) { await participantService.create(participant); } + // Afficher un message informatif si des participants ont été ignorés + if (skippedCount > 0) { + alert(`${skippedCount} participant(s) ignoré(s) car leur email existe déjà dans la campagne.`); + } + loadData(); } catch (error) { console.error('Erreur lors de l\'import des participants:', error); diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 29ccce7..c61b8af 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -15,6 +15,7 @@ import { Badge } from '@/components/ui/badge'; import AuthGuard from '@/components/AuthGuard'; import Footer from '@/components/Footer'; +import VersionDisplay from '@/components/VersionDisplay'; import { FolderOpen, Users, FileText, Plus, BarChart3, Settings, Check, Copy, Mail, Share2 } from 'lucide-react'; import StatusSwitch from '@/components/StatusSwitch'; import { MarkdownContent } from '@/components/MarkdownContent'; @@ -499,6 +500,9 @@ function AdminPageContent() { {/* Footer */}