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

View File

@@ -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,

View File

@@ -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<EventScreen> {
MaterialPageRoute(
builder: (context) => AddEventPage(
userId: widget.userId,
userName: widget.userName,
userFirstName: widget.userFirstName,
userLastName: widget.userLastName,
),
),
@@ -78,7 +78,7 @@ class _EventScreenState extends State<EventScreen> {
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),

View File

@@ -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<HomeScreen> with SingleTickerProviderStateM
const HomeContentScreen(),
EventScreen(
userId: widget.userId,
userName: widget.userName,
userFirstName: widget.userFirstName,
userLastName: widget.userLastName,
),
const EstablishmentsScreen(),

View File

@@ -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<LoginScreen>
_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<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 !");
// 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<LoginScreen>
_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 {

View File

@@ -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<UserProvider>(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<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
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<SignUpScreen> {
// 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');

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';
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,

View File

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

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) {
// 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();

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