Files
got-gaming/components/FeedbackManagement.tsx

234 lines
7.7 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
interface Feedback {
id: string;
rating: number;
comment: string | null;
createdAt: string;
event: {
id: string;
name: string;
date: string;
type: string;
};
user: {
id: string;
username: string;
email: string;
};
}
interface EventStatistics {
eventId: string;
eventName: string;
eventDate: string | null;
eventType: string | null;
averageRating: number;
feedbackCount: number;
}
export default function FeedbackManagement() {
const [feedbacks, setFeedbacks] = useState<Feedback[]>([]);
const [statistics, setStatistics] = useState<EventStatistics[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const [selectedEvent, setSelectedEvent] = useState<string | null>(null);
useEffect(() => {
fetchFeedbacks();
}, []);
const fetchFeedbacks = async () => {
try {
const response = await fetch("/api/admin/feedback");
if (!response.ok) {
setError("Erreur lors du chargement des feedbacks");
return;
}
const data = await response.json();
setFeedbacks(data.feedbacks || []);
setStatistics(data.statistics || []);
} catch {
setError("Erreur lors du chargement des feedbacks");
} finally {
setLoading(false);
}
};
const getEventTypeLabel = (type: string) => {
switch (type) {
case "ATELIER":
return "Atelier";
case "KATA":
return "Kata";
case "PRESENTATION":
return "Présentation";
case "LEARNING_HOUR":
return "Learning Hour";
default:
return type;
}
};
const renderStars = (rating: number) => {
return (
<div className="flex items-center gap-0.5 sm:gap-1">
{[1, 2, 3, 4, 5].map((star) => (
<span
key={star}
className={`text-sm sm:text-lg ${
star <= rating ? "text-pixel-gold" : "text-gray-600"
}`}
>
</span>
))}
<span className="text-gray-400 text-xs sm:text-sm ml-1 sm:ml-2">
({rating}/5)
</span>
</div>
);
};
const filteredFeedbacks = selectedEvent
? feedbacks.filter((f) => f.event.id === selectedEvent)
: feedbacks;
if (loading) {
return (
<div className="bg-black/60 border border-pixel-gold/30 rounded-lg p-4 sm:p-8">
<p className="text-gray-400 text-center text-sm">Chargement...</p>
</div>
);
}
return (
<div className="space-y-4 sm:space-y-6">
{/* Statistiques par événement */}
{statistics.length > 0 && (
<div className="bg-black/60 border border-pixel-gold/30 rounded-lg p-3 sm:p-6">
<h3 className="text-pixel-gold font-bold text-base sm:text-lg mb-4 break-words">
Statistiques par événement
</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4">
{statistics.map((stat) => (
<div
key={stat.eventId}
className={`bg-black/40 border rounded p-3 sm:p-4 cursor-pointer transition ${
selectedEvent === stat.eventId
? "border-pixel-gold bg-pixel-gold/10"
: "border-pixel-gold/30 hover:border-pixel-gold/50"
}`}
onClick={() =>
setSelectedEvent(
selectedEvent === stat.eventId ? null : stat.eventId
)
}
>
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-2 mb-2">
<h4 className="text-white font-semibold text-xs sm:text-sm break-words">
{stat.eventName}
</h4>
<span className="text-pixel-gold text-[10px] sm:text-xs uppercase whitespace-nowrap flex-shrink-0">
{stat.eventType && getEventTypeLabel(stat.eventType)}
</span>
</div>
<div className="flex items-center gap-2 mb-2">
{renderStars(Math.round(stat.averageRating))}
</div>
<div className="text-gray-400 text-[10px] sm:text-xs">
Moyenne: {stat.averageRating.toFixed(2)}/5
</div>
<div className="text-gray-400 text-[10px] sm:text-xs">
{stat.feedbackCount} feedback
{stat.feedbackCount > 1 ? "s" : ""}
</div>
</div>
))}
</div>
{selectedEvent && (
<button
onClick={() => setSelectedEvent(null)}
className="mt-4 text-pixel-gold text-xs sm:text-sm hover:text-orange-400 transition"
>
Voir tous les feedbacks
</button>
)}
</div>
)}
{/* Liste des feedbacks */}
<div className="bg-black/60 border border-pixel-gold/30 rounded-lg p-3 sm:p-6">
<h3 className="text-pixel-gold font-bold text-base sm:text-lg mb-4 break-words">
{selectedEvent
? `Feedbacks pour: ${
statistics.find((s) => s.eventId === selectedEvent)?.eventName
}`
: "Tous les feedbacks"}
</h3>
{error && (
<div className="bg-red-900/50 border border-red-500/50 text-red-400 px-3 sm:px-4 py-2 sm:py-3 rounded text-xs sm:text-sm mb-4">
{error}
</div>
)}
{filteredFeedbacks.length === 0 ? (
<p className="text-gray-400 text-center py-8 text-sm">
Aucun feedback pour le moment
</p>
) : (
<div className="space-y-3 sm:space-y-4">
{filteredFeedbacks.map((feedback) => (
<div
key={feedback.id}
className="bg-black/40 border border-pixel-gold/20 rounded p-3 sm:p-4"
>
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3 mb-3">
<div className="flex-1 min-w-0">
<div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-3 mb-2">
<h4 className="text-white font-semibold text-sm sm:text-base break-words">
{feedback.user.username}
</h4>
<span className="text-gray-500 text-[10px] sm:text-xs break-all">
{feedback.user.email}
</span>
</div>
<div className="text-pixel-gold text-xs sm:text-sm font-semibold mb-2 break-words">
{feedback.event.name}
</div>
<div className="text-gray-500 text-[10px] sm:text-xs mb-2">
{new Date(feedback.createdAt).toLocaleDateString(
"fr-FR",
{
day: "numeric",
month: "long",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
}
)}
</div>
</div>
<div className="flex-shrink-0">
{renderStars(feedback.rating)}
</div>
</div>
{feedback.comment && (
<div className="mt-3 pt-3 border-t border-pixel-gold/20">
<p className="text-gray-300 text-xs sm:text-sm whitespace-pre-wrap break-words">
{feedback.comment}
</p>
</div>
)}
</div>
))}
</div>
)}
</div>
</div>
);
}