/// Dialogue d'inscription à un événement library inscription_event_dialog; import 'package:flutter/material.dart'; import '../../../../shared/design_system/tokens/module_colors.dart'; import '../../../../shared/design_system/tokens/color_tokens.dart'; import '../../../../shared/design_system/tokens/app_colors.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../bloc/evenements_bloc.dart'; import '../../bloc/evenements_event.dart'; import '../../bloc/evenements_state.dart'; import '../../data/models/evenement_model.dart'; class InscriptionEventDialog extends StatefulWidget { final EvenementModel evenement; final bool isInscrit; const InscriptionEventDialog({ super.key, required this.evenement, this.isInscrit = false, }); @override State createState() => _InscriptionEventDialogState(); } class _InscriptionEventDialogState extends State { final _formKey = GlobalKey(); final _commentaireController = TextEditingController(); bool _actionSent = false; @override void dispose() { _commentaireController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return BlocListener( listenWhen: (_, state) => _actionSent && (state is EvenementInscrit || state is EvenementDesinscrit || state is EvenementsError), listener: (context, state) { if (!_actionSent || !mounted) return; if (state is EvenementsError) { setState(() => _actionSent = false); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(state.message), backgroundColor: ColorTokens.error), ); return; } if (state is EvenementInscrit || state is EvenementDesinscrit) { setState(() => _actionSent = false); Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( state is EvenementInscrit ? 'Inscription réussie' : 'Désinscription réussie', ), backgroundColor: state is EvenementInscrit ? ColorTokens.success : ColorTokens.warning, ), ); } }, child: Dialog( child: Container( width: MediaQuery.of(context).size.width * 0.9, constraints: const BoxConstraints(maxHeight: 500), child: Column( mainAxisSize: MainAxisSize.min, children: [ _buildHeader(), Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildEventInfo(), const SizedBox(height: 16), if (!widget.isInscrit) ...[ _buildPlacesInfo(), const SizedBox(height: 16), _buildCommentaireField(), ] else ...[ _buildDesinscriptionWarning(), ], ], ), ), ), ), _buildActionButtons(), ], ), ), ), ); } Widget _buildHeader() { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: widget.isInscrit ? ColorTokens.error : ModuleColors.evenements, borderRadius: const BorderRadius.only( topLeft: Radius.circular(4), topRight: Radius.circular(4), ), ), child: Row( children: [ Icon( widget.isInscrit ? Icons.cancel : Icons.event_available, color: Colors.white, ), const SizedBox(width: 12), Expanded( child: Text( widget.isInscrit ? 'Se désinscrire' : 'S\'inscrire à l\'événement', style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), ), IconButton( icon: const Icon(Icons.close, color: Colors.white), onPressed: () => Navigator.pop(context), ), ], ), ); } Widget _buildEventInfo() { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: ColorTokens.infoContainer, border: Border.all(color: ColorTokens.infoLight), borderRadius: BorderRadius.circular(4), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.event, color: ModuleColors.evenements), const SizedBox(width: 8), Expanded( child: Text( widget.evenement.titre, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ], ), const SizedBox(height: 8), Row( children: [ const Icon(Icons.calendar_today, size: 16, color: AppColors.textTertiary), const SizedBox(width: 8), Text( _formatDate(widget.evenement.dateDebut), style: const TextStyle(fontSize: 14), ), ], ), if (widget.evenement.lieu != null) ...[ const SizedBox(height: 4), Row( children: [ const Icon(Icons.location_on, size: 16, color: AppColors.textTertiary), const SizedBox(width: 8), Expanded( child: Text( widget.evenement.lieu!, style: const TextStyle(fontSize: 14), ), ), ], ), ], ], ), ); } Widget _buildPlacesInfo() { final placesRestantes = (widget.evenement.maxParticipants ?? 0) - widget.evenement.participantsActuels; final isComplet = placesRestantes <= 0 && widget.evenement.maxParticipants != null; return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: isComplet ? ColorTokens.errorContainer : ColorTokens.successContainer, border: Border.all( color: isComplet ? ColorTokens.errorLight : ColorTokens.successLight, ), borderRadius: BorderRadius.circular(4), ), child: Row( children: [ Icon( isComplet ? Icons.warning : Icons.check_circle, color: isComplet ? ColorTokens.error : ColorTokens.success, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( isComplet ? 'Événement complet' : 'Places disponibles', style: TextStyle( fontWeight: FontWeight.bold, color: isComplet ? ColorTokens.errorDark : ColorTokens.successDark, ), ), if (widget.evenement.maxParticipants != null) Text( '$placesRestantes places restantes sur ${widget.evenement.maxParticipants}', style: const TextStyle(fontSize: 12), ) else const Text( 'Nombre de places illimité', style: TextStyle(fontSize: 12), ), ], ), ), ], ), ); } Widget _buildCommentaireField() { return TextFormField( controller: _commentaireController, decoration: const InputDecoration( labelText: 'Commentaire (optionnel)', border: OutlineInputBorder(), prefixIcon: Icon(Icons.comment), hintText: 'Ajoutez un commentaire...', ), maxLines: 3, ); } Widget _buildDesinscriptionWarning() { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: ColorTokens.warningContainer, border: Border.all(color: ColorTokens.warningLight), borderRadius: BorderRadius.circular(4), ), child: const Row( children: [ Icon(Icons.warning, color: ColorTokens.warning), SizedBox(width: 12), Expanded( child: Text( 'Êtes-vous sûr de vouloir vous désinscrire de cet événement ?', style: TextStyle(fontSize: 14), ), ), ], ), ); } Widget _buildActionButtons() { final placesRestantes = (widget.evenement.maxParticipants ?? 0) - widget.evenement.participantsActuels; final isComplet = placesRestantes <= 0 && widget.evenement.maxParticipants != null; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surfaceContainerHighest, border: Border(top: BorderSide(color: Theme.of(context).colorScheme.outline)), ), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Annuler'), ), const SizedBox(width: 12), ElevatedButton( onPressed: (widget.isInscrit || !isComplet) ? _submitForm : null, style: ElevatedButton.styleFrom( backgroundColor: widget.isInscrit ? ColorTokens.error : ModuleColors.evenements, foregroundColor: Colors.white, ), child: Text(widget.isInscrit ? 'Se désinscrire' : 'S\'inscrire'), ), ], ), ); } String _formatDate(DateTime date) { final months = [ 'janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre' ]; return '${date.day} ${months[date.month - 1]} ${date.year} à ${date.hour}:${date.minute.toString().padLeft(2, '0')}'; } void _submitForm() { setState(() => _actionSent = true); if (widget.isInscrit) { context.read().add(DesinscrireEvenement(widget.evenement.id!.toString())); } else { context.read().add( InscrireEvenement(widget.evenement.id!.toString()), ); } } }