Add phone number field to UserModel and update ProfileContent to display user phone number

This commit is contained in:
Van Leemput Dayron
2025-11-13 15:06:42 +01:00
parent 0971b4cb3d
commit 2ca30088ca
2 changed files with 244 additions and 72 deletions

View File

@@ -75,16 +75,20 @@ class UserModel {
/// Platform used for authentication (e.g., 'google', 'apple', 'email'). /// Platform used for authentication (e.g., 'google', 'apple', 'email').
final String? authMethod; final String? authMethod;
/// User's phone number (optional).
final String? phoneNumber;
/// Creates a new [UserModel] instance. /// Creates a new [UserModel] instance.
/// ///
/// [id], [email], and [prenom] are required fields. /// [id], [email], and [prenom] are required fields.
/// [nom] and [authMethod] are optional and can be null. /// [nom], [authMethod], and [phoneNumber] are optional and can be null.
UserModel({ UserModel({
required this.id, required this.id,
required this.email, required this.email,
required this.prenom, required this.prenom,
this.nom, this.nom,
this.authMethod, this.authMethod,
this.phoneNumber,
}); });
/// Creates a [UserModel] instance from a JSON map. /// Creates a [UserModel] instance from a JSON map.
@@ -98,6 +102,7 @@ class UserModel {
prenom: json['prenom'] ?? 'Voyageur', prenom: json['prenom'] ?? 'Voyageur',
nom: json['nom'], nom: json['nom'],
authMethod: json['authMethod'] ?? json['platform'], authMethod: json['authMethod'] ?? json['platform'],
phoneNumber: json['phoneNumber'],
); );
} }
@@ -111,6 +116,7 @@ class UserModel {
'prenom': prenom, 'prenom': prenom,
'nom': nom, 'nom': nom,
'authMethod': authMethod, 'authMethod': authMethod,
'phoneNumber': phoneNumber,
}; };
} }
} }

View File

