404 lines
15 KiB
Dart
404 lines
15 KiB
Dart
/// Dialogue d'ajout de membre
|
|
/// Formulaire complet pour créer un nouveau membre
|
|
library add_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 d'ajout de membre
|
|
class AddMemberDialog extends StatefulWidget {
|
|
const AddMemberDialog({super.key});
|
|
|
|
@override
|
|
State<AddMemberDialog> createState() => _AddMemberDialogState();
|
|
}
|
|
|
|
class _AddMemberDialogState extends State<AddMemberDialog> {
|
|
final _formKey = GlobalKey<FormState>();
|
|
|
|
// Contrôleurs de texte
|
|
final _nomController = TextEditingController();
|
|
final _prenomController = TextEditingController();
|
|
final _emailController = TextEditingController();
|
|
final _telephoneController = TextEditingController();
|
|
final _adresseController = TextEditingController();
|
|
final _villeController = TextEditingController();
|
|
final _codePostalController = TextEditingController();
|
|
final _regionController = TextEditingController();
|
|
final _paysController = TextEditingController();
|
|
final _professionController = TextEditingController();
|
|
final _nationaliteController = TextEditingController();
|
|
|
|
// Valeurs sélectionnées
|
|
Genre? _selectedGenre;
|
|
DateTime? _dateNaissance;
|
|
final StatutMembre _selectedStatut = StatutMembre.actif;
|
|
|
|
@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.person_add, color: Colors.white),
|
|
const SizedBox(width: 12),
|
|
const Text(
|
|
'Ajouter un 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),
|
|
|
|
// 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(),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Informations professionnelles
|
|
_buildSectionTitle('Informations professionnelles'),
|
|
const SizedBox(height: 12),
|
|
|
|
TextFormField(
|
|
controller: _professionController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Profession',
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.work),
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
TextFormField(
|
|
controller: _nationaliteController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Nationalité',
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.flag),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// 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('Créer le membre'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
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';
|
|
}
|
|
}
|
|
|
|
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
|
|
final membre = MembreCompletModel(
|
|
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(CreateMembre(membre));
|
|
|
|
// Fermer le dialogue
|
|
Navigator.pop(context);
|
|
|
|
// Afficher un message de succès
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Membre créé avec succès'),
|
|
backgroundColor: Colors.green,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|