276 lines
9.2 KiB
Dart
276 lines
9.2 KiB
Dart
/// 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<EvenementsBloc>(
|
|
create: (context) {
|
|
AppLogger.info('EventsPageWrapper: Initialisation du EvenementsBloc');
|
|
final bloc = _getIt<EvenementsBloc>();
|
|
// 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<EvenementsBloc, EvenementsState>(
|
|
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<EvenementsBloc>().add(const LoadEvenements());
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
},
|
|
child: BlocBuilder<EvenementsBloc, EvenementsState>(
|
|
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<EvenementsBloc>().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<EvenementsBloc>().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<Map<String, dynamic>>
|
|
List<Map<String, dynamic>> _convertEvenementsToMapList(List<EvenementModel> evenements) {
|
|
return evenements.map((evenement) => _convertEvenementToMap(evenement)).toList();
|
|
}
|
|
|
|
/// Convertit un EvenementModel en Map<String, dynamic>
|
|
Map<String, dynamic> _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';
|
|
}
|
|
}
|
|
}
|
|
|