fix: improve dashboard data display and clean up ticket management

Dashboard:
- Filter out "Non spécifié" from gender chart
- Filter empty/unspecified values from age ranges and cities

TicketManagement:
- Remove debug panel and console logs
- Fix date column to show playedAt instead of createdAt
- Remove unused imports

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
soufiane 2025-12-01 15:43:05 +01:00
parent eded0187a0
commit 51ec802131
2 changed files with 18 additions and 76 deletions

View File

@ -153,20 +153,24 @@ export default function AdminDashboardAdvanced() {
percentage: prize.percentage, percentage: prize.percentage,
})) || []; })) || [];
// Ne montrer le graphique genre que s'il y a des données autres que "Non spécifié"
const genderChartData = stats.demographics?.gender const genderChartData = stats.demographics?.gender
? [ ? [
{ name: "Hommes", value: stats.demographics.gender.male, color: GENDER_COLORS.male }, { name: "Hommes", value: stats.demographics.gender.male, color: GENDER_COLORS.male },
{ name: "Femmes", value: stats.demographics.gender.female, color: GENDER_COLORS.female }, { name: "Femmes", value: stats.demographics.gender.female, color: GENDER_COLORS.female },
{ name: "Autre", value: stats.demographics.gender.other, color: GENDER_COLORS.other }, { name: "Autre", value: stats.demographics.gender.other, color: GENDER_COLORS.other },
{
name: "Non spécifié",
value: stats.demographics.gender.notSpecified,
color: GENDER_COLORS.notSpecified,
},
].filter((item) => item.value > 0) ].filter((item) => item.value > 0)
: []; : [];
const ageChartData = stats.demographics?.ageRanges || []; // Ne montrer le graphique âge que s'il y a des données avec des vraies tranches d'âge
const ageChartData = (stats.demographics?.ageRanges || []).filter(
(item) => item.range && !item.range.toLowerCase().includes("non spécifié") && item.count > 0
);
// Ne montrer les villes que s'il y a des vraies villes (pas vides ou "non spécifié")
const topCitiesData = (stats.demographics?.topCities || []).filter(
(city) => city.city && city.city.trim() !== "" && !city.city.toLowerCase().includes("non spécifié") && city.count > 0
);
const ticketDistributedPercent = stats.tickets.total > 0 const ticketDistributedPercent = stats.tickets.total > 0
? ((stats.tickets.distributed / stats.tickets.total) * 100).toFixed(1) ? ((stats.tickets.distributed / stats.tickets.total) * 100).toFixed(1)
@ -420,15 +424,15 @@ export default function AdminDashboardAdvanced() {
</div> </div>
</div> </div>
{/* Top Villes */} {/* Top Villes - affiché uniquement s'il y a des vraies données de villes */}
{stats?.demographics?.topCities && stats.demographics.topCities.length > 0 && ( {topCitiesData.length > 0 && (
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6"> <div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4 flex items-center gap-2"> <h3 className="text-lg font-semibold text-gray-900 mb-4 flex items-center gap-2">
<MapPin className="w-5 h-5" /> <MapPin className="w-5 h-5" />
Top 10 Villes des Participants Top 10 Villes des Participants
</h3> </h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 gap-4">
{stats.demographics.topCities.slice(0, 10).map((city, idx) => ( {topCitiesData.slice(0, 10).map((city, idx) => (
<CityCard <CityCard
key={idx} key={idx}
rank={idx + 1} rank={idx + 1}

View File

@ -2,8 +2,7 @@
import { useState, useEffect, useCallback } from 'react'; import { useState, useEffect, useCallback } from 'react';
import { adminService } from '@/services/admin.service'; import { adminService } from '@/services/admin.service';
import { Ticket, PaginatedResponse } from '@/types'; import { Ticket } from '@/types';
import { API_BASE_URL } from '@/utils/constants';
export default function TicketManagement() { export default function TicketManagement() {
const [tickets, setTickets] = useState<Ticket[]>([]); const [tickets, setTickets] = useState<Ticket[]>([]);
@ -15,7 +14,6 @@ export default function TicketManagement() {
const [filterStatus, setFilterStatus] = useState<string>(''); const [filterStatus, setFilterStatus] = useState<string>('');
const [filterPrizeType, setFilterPrizeType] = useState<string>(''); const [filterPrizeType, setFilterPrizeType] = useState<string>('');
const [selectedTicket, setSelectedTicket] = useState<Ticket | null>(null); const [selectedTicket, setSelectedTicket] = useState<Ticket | null>(null);
const [showDebug, setShowDebug] = useState(false);
const loadTickets = useCallback(async () => { const loadTickets = useCallback(async () => {
try { try {
@ -39,30 +37,19 @@ export default function TicketManagement() {
let totalPagesCount = 1; let totalPagesCount = 1;
if (Array.isArray(response)) { if (Array.isArray(response)) {
// Si la réponse est directement un tableau
console.log('📦 Réponse est un tableau direct');
ticketsData = response; ticketsData = response;
total = response.length; total = response.length;
totalPagesCount = 1; totalPagesCount = 1;
} else if (response.data && Array.isArray(response.data)) { } else if (response.data && Array.isArray(response.data)) {
// Si la réponse est un objet avec data
console.log('📦 Réponse est un objet avec data');
ticketsData = response.data; ticketsData = response.data;
total = response.total || response.data.length; total = response.total || response.data.length;
totalPagesCount = response.totalPages || 1; totalPagesCount = response.totalPages || 1;
} else {
console.warn('⚠️ Format de réponse inattendu:', response);
} }
console.log('🎯 Tickets à afficher:', ticketsData);
setTickets(ticketsData); setTickets(ticketsData);
setTotalPages(totalPagesCount); setTotalPages(totalPagesCount);
setTotalTickets(total); setTotalTickets(total);
} catch (err: any) { } catch (err: any) {
console.error('❌ Erreur lors du chargement:', err);
// Messages d'erreur personnalisés selon le type d'erreur
let errorMessage = 'Erreur lors du chargement des tickets'; let errorMessage = 'Erreur lors du chargement des tickets';
if (err.status === 401) { if (err.status === 401) {
@ -256,7 +243,7 @@ export default function TicketManagement() {
Statut Statut
</th> </th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Distrible Jole
</th> </th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Utilisé par Utilisé par
@ -313,13 +300,13 @@ export default function TicketManagement() {
</span> </span>
</td> </td>
{/* DISTRIBUÉ LE */} {/* DISTRIBUÉ LE (date d'utilisation du ticket) */}
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{ticket.createdAt ? new Date(ticket.createdAt).toLocaleDateString('fr-FR', { {ticket.playedAt ? new Date(ticket.playedAt).toLocaleDateString('fr-FR', {
year: 'numeric', year: 'numeric',
month: '2-digit', month: '2-digit',
day: '2-digit' day: '2-digit'
}) : 'N/A'} }) : '-'}
</td> </td>
{/* UTILISÉ PAR */} {/* UTILISÉ PAR */}
@ -371,55 +358,6 @@ export default function TicketManagement() {
</button> </button>
</div> </div>
{/* Panneau de Debug */}
<div className="mt-8">
<button
onClick={() => setShowDebug(!showDebug)}
className="text-sm text-gray-600 hover:text-gray-900 underline"
>
{showDebug ? '🔽 Masquer les infos de debug' : '🔍 Afficher les infos de debug'}
</button>
{showDebug && (
<div className="mt-4 bg-gray-100 rounded-lg p-4 border border-gray-300">
<h3 className="font-bold text-sm mb-2">📊 Informations de Debug</h3>
<div className="space-y-2 text-xs font-mono">
<div>
<strong>État:</strong> {loading ? 'Chargement...' : 'Chargé'}
</div>
<div>
<strong>Erreur:</strong> {error || 'Aucune'}
</div>
<div>
<strong>Nombre de tickets:</strong> {tickets.length}
</div>
<div>
<strong>Total tickets (API):</strong> {totalTickets}
</div>
<div>
<strong>Page actuelle:</strong> {page} / {totalPages}
</div>
<div>
<strong>Filtre statut:</strong> {filterStatus || 'Aucun'}
</div>
<div>
<strong>URL API:</strong> {API_BASE_URL}
</div>
<div className="pt-2">
<strong>Tickets reçus:</strong>
<pre className="mt-2 bg-white p-2 rounded overflow-auto max-h-60">
{JSON.stringify(tickets, null, 2)}
</pre>
</div>
</div>
<div className="mt-4">
<p className="text-xs text-gray-600">
💡 Astuce: Ouvrez la console du navigateur (F12) pour voir les logs détaillés
</p>
</div>
</div>
)}
</div>
{/* Modal détails ticket */} {/* Modal détails ticket */}
{selectedTicket && ( {selectedTicket && (