From 19f6efa9959c037c951e989ada5b3e546a738d7f Mon Sep 17 00:00:00 2001 From: DahoudG Date: Sat, 2 Nov 2024 22:37:47 +0000 Subject: [PATCH] Bon checkpoint + refactoring --- lib/config/router.dart | 12 +- lib/data/models/creator_model.dart | 9 +- lib/data/models/participant_model.dart | 9 +- lib/data/models/user_model.dart | 20 +- lib/data/providers/user_provider.dart | 92 ++-- lib/data/services/preferences_helper.dart | 12 +- lib/domain/entities/user.dart | 31 +- lib/main.dart | 59 +-- .../screens/dialogs/add_event_dialog.dart | 4 +- .../screens/event/event_card.dart | 6 +- .../screens/event/event_screen.dart | 8 +- .../screens/home/home_screen.dart | 6 +- .../screens/login/login_screen.dart | 51 ++- .../screens/profile/profile_screen.dart | 405 ++---------------- .../screens/signup/SignUpScreen.dart | 9 +- .../widgets/account_deletion_card.dart | 61 +++ .../widgets/custom_list_tile.dart | 31 ++ .../widgets/edit_options_card.dart | 35 ++ lib/presentation/widgets/event_header.dart | 6 +- lib/presentation/widgets/event_list.dart | 2 +- .../widgets/expandable_section_card.dart | 38 ++ lib/presentation/widgets/friends_circle.dart | 2 +- lib/presentation/widgets/profile_header.dart | 109 +++++ lib/presentation/widgets/stat_tile.dart | 31 ++ .../widgets/statistics_section_card.dart | 40 ++ .../widgets/support_section_card.dart | 46 ++ lib/presentation/widgets/user_info_card.dart | 49 +++ 27 files changed, 684 insertions(+), 499 deletions(-) create mode 100644 lib/presentation/widgets/account_deletion_card.dart create mode 100644 lib/presentation/widgets/custom_list_tile.dart create mode 100644 lib/presentation/widgets/edit_options_card.dart create mode 100644 lib/presentation/widgets/expandable_section_card.dart create mode 100644 lib/presentation/widgets/profile_header.dart create mode 100644 lib/presentation/widgets/stat_tile.dart create mode 100644 lib/presentation/widgets/statistics_section_card.dart create mode 100644 lib/presentation/widgets/support_section_card.dart create mode 100644 lib/presentation/widgets/user_info_card.dart diff --git a/lib/config/router.dart b/lib/config/router.dart index fabe7f9..77888da 100644 --- a/lib/config/router.dart +++ b/lib/config/router.dart @@ -13,22 +13,22 @@ import '../presentation/reservations/reservations_screen.dart'; class AppRouter { final EventRemoteDataSource eventRemoteDataSource; final String userId; - final String userName; + final String userFirstName; final String userLastName; /// Constructeur de [AppRouter] initialisant les informations utilisateur /// et la source de données pour les événements. /// /// [eventRemoteDataSource] : Source de données pour les événements. - /// [userId], [userName], [userLastName] : Informations de l'utilisateur. + /// [userId], [userFirstName], [userLastName] : Informations de l'utilisateur. AppRouter({ required this.eventRemoteDataSource, required this.userId, - required this.userName, + required this.userFirstName, required this.userLastName, }) { // Log d'initialisation avec les informations utilisateur - debugPrint("[LOG] AppRouter initialisé avec les infos utilisateur : $userId, $userName, $userLastName"); + debugPrint("[LOG] AppRouter initialisé avec les infos utilisateur : $userId, $userFirstName, $userLastName"); } /// Génère une route en fonction du [RouteSettings] fourni. @@ -49,7 +49,7 @@ class AppRouter { builder: (_) => HomeScreen( eventRemoteDataSource: eventRemoteDataSource, userId: userId, - userName: userName, + userFirstName: userFirstName, userLastName: userLastName, userProfileImage: 'lib/assets/images/profile_picture.png', ), @@ -60,7 +60,7 @@ class AppRouter { return MaterialPageRoute( builder: (_) => EventScreen( userId: userId, - userName: userName, + userFirstName: userFirstName, userLastName: userLastName, ), ); diff --git a/lib/data/models/creator_model.dart b/lib/data/models/creator_model.dart index d633011..ef991c5 100644 --- a/lib/data/models/creator_model.dart +++ b/lib/data/models/creator_model.dart @@ -9,10 +9,11 @@ class CreatorModel extends UserModel { }) : super( userId: id, - nom: nom, - prenoms: prenoms, + userLastName: nom, + userFirstName: prenoms, email: '', // Valeur par défaut vide motDePasse: '', // Valeur par défaut vide + profileImageUrl: '', ); factory CreatorModel.fromJson(Map json) { @@ -27,8 +28,8 @@ class CreatorModel extends UserModel { Map toJson() { return { 'id': userId, - 'nom': nom, - 'prenoms': prenoms, + 'nom': userLastName, + 'prenoms': userFirstName, }; } } diff --git a/lib/data/models/participant_model.dart b/lib/data/models/participant_model.dart index f42a9f7..fe2cc64 100644 --- a/lib/data/models/participant_model.dart +++ b/lib/data/models/participant_model.dart @@ -8,10 +8,11 @@ class ParticipantModel extends UserModel { required String prenoms, }) : super( userId: id, - nom: nom, - prenoms: prenoms, + userLastName: nom, + userFirstName: prenoms, email: '', // Valeur par défaut vide motDePasse: '', // Valeur par défaut vide + profileImageUrl: '', ); factory ParticipantModel.fromJson(Map json) { @@ -27,8 +28,8 @@ class ParticipantModel extends UserModel { Map toJson() { return { 'id': userId, - 'nom': nom, - 'prenoms': prenoms, + 'nom': userLastName, + 'prenoms': userFirstName, }; } } diff --git a/lib/data/models/user_model.dart b/lib/data/models/user_model.dart index e0b1e58..0d44523 100644 --- a/lib/data/models/user_model.dart +++ b/lib/data/models/user_model.dart @@ -5,26 +5,29 @@ import '../../domain/entities/user.dart'; class UserModel extends User { UserModel({ required String userId, - required String nom, - required String prenoms, + required String userLastName, + required String userFirstName, required String email, required String motDePasse, + required String profileImageUrl, }) : super( userId: userId, - nom: nom, - prenoms: prenoms, + userLastName: userLastName, + userFirstName: userFirstName, email: email, motDePasse: motDePasse, + profileImageUrl: profileImageUrl, ); /// Factory pour créer un `UserModel` à partir d'un JSON reçu depuis l'API. factory UserModel.fromJson(Map json) { return UserModel( userId: json['userId'] ?? '', - nom: json['nom'] ?? 'Inconnu', - prenoms: json['prenoms'] ?? 'Inconnu', + userLastName: json['nom'] ?? 'Inconnu', + userFirstName: json['prenoms'] ?? 'Inconnu', email: json['email'] ?? 'inconnu@example.com', motDePasse: json['motDePasse'] ?? '', + profileImageUrl: json['profileImageUrl'] ?? '', ); } @@ -32,10 +35,11 @@ class UserModel extends User { Map toJson() { return { 'id': userId, - 'nom': nom, - 'prenoms': prenoms, + 'nom': userLastName, + 'prenoms': userFirstName, 'email': email, 'motDePasse': motDePasse, // Mot de passe en clair (comme demandé temporairement) + 'profileImageUrl':profileImageUrl, }; } } diff --git a/lib/data/providers/user_provider.dart b/lib/data/providers/user_provider.dart index dbb7e9c..807f13f 100644 --- a/lib/data/providers/user_provider.dart +++ b/lib/data/providers/user_provider.dart @@ -1,37 +1,65 @@ import 'package:flutter/material.dart'; +import '../../domain/entities/user.dart'; -/// [UserProvider] est un `ChangeNotifier` qui gère les informations de l'utilisateur. +/// [UserProvider] est un `ChangeNotifier` qui gère les informations complètes de l'utilisateur. /// Toutes les modifications et actions sont loguées pour assurer une traçabilité complète dans le terminal. class UserProvider with ChangeNotifier { - String _userId = ''; - String _userName = ''; - String _userLastName = ''; + User _user = const User( + userId: '', + userLastName: '', + userFirstName: '', + email: '', + motDePasse: '', + profileImageUrl: '', + eventsCount: 0, + friendsCount: 0, + postsCount: 0, + visitedPlacesCount: 0, + ); - /// Getter pour l'ID de l'utilisateur - String get userId => _userId; - - /// Getter pour le nom de l'utilisateur - String get userName => _userName; - - /// Getter pour le prénom de l'utilisateur - String get userLastName => _userLastName; + /// Getter pour l'objet utilisateur. + User get user => _user; /// Méthode pour définir les informations de l'utilisateur. /// Logue les informations fournies et notifie les listeners des changements. /// - /// [id] : L'ID de l'utilisateur. - /// [name] : Le nom de l'utilisateur. - /// [lastName] : Le prénom de l'utilisateur. - void setUser(String id, String name, String lastName) { - debugPrint("[LOG] Tentative de définition des informations de l'utilisateur : ID = $id, Nom = $name, Prénom = $lastName"); + /// [user] : L'objet utilisateur contenant toutes les informations. + void setUser(User user) { + debugPrint("[LOG] Tentative de définition des informations de l'utilisateur : ${user.toString()}"); - _userId = id; - _userName = name; - _userLastName = lastName; + _user = user; - debugPrint("[LOG] Informations utilisateur définies : ID = $_userId, Nom = $_userName, Prénom = $_userLastName"); + debugPrint("[LOG] Informations utilisateur définies : ${_user.toString()}"); + + // Notifie les widgets écoutant ce provider qu'une modification a eu lieu. + notifyListeners(); + } + + /// Méthode pour mettre à jour des statistiques de l'utilisateur. + /// Cette méthode met à jour individuellement des attributs spécifiques comme le nombre d'amis ou d'événements. + void updateStatistics({ + int? eventsCount, + int? friendsCount, + int? postsCount, + int? visitedPlacesCount, + }) { + debugPrint("[LOG] Mise à jour des statistiques utilisateur"); + + _user = User( + userId: _user.userId, + userLastName: _user.userLastName, + userFirstName: _user.userFirstName, + email: _user.email, + motDePasse: _user.motDePasse, + profileImageUrl: _user.profileImageUrl, + eventsCount: eventsCount ?? _user.eventsCount, + friendsCount: friendsCount ?? _user.friendsCount, + postsCount: postsCount ?? _user.postsCount, + visitedPlacesCount: visitedPlacesCount ?? _user.visitedPlacesCount, + ); + + debugPrint("[LOG] Nouvelles statistiques utilisateur : ${_user.toString()}"); - // Notifie les widgets écoutant ce provider qu'une modification a eu lieu notifyListeners(); } @@ -39,15 +67,23 @@ class UserProvider with ChangeNotifier { /// Les valeurs sont loguées avant et après la réinitialisation. void resetUser() { debugPrint("[LOG] Réinitialisation des informations de l'utilisateur."); - debugPrint("[LOG] Valeurs avant réinitialisation : ID = $_userId, Nom = $_userName, Prénom = $_userLastName"); + debugPrint("[LOG] Valeurs avant réinitialisation : ${_user.toString()}"); - _userId = ''; - _userName = ''; - _userLastName = ''; + _user = const User( + userId: '', + userLastName: '', + userFirstName: '', + email: '', + motDePasse: '', + profileImageUrl: '', + eventsCount: 0, + friendsCount: 0, + postsCount: 0, + visitedPlacesCount: 0, + ); - debugPrint("[LOG] Informations utilisateur réinitialisées : ID = $_userId, Nom = $_userName, Prénom = $_userLastName"); + debugPrint("[LOG] Informations utilisateur réinitialisées : ${_user.toString()}"); - // Notifie les widgets écoutant ce provider que l'utilisateur a été réinitialisé notifyListeners(); } } diff --git a/lib/data/services/preferences_helper.dart b/lib/data/services/preferences_helper.dart index 7780379..0c815ac 100644 --- a/lib/data/services/preferences_helper.dart +++ b/lib/data/services/preferences_helper.dart @@ -60,15 +60,15 @@ class PreferencesHelper { /// Sauvegarde le nom d'utilisateur dans les préférences. /// Logue l'opération pour assurer un suivi complet. - Future saveUserName(String userName) async { - print("[LOG] Sauvegarde du userName dans les préférences : $userName"); - await setString('user_name', userName); + Future saveUserName(String userFirstName) async { + print("[LOG] Sauvegarde du userFirstName dans les préférences : $userFirstName"); + await setString('user_name', userFirstName); } /// Récupère le nom d'utilisateur depuis les préférences. /// Retourne le nom ou null en cas d'échec. - Future getUserName() async { - print("[LOG] Récupération du userName depuis les préférences."); + Future getUseFirstrName() async { + print("[LOG] Récupération du userFirstName depuis les préférences."); return await getString('user_name'); } @@ -89,7 +89,7 @@ class PreferencesHelper { /// Supprime toutes les informations utilisateur dans les préférences. /// Logue chaque étape de la suppression. Future clearUserInfo() async { - print("[LOG] Suppression des informations utilisateur (userId, userName, userLastName) des préférences."); + print("[LOG] Suppression des informations utilisateur (userId, userFirstName, userLastName) des préférences."); await remove('user_id'); await remove('user_name'); await remove('user_last_name'); diff --git a/lib/domain/entities/user.dart b/lib/domain/entities/user.dart index 788e4d6..47019d3 100644 --- a/lib/domain/entities/user.dart +++ b/lib/domain/entities/user.dart @@ -2,19 +2,40 @@ import 'package:equatable/equatable.dart'; class User extends Equatable { final String userId; - final String nom; - final String prenoms; + final String userLastName; + final String userFirstName; final String email; final String motDePasse; + final String profileImageUrl; + final int eventsCount; + final int friendsCount; + final int postsCount; + final int visitedPlacesCount; const User({ required this.userId, - required this.nom, - required this.prenoms, + required this.userLastName, + required this.userFirstName, required this.email, required this.motDePasse, + required this.profileImageUrl, + this.eventsCount = 0, + this.friendsCount = 0, + this.postsCount = 0, + this.visitedPlacesCount = 0, }); @override - List get props => [userId, nom, prenoms, email, motDePasse]; + List get props => [ + userId, + userLastName, + userFirstName, + email, + motDePasse, + profileImageUrl, + eventsCount, + friendsCount, + postsCount, + visitedPlacesCount, + ]; } diff --git a/lib/main.dart b/lib/main.dart index af89b11..68f81bf 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:afterwork/config/router.dart'; import 'package:afterwork/data/datasources/event_remote_data_source.dart'; import 'package:afterwork/data/providers/user_provider.dart'; @@ -6,12 +5,15 @@ import 'package:afterwork/data/repositories/friends_repository_impl.dart'; import 'package:afterwork/data/services/preferences_helper.dart'; import 'package:afterwork/data/services/secure_storage.dart'; import 'package:afterwork/presentation/state_management/event_bloc.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:http/http.dart' as http; import 'package:intl/date_symbol_data_local.dart'; import 'package:provider/provider.dart'; + import 'core/theme/theme_provider.dart'; import 'data/providers/friends_provider.dart'; +import 'domain/entities/user.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -23,60 +25,68 @@ void main() async { final eventRemoteDataSource = EventRemoteDataSource(http.Client()); final SecureStorage secureStorage = SecureStorage(); final PreferencesHelper preferencesHelper = PreferencesHelper(); - final http.Client httpClient = http.Client(); // Nouvelle instance http.Client + final http.Client httpClient = http.Client(); // Récupération des informations stockées avec logs détaillés String? userId = await secureStorage.getUserId(); - String? userName = await preferencesHelper.getUserName(); + String? userFirstName = await preferencesHelper.getUseFirstrName(); String? userLastName = await preferencesHelper.getUserLastName(); // Log de la récupération des informations - print("[LOG] Récupération des informations utilisateur : userId = $userId, userName = $userName, userLastName = $userLastName"); + print("[LOG] Récupération des informations utilisateur : userId = $userId, userFirstName = $userFirstName, userLastName = $userLastName"); // Gestion des valeurs par défaut si les informations ne sont pas trouvées userId ??= 'default_user_id'; - userName ??= 'Default'; + userFirstName ??= 'Default'; userLastName ??= 'User'; // Log des valeurs par défaut appliquées - print("[LOG] Valeurs par défaut appliquées : userId = $userId, userName = $userName, userLastName = $userLastName"); + print("[LOG] Valeurs par défaut appliquées : userId = $userId, userFirstName = $userFirstName, userLastName = $userLastName"); + + // Création de l'objet User + final User user = User( + userId: userId, + userLastName: userLastName, + userFirstName: userFirstName, + email: 'user@example.com', + motDePasse: 'motDePasseHashé', + profileImageUrl: 'lib/assets/images/profile_picture.png', + eventsCount: 0, + friendsCount: 0, + postsCount: 0, + visitedPlacesCount: 0, + ); runApp(MyApp( eventRemoteDataSource: eventRemoteDataSource, - userId: userId, - userName: userName, - userLastName: userLastName, - httpClient: httpClient, // Passe l'instance client ici + user: user, + httpClient: httpClient, )); } class MyApp extends StatelessWidget { final EventRemoteDataSource eventRemoteDataSource; - final String userId; - final String userName; - final String userLastName; - final http.Client httpClient; // Ajout de ce paramètre + final User user; + final http.Client httpClient; const MyApp({ super.key, required this.eventRemoteDataSource, - required this.userId, - required this.userName, - required this.userLastName, - required this.httpClient, // Spécifier l'argument + required this.user, + required this.httpClient, }); @override Widget build(BuildContext context) { - final friendsRepository = FriendsRepositoryImpl(client: httpClient); // Utilisation du client ici + final friendsRepository = FriendsRepositoryImpl(client: httpClient); // Log lors de la construction de l'application - print("[LOG] Construction de l'application avec userId : $userId, userName : $userName, userLastName : $userLastName"); + print("[LOG] Construction de l'application avec user : ${user.toString()}"); return MultiProvider( providers: [ ChangeNotifierProvider( - create: (_) => UserProvider()..setUser(userId, userName, userLastName), + create: (_) => UserProvider()..setUser(user), ), ChangeNotifierProvider( create: (_) => FriendsProvider(friendsRepository: friendsRepository), @@ -95,9 +105,9 @@ class MyApp extends StatelessWidget { theme: themeProvider.currentTheme, onGenerateRoute: AppRouter( eventRemoteDataSource: eventRemoteDataSource, - userId: userId, - userName: userName, - userLastName: userLastName, + userId: user.userId, + userFirstName: user.userFirstName, + userLastName: user.userLastName, ).generateRoute, initialRoute: '/', ); @@ -106,3 +116,4 @@ class MyApp extends StatelessWidget { ); } } + diff --git a/lib/presentation/screens/dialogs/add_event_dialog.dart b/lib/presentation/screens/dialogs/add_event_dialog.dart index 1e690e8..68ecfd3 100644 --- a/lib/presentation/screens/dialogs/add_event_dialog.dart +++ b/lib/presentation/screens/dialogs/add_event_dialog.dart @@ -13,13 +13,13 @@ import '../../widgets/image_preview_picker.dart'; class AddEventPage extends StatefulWidget { final String userId; - final String userName; + final String userFirstName; final String userLastName; const AddEventPage({ super.key, required this.userId, - required this.userName, + required this.userFirstName, required this.userLastName, }); diff --git a/lib/presentation/screens/event/event_card.dart b/lib/presentation/screens/event/event_card.dart index 7067ea9..064f883 100644 --- a/lib/presentation/screens/event/event_card.dart +++ b/lib/presentation/screens/event/event_card.dart @@ -10,7 +10,7 @@ import '../../widgets/swipe_background.dart'; class EventCard extends StatelessWidget { final EventModel event; final String userId; - final String userName; + final String userFirstName; final String userLastName; final String status; final VoidCallback onReact; @@ -25,7 +25,7 @@ class EventCard extends StatelessWidget { Key? key, required this.event, required this.userId, - required this.userName, + required this.userFirstName, required this.userLastName, required this.status, required this.onReact, @@ -70,7 +70,7 @@ class EventCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ EventHeader( - userName: userName, + userFirstName: userFirstName, userLastName: userLastName, eventDate: event.startDate, imageUrl: event.imageUrl, diff --git a/lib/presentation/screens/event/event_screen.dart b/lib/presentation/screens/event/event_screen.dart index a121a71..86c7a30 100644 --- a/lib/presentation/screens/event/event_screen.dart +++ b/lib/presentation/screens/event/event_screen.dart @@ -7,13 +7,13 @@ import '../dialogs/add_event_dialog.dart'; class EventScreen extends StatefulWidget { final String userId; - final String userName; + final String userFirstName; final String userLastName; const EventScreen({ Key? key, required this.userId, - required this.userName, + required this.userFirstName, required this.userLastName, }) : super(key: key); @@ -51,7 +51,7 @@ class _EventScreenState extends State { MaterialPageRoute( builder: (context) => AddEventPage( userId: widget.userId, - userName: widget.userName, + userFirstName: widget.userFirstName, userLastName: widget.userLastName, ), ), @@ -78,7 +78,7 @@ class _EventScreenState extends State { key: ValueKey(event.id), event: event, userId: widget.userId, - userName: widget.userName, + userFirstName: widget.userFirstName, userLastName: widget.userLastName, onReact: () => _onReact(event.id), onComment: () => _onComment(event.id), diff --git a/lib/presentation/screens/home/home_screen.dart b/lib/presentation/screens/home/home_screen.dart index c116f4e..4906827 100644 --- a/lib/presentation/screens/home/home_screen.dart +++ b/lib/presentation/screens/home/home_screen.dart @@ -14,7 +14,7 @@ import '../friends/friends_screen.dart'; // Écran des amis class HomeScreen extends StatefulWidget { final EventRemoteDataSource eventRemoteDataSource; final String userId; - final String userName; + final String userFirstName; final String userLastName; final String userProfileImage; // Image de profil de l'utilisateur @@ -22,7 +22,7 @@ class HomeScreen extends StatefulWidget { Key? key, required this.eventRemoteDataSource, required this.userId, - required this.userName, + required this.userFirstName, required this.userLastName, required this.userProfileImage, // Passer l'image de profil ici }) : super(key: key); @@ -127,7 +127,7 @@ class _HomeScreenState extends State with SingleTickerProviderStateM const HomeContentScreen(), EventScreen( userId: widget.userId, - userName: widget.userName, + userFirstName: widget.userFirstName, userLastName: widget.userLastName, ), const EstablishmentsScreen(), diff --git a/lib/presentation/screens/login/login_screen.dart b/lib/presentation/screens/login/login_screen.dart index 69def17..7b0cd04 100644 --- a/lib/presentation/screens/login/login_screen.dart +++ b/lib/presentation/screens/login/login_screen.dart @@ -14,6 +14,8 @@ import 'package:provider/provider.dart'; import '../../../core/errors/exceptions.dart'; import '../../../core/theme/theme_provider.dart'; import '../../../data/datasources/event_remote_data_source.dart'; +import '../../../data/providers/user_provider.dart'; +import '../../../domain/entities/user.dart'; import '../signup/SignUpScreen.dart'; /// L'écran de connexion où les utilisateurs peuvent s'authentifier. @@ -102,30 +104,41 @@ class _LoginScreenState extends State _formKey.currentState!.save(); // Sauvegarde des données saisies try { - _btnController.start(); // Démarre l'animation de chargement du bouton + _btnController.start(); debugPrint("[LOG] Appel à l'API pour authentifier l'utilisateur."); - // Appel à l'API pour authentifier l'utilisateur final UserModel user = await _userRemoteDataSource.authenticateUser(_email, _password); - // Validation de l'ID utilisateur if (user.userId.isNotEmpty) { debugPrint("[LOG] Utilisateur authentifié avec succès. ID: ${user.userId}"); - // Sauvegarde des informations utilisateur await _secureStorage.saveUserId(user.userId); - await _preferencesHelper.saveUserName(user.nom); - await _preferencesHelper.saveUserLastName(user.prenoms); + await _preferencesHelper.saveUserName(user.userLastName); + await _preferencesHelper.saveUserLastName(user.userFirstName); + + // Met à jour le `UserProvider` avec les informations utilisateur authentifiées + final userProvider = Provider.of(context, listen: false); + userProvider.setUser(User( + userId: user.userId, + userLastName: user.userLastName, + userFirstName: user.userFirstName, + email: user.email, + motDePasse: 'motDePasseHashé', + profileImageUrl: 'lib/assets/images/profile_picture.png', + eventsCount: user.eventsCount ?? 0, + friendsCount: user.friendsCount ?? 0, + postsCount: user.postsCount ?? 0, + visitedPlacesCount: user.visitedPlacesCount ?? 0, + )); _showToast("Connexion réussie !"); - // Redirection vers l'écran d'accueil Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => HomeScreen( userId: user.userId, - userName: user.nom, - userLastName: user.prenoms, + userFirstName: user.userFirstName, + userLastName: user.userLastName, userProfileImage: 'lib/assets/images/profile_picture.png', eventRemoteDataSource: EventRemoteDataSource(http.Client()), ), @@ -136,25 +149,17 @@ class _LoginScreenState extends State _showToast("Erreur : ID utilisateur manquant."); } } catch (e) { - // Gestion des erreurs spécifiques et log de chaque type d'erreur - if (e is ServerExceptionWithMessage) { - debugPrint("[ERROR] Erreur serveur : ${e.message}"); - _showToast("Erreur serveur : ${e.message}"); - } else if (e is UnauthorizedException) { - debugPrint("[ERROR] Erreur d'authentification : ${e.message}"); - _showToast("Erreur : ${e.message}"); - } else { - debugPrint("[ERROR] Erreur lors de la connexion : $e"); - _showToast("Erreur lors de la connexion : ${e.toString()}"); - } - _btnController.error(); // Affiche une erreur sur le bouton + // Gestion des erreurs + debugPrint("[ERROR] Erreur lors de la connexion : $e"); + _showToast("Erreur lors de la connexion : ${e.toString()}"); + _btnController.error(); setState(() { _showErrorMessage = true; }); } finally { - _btnController.reset(); // Réinitialise l'état du bouton + _btnController.reset(); setState(() { - _isSubmitting = false; // Réinitialise l'état de chargement + _isSubmitting = false; }); } } else { diff --git a/lib/presentation/screens/profile/profile_screen.dart b/lib/presentation/screens/profile/profile_screen.dart index 8690e67..436fcbe 100644 --- a/lib/presentation/screens/profile/profile_screen.dart +++ b/lib/presentation/screens/profile/profile_screen.dart @@ -1,129 +1,92 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import '../../../core/constants/colors.dart'; +import '../../../data/providers/user_provider.dart'; +import '../../widgets/account_deletion_card.dart'; +import '../../widgets/custom_list_tile.dart'; +import '../../widgets/edit_options_card.dart'; +import '../../widgets/expandable_section_card.dart'; +import '../../widgets/profile_header.dart'; +import '../../widgets/statistics_section_card.dart'; +import '../../widgets/support_section_card.dart'; +import '../../widgets/user_info_card.dart'; + class ProfileScreen extends StatelessWidget { const ProfileScreen({super.key}); @override Widget build(BuildContext context) { - print("Affichage de l'écran de profil."); + final userProvider = Provider.of(context); + final user = userProvider.user; return Scaffold( backgroundColor: AppColors.backgroundColor, body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 200.0, - floating: false, - pinned: true, - backgroundColor: AppColors.darkPrimary, - flexibleSpace: FlexibleSpaceBar( - title: Text( - 'Profil', - style: TextStyle( - color: AppColors.accentColor, - fontSize: 20.0, - fontWeight: FontWeight.bold, - ), - ), - background: Image.asset( - 'lib/assets/images/profile_picture.png', - fit: BoxFit.cover, - ), - ), - actions: [ - IconButton( - icon: const Icon(Icons.settings, color: Colors.white), - onPressed: () { - print("Bouton des paramètres cliqué."); - // Logique de navigation vers les paramètres - }, - ), - ], - ), + ProfileHeader(user: user), SliverList( delegate: SliverChildListDelegate( [ const SizedBox(height: 10), - _buildUserInfoCard(), + UserInfoCard(user: user), const SizedBox(height: 10), - _buildEditOptionsCard(), + const EditOptionsCard(), const SizedBox(height: 10), - _buildStatisticsSectionCard(), + StatisticsSectionCard(user: user), const SizedBox(height: 10), - _buildExpandableSectionCard( + ExpandableSectionCard( title: 'Historique', icon: Icons.history, children: [ - _buildAnimatedListTile( + CustomListTile( icon: Icons.event_note, label: 'Historique des Événements', - onTap: () { - print("Accès à l'historique des événements."); - // Logique de navigation vers l'historique des événements - }, + onTap: () => print("[LOG] Accès à l'historique des événements."), ), - _buildAnimatedListTile( + CustomListTile( icon: Icons.history, label: 'Historique des Publications', - onTap: () { - print("Accès à l'historique des publications."); - // Logique de navigation vers l'historique des publications - }, + onTap: () => print("[LOG] Accès à l'historique des publications."), ), - _buildAnimatedListTile( + CustomListTile( icon: Icons.bookmark, label: 'Historique de Réservations', - onTap: () { - print("Accès à l'historique des réservations."); - // Logique de navigation vers l'historique des réservations - }, + onTap: () => print("[LOG] Accès à l'historique des réservations."), ), ], ), const SizedBox(height: 10), - _buildExpandableSectionCard( + ExpandableSectionCard( title: 'Préférences et Paramètres', icon: Icons.settings, children: [ - _buildAnimatedListTile( + CustomListTile( icon: Icons.privacy_tip, label: 'Paramètres de confidentialité', - onTap: () { - print("Accès aux paramètres de confidentialité."); - // Logique de navigation vers les paramètres de confidentialité - }, + onTap: () => print("[LOG] Accès aux paramètres de confidentialité."), ), - _buildAnimatedListTile( + CustomListTile( icon: Icons.notifications, label: 'Notifications', - onTap: () { - print("Accès aux paramètres de notifications."); - // Logique de navigation vers les notifications - }, + onTap: () => print("[LOG] Accès aux paramètres de notifications."), ), - _buildAnimatedListTile( + CustomListTile( icon: Icons.language, label: 'Langue de l\'application', - onTap: () { - print("Accès aux paramètres de langue."); - // Logique de navigation vers les paramètres de langue - }, + onTap: () => print("[LOG] Accès aux paramètres de langue."), ), - _buildAnimatedListTile( + CustomListTile( icon: Icons.format_paint, label: 'Thème de l\'application', - onTap: () { - print("Accès aux paramètres de thème."); - // Logique de navigation vers les paramètres de thème - }, + onTap: () => print("[LOG] Accès aux paramètres de thème."), ), ], ), const SizedBox(height: 10), - _buildSupportSectionCard(), + const SupportSectionCard(), const SizedBox(height: 10), - _buildAccountDeletionCard(context), + AccountDeletionCard(context: context), ], ), ), @@ -131,302 +94,4 @@ class ProfileScreen extends StatelessWidget { ), ); } - - Widget _buildUserInfoCard() { - return Card( - color: AppColors.cardColor, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - elevation: 2, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - CircleAvatar( - radius: 50, - backgroundImage: AssetImage('lib/assets/images/profile_picture.png'), - backgroundColor: Colors.transparent, - ), - const SizedBox(height: 10), - const Text( - 'GBANE Dahoud', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Colors.white, - letterSpacing: 1.2, - ), - ), - const SizedBox(height: 5), - Text( - 'pseudo', - style: TextStyle( - fontSize: 16, - color: Colors.grey[400], - fontStyle: FontStyle.italic, - ), - ), - const SizedBox(height: 5), - Text( - 'gbanedahoud@lions.dev', - style: TextStyle( - fontSize: 14, - color: Colors.grey[600], - decoration: TextDecoration.underline, - ), - ), - ], - ), - ), - ); - } - - Widget _buildEditOptionsCard() { - return Card( - color: AppColors.cardColor, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - elevation: 2, - child: Column( - children: [ - _buildAnimatedListTile( - icon: Icons.edit, - label: 'Éditer le profil', - onTap: () { - print("Édition du profil."); - // Logique de navigation vers l'édition du profil - }, - ), - _buildAnimatedListTile( - icon: Icons.camera_alt, - label: 'Changer la photo de profil', - onTap: () { - print("Changement de la photo de profil."); - // Logique de changement de la photo de profil - }, - ), - _buildAnimatedListTile( - icon: Icons.lock, - label: 'Changer le mot de passe', - onTap: () { - print("Changement du mot de passe."); - // Logique de changement de mot de passe - }, - ), - ], - ), - ); - } - - Widget _buildStatisticsSectionCard() { - return Card( - color: AppColors.cardColor, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - elevation: 2, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Statistiques Personnelles', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - const SizedBox(height: 10), - _buildStatTile( - icon: Icons.event, - label: 'Événements Participés', - value: '12', - ), - _buildStatTile( - icon: Icons.place, - label: 'Établissements Visités', - value: '8', - ), - _buildStatTile( - icon: Icons.post_add, - label: 'Publications', - value: '24', - ), - _buildStatTile( - icon: Icons.group, - label: 'Amis/Followers', - value: '150', - ), - ], - ), - ), - ); - } - - Widget _buildExpandableSectionCard({ - required String title, - required IconData icon, - required List children, - }) { - return Card( - color: AppColors.cardColor, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - elevation: 2, - child: ExpansionTile( - title: Text( - title, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - leading: Icon(icon, color: AppColors.accentColor), - iconColor: AppColors.accentColor, - collapsedIconColor: AppColors.accentColor, - children: children, - ), - ); - } - - Widget _buildSupportSectionCard() { - return Card( - color: AppColors.cardColor, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - elevation: 2, - child: Column( - children: [ - const Padding( - padding: EdgeInsets.all(16.0), - child: Text( - 'Support et Assistance', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - ), - _buildAnimatedListTile( - icon: Icons.help, - label: 'Support et Assistance', - onTap: () { - print("Accès au Support et Assistance."); - // Logique de navigation vers le support - }, - ), - _buildAnimatedListTile( - icon: Icons.article, - label: 'Conditions d\'utilisation', - onTap: () { - print("Accès aux conditions d'utilisation."); - // Logique de navigation vers les conditions d'utilisation - }, - ), - _buildAnimatedListTile( - icon: Icons.privacy_tip, - label: 'Politique de confidentialité', - onTap: () { - print("Accès à la politique de confidentialité."); - // Logique de navigation vers la politique de confidentialité - }, - ), - ], - ), - ); - } - - Widget _buildAccountDeletionCard(BuildContext context) { - return Card( - color: AppColors.cardColor, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - elevation: 2, - child: ListTile( - leading: const Icon(Icons.delete, color: Colors.redAccent), - title: const Text( - 'Supprimer le compte', - style: TextStyle(color: Colors.redAccent, fontWeight: FontWeight.bold), - ), - onTap: () { - _showDeleteConfirmationDialog(context); - }, - ), - ); - } - - void _showDeleteConfirmationDialog(BuildContext context) { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - backgroundColor: AppColors.backgroundColor, - title: const Text( - 'Confirmer la suppression', - style: TextStyle(color: Colors.white), - ), - content: const Text( - 'Êtes-vous sûr de vouloir supprimer votre compte ? Cette action est irréversible.', - style: TextStyle(color: Colors.white70), - ), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: Text( - 'Annuler', - style: TextStyle(color: AppColors.accentColor), - ), - ), - TextButton( - onPressed: () { - print("Suppression du compte confirmée."); - Navigator.of(context).pop(); - }, - child: const Text( - 'Supprimer', - style: TextStyle(color: Colors.redAccent), - ), - ), - ], - ); - }, - ); - } - - Widget _buildAnimatedListTile({ - required IconData icon, - required String label, - required VoidCallback onTap, - }) { - return InkWell( - onTap: onTap, - borderRadius: BorderRadius.circular(10), - splashColor: Colors.blueAccent.withOpacity(0.2), - child: ListTile( - leading: Icon(icon, color: AppColors.accentColor), - title: Text( - label, - style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w600), - ), - ), - ); - } - - Widget _buildStatTile({ - required IconData icon, - required String label, - required String value, - }) { - return ListTile( - leading: Icon(icon, color: AppColors.accentColor), - title: Text(label, style: const TextStyle(color: Colors.white)), - trailing: Text( - value, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - ); - } } diff --git a/lib/presentation/screens/signup/SignUpScreen.dart b/lib/presentation/screens/signup/SignUpScreen.dart index 212eedf..4b2864b 100644 --- a/lib/presentation/screens/signup/SignUpScreen.dart +++ b/lib/presentation/screens/signup/SignUpScreen.dart @@ -81,10 +81,11 @@ class _SignUpScreenState extends State { // Créer l'utilisateur avec les informations fournies final UserModel user = UserModel( userId: '', // L'ID sera généré côté serveur - nom: _nom, - prenoms: _prenoms, + userLastName: _nom, + userFirstName: _prenoms, email: _email, motDePasse: _password, // Le mot de passe sera envoyé en clair pour l'instant + profileImageUrl: '', ); // Envoi des informations pour créer un nouvel utilisateur @@ -94,8 +95,8 @@ class _SignUpScreenState extends State { // Sauvegarder les informations de l'utilisateur await _secureStorage.saveUserId(createdUser.userId); - await _preferencesHelper.saveUserName(createdUser.nom); - await _preferencesHelper.saveUserLastName(createdUser.prenoms); + await _preferencesHelper.saveUserName(createdUser.userLastName); + await _preferencesHelper.saveUserLastName(createdUser.userFirstName); // Rediriger vers la page d'accueil ou une page de confirmation Navigator.pushReplacementNamed(context, '/home'); diff --git a/lib/presentation/widgets/account_deletion_card.dart b/lib/presentation/widgets/account_deletion_card.dart new file mode 100644 index 0000000..abde75b --- /dev/null +++ b/lib/presentation/widgets/account_deletion_card.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import '../../../../core/constants/colors.dart'; + +class AccountDeletionCard extends StatelessWidget { + final BuildContext context; + + const AccountDeletionCard({Key? key, required this.context}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Card( + color: AppColors.cardColor, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + elevation: 2, + child: ListTile( + leading: const Icon(Icons.delete, color: Colors.redAccent), + title: const Text( + 'Supprimer le compte', + style: TextStyle(color: Colors.redAccent, fontWeight: FontWeight.bold), + ), + onTap: () => _showDeleteConfirmationDialog(), + ), + ); + } + + void _showDeleteConfirmationDialog() { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + backgroundColor: AppColors.backgroundColor, + title: const Text( + 'Confirmer la suppression', + style: TextStyle(color: Colors.white), + ), + content: const Text( + 'Êtes-vous sûr de vouloir supprimer votre compte ? Cette action est irréversible.', + style: TextStyle(color: Colors.white70), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text('Annuler', style: TextStyle(color: AppColors.accentColor)), + ), + TextButton( + onPressed: () { + print("[LOG] Suppression du compte confirmée."); + Navigator.of(context).pop(); + // Logique de suppression du compte + }, + child: const Text( + 'Supprimer', + style: TextStyle(color: Colors.redAccent), + ), + ), + ], + ); + }, + ); + } +} diff --git a/lib/presentation/widgets/custom_list_tile.dart b/lib/presentation/widgets/custom_list_tile.dart new file mode 100644 index 0000000..ddd88fe --- /dev/null +++ b/lib/presentation/widgets/custom_list_tile.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import '../../../../core/constants/colors.dart'; + +class CustomListTile extends StatelessWidget { + final IconData icon; + final String label; + final VoidCallback onTap; + + const CustomListTile({ + Key? key, + required this.icon, + required this.label, + required this.onTap, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(10), + splashColor: Colors.blueAccent.withOpacity(0.2), + child: ListTile( + leading: Icon(icon, color: AppColors.accentColor), + title: Text( + label, + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w600), + ), + ), + ); + } +} diff --git a/lib/presentation/widgets/edit_options_card.dart b/lib/presentation/widgets/edit_options_card.dart new file mode 100644 index 0000000..64796f1 --- /dev/null +++ b/lib/presentation/widgets/edit_options_card.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'custom_list_tile.dart'; +import '../../../../core/constants/colors.dart'; + +class EditOptionsCard extends StatelessWidget { + const EditOptionsCard({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Card( + color: AppColors.cardColor, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + elevation: 2, + child: Column( + children: [ + CustomListTile( + icon: Icons.edit, + label: 'Éditer le profil', + onTap: () => print("[LOG] Édition du profil."), + ), + CustomListTile( + icon: Icons.camera_alt, + label: 'Changer la photo de profil', + onTap: () => print("[LOG] Changement de la photo de profil."), + ), + CustomListTile( + icon: Icons.lock, + label: 'Changer le mot de passe', + onTap: () => print("[LOG] Changement du mot de passe."), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/widgets/event_header.dart b/lib/presentation/widgets/event_header.dart index 71c0f41..a4c9ebf 100644 --- a/lib/presentation/widgets/event_header.dart +++ b/lib/presentation/widgets/event_header.dart @@ -3,7 +3,7 @@ import 'package:afterwork/core/utils/date_formatter.dart'; import 'event_menu.dart'; class EventHeader extends StatelessWidget { - final String userName; + final String userFirstName; final String userLastName; final String? eventDate; final String? imageUrl; @@ -14,7 +14,7 @@ class EventHeader extends StatelessWidget { const EventHeader({ Key? key, - required this.userName, + required this.userFirstName, required this.userLastName, this.eventDate, this.imageUrl, @@ -51,7 +51,7 @@ class EventHeader extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '$userName $userLastName', + '$userFirstName $userLastName', style: const TextStyle( color: Colors.white, fontSize: 14, diff --git a/lib/presentation/widgets/event_list.dart b/lib/presentation/widgets/event_list.dart index 1644a82..06af335 100644 --- a/lib/presentation/widgets/event_list.dart +++ b/lib/presentation/widgets/event_list.dart @@ -18,7 +18,7 @@ class EventList extends StatelessWidget { return EventCard( event: event, userId: 'user_id_here', // Vous pouvez passer l'ID réel de l'utilisateur connecté - userName: 'John', // Vous pouvez passer le prénom réel de l'utilisateur + userFirstName: 'John', // Vous pouvez passer le prénom réel de l'utilisateur userLastName: 'Doe', // Vous pouvez passer le nom réel de l'utilisateur onReact: () => _handleReact(event), onComment: () => _handleComment(event), diff --git a/lib/presentation/widgets/expandable_section_card.dart b/lib/presentation/widgets/expandable_section_card.dart new file mode 100644 index 0000000..29fbe0a --- /dev/null +++ b/lib/presentation/widgets/expandable_section_card.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import '../../../../core/constants/colors.dart'; + +class ExpandableSectionCard extends StatelessWidget { + final String title; + final IconData icon; + final List children; + + const ExpandableSectionCard({ + Key? key, + required this.title, + required this.icon, + required this.children, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Card( + color: AppColors.cardColor, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + elevation: 2, + child: ExpansionTile( + title: Text( + title, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + leading: Icon(icon, color: AppColors.accentColor), + iconColor: AppColors.accentColor, + collapsedIconColor: AppColors.accentColor, + children: children, + ), + ); + } +} diff --git a/lib/presentation/widgets/friends_circle.dart b/lib/presentation/widgets/friends_circle.dart index 5d74df8..99bb391 100644 --- a/lib/presentation/widgets/friends_circle.dart +++ b/lib/presentation/widgets/friends_circle.dart @@ -23,7 +23,7 @@ class FriendsCircle extends StatelessWidget { Widget build(BuildContext context) { // Combine firstName et lastName ou utilise "Ami inconnu" par défaut. String displayName = [friend.firstName, friend.lastName] - .where((namePart) => namePart != null && namePart.isNotEmpty) + .where((namePart) => namePart.isNotEmpty) .join(" ") .trim(); diff --git a/lib/presentation/widgets/profile_header.dart b/lib/presentation/widgets/profile_header.dart new file mode 100644 index 0000000..06993b8 --- /dev/null +++ b/lib/presentation/widgets/profile_header.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../../../../core/constants/colors.dart'; +import '../../../../data/providers/user_provider.dart'; +import '../../../../domain/entities/user.dart'; + +/// [ProfileHeader] est un widget qui affiche l'en-tête du profil utilisateur. +/// Comprend le nom de l'utilisateur, une image de fond, et un bouton de déconnexion avec confirmation. +class ProfileHeader extends StatelessWidget { + final User user; + + const ProfileHeader({Key? key, required this.user}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SliverAppBar( + expandedHeight: 200.0, + floating: false, + pinned: true, + backgroundColor: AppColors.darkPrimary, + flexibleSpace: FlexibleSpaceBar( + title: Text( + 'Profil de ${user.userFirstName}', + style: TextStyle( + color: AppColors.accentColor, + fontSize: 20.0, + fontWeight: FontWeight.bold, + ), + ), + background: Image.network( + user.profileImageUrl, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + // Log en cas d'erreur de chargement de l'image + print("[ERROR] Erreur lors du chargement de l'image de profil : $error"); + return Image.asset('lib/assets/images/default_avatar.png', fit: BoxFit.cover); + }, + ), + ), + actions: [ + IconButton( + icon: const Icon(Icons.logout, color: Colors.white), + onPressed: () { + print("[LOG] Bouton de déconnexion cliqué."); // Log du clic du bouton de déconnexion + _showLogoutConfirmationDialog(context); // Affiche le dialogue de confirmation + }, + ), + ], + ); + } + + /// Affiche une boîte de dialogue de confirmation pour la déconnexion. + /// Log chaque action et résultat dans le terminal. + void _showLogoutConfirmationDialog(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext context) { + // Log affichage du dialogue + print("[LOG] Affichage de la boîte de dialogue de confirmation de déconnexion."); + + return AlertDialog( + backgroundColor: AppColors.backgroundColor, + title: const Text( + 'Confirmer la déconnexion', + style: TextStyle(color: Colors.white), + ), + content: const Text( + 'Voulez-vous vraiment vous déconnecter ?', + style: TextStyle(color: Colors.white70), + ), + actions: [ + // Bouton d'annulation de la déconnexion + TextButton( + onPressed: () { + print("[LOG] Déconnexion annulée par l'utilisateur."); + Navigator.of(context).pop(); // Ferme le dialogue sans déconnecter + }, + child: Text( + 'Annuler', + style: TextStyle(color: AppColors.accentColor), + ), + ), + // Bouton de confirmation de la déconnexion + TextButton( + onPressed: () { + print("[LOG] Déconnexion confirmée."); // Log de la confirmation + Provider.of(context, listen: false).resetUser(); // Réinitialise les infos utilisateur + print("[LOG] Informations utilisateur réinitialisées dans UserProvider."); + + Navigator.of(context).pop(); // Ferme la boîte de dialogue + print("[LOG] Boîte de dialogue de confirmation fermée."); + + Navigator.of(context).pushReplacementNamed('/'); // Redirige vers l'écran de connexion + print("[LOG] Redirection vers l'écran de connexion."); + }, + child: const Text( + 'Déconnexion', + style: TextStyle(color: Colors.redAccent), + ), + ), + ], + ); + }, + ).then((_) { + // Log lorsque le dialogue est fermé pour toute raison (confirmation ou annulation) + print("[LOG] La boîte de dialogue de confirmation de déconnexion est fermée."); + }); + } +} diff --git a/lib/presentation/widgets/stat_tile.dart b/lib/presentation/widgets/stat_tile.dart new file mode 100644 index 0000000..494aabb --- /dev/null +++ b/lib/presentation/widgets/stat_tile.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import '../../../../core/constants/colors.dart'; + +class StatTile extends StatelessWidget { + final IconData icon; + final String label; + final String value; + + const StatTile({ + Key? key, + required this.icon, + required this.label, + required this.value, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ListTile( + leading: Icon(icon, color: AppColors.accentColor), + title: Text(label, style: const TextStyle(color: Colors.white)), + trailing: Text( + value, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ); + } +} diff --git a/lib/presentation/widgets/statistics_section_card.dart b/lib/presentation/widgets/statistics_section_card.dart new file mode 100644 index 0000000..f81adca --- /dev/null +++ b/lib/presentation/widgets/statistics_section_card.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import '../../../../core/constants/colors.dart'; +import '../../../../domain/entities/user.dart'; +import 'stat_tile.dart'; + +class StatisticsSectionCard extends StatelessWidget { + final User user; + + const StatisticsSectionCard({Key? key, required this.user}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Card( + color: AppColors.cardColor, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + elevation: 2, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Statistiques Personnelles', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + const SizedBox(height: 10), + StatTile(icon: Icons.event, label: 'Événements Participés', value: '${user.eventsCount}'), + StatTile(icon: Icons.place, label: 'Établissements Visités', value: '${user.visitedPlacesCount}'), + StatTile(icon: Icons.post_add, label: 'Publications', value: '${user.postsCount}'), + StatTile(icon: Icons.group, label: 'Amis/Followers', value: '${user.friendsCount}'), + ], + ), + ), + ); + } +} diff --git a/lib/presentation/widgets/support_section_card.dart b/lib/presentation/widgets/support_section_card.dart new file mode 100644 index 0000000..064d605 --- /dev/null +++ b/lib/presentation/widgets/support_section_card.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import '../../../../core/constants/colors.dart'; +import 'custom_list_tile.dart'; + +class SupportSectionCard extends StatelessWidget { + const SupportSectionCard({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Card( + color: AppColors.cardColor, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + elevation: 2, + child: Column( + children: [ + const Padding( + padding: EdgeInsets.all(16.0), + child: Text( + 'Support et Assistance', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ), + CustomListTile( + icon: Icons.help, + label: 'Support et Assistance', + onTap: () => print("[LOG] Accès au Support et Assistance."), + ), + CustomListTile( + icon: Icons.article, + label: 'Conditions d\'utilisation', + onTap: () => print("[LOG] Accès aux conditions d'utilisation."), + ), + CustomListTile( + icon: Icons.privacy_tip, + label: 'Politique de confidentialité', + onTap: () => print("[LOG] Accès à la politique de confidentialité."), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/widgets/user_info_card.dart b/lib/presentation/widgets/user_info_card.dart new file mode 100644 index 0000000..344b4a0 --- /dev/null +++ b/lib/presentation/widgets/user_info_card.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import '../../../../core/constants/colors.dart'; +import '../../../../domain/entities/user.dart'; + +class UserInfoCard extends StatelessWidget { + final User user; + + const UserInfoCard({Key? key, required this.user}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Card( + color: AppColors.cardColor, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + elevation: 2, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + CircleAvatar( + radius: 50, + backgroundImage: NetworkImage(user.profileImageUrl), + backgroundColor: Colors.transparent, + ), + const SizedBox(height: 10), + Text( + '${user.userFirstName} ${user.userLastName}', + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.white, + letterSpacing: 1.2, + ), + ), + const SizedBox(height: 5), + Text( + user.email, + style: TextStyle( + fontSize: 14, + color: Colors.grey[600], + decoration: TextDecoration.underline, + ), + ), + ], + ), + ), + ); + } +}