Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 9x 9x 9x 9x 9x 9x 8x 9x 7x 7x 7x 9x 9x 1x 1x 9x 9x 7x 7x 1x | /**
* @module components/evenements/ModalEvenement
* Module de composant ModalEvenement pour l'affichage détaillé des événements olympiques
*
* Ce module contient le composant ModalEvenement qui affiche un modal complet présentant
* toutes les informations d'un événement olympique avec ses épreuves associées.
* Il gère la réservation de places, l'authentification et l'interaction utilisateur.
*
* ## Fonctionnalités principales
* - Modal plein écran avec overlay semi-transparent
* - Affichage détaillé des informations d'événement
* - Groupement des épreuves par discipline
* - Gestion des réservations de places en temps réel
* - Intégration avec le système d'authentification
* - Calcul dynamique des places restantes
* - Interface responsive avec scrolling automatique
*
* ## Informations affichées
* - **Date** : Date formatée de l'événement
* - **Description** : Description complète de l'événement
* - **Épreuves** : Liste groupée par discipline avec détails
* - **Lieu** : Nom du lieu et horaires
* - **Places disponibles** : Calcul en temps réel
* - **Offres** : Prix et options de réservation
*
* ## Gestion des réservations
* - Calcul automatique des places réservées
* - Mise à jour en temps réel des disponibilités
* - Integration avec le système de réservation
* - Validation des capacités restantes
*
* ## Authentification
* - Vérification du statut de connexion
* - Modal de connexion intégré si non authentifié
* - Accès conditionnel aux fonctionnalités de réservation
*
* ## Design et UX
* - Modal centré avec coins arrondis
* - Bordure accent distinctive
* - Bouton de fermeture en overlay
* - Sections organisées avec séparateurs visuels
* - Scrolling fluide pour contenu long
*
* @group Components
*/
'use client';
import {useEvenementByEpreuveId} from "@/hook/useEpreuve";
import {formatDateFr, formatHeure} from "@/utils/formatDate";
import {Epreuve} from "@/type/evenement/epreuve";
import {useAuth} from "@/context/userContext";
import {useEffect, useState} from "react";
import ModalAuthentication from "@/components/connexion/modalAuthentication";
import DisplayedOffre from "@/components/evenements/DisplayedOffre";
import {useOffres} from "@/hook/useOffre";
import {useReservationOffer} from "@/hook/useReservationOffer";
/**
* Props du composant ModalEvenement
*/
type Props = {
/** ID de l'épreuve pour charger les données de l'événement associé */
epreuveId: number;
/** Fonction appelée lors de la fermeture du modal */
onCloseAction: () => void;
};
/**
* Composant ModalEvenement pour l'affichage détaillé des événements olympiques.
* Voir la documentation du module ci-dessus pour les détails complets.
*
* Ce composant affiche un modal complet avec toutes les informations d'un événement,
* incluant les épreuves groupées par discipline, les informations de lieu et horaires,
* ainsi que la gestion des réservations avec authentification intégrée.
*
* @param props - Les propriétés du composant
* @param props.epreuveId - ID de l'épreuve pour charger l'événement
* @param props.onCloseAction - Callback de fermeture du modal
*
* @returns Modal d'événement avec informations complètes et gestion des réservations
*
* @example
* ```tsx
* // Utilisation dans une liste d'épreuves
* const [selectedEpreuve, setSelectedEpreuve] = useState<number | null>(null);
*
* <ModalEvenement
* epreuveId={selectedEpreuve}
* onCloseAction={() => setSelectedEpreuve(null)}
* />
*
* // Avec gestion d'état pour l'ouverture
* <CardEpreuve
* epreuve={epreuve}
* onClick={(id) => setSelectedEpreuve(id)}
* />
* ```
*/
export default function ModalEvenement({epreuveId, onCloseAction}: Props) {
const {offres} = useOffres();
const {evenement, loading, error} = useEvenementByEpreuveId(epreuveId);
const {reservedOffers, reservePlaces, unReservePlaces} = useReservationOffer();
const {isAuthenticated} = useAuth();
const [showAuthModal, setShowAuthModal] = useState(false);
useEffect(() => {
console.log("reservedOffer: " ,reservedOffers);
}, [reservedOffers]);
// Grouper les épreuves par discipline
const groupedEpreuves = evenement
? evenement.epreuves.reduce<Record<string, Epreuve[]>>((acc, epreuve) => {
const discipline = epreuve.discipline.nom;
(acc[discipline] ??= []).push(epreuve);
return acc;
}, {})
: {};
// Filtrer les réservations pour ne garder que celles de l'événement courant
const reservedOffersForEvent = reservedOffers.filter(ro => ro.evenementId === evenement?.id);
// Calcul dynamique des places restantes
const totalReservedPlaces = reservedOffersForEvent.reduce((acc, ro) => {
const offre = offres.find(o => o.id === ro.offreId);
return acc + (offre ? offre.nb_personne * ro.quantity : 0);
}, 0);
const remainingTickets = evenement ? evenement.nb_place_restante - totalReservedPlaces : 0;
return (
<div className="fixed w-screen h-screen flex justify-center bg-black/70 pt-[20px] z-50">
<div
className="bg-white w-[90%] max-w-[600px] h-[85%] relative flex flex-col rounded-[40px] border-4 border-accent">
{/* Contenu */}
<div className="flex-1 overflow-auto">
{loading && <div>Loading...</div>}
{error && <div>Error: {error.message}</div>}
{/* liste des épreuves */}
{evenement && (
<>
{/* Bouton fermer */}
<button
onClick={onCloseAction}
className="absolute top-3 right-3 text-white bg-black rounded-full p-2 hover:bg-black/70 transition"
>
✕
</button>
{/*date*/}
<div
className="text-black text-[130%] font-bold mb-4 mt-4 text-center">{formatDateFr(evenement.date)}</div>
<hr className="border border-black w-[90%] mx-auto"/>
{/*evenement*/}
<div className="mt-4">
{/*description*/}
<div
className="text-black font-bold text-center mx-[10%] py-[8px] bg-accent rounded-[8px]">{evenement.description}</div>
{/*liste des épreuves*/}
<div className="mt-4 mx-[10%] gap-2">
{Object.entries(groupedEpreuves).map(([discipline, epreuves]) => (
<div key={discipline} className="mb-6">
{/* Rupture = titre discipline */}
<div className="text-lg font-bold text-black mb-2 rounded-[20px] border border-accent w-[90%]">
<h2 className="pl-4 text-lg font-bold text-black">{discipline}</h2>
</div>
<div className="pl-3">
{epreuves.map((epreuve) => (
<div
key={epreuve.id}
className="bg-white text-black flex gap-2"
>
<div className="font-semibold"> {epreuve.tour}</div>
<div className="font-semibold"> {epreuve.genre}</div>
<div className="">{epreuve.libelle}</div>
</div>
))}
</div>
</div>
))}
</div>
{/* lieu */}
<hr className="border border-black w-[85%] mx-auto"/>
<div className="text-black my-2 font-bold text-center mx-[10%] py-[8px] bg-base-200 rounded-[8px]">
<div>{evenement.lieu.nom}</div>
<div> A partir de : {formatHeure(evenement.horraire)}</div>
<div> PLACES RESTANTE : {remainingTickets}</div>
</div>
<hr className="border border-black w-[85%] mx-auto"/>
{/* {gestion de l'offre*/}
<div className="text-black my-4 text-center mx-[10%]">
{isAuthenticated ? (
<div>
<DisplayedOffre
evenementId={evenement.id}
remainingTickets={remainingTickets}
onReservePlaces={reservePlaces}
onUnReservePlaces={unReservePlaces}
/>
</div>
) : (
<div>
<h3 className="font-bold text-lg mb-3">Connectez-vous pour voir les offres</h3>
<button
onClick={() => setShowAuthModal(true)}
className="bg-accent text-white px-6 py-2 rounded-lg hover:bg-accent/80 transition"
>
Se connecter
</button>
</div>
)}
</div>
</div>
</>
)}
</div>
</div>
{/* Modal d'authentification */}
{showAuthModal && (
<ModalAuthentication onCloseAction={() => setShowAuthModal(false)}/>
)}
</div>
);
}
|