-
+ {/* Stats */}
+
+
+
{drawResult.statistics.totalParticipants}
-
Total participants
+
Total participants
-
-
+
+
{drawResult.statistics.eligibleParticipants}
-
Éligibles
+
Éligibles
-
-
-
-
+
)}
);
diff --git a/components/admin/PrizeManagement.tsx b/components/admin/PrizeManagement.tsx
index a5c7790..f812fbc 100644
--- a/components/admin/PrizeManagement.tsx
+++ b/components/admin/PrizeManagement.tsx
@@ -1,8 +1,9 @@
'use client';
-import { useState, useEffect } from 'react';
+import { useState, useEffect, useMemo } from 'react';
import { adminService } from '@/services/admin.service';
import { Prize, CreatePrizeData, UpdatePrizeData } from '@/types';
+import { Gift, Package, Trophy, Percent, Archive, RefreshCw, X } from 'lucide-react';
export default function PrizeManagement() {
const [prizes, setPrizes] = useState
([]);
@@ -91,86 +92,189 @@ export default function PrizeManagement() {
setIsModalOpen(false);
};
+ // Calculer les stats
+ const prizeStats = useMemo(() => {
+ const totalStock = prizes.reduce((acc, p) => acc + (p.initialStock || p.stock || 0), 0);
+ const totalUsed = prizes.reduce((acc, p) => acc + (p.ticketsUsed || 0), 0);
+ const activeCount = prizes.filter(p => p.isActive).length;
+ return { totalStock, totalUsed, activeCount, totalPrizes: prizes.length };
+ }, [prizes]);
+
+ // Fonction pour obtenir l'icône et la couleur selon le type
+ const getPrizeStyle = (type: string) => {
+ switch (type) {
+ case 'GRAND_PRIZE':
+ return { bg: 'from-purple-500 to-purple-600', icon: Trophy, color: 'purple' };
+ case 'PHYSICAL':
+ return { bg: 'from-blue-500 to-blue-600', icon: Package, color: 'blue' };
+ default:
+ return { bg: 'from-emerald-500 to-emerald-600', icon: Gift, color: 'emerald' };
+ }
+ };
+
if (loading) {
- return Chargement des prix...
;
+ return (
+
+
+
+
Chargement des lots...
+
+
+ );
}
return (
-
-
Gestion des Prix
+ {/* Stats rapides */}
+
+
+
+
+
+
+
+
{prizeStats.totalPrizes}
+
Total Lots
+
+
+
+
+
+
+
+
{prizeStats.totalStock}
+
Stock Total
+
+
+
+
+
+
+
+
{prizeStats.totalUsed}
+
Distribués
+
+
+
+
+
+
+
+
{prizeStats.totalStock > 0 ? ((prizeStats.totalUsed / prizeStats.totalStock) * 100).toFixed(1) : 0}%
+
Taux distrib.
+
+
+
{error && (
-
- {error}
+
)}
{/* Liste des prix */}
{prizes.length === 0 ? (
-
- Aucun prix trouvé. Cliquez sur "Ajouter un prix" pour commencer.
+
) : (
-
- {prizes.map((prize) => (
-
-
-
{prize.name}
-
+ {prizes.map((prize) => {
+ const style = getPrizeStyle(prize.type);
+ const IconComponent = style.icon;
+ const stockRemaining = (prize.initialStock || 0) - (prize.ticketsUsed || 0);
+
+ return (
+
- {prize.isActive ? 'Actif' : 'Inactif'}
-
-
-
- {prize.description}
-
-
-
Type: {prize.type}
-
Valeur: {prize.value}€
-
- {/* Affichage spécial pour le Grand Prix (tirage au sort) */}
- {prize.type === 'GRAND_PRIZE' ? (
- <>
-
Probabilité: N/A (Tirage au sort)
-
-
- 🎯 TIRAGE AU SORT
-
-
- Attribué lors du tirage final parmi les participants éligibles
-
-
- >
- ) : (
- <>
-
Probabilité: {(prize.probability * 100).toFixed(1)}%
-
- Stock généré: {prize.initialStock !== undefined ? prize.initialStock : prize.stock}
-
-
- Stock restant:{' '}
-
- {(prize.initialStock || 0) - (prize.ticketsUsed || 0)}
+ {/* Header avec gradient */}
+
+
+
+
+ {prize.isActive ? 'Actif' : 'Inactif'}
-
- {prize.ticketsUsed !== undefined && prize.ticketsUsed > 0 && (
-
Tickets utilisés: {prize.ticketsUsed}
+
+
+
+ {/* Contenu */}
+
+
{prize.description}
+
+ {/* Affichage spécial pour le Grand Prix */}
+ {prize.type === 'GRAND_PRIZE' ? (
+
+
+
+ TIRAGE AU SORT
+
+
+ Attribué lors du tirage final parmi les participants éligibles
+
+
+ ) : (
+
+ {/* Probabilité */}
+
+ Probabilité
+ {(prize.probability * 100).toFixed(1)}%
+
+
+ {/* Stock */}
+
+
+ Stock
+
+ {stockRemaining} / {prize.initialStock || prize.stock}
+
+
+
+
0 ? (stockRemaining / (prize.initialStock || prize.stock)) * 100 : 0)}%` }}
+ >
+
+
+
+ {/* Tickets utilisés */}
+ {prize.ticketsUsed !== undefined && prize.ticketsUsed > 0 && (
+
+ Distribués
+ {prize.ticketsUsed}
+
+ )}
+
)}
- >
- )}
-
-
- ))}
+
+
+ );
+ })}
)}