Bon checkpoint + refactoring

This commit is contained in:
DahoudG
2024-11-02 22:37:47 +00:00
parent 9cf96b7acf
commit 19f6efa995
27 changed files with 684 additions and 499 deletions

View File

@@ -13,22 +13,22 @@ import '../presentation/reservations/reservations_screen.dart';
class AppRouter { class AppRouter {
final EventRemoteDataSource eventRemoteDataSource; final EventRemoteDataSource eventRemoteDataSource;
final String userId; final String userId;
final String userName; final String userFirstName;
final String userLastName; final String userLastName;
/// Constructeur de [AppRouter] initialisant les informations utilisateur /// Constructeur de [AppRouter] initialisant les informations utilisateur
/// et la source de données pour les événements. /// et la source de données pour les événements.
/// ///
/// [eventRemoteDataSource] : 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({ AppRouter({
required this.eventRemoteDataSource, required this.eventRemoteDataSource,
required this.userId, required this.userId,
required this.userName, required this.userFirstName,
required this.userLastName, required this.userLastName,
}) { }) {
// Log d'initialisation avec les informations utilisateur // 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. /// Génère une route en fonction du [RouteSettings] fourni.
@@ -49,7 +49,7 @@ class AppRouter {
builder: (_) => HomeScreen( builder: (_) => HomeScreen(
eventRemoteDataSource: eventRemoteDataSource, eventRemoteDataSource: eventRemoteDataSource,
userId: userId, userId: userId,
userName: userName, userFirstName: userFirstName,
userLastName: userLastName, userLastName: userLastName,
userProfileImage: 'lib/assets/images/profile_picture.png', userProfileImage: 'lib/assets/images/profile_picture.png',
), ),
@@ -60,7 +60,7 @@ class AppRouter {
return MaterialPageRoute( return MaterialPageRoute(
builder: (_) => EventScreen( builder: (_) => EventScreen(
userId: userId, userId: userId,
userName: userName, userFirstName: userFirstName,
userLastName: userLastName, userLastName: userLastName,
), ),
); );

View File

@@ -9,10 +9,11 @@ class CreatorModel extends UserModel {
}) : super( }) : super(
userId: id, userId: id,
nom: nom, userLastName: nom,
prenoms: prenoms, userFirstName: prenoms,
email: '', // Valeur par défaut vide email: '', // Valeur par défaut vide
motDePasse: '', // Valeur par défaut vide motDePasse: '', // Valeur par défaut vide
profileImageUrl: '',
); );
factory CreatorModel.fromJson(Map<String, dynamic> json) { factory CreatorModel.fromJson(Map<String, dynamic> json) {
@@ -27,8 +28,8 @@ class CreatorModel extends UserModel {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'id': userId, 'id': userId,
'nom': nom, 'nom': userLastName,
'prenoms': prenoms, 'prenoms': userFirstName,
}; };
} }
} }

View File

@@ -8,10 +8,11 @@ class ParticipantModel extends UserModel {
required String prenoms, required String prenoms,
}) : super( }) : super(
userId: id, userId: id,
nom: nom, userLastName: nom,
prenoms: prenoms, userFirstName: prenoms,
email: '', // Valeur par défaut vide email: '', // Valeur par défaut vide
motDePasse: '', // Valeur par défaut vide motDePasse: '', // Valeur par défaut vide
profileImageUrl: '',
); );
factory ParticipantModel.fromJson(Map<String, dynamic> json) { factory ParticipantModel.fromJson(Map<String, dynamic> json) {
@@ -27,8 +28,8 @@ class ParticipantModel extends UserModel {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'id': userId, 'id': userId,
'nom': nom, 'nom': userLastName,
'prenoms': prenoms, 'prenoms': userFirstName,
}; };
} }
} }

View File

