Skip to content

Commit 8225493

Browse files
Refactor and optimize project
Review and optimize the entire project for cleanliness and efficiency.
1 parent 9d6a8c8 commit 8225493

13 files changed

Lines changed: 960 additions & 14 deletions

File tree

src/components/CampaignsList.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { CampaignCard } from '@/components/CampaignCard';
99
import { CompactCampaignCard } from '@/components/CompactCampaignCard';
1010
import { useState, useCallback, memo } from 'react';
1111
import { CreateCampaignDialog } from '@/components/CreateCampaignDialog';
12+
import Logger from '@/utils/logger';
1213

1314
interface CampaignsListProps {
1415
onSuccessModalTrigger: (campaignId: string, campaignName: string) => void;
@@ -19,7 +20,7 @@ export const CampaignsList = memo(({ onSuccessModalTrigger }: CampaignsListProps
1920
const { toast } = useToast();
2021
const [viewMode, setViewMode] = useState<'normal' | 'compact'>('normal');
2122

22-
console.log('CampaignsList render - campaigns:', campaigns, 'loading:', loading);
23+
Logger.debug('CampaignsList render - campaigns:', campaigns.length, 'loading:', loading);
2324

2425
const copyTrackingUrl = useCallback((campaignId: string) => {
2526
const trackingUrl = `https://refspring.com/r/${campaignId}`;
@@ -35,7 +36,7 @@ export const CampaignsList = memo(({ onSuccessModalTrigger }: CampaignsListProps
3536
}, []);
3637

3738
if (loading) {
38-
console.log('CampaignsList: showing loading state');
39+
Logger.debug('CampaignsList: showing loading state');
3940
return (
4041
<div className="space-y-6">
4142
{[1, 2, 3].map((i) => (
@@ -54,7 +55,7 @@ export const CampaignsList = memo(({ onSuccessModalTrigger }: CampaignsListProps
5455
}
5556

5657
if (campaigns.length === 0) {
57-
console.log('CampaignsList: showing empty state');
58+
Logger.debug('CampaignsList: showing empty state');
5859
return (
5960
<Card className="bg-gradient-to-br from-blue-50 to-purple-50 border-slate-200 rounded-2xl">
6061
<CardContent className="flex flex-col items-center justify-center py-16">
@@ -88,7 +89,7 @@ export const CampaignsList = memo(({ onSuccessModalTrigger }: CampaignsListProps
8889
);
8990
}
9091

91-
console.log('CampaignsList: rendering campaigns list');
92+
Logger.debug('CampaignsList: rendering campaigns list');
9293
return (
9394
<div className="space-y-6">
9495
{/* Sélecteur de mode d'affichage */}

src/components/Dashboard.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { BarChart3, Users, DollarSign, Percent } from 'lucide-react';
2424
import { auth } from '@/lib/firebase';
2525
import { collection, query, where, getDocs } from 'firebase/firestore';
2626
import { db } from '@/lib/firebase';
27+
import Logger from '@/utils/logger';
2728

2829
interface GlobalStats {
2930
totalRevenue: number;
@@ -48,7 +49,7 @@ const DashboardStats = ({ activeCampaigns, totalCampaigns, totalAffiliates, user
4849
useEffect(() => {
4950
const loadGlobalStats = async () => {
5051
if (!userId) {
51-
console.log('📊 SECURITY - No userId provided for stats');
52+
Logger.security('No userId provided for stats');
5253
return;
5354
}
5455

@@ -58,15 +59,15 @@ const DashboardStats = ({ activeCampaigns, totalCampaigns, totalAffiliates, user
5859
return; // Sortir si l'auth n'est pas prête
5960
}
6061

61-
console.log(`📊 SECURITY - Loading secured global stats ${periodLabel} for:`, userId);
62+
Logger.security(`Loading secured global stats ${periodLabel} for: ${userId}`);
6263

6364
// Récupérer les campagnes de l'utilisateur avec vérification de propriété
6465
const campaignsQuery = query(collection(db, 'campaigns'), where('userId', '==', userId));
6566
const campaignsSnapshot = await getDocs(campaignsQuery);
6667
const campaignIds = campaignsSnapshot.docs.map(doc => doc.id);
6768

6869
if (campaignIds.length === 0) {
69-
console.log('📊 SECURITY - No campaigns found for user');
70+
Logger.security('No campaigns found for user');
7071
setLoading(false);
7172
return;
7273
}
@@ -113,7 +114,7 @@ const DashboardStats = ({ activeCampaigns, totalCampaigns, totalAffiliates, user
113114

114115
const conversionRate = totalClicks > 0 ? (totalConversions / totalClicks) * 100 : 0;
115116

116-
console.log(`📊 SECURITY - Secured stats calculated ${periodLabel}:`, {
117+
Logger.security(`Secured stats calculated ${periodLabel}:`, {
117118
totalClicks,
118119
totalConversions,
119120
totalRevenue,
@@ -247,9 +248,9 @@ export const Dashboard = memo(() => {
247248
// CORRECTION: Vérification de sécurité au montage SEULEMENT quand l'auth est prête
248249
useEffect(() => {
249250
if (!isLoading) {
250-
console.log('📊 SECURITY - Dashboard mounted, checking authentication');
251+
Logger.security('Dashboard mounted, checking authentication');
251252
if (!requireAuthentication('accéder au dashboard')) {
252-
console.log('📊 SECURITY - Auth not ready or failed, will retry when ready');
253+
Logger.warning('Auth not ready or failed, will retry when ready');
253254
}
254255
}
255256
}, [requireAuthentication, isLoading]);
@@ -261,7 +262,7 @@ export const Dashboard = memo(() => {
261262
if (newCampaignCreated) {
262263
try {
263264
const campaignData = JSON.parse(newCampaignCreated);
264-
console.log('🎉 DASHBOARD: Nouvelle campagne détectée:', campaignData);
265+
Logger.info('Nouvelle campagne détectée:', campaignData);
265266

266267
setNewCampaignData(campaignData);
267268
setShowSuccessModal(true);
@@ -284,7 +285,7 @@ export const Dashboard = memo(() => {
284285
if (isAuthenticated && !tourCompleted && !campaignsLoading && !affiliatesLoading) {
285286
// Attendre un peu que le dashboard soit rendu
286287
const timer = setTimeout(() => {
287-
console.log('🎯 Starting guided tour for new user');
288+
Logger.info('Starting guided tour for new user');
288289
startTour();
289290
}, 1000);
290291

@@ -294,11 +295,11 @@ export const Dashboard = memo(() => {
294295

295296
const handleLogout = useCallback(async () => {
296297
try {
297-
console.log('🔐 SECURITY - User logout initiated');
298+
Logger.security('User logout initiated');
298299
await auth.signOut();
299300
localStorage.removeItem('auth_user');
300301
sessionStorage.clear(); // Nettoyer toutes les données de session
301-
console.log('🔐 SECURITY - User logged out successfully');
302+
Logger.security('User logged out successfully');
302303
} catch (error) {
303304
console.error('🔐 SECURITY - Logout error:', error);
304305
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { memo, ReactNode } from 'react';
2+
import { AlertCircle, RefreshCw } from 'lucide-react';
3+
import { Button } from '@/components/ui/button';
4+
import { Alert, AlertDescription } from '@/components/ui/alert';
5+
6+
interface ErrorDisplayProps {
7+
error: string | Error;
8+
onRetry?: () => void;
9+
retryText?: string;
10+
className?: string;
11+
variant?: 'destructive' | 'default';
12+
icon?: ReactNode;
13+
showRetry?: boolean;
14+
}
15+
16+
export const ErrorDisplay = memo<ErrorDisplayProps>(({
17+
error,
18+
onRetry,
19+
retryText = 'Réessayer',
20+
className,
21+
variant = 'destructive',
22+
icon = <AlertCircle className="h-4 w-4" />,
23+
showRetry = true
24+
}) => {
25+
const errorMessage = error instanceof Error ? error.message : error;
26+
27+
return (
28+
<Alert variant={variant} className={className}>
29+
{icon}
30+
<AlertDescription className="flex items-center justify-between">
31+
<span>{errorMessage}</span>
32+
{showRetry && onRetry && (
33+
<Button
34+
variant="outline"
35+
size="sm"
36+
onClick={onRetry}
37+
className="ml-4"
38+
>
39+
<RefreshCw className="h-3 w-3 mr-1" />
40+
{retryText}
41+
</Button>
42+
)}
43+
</AlertDescription>
44+
</Alert>
45+
);
46+
});
47+
48+
ErrorDisplay.displayName = 'ErrorDisplay';
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { memo } from 'react';
2+
import { Loader2 } from 'lucide-react';
3+
import { cn } from '@/lib/utils';
4+
5+
interface LoadingSpinnerProps {
6+
size?: 'sm' | 'md' | 'lg';
7+
className?: string;
8+
text?: string;
9+
}
10+
11+
const sizeClasses = {
12+
sm: 'h-4 w-4',
13+
md: 'h-6 w-6',
14+
lg: 'h-8 w-8'
15+
};
16+
17+
export const LoadingSpinner = memo<LoadingSpinnerProps>(({
18+
size = 'md',
19+
className,
20+
text
21+
}) => {
22+
if (text) {
23+
return (
24+
<div className="flex items-center gap-2 text-muted-foreground">
25+
<Loader2 className={cn(sizeClasses[size], 'animate-spin', className)} />
26+
<span className="text-sm">{text}</span>
27+
</div>
28+
);
29+
}
30+
31+
return (
32+
<Loader2 className={cn(sizeClasses[size], 'animate-spin', className)} />
33+
);
34+
});
35+
36+
LoadingSpinner.displayName = 'LoadingSpinner';
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { memo, forwardRef, ReactNode } from 'react';
2+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
3+
import { cn } from '@/lib/utils';
4+
5+
interface OptimizedCardProps {
6+
title?: string;
7+
description?: string;
8+
children: ReactNode;
9+
className?: string;
10+
headerClassName?: string;
11+
contentClassName?: string;
12+
loading?: boolean;
13+
error?: string;
14+
action?: ReactNode;
15+
variant?: 'default' | 'success' | 'warning' | 'error';
16+
}
17+
18+
const cardVariants = {
19+
default: 'border-slate-200',
20+
success: 'border-green-200 bg-green-50/50',
21+
warning: 'border-orange-200 bg-orange-50/50',
22+
error: 'border-red-200 bg-red-50/50'
23+
};
24+
25+
export const OptimizedCard = memo(forwardRef<HTMLDivElement, OptimizedCardProps>(
26+
({
27+
title,
28+
description,
29+
children,
30+
className,
31+
headerClassName,
32+
contentClassName,
33+
loading = false,
34+
error,
35+
action,
36+
variant = 'default',
37+
...props
38+
}, ref) => {
39+
if (loading) {
40+
return (
41+
<Card ref={ref} className={cn(cardVariants[variant], className)} {...props}>
42+
<CardHeader className={headerClassName}>
43+
<div className="animate-pulse space-y-2">
44+
<div className="h-6 bg-slate-200 rounded w-3/4"></div>
45+
{description && <div className="h-4 bg-slate-200 rounded w-1/2"></div>}
46+
</div>
47+
</CardHeader>
48+
<CardContent className={contentClassName}>
49+
<div className="animate-pulse space-y-3">
50+
<div className="h-4 bg-slate-200 rounded"></div>
51+
<div className="h-4 bg-slate-200 rounded w-5/6"></div>
52+
<div className="h-4 bg-slate-200 rounded w-4/6"></div>
53+
</div>
54+
</CardContent>
55+
</Card>
56+
);
57+
}
58+
59+
if (error) {
60+
return (
61+
<Card ref={ref} className={cn(cardVariants.error, className)} {...props}>
62+
<CardContent className="p-6">
63+
<div className="text-center text-red-600">
64+
<p className="font-semibold">Erreur</p>
65+
<p className="text-sm">{error}</p>
66+
</div>
67+
</CardContent>
68+
</Card>
69+
);
70+
}
71+
72+
return (
73+
<Card ref={ref} className={cn(cardVariants[variant], className)} {...props}>
74+
{(title || description || action) && (
75+
<CardHeader className={cn('flex flex-row items-center justify-between', headerClassName)}>
76+
<div className="space-y-1.5">
77+
{title && <CardTitle>{title}</CardTitle>}
78+
{description && <CardDescription>{description}</CardDescription>}
79+
</div>
80+
{action}
81+
</CardHeader>
82+
)}
83+
<CardContent className={contentClassName}>
84+
{children}
85+
</CardContent>
86+
</Card>
87+
);
88+
}
89+
));
90+
91+
OptimizedCard.displayName = 'OptimizedCard';

0 commit comments

Comments
 (0)