357 lines
11 KiB
Dart
357 lines
11 KiB
Dart
import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
|
|
import 'package:flutter/material.dart';
|
|
import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
|
|
import 'package:geosector_app/core/repositories/user_repository.dart';
|
|
|
|
/// Widget de profil commun pour toute l'application
|
|
/// Affiche une boîte de dialogue modale avec un formulaire de mise à jour
|
|
/// des données utilisateur
|
|
class ProfileDialog extends StatefulWidget {
|
|
/// ID de l'utilisateur dont on veut afficher/modifier le profil
|
|
final String userId;
|
|
|
|
const ProfileDialog({
|
|
Key? key,
|
|
required this.userId,
|
|
}) : super(key: key);
|
|
|
|
/// Affiche la boîte de dialogue de profil
|
|
static void show(BuildContext context, String userId) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => ProfileDialog(userId: userId),
|
|
);
|
|
}
|
|
|
|
@override
|
|
State<ProfileDialog> createState() => _ProfileDialogState();
|
|
}
|
|
|
|
class _ProfileDialogState extends State<ProfileDialog> {
|
|
/// Contrôleurs pour les champs du formulaire
|
|
final TextEditingController _firstNameController = TextEditingController();
|
|
final TextEditingController _lastNameController = TextEditingController();
|
|
final TextEditingController _emailController = TextEditingController();
|
|
final TextEditingController _phoneController = TextEditingController();
|
|
|
|
/// État de chargement
|
|
bool _isLoading = true;
|
|
|
|
/// État d'erreur
|
|
String? _errorMessage;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_loadUserData();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_firstNameController.dispose();
|
|
_lastNameController.dispose();
|
|
_emailController.dispose();
|
|
_phoneController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
/// Charge les données de l'utilisateur depuis l'API
|
|
Future<void> _loadUserData() async {
|
|
try {
|
|
setState(() {
|
|
_isLoading = true;
|
|
_errorMessage = null;
|
|
});
|
|
|
|
// Utiliser l'instance globale définie dans app.dart
|
|
final user = userRepository.currentUser;
|
|
|
|
// Si l'utilisateur est trouvé, remplir les champs du formulaire
|
|
if (user != null) {
|
|
_firstNameController.text = user.firstName ?? '';
|
|
_lastNameController.text = user.name ?? '';
|
|
_emailController.text = user.email ?? '';
|
|
// Note: Utiliser la propriété appropriée pour le téléphone si elle existe
|
|
// ou laisser vide si elle n'existe pas
|
|
_phoneController.text = ''; // Champ laissé vide par défaut
|
|
} else {
|
|
_errorMessage = 'Utilisateur non trouvé';
|
|
}
|
|
} catch (e) {
|
|
_errorMessage = 'Erreur lors du chargement des données: $e';
|
|
} finally {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
/// Enregistre les modifications du profil
|
|
Future<void> _saveProfile() async {
|
|
try {
|
|
setState(() {
|
|
_isLoading = true;
|
|
_errorMessage = null;
|
|
});
|
|
|
|
// Utiliser l'instance globale définie dans app.dart
|
|
|
|
// Mettre à jour les données de l'utilisateur
|
|
// Note: Cette partie dépend de l'implémentation réelle du UserRepository
|
|
// et devrait être adaptée en fonction de l'API disponible
|
|
|
|
// Récupérer l'utilisateur actuel
|
|
final user = userRepository.currentUser;
|
|
if (user != null) {
|
|
// Mettre à jour les propriétés de l'utilisateur
|
|
user.firstName = _firstNameController.text;
|
|
user.name = _lastNameController.text;
|
|
|
|
// Sauvegarder les modifications
|
|
// Note: Utiliser la méthode appropriée du repository
|
|
// Exemple: userRepo.saveUser(user) ou userRepo.updateUser(user)
|
|
|
|
// Pour l'instant, nous simulons une mise à jour réussie
|
|
// Cette partie devra être adaptée à l'API réelle
|
|
await Future.delayed(const Duration(milliseconds: 500));
|
|
|
|
// Fermer la boîte de dialogue
|
|
if (mounted) {
|
|
Navigator.of(context)
|
|
.pop(true); // Retourne true pour indiquer le succès
|
|
|
|
// Afficher un message de succès
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: const Text('Profil mis à jour avec succès'),
|
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
),
|
|
);
|
|
}
|
|
} else {
|
|
throw Exception('Utilisateur non trouvé');
|
|
}
|
|
} catch (e) {
|
|
setState(() {
|
|
_errorMessage = 'Erreur lors de la mise à jour du profil: $e';
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final size = MediaQuery.of(context).size;
|
|
final theme = Theme.of(context);
|
|
|
|
// Déterminer si nous sommes sur un appareil mobile ou un ordinateur de bureau
|
|
final isDesktop = size.width > 900;
|
|
|
|
// Calculer la largeur de la boîte de dialogue
|
|
// 90% de la largeur de l'écran pour les mobiles
|
|
// 50% de la largeur de l'écran pour les ordinateurs de bureau (max 600px)
|
|
final dialogWidth = isDesktop
|
|
? size.width * 0.5 > 600
|
|
? 600.0
|
|
: size.width * 0.5
|
|
: size.width * 0.9;
|
|
|
|
return Dialog(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
// Définir la largeur de la boîte de dialogue
|
|
child: Container(
|
|
width: dialogWidth,
|
|
padding: const EdgeInsets.all(24),
|
|
child: _isLoading
|
|
? const Center(
|
|
child: CircularProgressIndicator(),
|
|
)
|
|
: _errorMessage != null
|
|
? _buildErrorView()
|
|
: _buildProfileForm(),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Construit la vue d'erreur
|
|
Widget _buildErrorView() {
|
|
final theme = Theme.of(context);
|
|
|
|
return Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(
|
|
Icons.error_outline,
|
|
color: theme.colorScheme.error,
|
|
size: 48,
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
'Erreur',
|
|
style: theme.textTheme.titleLarge?.copyWith(
|
|
color: theme.colorScheme.error,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
_errorMessage ?? 'Une erreur inconnue est survenue',
|
|
textAlign: TextAlign.center,
|
|
style: theme.textTheme.bodyMedium,
|
|
),
|
|
const SizedBox(height: 24),
|
|
ElevatedButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: theme.colorScheme.primary,
|
|
foregroundColor: theme.colorScheme.onPrimary,
|
|
),
|
|
child: const Text('Fermer'),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
/// Construit le formulaire de profil
|
|
Widget _buildProfileForm() {
|
|
final theme = Theme.of(context);
|
|
|
|
return Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Titre du formulaire
|
|
Row(
|
|
children: [
|
|
Icon(
|
|
Icons.person,
|
|
color: theme.colorScheme.primary,
|
|
size: 28,
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Text(
|
|
'Mon compte',
|
|
style: theme.textTheme.titleLarge?.copyWith(
|
|
color: theme.colorScheme.primary,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.close),
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
tooltip: 'Fermer',
|
|
),
|
|
],
|
|
),
|
|
const Divider(height: 32),
|
|
|
|
// Formulaire
|
|
Form(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Prénom
|
|
TextFormField(
|
|
controller: _firstNameController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Prénom',
|
|
prefixIcon: Icon(Icons.person_outline),
|
|
border: OutlineInputBorder(),
|
|
),
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'Veuillez entrer votre prénom';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Nom
|
|
TextFormField(
|
|
controller: _lastNameController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Nom',
|
|
prefixIcon: Icon(Icons.person_outline),
|
|
border: OutlineInputBorder(),
|
|
),
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'Veuillez entrer votre nom';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Email
|
|
TextFormField(
|
|
controller: _emailController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Email',
|
|
prefixIcon: Icon(Icons.email_outlined),
|
|
border: OutlineInputBorder(),
|
|
),
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'Veuillez entrer votre email';
|
|
}
|
|
if (!value.contains('@')) {
|
|
return 'Veuillez entrer un email valide';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Téléphone
|
|
TextFormField(
|
|
controller: _phoneController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Téléphone',
|
|
prefixIcon: Icon(Icons.phone_outlined),
|
|
border: OutlineInputBorder(),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
|
|
// Boutons d'action
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: Text(
|
|
'Annuler',
|
|
style: TextStyle(
|
|
color: theme.colorScheme.error,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 16),
|
|
ElevatedButton(
|
|
onPressed: _saveProfile,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: theme.colorScheme.primary,
|
|
foregroundColor: theme.colorScheme.onPrimary,
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 24,
|
|
vertical: 12,
|
|
),
|
|
),
|
|
child: const Text('Enregistrer'),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|