@@ -5,26 +5,29 @@ import '../../domain/entities/user.dart';
class UserModel extends User { class UserModel extends User {
UserModel({ UserModel({
required String userId, required String userId,
required String nom, required String userLastName,
required String prenoms, required String userFirstName,
required String email, required String email,
required String motDePasse, required String motDePasse,
required String profileImageUrl,
}) : super( }) : super(
userId: userId, userId: userId,
nom: nom, userLastName: userLastName,
prenoms: prenoms, userFirstName: userFirstName,
email: email, email: email,
motDePasse: motDePasse, motDePasse: motDePasse,
profileImageUrl: profileImageUrl,
); );
/// Factory pour créer un `UserModel` à partir d'un JSON reçu depuis l'API. /// Factory pour créer un `UserModel` à partir d'un JSON reçu depuis l'API.
factory UserModel.fromJson(Map<String, dynamic> json) { factory UserModel.fromJson(Map<String, dynamic> json) {
return UserModel( return UserModel(
userId: json['userId'] ?? '', userId: json['userId'] ?? '',
nom: json['nom'] ?? 'Inconnu', userLastName: json['nom'] ?? 'Inconnu',
prenoms: json['prenoms'] ?? 'Inconnu', userFirstName: json['prenoms'] ?? 'Inconnu',
email: json['email'] ?? 'inconnu@example.com', email: json['email'] ?? 'inconnu@example.com',
motDePasse: json['motDePasse'] ?? '', motDePasse: json['motDePasse'] ?? '',
profileImageUrl: json['profileImageUrl'] ?? '',
); );
} }
@@ -32,10 +35,11 @@ class UserModel extends User {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'id': userId, 'id': userId,
'nom': nom, 'nom': userLastName,
'prenoms': prenoms, 'prenoms': userFirstName,
'email': email, 'email': email,
'motDePasse': motDePasse, // Mot de passe en clair (comme demandé temporairement) 'motDePasse': motDePasse, // Mot de passe en clair (comme demandé temporairement)
'profileImageUrl':profileImageUrl,
}; };
} }
} }

View File

@@ -1,37 +1,65 @@
import 'package:flutter/material.dart'; 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. /// Toutes les modifications et actions sont loguées pour assurer une traçabilité complète dans le terminal.
class UserProvider with ChangeNotifier { class UserProvider with ChangeNotifier {
String _userId = ''; User _user = const User(
String _userName = ''; userId: '',
String _userLastName = ''; userLastName: '',
userFirstName: '',
email: '',
motDePasse: '',
profileImageUrl: '',
eventsCount: 0,
friendsCount: 0,
postsCount: 0,
visitedPlacesCount: 0,
);
/// Getter pour l'ID de l'utilisateur /// Getter pour l'objet utilisateur.
String get userId => _userId; User get user => _user;
/// Getter pour le nom de l'utilisateur
String get userName => _userName;
/// Getter pour le prénom de l'utilisateur
String get userLastName => _userLastName;
/// Méthode pour définir les informations de l'utilisateur. /// Méthode pour définir les informations de l'utilisateur.
/// Logue les informations fournies et notifie les listeners des changements. /// Logue les informations fournies et notifie les listeners des changements.
/// ///
/// [id] : L'ID de l'utilisateur. /// [user] : L'objet utilisateur contenant toutes les informations.
/// [name] : Le nom de l'utilisateur. void setUser(User user) {
/// [lastName] : Le prénom de l'utilisateur. debugPrint("[LOG] Tentative de définition des informations de l'utilisateur : ${user.toString()}");
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");
_userId = id; _user = user;
_userName = name;
_userLastName = lastName;
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(); notifyListeners();
} }
@@ -39,15 +67,23 @@ class UserProvider with ChangeNotifier {
/// Les valeurs sont loguées avant et après la réinitialisation. /// Les valeurs sont loguées avant et après la réinitialisation.
void resetUser() { void resetUser() {
debugPrint("[LOG] Réinitialisation des informations de l'utilisateur."); 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 = ''; _user = const User(
_userName = ''; userId: '',
_userLastName = ''; 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(); notifyListeners();
} }
} }

