Skip to content

Commit 1d87ad2

Browse files
Refactor: Split useCampaigns hook into smaller files
Refactored `src/hooks/useCampaigns.ts` into smaller, more focused files to improve maintainability.
1 parent 05a0418 commit 1d87ad2

File tree

4 files changed

+267
-211
lines changed

4 files changed

+267
-211
lines changed

src/hooks/useCampaignData.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
2+
import { useEffect, useState } from 'react';
3+
import {
4+
collection,
5+
query,
6+
where,
7+
onSnapshot
8+
} from 'firebase/firestore';
9+
import { db } from '@/lib/firebase';
10+
import { Campaign } from '@/types';
11+
12+
export const useCampaignData = (userId: string | null, authLoading: boolean) => {
13+
const [campaigns, setCampaigns] = useState<Campaign[]>([]);
14+
const [loading, setLoading] = useState(true);
15+
16+
useEffect(() => {
17+
console.log('🎯 useCampaignData - Effect triggered');
18+
console.log('🎯 authLoading:', authLoading, 'user:', !!userId);
19+
20+
// PROTECTION STRICTE : Aucune requête avant auth complète
21+
if (authLoading) {
22+
console.log('🎯 Auth encore en cours, pas de requête Firebase');
23+
return;
24+
}
25+
26+
if (!userId) {
27+
console.log('🎯 Pas d\'utilisateur, nettoyage des campagnes');
28+
setCampaigns([]);
29+
setLoading(false);
30+
return;
31+
}
32+
33+
console.log('🎯 Auth OK, démarrage requête Firestore pour user:', userId);
34+
35+
const campaignsRef = collection(db, 'campaigns');
36+
const q = query(
37+
campaignsRef,
38+
where('userId', '==', userId)
39+
);
40+
41+
const unsubscribe = onSnapshot(q, (snapshot) => {
42+
console.log('🎯 Firestore snapshot reçu, docs:', snapshot.docs.length);
43+
44+
const campaignsData = snapshot.docs.map(doc => {
45+
const data = doc.data();
46+
return {
47+
id: doc.id,
48+
...data,
49+
createdAt: data.createdAt?.toDate(),
50+
updatedAt: data.updatedAt?.toDate(),
51+
// Valeurs par défaut pour les nouveaux champs Stripe
52+
paymentConfigured: data.paymentConfigured || false,
53+
isDraft: data.isDraft || false,
54+
};
55+
}) as Campaign[];
56+
57+
// Tri côté client - Ne montrer que les campagnes finalisées (non-draft)
58+
const finalizedCampaigns = campaignsData
59+
.filter(campaign => !campaign.isDraft)
60+
.sort((a, b) => {
61+
if (!a.createdAt || !b.createdAt) return 0;
62+
return b.createdAt.getTime() - a.createdAt.getTime();
63+
});
64+
65+
console.log('🎯 Campagnes chargées:', finalizedCampaigns.length);
66+
setCampaigns(finalizedCampaigns);
67+
setLoading(false);
68+
}, (error) => {
69+
console.error('🎯 Erreur Firestore:', error);
70+
setLoading(false);
71+
});
72+
73+
return unsubscribe;
74+
}, [userId, authLoading]);
75+
76+
return { campaigns, loading };
77+
};

src/hooks/useCampaignOperations.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
import { useCallback } from 'react';
3+
import { Campaign } from '@/types';
4+
import {
5+
createCampaignInFirestore,
6+
updateCampaignInFirestore,
7+
finalizeCampaignInFirestore,
8+
deleteCampaignFromFirestore
9+
} from '@/services/campaignService';
10+
11+
export const useCampaignOperations = (userId: string | null) => {
12+
const createCampaign = useCallback(async (
13+
campaignData: Omit<Campaign, 'id' | 'createdAt' | 'updatedAt' | 'userId'>
14+
) => {
15+
if (!userId) throw new Error('User not authenticated');
16+
return createCampaignInFirestore(campaignData, userId);
17+
}, [userId]);
18+
19+
const updateCampaign = useCallback(async (id: string, updates: Partial<Campaign>) => {
20+
return updateCampaignInFirestore(id, updates);
21+
}, []);
22+
23+
const finalizeCampaign = useCallback(async (
24+
id: string,
25+
stripeData: { customerId: string; setupIntentId: string }
26+
) => {
27+
return finalizeCampaignInFirestore(id, stripeData);
28+
}, []);
29+
30+
const deleteCampaign = useCallback(async (id: string) => {
31+
if (!userId) throw new Error('User not authenticated');
32+
return deleteCampaignFromFirestore(id, userId);
33+
}, [userId]);
34+
35+
return {
36+
createCampaign,
37+
updateCampaign,
38+
finalizeCampaign,
39+
deleteCampaign,
40+
};
41+
};

