- Add slug/short_id fields to database with auto-generation
- Create migration script for existing data - Update admin interface to show only short URLs - Implement redirect system to avoid code duplication - Maintain backward compatibility with old URLs
This commit is contained in:
@@ -3,6 +3,39 @@ import { Campaign, Proposition, Participant, Vote, ParticipantWithVoteStatus, Se
|
||||
import { encryptionService } from './encryption';
|
||||
import { emailService } from './email';
|
||||
|
||||
// Fonction utilitaire pour générer un slug côté client
|
||||
function generateSlugClient(title: string): string {
|
||||
// Convertir en minuscules et remplacer les caractères spéciaux
|
||||
let slug = title.toLowerCase()
|
||||
.replace(/[^a-z0-9\s]/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
.trim();
|
||||
|
||||
// Si le slug est vide, utiliser 'campagne'
|
||||
if (!slug) {
|
||||
slug = 'campagne';
|
||||
}
|
||||
|
||||
// Ajouter un timestamp pour éviter les conflits
|
||||
const timestamp = Date.now().toString().slice(-6);
|
||||
return `${slug}-${timestamp}`;
|
||||
}
|
||||
|
||||
// Fonction utilitaire pour générer un short_id côté client
|
||||
function generateShortIdClient(): string {
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
let result = '';
|
||||
|
||||
// Générer un identifiant de 6 caractères
|
||||
for (let i = 0; i < 6; i++) {
|
||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
|
||||
// Ajouter un timestamp pour éviter les conflits
|
||||
const timestamp = Date.now().toString().slice(-3);
|
||||
return `${result}${timestamp}`;
|
||||
}
|
||||
|
||||
// Services pour les campagnes
|
||||
export const campaignService = {
|
||||
async getAll(): Promise<Campaign[]> {
|
||||
@@ -17,6 +50,27 @@ export const campaignService = {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async create(campaign: any): Promise<Campaign> {
|
||||
// Générer automatiquement le slug si non fourni
|
||||
if (!campaign.slug) {
|
||||
try {
|
||||
// Essayer d'utiliser la fonction PostgreSQL
|
||||
const { data: slugData, error: slugError } = await supabase
|
||||
.rpc('generate_slug', { title: campaign.title });
|
||||
|
||||
if (slugError) {
|
||||
// Si la fonction n'existe pas, générer le slug côté client
|
||||
console.warn('Fonction generate_slug non disponible, génération côté client:', slugError);
|
||||
campaign.slug = generateSlugClient(campaign.title);
|
||||
} else {
|
||||
campaign.slug = slugData;
|
||||
}
|
||||
} catch (error) {
|
||||
// Fallback vers la génération côté client
|
||||
console.warn('Erreur avec generate_slug, génération côté client:', error);
|
||||
campaign.slug = generateSlugClient(campaign.title);
|
||||
}
|
||||
}
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('campaigns')
|
||||
.insert(campaign)
|
||||
@@ -29,6 +83,27 @@ export const campaignService = {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async update(id: string, updates: any): Promise<Campaign> {
|
||||
// Générer automatiquement le slug si le titre a changé et qu'aucun slug n'est fourni
|
||||
if (updates.title && !updates.slug) {
|
||||
try {
|
||||
// Essayer d'utiliser la fonction PostgreSQL
|
||||
const { data: slugData, error: slugError } = await supabase
|
||||
.rpc('generate_slug', { title: updates.title });
|
||||
|
||||
if (slugError) {
|
||||
// Si la fonction n'existe pas, générer le slug côté client
|
||||
console.warn('Fonction generate_slug non disponible, génération côté client:', slugError);
|
||||
updates.slug = generateSlugClient(updates.title);
|
||||
} else {
|
||||
updates.slug = slugData;
|
||||
}
|
||||
} catch (error) {
|
||||
// Fallback vers la génération côté client
|
||||
console.warn('Erreur avec generate_slug, génération côté client:', error);
|
||||
updates.slug = generateSlugClient(updates.title);
|
||||
}
|
||||
}
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('campaigns')
|
||||
.update(updates)
|
||||
@@ -77,6 +152,23 @@ export const campaignService = {
|
||||
propositions: propositionsResult.count || 0,
|
||||
participants: participantsResult.count || 0
|
||||
};
|
||||
},
|
||||
|
||||
// Nouvelle méthode pour récupérer une campagne par slug
|
||||
async getBySlug(slug: string): Promise<Campaign | null> {
|
||||
const { data, error } = await supabase
|
||||
.from('campaigns')
|
||||
.select('*')
|
||||
.eq('slug', slug)
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
if (error.code === 'PGRST116') {
|
||||
return null; // Aucune campagne trouvée
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -160,6 +252,27 @@ export const participantService = {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async create(participant: any): Promise<Participant> {
|
||||
// Générer automatiquement le short_id si non fourni
|
||||
if (!participant.short_id) {
|
||||
try {
|
||||
// Essayer d'utiliser la fonction PostgreSQL
|
||||
const { data: shortIdData, error: shortIdError } = await supabase
|
||||
.rpc('generate_short_id');
|
||||
|
||||
if (shortIdError) {
|
||||
// Si la fonction n'existe pas, générer le short_id côté client
|
||||
console.warn('Fonction generate_short_id non disponible, génération côté client:', shortIdError);
|
||||
participant.short_id = generateShortIdClient();
|
||||
} else {
|
||||
participant.short_id = shortIdData;
|
||||
}
|
||||
} catch (error) {
|
||||
// Fallback vers la génération côté client
|
||||
console.warn('Erreur avec generate_short_id, génération côté client:', error);
|
||||
participant.short_id = generateShortIdClient();
|
||||
}
|
||||
}
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('participants')
|
||||
.insert(participant)
|
||||
@@ -207,6 +320,23 @@ export const participantService = {
|
||||
.eq('id', id);
|
||||
|
||||
if (error) throw error;
|
||||
},
|
||||
|
||||
// Nouvelle méthode pour récupérer un participant par short_id
|
||||
async getByShortId(shortId: string): Promise<Participant | null> {
|
||||
const { data, error } = await supabase
|
||||
.from('participants')
|
||||
.select('*')
|
||||
.eq('short_id', shortId)
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
if (error.code === 'PGRST116') {
|
||||
return null; // Aucun participant trouvé
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user