View File

@@ -60,15 +60,15 @@ class PreferencesHelper {
/// Sauvegarde le nom d'utilisateur dans les préférences. /// Sauvegarde le nom d'utilisateur dans les préférences.
/// Logue l'opération pour assurer un suivi complet. /// Logue l'opération pour assurer un suivi complet.
Future<void> saveUserName(String userName) async { Future<void> saveUserName(String userFirstName) async {
print("[LOG] Sauvegarde du userName dans les préférences : $userName"); print("[LOG] Sauvegarde du userFirstName dans les préférences : $userFirstName");
await setString('user_name', userName); await setString('user_name', userFirstName);
} }
/// Récupère le nom d'utilisateur depuis les préférences. /// Récupère le nom d'utilisateur depuis les préférences.
/// Retourne le nom ou null en cas d'échec. /// Retourne le nom ou null en cas d'échec.
Future<String?> getUserName() async { Future<String?> getUseFirstrName() async {
print("[LOG] Récupération du userName depuis les préférences."); print("[LOG] Récupération du userFirstName depuis les préférences.");
return await getString('user_name'); return await getString('user_name');
} }
@@ -89,7 +89,7 @@ class PreferencesHelper {
/// Supprime toutes les informations utilisateur dans les préférences. /// Supprime toutes les informations utilisateur dans les préférences.
/// Logue chaque étape de la suppression. /// Logue chaque étape de la suppression.
Future<void> clearUserInfo() async { Future<void> 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_id');
await remove('user_name'); await remove('user_name');
await remove('user_last_name'); await remove('user_last_name');

View File

@@ -2,19 +2,40 @@ import 'package:equatable/equatable.dart';
class User extends Equatable { class User extends Equatable {
final String userId; final String userId;
final String nom; final String userLastName;
final String prenoms; final String userFirstName;
final String email; final String email;
final String motDePasse; final String motDePasse;
final String profileImageUrl;
final int eventsCount;
final int friendsCount;
final int postsCount;
final int visitedPlacesCount;
const User({ const User({
required this.userId, required this.userId,
required this.nom, required this.userLastName,
required this.prenoms, required this.userFirstName,
required this.email, required this.email,
required this.motDePasse, required this.motDePasse,
required this.profileImageUrl,
this.eventsCount = 0,
this.friendsCount = 0,
this.postsCount = 0,
this.visitedPlacesCount = 0,
}); });
@override @override
List<Object?> get props => [userId, nom, prenoms, email, motDePasse]; List<Object?> get props => [
userId,
userLastName,
userFirstName,
email,
motDePasse,
profileImageUrl,
eventsCount,
friendsCount,
postsCount,
visitedPlacesCount,
];
} }

View File

@@ -1,4 +1,3 @@
import 'package:flutter/material.dart';
import 'package:afterwork/config/router.dart'; import 'package:afterwork/config/router.dart';
import 'package:afterwork/data/datasources/event_remote_data_source.dart'; import 'package:afterwork/data/datasources/event_remote_data_source.dart';
import 'package:afterwork/data/providers/user_provider.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/preferences_helper.dart';
import 'package:afterwork/data/services/secure_storage.dart'; import 'package:afterwork/data/services/secure_storage.dart';
import 'package:afterwork/presentation/state_management/event_bloc.dart'; import 'package:afterwork/presentation/state_management/event_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:intl/date_symbol_data_local.dart'; import 'package:intl/date_symbol_data_local.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'core/theme/theme_provider.dart'; import 'core/theme/theme_provider.dart';
import 'data/providers/friends_provider.dart'; import 'data/providers/friends_provider.dart';
import 'domain/entities/user.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
@@ -23,60 +25,68 @@ void main() async {
final eventRemoteDataSource = EventRemoteDataSource(http.Client()); final eventRemoteDataSource = EventRemoteDataSource(http.Client());
final SecureStorage secureStorage = SecureStorage(); final SecureStorage secureStorage = SecureStorage();
final PreferencesHelper preferencesHelper = PreferencesHelper(); 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 // Récupération des informations stockées avec logs détaillés
String? userId = await secureStorage.getUserId(); String? userId = await secureStorage.getUserId();
String? userName = await preferencesHelper.getUserName(); String? userFirstName = await preferencesHelper.getUseFirstrName();
String? userLastName = await preferencesHelper.getUserLastName(); String? userLastName = await preferencesHelper.getUserLastName();
// Log de la récupération des informations // 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 // Gestion des valeurs par défaut si les informations ne sont pas trouvées
userId ??= 'default_user_id'; userId ??= 'default_user_id';
userName ??= 'Default'; userFirstName ??= 'Default';
userLastName ??= 'User'; userLastName ??= 'User';
// Log des valeurs par défaut appliquées // 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( runApp(MyApp(
eventRemoteDataSource: eventRemoteDataSource, eventRemoteDataSource: eventRemoteDataSource,
userId: userId, user: user,
userName: userName, httpClient: httpClient,
userLastName: userLastName,
httpClient: httpClient, // Passe l'instance client ici
)); ));
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
final EventRemoteDataSource eventRemoteDataSource; final EventRemoteDataSource eventRemoteDataSource;
final String userId; final User user;
final String userName; final http.Client httpClient;
final String userLastName;
final http.Client httpClient; // Ajout de ce paramètre
const MyApp({ const MyApp({
super.key, super.key,
required this.eventRemoteDataSource, required this.eventRemoteDataSource,
required this.userId, required this.user,
required this.userName, required this.httpClient,
required this.userLastName,
required this.httpClient, // Spécifier l'argument
}); });
@override @override
Widget build(BuildContext context) { 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 // 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( return MultiProvider(
providers: [ providers: [
ChangeNotifierProvider( ChangeNotifierProvider(
create: (_) => UserProvider()..setUser(userId, userName, userLastName), create: (_) => UserProvider()..setUser(user),
), ),
ChangeNotifierProvider( ChangeNotifierProvider(
create: (_) => FriendsProvider(friendsRepository: friendsRepository), create: (_) => FriendsProvider(friendsRepository: friendsRepository),
@@ -95,9 +105,9 @@ class MyApp extends StatelessWidget {
theme: themeProvider.currentTheme, theme: themeProvider.currentTheme,
onGenerateRoute: AppRouter( onGenerateRoute: AppRouter(
eventRemoteDataSource: eventRemoteDataSource, eventRemoteDataSource: eventRemoteDataSource,
userId: userId, userId: user.userId,
userName: userName, userFirstName: user.userFirstName,
userLastName: userLastName, userLastName: user.userLastName,
).generateRoute, ).generateRoute,
initialRoute: '/', initialRoute: '/',
); );
@@ -106,3 +116,4 @@ class MyApp extends StatelessWidget {
); );
} }
} }