src/hooks/useCampaigns.ts

Lines changed: 6 additions & 211 deletions
Original file line numberDiff line numberDiff line change
@@ -1,221 +1,16 @@
1-
import { useEffect, useState } from 'react';
2-
import {
3-
collection,
4-
addDoc,
5-
query,
6-
where,
7-
onSnapshot,
8-
updateDoc,
9-
deleteDoc,
10-
doc,
11-
getDocs
12-
} from 'firebase/firestore';
13-
import { db } from '@/lib/firebase';
1+
142
import { useAuth } from '@/hooks/useAuth';
15-
import { Campaign } from '@/types';
3+
import { useCampaignData } from '@/hooks/useCampaignData';
4+
import { useCampaignOperations } from '@/hooks/useCampaignOperations';
165

176
export const useCampaigns = () => {
18-
const [campaigns, setCampaigns] = useState<Campaign[]>([]);
19-
const [loading, setLoading] = useState(true);
207
const { user, loading: authLoading } = useAuth();
21-
22-
useEffect(() => {
23-
console.log('🎯 useCampaigns - Effect triggered');
24-
console.log('🎯 authLoading:', authLoading, 'user:', !!user);
25-
26-
// PROTECTION STRICTE : Aucune requête avant auth complète
27-
if (authLoading) {
28-
console.log('🎯 Auth encore en cours, pas de requête Firebase');
29-
return;
30-
}
31-
32-
if (!user) {
33-
console.log('🎯 Pas d\'utilisateur, nettoyage des campagnes');
34-
setCampaigns([]);
35-
setLoading(false);
36-
return;
37-
}
38-
39-
console.log('🎯 Auth OK, démarrage requête Firestore pour user:', user.uid);
40-
41-
const campaignsRef = collection(db, 'campaigns');
42-
const q = query(
43-
campaignsRef,
44-
where('userId', '==', user.uid)
45-
);
46-
47-
const unsubscribe = onSnapshot(q, (snapshot) => {
48-
console.log('🎯 Firestore snapshot reçu, docs:', snapshot.docs.length);
49-
50-
const campaignsData = snapshot.docs.map(doc => {
51-
const data = doc.data();
52-
return {
53-
id: doc.id,
54-
...data,
55-
createdAt: data.createdAt?.toDate(),
56-
updatedAt: data.updatedAt?.toDate(),
57-
// Valeurs par défaut pour les nouveaux champs Stripe
58-
paymentConfigured: data.paymentConfigured || false,
59-
isDraft: data.isDraft || false,
60-
};
61-
}) as Campaign[];
62-
63-
// Tri côté client - Ne montrer que les campagnes finalisées (non-draft)
64-
const finalizedCampaigns = campaignsData
65-
.filter(campaign => !campaign.isDraft)
66-
.sort((a, b) => {
67-
if (!a.createdAt || !b.createdAt) return 0;
68-
return b.createdAt.getTime() - a.createdAt.getTime();
69-
});
70-
71-
console.log('🎯 Campagnes chargées:', finalizedCampaigns.length);
72-
setCampaigns(finalizedCampaigns);
73-
setLoading(false);
74-
}, (error) => {
75-
console.error('🎯 Erreur Firestore:', error);
76-
setLoading(false);
77-
});
78-
79-
return unsubscribe;
80-
}, [user, authLoading]);
81-
82-
const createCampaign = async (campaignData: Omit<Campaign, 'id' | 'createdAt' | 'updatedAt' | 'userId'>) => {
83-
if (!user) throw new Error('User not authenticated');
84-
85-
const newCampaign = {
86-
...campaignData,
87-
userId: user.uid,
88-
createdAt: new Date(),
89-
updatedAt: new Date(),
90-
// Valeurs par défaut pour Stripe
91-
paymentConfigured: campaignData.paymentConfigured || false,
92-
isDraft: campaignData.isDraft || false,
93-
};
94-
95-
console.log('Creating campaign:', newCampaign);
96-
const docRef = await addDoc(collection(db, 'campaigns'), newCampaign);
97-
console.log('Campaign created with ID:', docRef.id);
98-
return docRef.id;
99-
};
100-
101-
const updateCampaign = async (id: string, updates: Partial<Campaign>) => {
102-
const campaignRef = doc(db, 'campaigns', id);
103-
await updateDoc(campaignRef, {
104-
...updates,
105-
updatedAt: new Date(),
106-
});
107-
};
108-
109-
const finalizeCampaign = async (id: string, stripeData: { customerId: string; setupIntentId: string }) => {
110-
const campaignRef = doc(db, 'campaigns', id);
111-
await updateDoc(campaignRef, {
112-
isDraft: false,
113-
paymentConfigured: true,
114-
stripeCustomerId: stripeData.customerId,
115-
stripeSetupIntentId: stripeData.setupIntentId,
116-
updatedAt: new Date(),
117-
});
118-
console.log('✅ Campagne finalisée avec succès:', id);
119-
};
120-
121-
const deleteCampaign = async (id: string) => {
122-
if (!user) {
123-
throw new Error('User not authenticated');
124-
}
125-
126-
console.log('🗑️ Début suppression campagne:', id);
127-
console.log('🗑️ User connecté:', user.uid);
128-
129-
try {
130-
// 1. Supprimer tous les affiliés de cette campagne
131-
console.log('🗑️ Suppression des affiliés...');
132-
const affiliatesQuery = query(
133-
collection(db, 'affiliates'),
134-
where('campaignId', '==', id),
135-
where('userId', '==', user.uid) // Ajout de l'userId pour respecter les règles
136-
);
137-
const affiliatesSnapshot = await getDocs(affiliatesQuery);
138-
console.log('🗑️ Affiliés trouvés:', affiliatesSnapshot.size);
139-
140-
const deleteAffiliatesPromises = affiliatesSnapshot.docs.map(doc => {
141-
console.log('🗑️ Suppression affilié:', doc.id);
142-
return deleteDoc(doc.ref);
143-
});
144-
await Promise.all(deleteAffiliatesPromises);
145-
console.log('✅ Affiliés supprimés avec succès');
146-
147-
// 2. Supprimer tous les clics de cette campagne
148-
console.log('🗑️ Suppression des clics...');
149-
const clicksQuery = query(
150-
collection(db, 'clicks'),
151-
where('campaignId', '==', id)
152-
);
153-
const clicksSnapshot = await getDocs(clicksQuery);
154-
console.log('🗑️ Clics trouvés:', clicksSnapshot.size);
155-
156-
const deleteClicksPromises = clicksSnapshot.docs.map(doc => {
157-
console.log('🗑️ Suppression clic:', doc.id);
158-
return deleteDoc(doc.ref);
159-
});
160-
await Promise.all(deleteClicksPromises);
161-
console.log('✅ Clics supprimés avec succès');
162-
163-
// 3. Supprimer tous les liens courts de cette campagne
164-
console.log('🗑️ Suppression des liens courts...');
165-
const shortLinksQuery = query(
166-
collection(db, 'shortLinks'),
167-
where('campaignId', '==', id)
168-
);
169-
const shortLinksSnapshot = await getDocs(shortLinksQuery);
170-
console.log('🗑️ Liens courts trouvés:', shortLinksSnapshot.size);
171-
172-
const deleteShortLinksPromises = shortLinksSnapshot.docs.map(doc => {
173-
console.log('🗑️ Suppression lien court:', doc.id);
174-
return deleteDoc(doc.ref);
175-
});
176-
await Promise.all(deleteShortLinksPromises);
177-
console.log('✅ Liens courts supprimés avec succès');
178-
179-
// 4. Supprimer toutes les conversions de cette campagne
180-
console.log('🗑️ Suppression des conversions...');
181-
const conversionsQuery = query(
182-
collection(db, 'conversions'),
183-
where('campaignId', '==', id)
184-
);
185-
const conversionsSnapshot = await getDocs(conversionsQuery);
186-
console.log('🗑️ Conversions trouvées:', conversionsSnapshot.size);
187-
188-
const deleteConversionsPromises = conversionsSnapshot.docs.map(doc => {
189-
console.log('🗑️ Suppression conversion:', doc.id);
190-
return deleteDoc(doc.ref);
191-
});
192-
await Promise.all(deleteConversionsPromises);
193-
console.log('✅ Conversions supprimées avec succès');
194-
195-
// 5. Finalement, supprimer la campagne elle-même
196-
console.log('🗑️ Suppression de la campagne...');
197-
const campaignRef = doc(db, 'campaigns', id);
198-
await deleteDoc(campaignRef);
199-
200-
console.log('✅ Suppression complète terminée pour la campagne:', id);
201-
202-
} catch (error) {
203-
console.error('❌ Erreur lors de la suppression en cascade:', error);
204-
console.error('❌ Détails de l\'erreur:', {
205-
message: error.message,
206-
code: error.code,
207-
stack: error.stack
208-
});
209-
throw error;
210-
}
211-
};
8+
const { campaigns, loading } = useCampaignData(user?.uid || null, authLoading);
9+
const operations = useCampaignOperations(user?.uid || null);
21210

21311
return {
21412
campaigns,
21513
loading,
216-
createCampaign,
217-
updateCampaign,
218-
finalizeCampaign,
219-
deleteCampaign,
14+
...operations,
22015
};
22116
};

0 commit comments

Comments
 (0)