Files
got-gaming/components/feedback/FeedbackModal.tsx

275 lines
7.0 KiB
TypeScript

"use client";
import { useState, useEffect, useTransition, type FormEvent } from "react";
import { useSession } from "next-auth/react";
import { createFeedback } from "@/actions/events/feedback";
import {
Modal,
StarRating,
Textarea,
Button,
Alert,
SectionTitle,
CloseButton,
} from "@/components/ui";
interface Event {
id: string;
name: string;
date: string;
description: string;
}
interface Feedback {
id: string;
rating: number;
comment: string | null;
}
interface FeedbackModalProps {
eventId: string | null;
eventName?: string;
onClose: () => void;
}
export default function FeedbackModal({
eventId,
eventName: _eventName,
onClose,
}: FeedbackModalProps) {
const { status } = useSession();
const [event, setEvent] = useState<Event | null>(null);
const [existingFeedback, setExistingFeedback] = useState<Feedback | null>(
null
);
const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState("");
const [success, setSuccess] = useState(false);
const [, startTransition] = useTransition();
const [rating, setRating] = useState(0);
const [comment, setComment] = useState("");
// Réinitialiser les états quand eventId change
useEffect(() => {
if (eventId) {
setEvent(null);
setExistingFeedback(null);
setRating(0);
setComment("");
setError("");
setSuccess(false);
setLoading(true);
}
}, [eventId]);
const fetchEventAndFeedback = async () => {
if (!eventId) return;
try {
// Paralléliser les appels API
const [eventResponse, feedbackResponse] = await Promise.all([
fetch(`/api/events/${eventId}`),
fetch(`/api/feedback/${eventId}`),
]);
if (!eventResponse.ok) {
setError("Événement introuvable");
setLoading(false);
return;
}
const eventData = await eventResponse.json();
setEvent(eventData);
// Traiter le feedback
if (feedbackResponse.ok) {
const feedbackData = await feedbackResponse.json();
if (feedbackData.feedback) {
setExistingFeedback(feedbackData.feedback);
setRating(feedbackData.feedback.rating);
setComment(feedbackData.feedback.comment || "");
} else {
// Pas de feedback existant, réinitialiser
setRating(0);
setComment("");
}
} else {
// Pas de feedback existant, réinitialiser
setRating(0);
setComment("");
}
} catch {
setError("Erreur lors du chargement des données");
} finally {
setLoading(false);
}
};
useEffect(() => {
if (status === "unauthenticated") {
onClose();
return;
}
if (status === "authenticated" && eventId) {
fetchEventAndFeedback();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [status, eventId]);
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
if (!eventId) return;
setError("");
setSuccess(false);
if (rating === 0) {
setError("Veuillez sélectionner une note");
return;
}
setSubmitting(true);
startTransition(async () => {
try {
const result = await createFeedback(eventId, {
rating,
comment: comment.trim() || null,
});
if (!result.success) {
setError(result.error || "Erreur lors de l'enregistrement");
setSubmitting(false);
return;
}
setSuccess(true);
if (result.data) {
setExistingFeedback({
id: result.data.id,
rating: result.data.rating,
comment: result.data.comment,
});
}
// Rafraîchir le score dans le header
window.dispatchEvent(new Event("refreshUserScore"));
// Fermer la modale après 1.5 secondes
setTimeout(() => {
onClose();
}, 1500);
} catch {
setError("Erreur lors de l'enregistrement");
} finally {
setSubmitting(false);
}
});
};
const handleClose = () => {
if (!submitting) {
onClose();
}
};
if (!eventId) return null;
return (
<Modal
isOpen={!!eventId}
onClose={handleClose}
size="md"
closeOnOverlayClick={!submitting}
>
<div className="p-8">
{/* Header */}
<div className="flex items-center justify-between mb-6">
<SectionTitle variant="gradient" size="lg">
FEEDBACK
</SectionTitle>
<CloseButton onClick={handleClose} disabled={submitting} size="lg" />
</div>
{loading ? (
<div className="text-white text-center py-8">Chargement...</div>
) : !event ? (
<Alert variant="error" className="text-center">
Événement introuvable
</Alert>
) : (
<>
<p className="text-gray-400 text-sm text-center mb-2">
{existingFeedback
? "Modifier votre feedback pour"
: "Donnez votre avis sur"}
</p>
<p className="text-pixel-gold text-lg font-semibold text-center mb-8">
{event.name}
</p>
{success && (
<Alert variant="success" className="mb-6">
Feedback enregistré avec succès !
</Alert>
)}
{error && (
<Alert variant="error" className="mb-6">
{error}
</Alert>
)}
<form onSubmit={handleSubmit} className="space-y-6">
{/* Rating */}
<div>
<label className="block text-sm font-semibold text-gray-300 mb-4 uppercase tracking-wider">
Note
</label>
<StarRating
value={rating}
onChange={setRating}
disabled={submitting}
size="lg"
showValue
/>
</div>
{/* Comment */}
<Textarea
id="comment"
label="Commentaire (optionnel)"
value={comment}
onChange={(e) => setComment(e.target.value)}
rows={6}
maxLength={1000}
disabled={submitting}
showCharCount
placeholder="Partagez votre expérience, vos suggestions..."
/>
{/* Submit Button */}
<Button
type="submit"
variant="primary"
size="lg"
disabled={submitting || rating === 0}
className="w-full"
>
{submitting
? "Enregistrement..."
: existingFeedback
? "Modifier le feedback"
: "Envoyer le feedback"}
</Button>
</form>
</>
)}
</div>
</Modal>
);
}