View File

@@ -13,13 +13,13 @@ import '../../widgets/image_preview_picker.dart';
class AddEventPage extends StatefulWidget { class AddEventPage extends StatefulWidget {
final String userId; final String userId;
final String userName; final String userFirstName;
final String userLastName; final String userLastName;
const AddEventPage({ const AddEventPage({
super.key, super.key,
required this.userId, required this.userId,
required this.userName, required this.userFirstName,
required this.userLastName, required this.userLastName,
}); });

View File

@@ -10,7 +10,7 @@ import '../../widgets/swipe_background.dart';
class EventCard extends StatelessWidget { class EventCard extends StatelessWidget {
final EventModel event; final EventModel event;
final String userId; final String userId;
final String userName; final String userFirstName;
final String userLastName; final String userLastName;
final String status; final String status;
final VoidCallback onReact; final VoidCallback onReact;
@@ -25,7 +25,7 @@ class EventCard extends StatelessWidget {
Key? key, Key? key,
required this.event, required this.event,
required this.userId, required this.userId,
required this.userName, required this.userFirstName,
required this.userLastName, required this.userLastName,
required this.status, required this.status,
required this.onReact, required this.onReact,
@@ -70,7 +70,7 @@ class EventCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
EventHeader( EventHeader(
userName: userName, userFirstName: userFirstName,
userLastName: userLastName, userLastName: userLastName,
eventDate: event.startDate, eventDate: event.startDate,
imageUrl: event.imageUrl, imageUrl: event.imageUrl,

View File

@@ -7,13 +7,13 @@ import '../dialogs/add_event_dialog.dart';
class EventScreen extends StatefulWidget { class EventScreen extends StatefulWidget {
final String userId; final String userId;
final String userName; final String userFirstName;
final String userLastName; final String userLastName;
const EventScreen({ const EventScreen({
Key? key, Key? key,
required this.userId, required this.userId,
required this.userName, required this.userFirstName,
required this.userLastName, required this.userLastName,
}) : super(key: key); }) : super(key: key);
@@ -51,7 +51,7 @@ class _EventScreenState extends State<EventScreen> {
MaterialPageRoute( MaterialPageRoute(
builder: (context) => AddEventPage( builder: (context) => AddEventPage(
userId: widget.userId, userId: widget.userId,
userName: widget.userName, userFirstName: widget.userFirstName,
userLastName: widget.userLastName, userLastName: widget.userLastName,
), ),
), ),
@@ -78,7 +78,7 @@ class _EventScreenState extends State<EventScreen> {
key: ValueKey(event.id), key: ValueKey(event.id),
event: event, event: event,
userId: widget.userId, userId: widget.userId,
userName: widget.userName, userFirstName: widget.userFirstName,
userLastName: widget.userLastName, userLastName: widget.userLastName,
onReact: () => _onReact(event.id), onReact: () => _onReact(event.id),
onComment: () => _onComment(event.id), onComment: () => _onComment(event.id),

View File

@@ -14,7 +14,7 @@ import '../friends/friends_screen.dart'; // Écran des amis
class HomeScreen extends StatefulWidget { class HomeScreen extends StatefulWidget {
final EventRemoteDataSource eventRemoteDataSource; final EventRemoteDataSource eventRemoteDataSource;
final String userId; final String userId;
final String userName; final String userFirstName;
final String userLastName; final String userLastName;
final String userProfileImage; // Image de profil de l'utilisateur final String userProfileImage; // Image de profil de l'utilisateur
@@ -22,7 +22,7 @@ class HomeScreen extends StatefulWidget {
Key? key, Key? key,
required this.eventRemoteDataSource, required this.eventRemoteDataSource,
required this.userId, required this.userId,
required this.userName, required this.userFirstName,
required this.userLastName, required this.userLastName,
required this.userProfileImage, // Passer l'image de profil ici required this.userProfileImage, // Passer l'image de profil ici
}) : super(key: key); }) : super(key: key);
@@ -127,7 +127,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
const HomeContentScreen(), const HomeContentScreen(),
EventScreen( EventScreen(
userId: widget.userId, userId: widget.userId,
userName: widget.userName, userFirstName: widget.userFirstName,
userLastName: widget.userLastName, userLastName: widget.userLastName,
), ),
const EstablishmentsScreen(), const EstablishmentsScreen(),

View File

@@ -14,6 +14,8 @@ import 'package:provider/provider.dart';
import '../../../core/errors/exceptions.dart'; import '../../../core/errors/exceptions.dart';
import '../../../core/theme/theme_provider.dart'; import '../../../core/theme/theme_provider.dart';
import '../../../data/datasources/event_remote_data_source.dart'; import '../../../data/datasources/event_remote_data_source.dart';
import '../../../data/providers/user_provider.dart';
import '../../../domain/entities/user.dart';
import '../signup/SignUpScreen.dart'; import '../signup/SignUpScreen.dart';
/// L'écran de connexion où les utilisateurs peuvent s'authentifier. /// L'écran de connexion où les utilisateurs peuvent s'authentifier.
@@ -102,30 +104,41 @@ class _LoginScreenState extends State<LoginScreen>
_formKey.currentState!.save(); // Sauvegarde des données saisies _formKey.currentState!.save(); // Sauvegarde des données saisies
try { try {
_btnController.start(); // Démarre l'animation de chargement du bouton _btnController.start();
debugPrint("[LOG] Appel à l'API pour authentifier l'utilisateur."); debugPrint("[LOG] Appel à l'API pour authentifier l'utilisateur.");
// Appel à l'API pour authentifier l'utilisateur
final UserModel user = await _userRemoteDataSource.authenticateUser(_email, _password); final UserModel user = await _userRemoteDataSource.authenticateUser(_email, _password);
// Validation de l'ID utilisateur
if (user.userId.isNotEmpty) { if (user.userId.isNotEmpty) {
debugPrint("[LOG] Utilisateur authentifié avec succès. ID: ${user.userId}"); debugPrint("[LOG] Utilisateur authentifié avec succès. ID: ${user.userId}");
// Sauvegarde des informations utilisateur
await _secureStorage.saveUserId(user.userId); await _secureStorage.saveUserId(user.userId);
await _preferencesHelper.saveUserName(user.nom); await _preferencesHelper.saveUserName(user.userLastName);
await _preferencesHelper.saveUserLastName(user.prenoms); await _preferencesHelper.saveUserLastName(user.userFirstName);
// Met à jour le `UserProvider` avec les informations utilisateur authentifiées
final userProvider = Provider.of<UserProvider>(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 !"); _showToast("Connexion réussie !");
// Redirection vers l'écran d'accueil
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => HomeScreen( builder: (context) => HomeScreen(
userId: user.userId, userId: user.userId,
userName: user.nom, userFirstName: user.userFirstName,
userLastName: user.prenoms, userLastName: user.userLastName,
userProfileImage: 'lib/assets/images/profile_picture.png', userProfileImage: 'lib/assets/images/profile_picture.png',
eventRemoteDataSource: EventRemoteDataSource(http.Client()), eventRemoteDataSource: EventRemoteDataSource(http.Client()),
), ),
@@ -136,25 +149,17 @@ class _LoginScreenState extends State<LoginScreen>
_showToast("Erreur : ID utilisateur manquant."); _showToast("Erreur : ID utilisateur manquant.");
} }
} catch (e) { } catch (e) {
// Gestion des erreurs spécifiques et log de chaque type d'erreur // Gestion des erreurs
if (e is ServerExceptionWithMessage) { debugPrint("[ERROR] Erreur lors de la connexion : $e");
debugPrint("[ERROR] Erreur serveur : ${e.message}"); _showToast("Erreur lors de la connexion : ${e.toString()}");
_showToast("Erreur serveur : ${e.message}"); _btnController.error();
} 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
setState(() { setState(() {
_showErrorMessage = true; _showErrorMessage = true;
}); });
} finally { } finally {
_btnController.reset(); // Réinitialise l'état du bouton _btnController.reset();
setState(() { setState(() {
_isSubmitting = false; // Réinitialise l'état de chargement _isSubmitting = false;
}); });
} }
} else { } else {

View File

@@ -1,129 +1,92 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../../core/constants/colors.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 { class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key}); const ProfileScreen({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
print("Affichage de l'écran de profil."); final userProvider = Provider.of<UserProvider>(context);
final user = userProvider.user;
return Scaffold( return Scaffold(
backgroundColor: AppColors.backgroundColor, backgroundColor: AppColors.backgroundColor,
body: CustomScrollView( body: CustomScrollView(
slivers: [ slivers: [
SliverAppBar( ProfileHeader(user: user),
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
},
),
],
),
SliverList( SliverList(
delegate: SliverChildListDelegate( delegate: SliverChildListDelegate(
[ [
const SizedBox(height: 10), const SizedBox(height: 10),
_buildUserInfoCard(), UserInfoCard(user: user),
const SizedBox(height: 10), const SizedBox(height: 10),
_buildEditOptionsCard(), const EditOptionsCard(),
const SizedBox(height: 10), const SizedBox(height: 10),
_buildStatisticsSectionCard(), StatisticsSectionCard(user: user),
const SizedBox(height: 10), const SizedBox(height: 10),
_buildExpandableSectionCard( ExpandableSectionCard(
title: 'Historique', title: 'Historique',
icon: Icons.history, icon: Icons.history,
children: [ children: [
_buildAnimatedListTile( CustomListTile(
icon: Icons.event_note, icon: Icons.event_note,
label: 'Historique des Événements', label: 'Historique des Événements',
onTap: () { onTap: () => print("[LOG] Accès à l'historique des événements."),
print("Accès à l'historique des événements.");
// Logique de navigation vers l'historique des événements
},
), ),
_buildAnimatedListTile( CustomListTile(
icon: Icons.history, icon: Icons.history,
label: 'Historique des Publications', label: 'Historique des Publications',
onTap: () { onTap: () => print("[LOG] Accès à l'historique des publications."),
print("Accès à l'historique des publications.");
// Logique de navigation vers l'historique des publications
},
), ),
_buildAnimatedListTile( CustomListTile(
icon: Icons.bookmark, icon: Icons.bookmark,
label: 'Historique de Réservations', label: 'Historique de Réservations',
onTap: () { onTap: () => print("[LOG] Accès à l'historique des réservations."),
print("Accès à l'historique des réservations.");
// Logique de navigation vers l'historique des réservations
},
), ),
], ],
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
_buildExpandableSectionCard( ExpandableSectionCard(
title: 'Préférences et Paramètres', title: 'Préférences et Paramètres',
icon: Icons.settings, icon: Icons.settings,
children: [ children: [
_buildAnimatedListTile( CustomListTile(
icon: Icons.privacy_tip, icon: Icons.privacy_tip,
label: 'Paramètres de confidentialité', label: 'Paramètres de confidentialité',
onTap: () { onTap: () => print("[LOG] Accès aux paramètres de confidentialité."),
print("Accès aux paramètres de confidentialité.");
// Logique de navigation vers les paramètres de confidentialité
},
), ),
_buildAnimatedListTile( CustomListTile(
icon: Icons.notifications, icon: Icons.notifications,
label: 'Notifications', label: 'Notifications',
onTap: () { onTap: () => print("[LOG] Accès aux paramètres de notifications."),
print("Accès aux paramètres de notifications.");
// Logique de navigation vers les notifications
},
), ),
_buildAnimatedListTile( CustomListTile(
icon: Icons.language, icon: Icons.language,
label: 'Langue de l\'application', label: 'Langue de l\'application',
onTap: () { onTap: () => print("[LOG] Accès aux paramètres de langue."),
print("Accès aux paramètres de langue.");
// Logique de navigation vers les paramètres de langue
},
), ),
_buildAnimatedListTile( CustomListTile(
icon: Icons.format_paint, icon: Icons.format_paint,
label: 'Thème de l\'application', label: 'Thème de l\'application',
onTap: () { onTap: () => print("[LOG] Accès aux paramètres de thème."),
print("Accès aux paramètres de thème.");
// Logique de navigation vers les paramètres de thème
},
), ),
], ],
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
_buildSupportSectionCard(), const SupportSectionCard(),
const SizedBox(height: 10), 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<Widget> 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,
),
),
);
}
} }

View File

@@ -81,10 +81,11 @@ class _SignUpScreenState extends State<SignUpScreen> {
// Créer l'utilisateur avec les informations fournies // Créer l'utilisateur avec les informations fournies
final UserModel user = UserModel( final UserModel user = UserModel(
userId: '', // L'ID sera généré côté serveur userId: '', // L'ID sera généré côté serveur
nom: _nom, userLastName: _nom,
prenoms: _prenoms, userFirstName: _prenoms,
email: _email, email: _email,
motDePasse: _password, // Le mot de passe sera envoyé en clair pour l'instant motDePasse: _password, // Le mot de passe sera envoyé en clair pour l'instant
profileImageUrl: '',
); );
// Envoi des informations pour créer un nouvel utilisateur // Envoi des informations pour créer un nouvel utilisateur
@@ -94,8 +95,8 @@ class _SignUpScreenState extends State<SignUpScreen> {
// Sauvegarder les informations de l'utilisateur // Sauvegarder les informations de l'utilisateur
await _secureStorage.saveUserId(createdUser.userId); await _secureStorage.saveUserId(createdUser.userId);
await _preferencesHelper.saveUserName(createdUser.nom); await _preferencesHelper.saveUserName(createdUser.userLastName);
await _preferencesHelper.saveUserLastName(createdUser.prenoms); await _preferencesHelper.saveUserLastName(createdUser.userFirstName);
// Rediriger vers la page d'accueil ou une page de confirmation // Rediriger vers la page d'accueil ou une page de confirmation
Navigator.pushReplacementNamed(context, '/home'); Navigator.pushReplacementNamed(context, '/home');

View File

@@ -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),
),
),
],
);
},
);
}
}

View File

@@ -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),
),
),
);
}
}

View File

@@ -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."),
),
],
),
);
}
}

