442 lines
16 KiB
Dart
442 lines
16 KiB
Dart
/// Dialogue de modification de membre
|
|
/// Formulaire complet pour modifier un membre existant
|
|
library edit_member_dialog;
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:intl/intl.dart';
|
|
import '../../bloc/membres_bloc.dart';
|
|
import '../../bloc/membres_event.dart';
|
|
import '../../data/models/membre_complete_model.dart';
|
|
|
|
/// Dialogue de modification de membre
|
|
class EditMemberDialog extends StatefulWidget {
|
|
final MembreCompletModel membre;
|
|
|
|
const EditMemberDialog({
|
|
super.key,
|
|
required this.membre,
|
|
});
|
|
|
|
@override
|
|
State<EditMemberDialog> createState() => _EditMemberDialogState();
|
|
}
|
|
|
|
class _EditMemberDialogState extends State<EditMemberDialog> {
|
|
final _formKey = GlobalKey<FormState>();
|
|
|
|
// Contrôleurs de texte
|
|
late final TextEditingController _nomController;
|
|
late final TextEditingController _prenomController;
|
|
late final TextEditingController _emailController;
|
|
late final TextEditingController _telephoneController;
|
|
late final TextEditingController _adresseController;
|
|
late final TextEditingController _villeController;
|
|
late final TextEditingController _codePostalController;
|
|
late final TextEditingController _regionController;
|
|
late final TextEditingController _paysController;
|
|
late final TextEditingController _professionController;
|
|
late final TextEditingController _nationaliteController;
|
|
|
|
// Valeurs sélectionnées
|
|
Genre? _selectedGenre;
|
|
DateTime? _dateNaissance;
|
|
StatutMembre? _selectedStatut;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
// Initialiser les contrôleurs avec les valeurs existantes
|
|
_nomController = TextEditingController(text: widget.membre.nom);
|
|
_prenomController = TextEditingController(text: widget.membre.prenom);
|
|
_emailController = TextEditingController(text: widget.membre.email);
|
|
_telephoneController = TextEditingController(text: widget.membre.telephone ?? '');
|
|
_adresseController = TextEditingController(text: widget.membre.adresse ?? '');
|
|
_villeController = TextEditingController(text: widget.membre.ville ?? '');
|
|
_codePostalController = TextEditingController(text: widget.membre.codePostal ?? '');
|
|
_regionController = TextEditingController(text: widget.membre.region ?? '');
|
|
_paysController = TextEditingController(text: widget.membre.pays ?? '');
|
|
_professionController = TextEditingController(text: widget.membre.profession ?? '');
|
|
_nationaliteController = TextEditingController(text: widget.membre.nationalite ?? '');
|
|
|
|
_selectedGenre = widget.membre.genre;
|
|
_dateNaissance = widget.membre.dateNaissance;
|
|
_selectedStatut = widget.membre.statut;
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_nomController.dispose();
|
|
_prenomController.dispose();
|
|
_emailController.dispose();
|
|
_telephoneController.dispose();
|
|
_adresseController.dispose();
|
|
_villeController.dispose();
|
|
_codePostalController.dispose();
|
|
_regionController.dispose();
|
|
_paysController.dispose();
|
|
_professionController.dispose();
|
|
_nationaliteController.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(0xFF6C5CE7),
|
|
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 le membre',
|
|
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 personnelles
|
|
_buildSectionTitle('Informations personnelles'),
|
|
const SizedBox(height: 12),
|
|
|
|
TextFormField(
|
|
controller: _nomController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Nom *',
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.person),
|
|
),
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'Le nom est obligatoire';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
TextFormField(
|
|
controller: _prenomController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Prénom *',
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.person_outline),
|
|
),
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'Le prénom est obligatoire';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
TextFormField(
|
|
controller: _emailController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Email *',
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.email),
|
|
),
|
|
keyboardType: TextInputType.emailAddress,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'L\'email est obligatoire';
|
|
}
|
|
if (!value.contains('@')) {
|
|
return 'Email invalide';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
TextFormField(
|
|
controller: _telephoneController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Téléphone',
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.phone),
|
|
),
|
|
keyboardType: TextInputType.phone,
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
// Genre
|
|
DropdownButtonFormField<Genre>(
|
|
value: _selectedGenre,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Genre',
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.wc),
|
|
),
|
|
items: Genre.values.map((genre) {
|
|
return DropdownMenuItem(
|
|
value: genre,
|
|
child: Text(_getGenreLabel(genre)),
|
|
);
|
|
}).toList(),
|
|
onChanged: (value) {
|
|
setState(() {
|
|
_selectedGenre = value;
|
|
});
|
|
},
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
// Statut
|
|
DropdownButtonFormField<StatutMembre>(
|
|
value: _selectedStatut,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Statut *',
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.info),
|
|
),
|
|
items: StatutMembre.values.map((statut) {
|
|
return DropdownMenuItem(
|
|
value: statut,
|
|
child: Text(_getStatutLabel(statut)),
|
|
);
|
|
}).toList(),
|
|
onChanged: (value) {
|
|
setState(() {
|
|
_selectedStatut = value;
|
|
});
|
|
},
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
// Date de naissance
|
|
InkWell(
|
|
onTap: () => _selectDate(context),
|
|
child: InputDecorator(
|
|
decoration: const InputDecoration(
|
|
labelText: 'Date de naissance',
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.calendar_today),
|
|
),
|
|
child: Text(
|
|
_dateNaissance != null
|
|
? DateFormat('dd/MM/yyyy').format(_dateNaissance!)
|
|
: 'Sélectionner une date',
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Adresse
|
|
_buildSectionTitle('Adresse'),
|
|
const SizedBox(height: 12),
|
|
|
|
TextFormField(
|
|
controller: _adresseController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Adresse',
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.home),
|
|
),
|
|
maxLines: 2,
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: TextFormField(
|
|
controller: _villeController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Ville',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: TextFormField(
|
|
controller: _codePostalController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Code postal',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
TextFormField(
|
|
controller: _regionController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Région',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
TextFormField(
|
|
controller: _paysController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Pays',
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// 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(0xFF6C5CE7),
|
|
foregroundColor: Colors.white,
|
|
),
|
|
child: const Text('Enregistrer'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSectionTitle(String title) {
|
|
return Text(
|
|
title,
|
|
style: const TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
color: Color(0xFF6C5CE7),
|
|
),
|
|
);
|
|
}
|
|
|
|
String _getGenreLabel(Genre genre) {
|
|
switch (genre) {
|
|
case Genre.homme:
|
|
return 'Homme';
|
|
case Genre.femme:
|
|
return 'Femme';
|
|
case Genre.autre:
|
|
return 'Autre';
|
|
}
|
|
}
|
|
|
|
String _getStatutLabel(StatutMembre statut) {
|
|
switch (statut) {
|
|
case StatutMembre.actif:
|
|
return 'Actif';
|
|
case StatutMembre.inactif:
|
|
return 'Inactif';
|
|
case StatutMembre.suspendu:
|
|
return 'Suspendu';
|
|
case StatutMembre.enAttente:
|
|
return 'En attente';
|
|
}
|
|
}
|
|
|
|
Future<void> _selectDate(BuildContext context) async {
|
|
final DateTime? picked = await showDatePicker(
|
|
context: context,
|
|
initialDate: _dateNaissance ?? DateTime.now().subtract(const Duration(days: 365 * 25)),
|
|
firstDate: DateTime(1900),
|
|
lastDate: DateTime.now(),
|
|
);
|
|
if (picked != null && picked != _dateNaissance) {
|
|
setState(() {
|
|
_dateNaissance = picked;
|
|
});
|
|
}
|
|
}
|
|
|
|
void _submitForm() {
|
|
if (_formKey.currentState!.validate()) {
|
|
// Créer le modèle de membre mis à jour
|
|
final membreUpdated = widget.membre.copyWith(
|
|
nom: _nomController.text,
|
|
prenom: _prenomController.text,
|
|
email: _emailController.text,
|
|
telephone: _telephoneController.text.isNotEmpty ? _telephoneController.text : null,
|
|
dateNaissance: _dateNaissance,
|
|
genre: _selectedGenre,
|
|
adresse: _adresseController.text.isNotEmpty ? _adresseController.text : null,
|
|
ville: _villeController.text.isNotEmpty ? _villeController.text : null,
|
|
codePostal: _codePostalController.text.isNotEmpty ? _codePostalController.text : null,
|
|
region: _regionController.text.isNotEmpty ? _regionController.text : null,
|
|
pays: _paysController.text.isNotEmpty ? _paysController.text : null,
|
|
profession: _professionController.text.isNotEmpty ? _professionController.text : null,
|
|
nationalite: _nationaliteController.text.isNotEmpty ? _nationaliteController.text : null,
|
|
statut: _selectedStatut!,
|
|
);
|
|
|
|
// Envoyer l'événement au BLoC
|
|
context.read<MembresBloc>().add(UpdateMembre(widget.membre.id!, membreUpdated));
|
|
|
|
// Fermer le dialogue
|
|
Navigator.pop(context);
|
|
|
|
// Afficher un message de succès
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Membre modifié avec succès'),
|
|
backgroundColor: Colors.green,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|