/// Wrapper BLoC pour la page des événements /// /// Ce fichier enveloppe la EventsPage existante avec le EvenementsBloc /// pour connecter l'UI riche existante à l'API backend réelle. library events_page_wrapper; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get_it/get_it.dart'; import '../../../../core/widgets/error_widget.dart'; import '../../../../core/widgets/loading_widget.dart'; import '../../../../core/utils/logger.dart'; import '../../bloc/evenements_bloc.dart'; import '../../bloc/evenements_event.dart'; import '../../bloc/evenements_state.dart'; import '../../data/models/evenement_model.dart'; import 'events_page_connected.dart'; final _getIt = GetIt.instance; /// Wrapper qui fournit le BLoC à la page des événements class EventsPageWrapper extends StatelessWidget { const EventsPageWrapper({super.key}); @override Widget build(BuildContext context) { AppLogger.info('EventsPageWrapper: Création du BlocProvider'); return BlocProvider( create: (context) { AppLogger.info('EventsPageWrapper: Initialisation du EvenementsBloc'); final bloc = _getIt(); // Charger les événements au démarrage bloc.add(const LoadEvenements()); return bloc; }, child: const EventsPageConnected(), ); } } /// Page des événements connectée au BLoC /// /// Cette page gère les états du BLoC et affiche l'UI appropriée class EventsPageConnected extends StatelessWidget { const EventsPageConnected({super.key}); @override Widget build(BuildContext context) { return BlocListener( listener: (context, state) { // Gestion des erreurs avec SnackBar if (state is EvenementsError) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.message), backgroundColor: Colors.red, duration: const Duration(seconds: 4), action: SnackBarAction( label: 'Réessayer', textColor: Colors.white, onPressed: () { context.read().add(const LoadEvenements()); }, ), ), ); } }, child: BlocBuilder( builder: (context, state) { AppLogger.blocState('EvenementsBloc', state.runtimeType.toString()); // État initial if (state is EvenementsInitial) { return Container( color: const Color(0xFFF8F9FA), child: const Center( child: AppLoadingWidget(message: 'Initialisation...'), ), ); } // État de chargement if (state is EvenementsLoading) { return Container( color: const Color(0xFFF8F9FA), child: const Center( child: AppLoadingWidget(message: 'Chargement des événements...'), ), ); } // État de rafraîchissement if (state is EvenementsRefreshing) { return Container( color: const Color(0xFFF8F9FA), child: const Center( child: AppLoadingWidget(message: 'Actualisation...'), ), ); } // État chargé avec succès if (state is EvenementsLoaded) { final evenements = state.evenements; AppLogger.info('EventsPageConnected: ${evenements.length} événements chargés'); // Convertir les événements en format Map pour l'UI existante final eventsData = _convertEvenementsToMapList(evenements); return EventsPageWithData( events: eventsData, totalCount: state.total, currentPage: state.page, totalPages: state.totalPages, ); } // État d'erreur réseau if (state is EvenementsNetworkError) { AppLogger.error('EventsPageConnected: Erreur réseau', error: state.message); return Container( color: const Color(0xFFF8F9FA), child: NetworkErrorWidget( onRetry: () { AppLogger.userAction('Retry load evenements after network error'); context.read().add(const LoadEvenements()); }, ), ); } // État d'erreur générale if (state is EvenementsError) { AppLogger.error('EventsPageConnected: Erreur', error: state.message); return Container( color: const Color(0xFFF8F9FA), child: AppErrorWidget( message: state.message, onRetry: () { AppLogger.userAction('Retry load evenements after error'); context.read().add(const LoadEvenements()); }, ), ); } // État par défaut AppLogger.warning('EventsPageConnected: État non géré: ${state.runtimeType}'); return Container( color: const Color(0xFFF8F9FA), child: const Center( child: AppLoadingWidget(message: 'Chargement...'), ), ); }, ), ); } /// Convertit une liste de EvenementModel en List> List> _convertEvenementsToMapList(List evenements) { return evenements.map((evenement) => _convertEvenementToMap(evenement)).toList(); } /// Convertit un EvenementModel en Map Map _convertEvenementToMap(EvenementModel evenement) { return { 'id': evenement.id ?? '', 'title': evenement.titre, 'description': evenement.description ?? '', 'startDate': evenement.dateDebut, 'endDate': evenement.dateFin, 'location': evenement.lieu ?? '', 'address': evenement.adresse ?? '', 'type': _mapTypeToString(evenement.type), 'status': _mapStatutToString(evenement.statut), 'maxParticipants': evenement.maxParticipants ?? 0, 'currentParticipants': evenement.participantsActuels ?? 0, 'organizer': 'Organisateur', // TODO: Récupérer depuis organisateurId 'priority': _mapPrioriteToString(evenement.priorite), 'isPublic': evenement.estPublic ?? true, 'requiresRegistration': evenement.inscriptionRequise ?? false, 'cost': evenement.cout ?? 0.0, 'tags': evenement.tags ?? [], 'createdBy': 'Créateur', // TODO: Récupérer depuis organisateurId 'createdAt': DateTime.now(), // TODO: Ajouter au modèle 'lastModified': DateTime.now(), // TODO: Ajouter au modèle // Champs supplémentaires du modèle 'ville': evenement.ville, 'codePostal': evenement.codePostal, 'organisateurId': evenement.organisateurId, 'organisationId': evenement.organisationId, 'devise': evenement.devise, 'imageUrl': evenement.imageUrl, 'documentUrl': evenement.documentUrl, // Propriétés calculées 'dureeHeures': evenement.dureeHeures, 'joursAvantEvenement': evenement.joursAvantEvenement, 'estAVenir': evenement.estAVenir, 'estEnCours': evenement.estEnCours, 'estPasse': evenement.estPasse, 'placesDisponibles': evenement.placesDisponibles, 'estComplet': evenement.estComplet, 'peutSinscrire': evenement.peutSinscrire, }; } /// Mappe le type du modèle vers une chaîne lisible String _mapTypeToString(TypeEvenement? type) { if (type == null) return 'Autre'; switch (type) { case TypeEvenement.assembleeGenerale: return 'Assemblée Générale'; case TypeEvenement.reunion: return 'Réunion'; case TypeEvenement.formation: return 'Formation'; case TypeEvenement.conference: return 'Conférence'; case TypeEvenement.atelier: return 'Atelier'; case TypeEvenement.seminaire: return 'Séminaire'; case TypeEvenement.evenementSocial: return 'Événement Social'; case TypeEvenement.manifestation: return 'Manifestation'; case TypeEvenement.celebration: return 'Célébration'; case TypeEvenement.autre: return 'Autre'; } } /// Mappe le statut du modèle vers une chaîne lisible String _mapStatutToString(StatutEvenement? statut) { if (statut == null) return 'Planifié'; switch (statut) { case StatutEvenement.planifie: return 'Planifié'; case StatutEvenement.confirme: return 'Confirmé'; case StatutEvenement.enCours: return 'En cours'; case StatutEvenement.termine: return 'Terminé'; case StatutEvenement.annule: return 'Annulé'; case StatutEvenement.reporte: return 'Reporté'; } } /// Mappe la priorité du modèle vers une chaîne lisible String _mapPrioriteToString(PrioriteEvenement? priorite) { if (priorite == null) return 'Moyenne'; switch (priorite) { case PrioriteEvenement.basse: return 'Basse'; case PrioriteEvenement.moyenne: return 'Moyenne'; case PrioriteEvenement.haute: return 'Haute'; } } }