View File

@@ -3,7 +3,7 @@ import 'package:afterwork/core/utils/date_formatter.dart';
import 'event_menu.dart'; import 'event_menu.dart';
class EventHeader extends StatelessWidget { class EventHeader extends StatelessWidget {
final String userName; final String userFirstName;
final String userLastName; final String userLastName;
final String? eventDate; final String? eventDate;
final String? imageUrl; final String? imageUrl;
@@ -14,7 +14,7 @@ class EventHeader extends StatelessWidget {
const EventHeader({ const EventHeader({
Key? key, Key? key,
required this.userName, required this.userFirstName,
required this.userLastName, required this.userLastName,
this.eventDate, this.eventDate,
this.imageUrl, this.imageUrl,
@@ -51,7 +51,7 @@ class EventHeader extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'$userName $userLastName', '$userFirstName $userLastName',
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 14, fontSize: 14,

View File

@@ -18,7 +18,7 @@ class EventList extends StatelessWidget {
return EventCard( return EventCard(
event: event, event: event,
userId: 'user_id_here', // Vous pouvez passer l'ID réel de l'utilisateur connecté 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 userLastName: 'Doe', // Vous pouvez passer le nom réel de l'utilisateur
onReact: () => _handleReact(event), onReact: () => _handleReact(event),
onComment: () => _handleComment(event), onComment: () => _handleComment(event),

View File

@@ -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<Widget> 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,
),
);
}
}

View File

@@ -23,7 +23,7 @@ class FriendsCircle extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Combine firstName et lastName ou utilise "Ami inconnu" par défaut. // Combine firstName et lastName ou utilise "Ami inconnu" par défaut.
String displayName = [friend.firstName, friend.lastName] String displayName = [friend.firstName, friend.lastName]
.where((namePart) => namePart != null && namePart.isNotEmpty) .where((namePart) => namePart.isNotEmpty)
.join(" ") .join(" ")
.trim(); .trim();

View File

@@ -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<UserProvider>(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.");
});
}
}

View File

@@ -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,
),
),
);
}
}

View File

@@ -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}'),
],
),
),
);
}
}

View File

@@ -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é."),
),
],
),
);
}
}

View File

@@ -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,
),
),
],
),
),
);
}
}