Restructuration majeure du projet: migration de flutt vers app, ajout de l'API et mise à jour du site web
This commit is contained in:
268
app/lib/presentation/widgets/profile_dialog.dart
Normal file
268
app/lib/presentation/widgets/profile_dialog.dart
Normal file
@@ -0,0 +1,268 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:geosector_app/app.dart';
|
||||
import 'package:geosector_app/core/data/models/user_model.dart';
|
||||
import 'package:geosector_app/presentation/widgets/user_form.dart';
|
||||
|
||||
class ProfileDialog extends StatefulWidget {
|
||||
final UserModel user;
|
||||
|
||||
const ProfileDialog({
|
||||
Key? key,
|
||||
required this.user,
|
||||
}) : super(key: key);
|
||||
|
||||
/// Méthode statique pour afficher la boîte de dialogue
|
||||
static Future<bool?> show(BuildContext context, UserModel user) {
|
||||
return showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => ProfileDialog(user: user),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
State<ProfileDialog> createState() => _ProfileDialogState();
|
||||
}
|
||||
|
||||
class _ProfileDialogState extends State<ProfileDialog> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
late UserModel _user;
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_user = widget.user;
|
||||
}
|
||||
|
||||
// Fonction pour capitaliser la première lettre de chaque mot
|
||||
String _capitalizeFirstLetter(String text) {
|
||||
if (text.isEmpty) return text;
|
||||
|
||||
return text.split(' ').map((word) {
|
||||
if (word.isEmpty) return word;
|
||||
return word[0].toUpperCase() + word.substring(1).toLowerCase();
|
||||
}).join(' ');
|
||||
}
|
||||
|
||||
// Fonction pour mettre en majuscule
|
||||
String _toUpperCase(String text) {
|
||||
return text.toUpperCase();
|
||||
}
|
||||
|
||||
// Fonction pour valider et soumettre le formulaire
|
||||
Future<void> _saveProfile(UserModel updatedUser) async {
|
||||
// Validation supplémentaire
|
||||
if (!_validateUser(updatedUser)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
// Formatage des données
|
||||
final formattedUser = updatedUser.copyWith(
|
||||
name: _toUpperCase(updatedUser.name ?? ''),
|
||||
firstName: _capitalizeFirstLetter(updatedUser.firstName ?? ''),
|
||||
);
|
||||
|
||||
// Sauvegarde de l'utilisateur
|
||||
await userRepository.saveUser(formattedUser);
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Profil mis à jour avec succès'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
Navigator.of(context).pop(true); // Fermer la modale avec succès
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Erreur lors de la mise à jour du profil: $e'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validation supplémentaire
|
||||
bool _validateUser(UserModel user) {
|
||||
// Vérifier que l'email est valide
|
||||
if (user.email.isEmpty ||
|
||||
!user.email.contains('@') ||
|
||||
!user.email.contains('.')) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Veuillez entrer une adresse email valide'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vérifier que le nom ou le sectName est renseigné
|
||||
if ((user.name == null || user.name!.isEmpty) &&
|
||||
(user.sectName == null || user.sectName!.isEmpty)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Le nom ou le nom du secteur doit être renseigné'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vérifier que le téléphone fixe est valide s'il est renseigné
|
||||
if (user.phone != null &&
|
||||
user.phone!.isNotEmpty &&
|
||||
user.phone!.length != 10) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content:
|
||||
Text('Le numéro de téléphone fixe doit contenir 10 chiffres'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vérifier que le téléphone mobile est valide s'il est renseigné
|
||||
if (user.mobile != null &&
|
||||
user.mobile!.isNotEmpty &&
|
||||
user.mobile!.length != 10) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content:
|
||||
Text('Le numéro de téléphone mobile doit contenir 10 chiffres'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 5),
|
||||
),
|
||||
],
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 600,
|
||||
maxHeight: 700,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Titre
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Mon compte',
|
||||
style: theme.textTheme.headlineSmall?.copyWith(
|
||||
color: theme.colorScheme.primary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
tooltip: 'Fermer',
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
// Formulaire
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: UserForm(
|
||||
user: _user,
|
||||
onSubmit: _saveProfile,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Boutons
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed:
|
||||
_isLoading ? null : () => Navigator.of(context).pop(),
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20, vertical: 12),
|
||||
),
|
||||
child: const Text('Fermer'),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
ElevatedButton(
|
||||
onPressed: _isLoading
|
||||
? null
|
||||
: () {
|
||||
// Appeler directement la méthode onSubmit du UserForm
|
||||
// qui va déclencher la validation et la soumission
|
||||
_saveProfile(_user);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
foregroundColor: theme.colorScheme.onPrimary,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20, vertical: 12),
|
||||
),
|
||||
child: _isLoading
|
||||
? const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor:
|
||||
AlwaysStoppedAnimation<Color>(Colors.white),
|
||||
),
|
||||
)
|
||||
: const Text('Enregistrer'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user