/// Dialogue de modification d'événement /// Formulaire pré-rempli pour modifier un événement existant library edit_event_dialog; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:intl/intl.dart'; import '../../bloc/evenements_bloc.dart'; import '../../bloc/evenements_event.dart'; import '../../data/models/evenement_model.dart'; /// Dialogue de modification d'événement class EditEventDialog extends StatefulWidget { final EvenementModel evenement; const EditEventDialog({ super.key, required this.evenement, }); @override State createState() => _EditEventDialogState(); } class _EditEventDialogState extends State { final _formKey = GlobalKey(); // Contrôleurs de texte late final TextEditingController _titreController; late final TextEditingController _descriptionController; late final TextEditingController _lieuController; late final TextEditingController _adresseController; late final TextEditingController _capaciteController; // Valeurs sélectionnées late DateTime _dateDebut; late DateTime _dateFin; late TypeEvenement _selectedType; late StatutEvenement _selectedStatut; late bool _inscriptionRequise; late bool _estPublic; @override void initState() { super.initState(); // Initialiser les contrôleurs avec les valeurs existantes _titreController = TextEditingController(text: widget.evenement.titre); _descriptionController = TextEditingController(text: widget.evenement.description ?? ''); _lieuController = TextEditingController(text: widget.evenement.lieu ?? ''); _adresseController = TextEditingController(text: widget.evenement.adresse ?? ''); _capaciteController = TextEditingController( text: widget.evenement.maxParticipants?.toString() ?? '', ); // Initialiser les valeurs _dateDebut = widget.evenement.dateDebut; _dateFin = widget.evenement.dateFin; _selectedType = widget.evenement.type; _selectedStatut = widget.evenement.statut; _inscriptionRequise = widget.evenement.inscriptionRequise; _estPublic = widget.evenement.estPublic; } @override void dispose() { _titreController.dispose(); _descriptionController.dispose(); _lieuController.dispose(); _adresseController.dispose(); _capaciteController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Dialog( child: Container( width: MediaQuery.of(context).size.width * 0.9, constraints: const BoxConstraints(maxHeight: 600), child: Column( mainAxisSize: MainAxisSize.min, children: [ // En-tête Container( padding: const EdgeInsets.all(16), decoration: const BoxDecoration( color: Color(0xFF3B82F6), borderRadius: BorderRadius.only( topLeft: Radius.circular(4), topRight: Radius.circular(4), ), ), child: Row( children: [ const Icon(Icons.edit, color: Colors.white), const SizedBox(width: 12), const Text( 'Modifier l\'événement', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), const Spacer(), IconButton( icon: const Icon(Icons.close, color: Colors.white), onPressed: () => Navigator.pop(context), ), ], ), ), // Formulaire Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Informations de base _buildSectionTitle('Informations de base'), const SizedBox(height: 12), TextFormField( controller: _titreController, decoration: const InputDecoration( labelText: 'Titre *', border: OutlineInputBorder(), prefixIcon: Icon(Icons.title), ), validator: (value) { if (value == null || value.isEmpty) { return 'Le titre est obligatoire'; } if (value.length < 3) { return 'Le titre doit contenir au moins 3 caractères'; } return null; }, ), const SizedBox(height: 12), TextFormField( controller: _descriptionController, decoration: const InputDecoration( labelText: 'Description', border: OutlineInputBorder(), prefixIcon: Icon(Icons.description), ), maxLines: 3, ), const SizedBox(height: 12), // Type et statut Row( children: [ Expanded( child: DropdownButtonFormField( value: _selectedType, decoration: const InputDecoration( labelText: 'Type *', border: OutlineInputBorder(), prefixIcon: Icon(Icons.category), ), items: TypeEvenement.values.map((type) { return DropdownMenuItem( value: type, child: Text(_getTypeLabel(type)), ); }).toList(), onChanged: (value) { setState(() { _selectedType = value!; }); }, ), ), const SizedBox(width: 12), Expanded( child: DropdownButtonFormField( value: _selectedStatut, decoration: const InputDecoration( labelText: 'Statut *', border: OutlineInputBorder(), prefixIcon: Icon(Icons.flag), ), items: StatutEvenement.values.map((statut) { return DropdownMenuItem( value: statut, child: Text(_getStatutLabel(statut)), ); }).toList(), onChanged: (value) { setState(() { _selectedStatut = value!; }); }, ), ), ], ), const SizedBox(height: 16), // Dates _buildSectionTitle('Dates et horaires'), const SizedBox(height: 12), InkWell( onTap: () => _selectDateDebut(context), child: InputDecorator( decoration: const InputDecoration( labelText: 'Date de début *', border: OutlineInputBorder(), prefixIcon: Icon(Icons.calendar_today), ), child: Text( DateFormat('dd/MM/yyyy HH:mm').format(_dateDebut), ), ), ), const SizedBox(height: 12), InkWell( onTap: () => _selectDateFin(context), child: InputDecorator( decoration: const InputDecoration( labelText: 'Date de fin *', border: OutlineInputBorder(), prefixIcon: Icon(Icons.event_available), ), child: Text( DateFormat('dd/MM/yyyy HH:mm').format(_dateFin), ), ), ), const SizedBox(height: 16), // Lieu _buildSectionTitle('Lieu'), const SizedBox(height: 12), TextFormField( controller: _lieuController, decoration: const InputDecoration( labelText: 'Lieu *', border: OutlineInputBorder(), prefixIcon: Icon(Icons.place), ), validator: (value) { if (value == null || value.isEmpty) { return 'Le lieu est obligatoire'; } return null; }, ), const SizedBox(height: 12), TextFormField( controller: _adresseController, decoration: const InputDecoration( labelText: 'Adresse complète', border: OutlineInputBorder(), prefixIcon: Icon(Icons.location_on), ), maxLines: 2, ), const SizedBox(height: 16), // Capacité _buildSectionTitle('Paramètres'), const SizedBox(height: 12), TextFormField( controller: _capaciteController, decoration: InputDecoration( labelText: 'Capacité maximale', border: const OutlineInputBorder(), prefixIcon: const Icon(Icons.people), suffixText: widget.evenement.participantsActuels > 0 ? '${widget.evenement.participantsActuels} inscrits' : null, ), keyboardType: TextInputType.number, validator: (value) { if (value != null && value.isNotEmpty) { final capacite = int.tryParse(value); if (capacite == null || capacite <= 0) { return 'La capacité doit être un nombre positif'; } if (capacite < widget.evenement.participantsActuels) { return 'La capacité ne peut pas être inférieure au nombre d\'inscrits (${widget.evenement.participantsActuels})'; } } return null; }, ), const SizedBox(height: 12), SwitchListTile( title: const Text('Inscription requise'), subtitle: const Text('Les participants doivent s\'inscrire'), value: _inscriptionRequise, onChanged: (value) { setState(() { _inscriptionRequise = value; }); }, ), SwitchListTile( title: const Text('Événement public'), subtitle: const Text('Visible par tous les membres'), value: _estPublic, onChanged: (value) { setState(() { _estPublic = value; }); }, ), ], ), ), ), ), // Boutons d'action Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.grey[100], border: Border(top: BorderSide(color: Colors.grey[300]!)), ), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Annuler'), ), const SizedBox(width: 12), ElevatedButton( onPressed: _submitForm, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF3B82F6), foregroundColor: Colors.white, ), child: const Text('Enregistrer'), ), ], ), ), ], ), ), ); } Widget _buildSectionTitle(String title) { return Text( title, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF3B82F6), ), ); } String _getTypeLabel(TypeEvenement type) { 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'; } } String _getStatutLabel(StatutEvenement statut) { 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é'; } } Future _selectDateDebut(BuildContext context) async { final DateTime? pickedDate = await showDatePicker( context: context, initialDate: _dateDebut, firstDate: DateTime.now().subtract(const Duration(days: 365)), lastDate: DateTime.now().add(const Duration(days: 365 * 2)), ); if (pickedDate != null) { final TimeOfDay? pickedTime = await showTimePicker( context: context, initialTime: TimeOfDay.fromDateTime(_dateDebut), ); if (pickedTime != null) { setState(() { _dateDebut = DateTime( pickedDate.year, pickedDate.month, pickedDate.day, pickedTime.hour, pickedTime.minute, ); // Ajuster la date de fin si elle est avant la date de début if (_dateFin.isBefore(_dateDebut)) { _dateFin = _dateDebut.add(const Duration(hours: 2)); } }); } } } Future _selectDateFin(BuildContext context) async { final DateTime? pickedDate = await showDatePicker( context: context, initialDate: _dateFin, firstDate: _dateDebut, lastDate: DateTime.now().add(const Duration(days: 365 * 2)), ); if (pickedDate != null) { final TimeOfDay? pickedTime = await showTimePicker( context: context, initialTime: TimeOfDay.fromDateTime(_dateFin), ); if (pickedTime != null) { setState(() { _dateFin = DateTime( pickedDate.year, pickedDate.month, pickedDate.day, pickedTime.hour, pickedTime.minute, ); }); } } } void _submitForm() { if (_formKey.currentState!.validate()) { // Créer le modèle d'événement mis à jour final evenementUpdated = widget.evenement.copyWith( titre: _titreController.text, description: _descriptionController.text.isNotEmpty ? _descriptionController.text : null, dateDebut: _dateDebut, dateFin: _dateFin, lieu: _lieuController.text, adresse: _adresseController.text.isNotEmpty ? _adresseController.text : null, type: _selectedType, statut: _selectedStatut, maxParticipants: _capaciteController.text.isNotEmpty ? int.parse(_capaciteController.text) : null, inscriptionRequise: _inscriptionRequise, estPublic: _estPublic, ); // Envoyer l'événement au BLoC context.read().add(UpdateEvenement(widget.evenement.id!.toString(), evenementUpdated)); // Fermer le dialogue Navigator.pop(context); // Afficher un message de succès ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Événement modifié avec succès'), backgroundColor: Colors.green, ), ); } } }