diff --git a/app/feedback/layout.tsx b/app/feedback/layout.tsx new file mode 100644 index 0000000..96c263b --- /dev/null +++ b/app/feedback/layout.tsx @@ -0,0 +1,5 @@ +import type { ReactNode } from "react"; + +export default function FeedbackLayout({ children }: { children: ReactNode }) { + return <>{children}; +} diff --git a/components/EventsPageSection.tsx b/components/EventsPageSection.tsx index 8203971..1600253 100644 --- a/components/EventsPageSection.tsx +++ b/components/EventsPageSection.tsx @@ -4,6 +4,7 @@ import { useState, useEffect, useMemo, useRef } from "react"; import { useSession } from "next-auth/react"; import { useRouter } from "next/navigation"; import { calculateEventStatus } from "@/lib/eventStatus"; +import FeedbackModal from "@/components/FeedbackModal"; interface Event { id: string; @@ -88,6 +89,7 @@ export default function EventsPageSection({ const [error, setError] = useState(""); const [currentMonth, setCurrentMonth] = useState(new Date()); const [selectedEvent, setSelectedEvent] = useState(null); + const [feedbackEventId, setFeedbackEventId] = useState(null); // Helper function pour obtenir le statut d'un événement const getEventStatus = (event: Event) => calculateEventStatus(event.date); @@ -500,7 +502,7 @@ export default function EventsPageSection({ )} @@ -826,6 +835,12 @@ export default function EventsPageSection({ )} + + {/* Feedback Modal */} + setFeedbackEventId(null)} + /> ); } diff --git a/components/FeedbackModal.tsx b/components/FeedbackModal.tsx new file mode 100644 index 0000000..5b6a0a4 --- /dev/null +++ b/components/FeedbackModal.tsx @@ -0,0 +1,288 @@ +"use client"; + +import { useState, useEffect, type FormEvent } from "react"; +import { useSession } from "next-auth/react"; + +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, + onClose, +}: FeedbackModalProps) { + const { status } = useSession(); + const [event, setEvent] = useState(null); + const [existingFeedback, setExistingFeedback] = useState( + null + ); + const [loading, setLoading] = useState(true); + const [submitting, setSubmitting] = useState(false); + const [error, setError] = useState(""); + const [success, setSuccess] = useState(false); + + 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 { + // Récupérer l'événement + const eventResponse = await fetch(`/api/events/${eventId}`); + if (!eventResponse.ok) { + setError("Événement introuvable"); + setLoading(false); + return; + } + const eventData = await eventResponse.json(); + setEvent(eventData); + + // Récupérer le feedback existant si disponible + const feedbackResponse = await fetch(`/api/feedback/${eventId}`); + 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); + + try { + const response = await fetch(`/api/feedback/${eventId}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + rating, + comment: comment.trim() || null, + }), + }); + + const data = await response.json(); + + if (!response.ok) { + setError(data.error || "Erreur lors de l'enregistrement"); + return; + } + + setSuccess(true); + setExistingFeedback(data.feedback); + + // 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 ( +
+
e.stopPropagation()} + > +
+ {/* Header */} +
+

+ + FEEDBACK + +

+ +
+ + {loading ? ( +
Chargement...
+ ) : !event ? ( +
+ Événement introuvable +
+ ) : ( + <> +

+ {existingFeedback + ? "Modifier votre feedback pour" + : "Donnez votre avis sur"} +

+

+ {event.name} +

+ + {success && ( +
+ Feedback enregistré avec succès ! +
+ )} + + {error && ( +
+ {error} +
+ )} + +
+ {/* Rating */} +
+ +
+ {[1, 2, 3, 4, 5].map((star) => ( + + ))} +
+

+ {rating > 0 && `${rating}/5`} +

+
+ + {/* Comment */} +
+ +