@@ -18,44 +18,40 @@ class ProfileContent extends StatelessWidget {
builder: (context, user) { builder: (context, user) {
final isEmailAuth = user.authMethod == 'email' || user.authMethod == null; final isEmailAuth = user.authMethod == 'email' || user.authMethod == null;
return Column( return SingleChildScrollView(
children: [ child: Column(
// Section titre crossAxisAlignment: CrossAxisAlignment.center,
Padding( children: [
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), // En-tête avec photo de profil
child: Row( Container(
children: [ padding: EdgeInsets.symmetric(vertical: 24, horizontal: 16),
Text(
'Profil utilisateur',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey[600],
),
),
],
),
),
// Card du profil
Card(
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Padding(
padding: EdgeInsets.all(16),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
// Photo de profil // Photo de profil
CircleAvatar( Container(
radius: 40, decoration: BoxDecoration(
backgroundColor: Theme.of(context).colorScheme.primary, shape: BoxShape.circle,
child: Text( boxShadow: [
user.prenom.isNotEmpty BoxShadow(
? user.prenom[0].toUpperCase() color: Colors.black.withValues(alpha: 0.1),
: 'U', blurRadius: 8,
style: TextStyle( offset: Offset(0, 2),
fontSize: 24, )
fontWeight: FontWeight.bold, ],
color: Colors.white, ),
child: CircleAvatar(
radius: 50,
backgroundColor: Theme.of(context).colorScheme.primary,
child: Text(
user.prenom.isNotEmpty
? user.prenom[0].toUpperCase()
: 'U',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Colors.white,
),
), ),
), ),
), ),
@@ -66,17 +62,22 @@ class ProfileContent extends StatelessWidget {
Text( Text(
'${user.prenom} ${user.nom ?? ''}', '${user.prenom} ${user.nom ?? ''}',
style: TextStyle( style: TextStyle(
fontSize: 20, fontSize: 22,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
textAlign: TextAlign.center,
), ),
SizedBox(height: 8), SizedBox(height: 4),
// Email // Email
Text( Text(
user.email, user.email,
style: TextStyle(fontSize: 14, color: Colors.grey[600]), style: TextStyle(
fontSize: 14,
color: Colors.grey[600],
),
textAlign: TextAlign.center,
), ),
SizedBox(height: 12), SizedBox(height: 12),
@@ -91,12 +92,14 @@ class ProfileContent extends StatelessWidget {
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Image.asset( if (_getAuthMethodIcon(user.authMethod).isNotEmpty)
_getAuthMethodIcon(user.authMethod), Image.asset(
height: 16, _getAuthMethodIcon(user.authMethod),
width: 16, height: 16,
), width: 16,
SizedBox(width: 8), ),
if (_getAuthMethodIcon(user.authMethod).isNotEmpty)
SizedBox(width: 8),
Text( Text(
_getAuthMethodLabel(user.authMethod), _getAuthMethodLabel(user.authMethod),
style: TextStyle( style: TextStyle(
@@ -111,45 +114,208 @@ class ProfileContent extends StatelessWidget {
], ],
), ),
), ),
),
// Actions du profil // Section Informations personnelles
ListTile( Padding(
leading: Icon(Icons.edit), padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
title: Text('Modifier le profil'), child: Row(
trailing: Icon(Icons.arrow_forward_ios), children: [
onTap: () { Text(
_showEditProfileDialog(context, user); 'Informations personnelles',
}, style: TextStyle(
), fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
],
),
),
// Afficher l'option de changement de mot de passe seulement pour email // Tuiles d'information
if (isEmailAuth) _buildInfoTile(
ListTile( icon: Icons.person_outline,
leading: Icon(Icons.lock), label: 'Nom complet',
title: Text('Changer le mot de passe'), value: '${user.prenom} ${user.nom ?? ''}',
trailing: Icon(Icons.arrow_forward_ios), context: context,
),
_buildInfoTile(
icon: Icons.email_outlined,
label: 'Adresse e-mail',
value: user.email,
context: context,
),
_buildInfoTile(
icon: Icons.phone_outlined,
label: 'Téléphone',
value: user.phoneNumber ?? 'Non défini',
context: context,
),
// Option de changement de mot de passe seulement pour email
if (isEmailAuth)
_buildActionTile(
icon: Icons.lock_outlined,
label: 'Changer de mot de passe',
context: context,
onTap: () {
_showChangePasswordDialog(context, user);
},
),
SizedBox(height: 8),
// Option pour modifier le profil
_buildActionTile(
icon: Icons.edit_outlined,
label: 'Modifier le profil',
context: context,
onTap: () { onTap: () {
_showChangePasswordDialog(context, user); _showEditProfileDialog(context, user);
}, },
), ),
Divider(), SizedBox(height: 8),
ListTile( // Option pour supprimer le compte
leading: Icon(Icons.delete, color: Colors.red), _buildActionTile(
title: Text('Supprimer le compte'), icon: Icons.delete_outline,
trailing: Icon(Icons.arrow_forward_ios), label: 'Supprimer le compte',
onTap: () { context: context,
_showDeleteAccountDialog(context, user); isDestructive: true,
}, onTap: () {
), _showDeleteAccountDialog(context, user);
], },
),
SizedBox(height: 24),
],
),
); );
}, },
); );
} }
Widget _buildInfoTile({
required IconData icon,
required String label,
required String value,
required BuildContext context,
}) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Container(
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[850] : Colors.grey[100],
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[800] : Colors.grey[200],
borderRadius: BorderRadius.circular(6),
),
child: Icon(
icon,
size: 20,
color: Theme.of(context).colorScheme.primary,
),
),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 2),
Text(
value,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black87,
),
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
);
}
Widget _buildActionTile({
required IconData icon,
required String label,
required BuildContext context,
required VoidCallback onTap,
bool isDestructive = false,
}) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return GestureDetector(
onTap: onTap,
child: Container(
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[850] : Colors.grey[100],
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[800] : Colors.grey[200],
borderRadius: BorderRadius.circular(6),
),
child: Icon(
icon,
size: 20,
color: isDestructive
? Colors.red
: Theme.of(context).colorScheme.primary,
),
),
SizedBox(width: 16),
Expanded(
child: Text(
label,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isDestructive
? Colors.red
: (isDarkMode ? Colors.white : Colors.black87),
),
),
),
Icon(
Icons.arrow_forward_ios,
size: 16,
color: Colors.grey[400],
),
],
),
),
);
}
String _getAuthMethodLabel(String? authMethod) { String _getAuthMethodLabel(String? authMethod) {
switch (authMethod) { switch (authMethod) {
case 'apple': case 'apple':