Auth réparée

This commit is contained in:
PC-PAUL\paulf 2025-03-05 19:56:58 +01:00
parent 3c5d3b4c5a
commit d3813bfcdb
6 changed files with 136 additions and 177 deletions

View File

@ -4,7 +4,22 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Attach to Chrome",
"port": 9222,
"request": "attach",
"type": "chrome",
"webRoot": "${workspaceFolder}",
"preLaunchTask": "npm: vite",
},
{
"name": "Launch Chrome",
"request": "launch",
"type": "chrome",
"url": "http://localhost:8080",
"webRoot": "${workspaceFolder}"
},
{
"name": "Launch Edge",
"request": "launch",

View File

@ -31,68 +31,50 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
print("test");
return MaterialApp(
title: 'EM2 ERP',
theme: ThemeData(
primarySwatch: Colors.red,
primaryColor: AppColors.noir,
colorScheme:
ColorScheme.fromSwatch().copyWith(secondary: AppColors.rouge),
textTheme: const TextTheme(
bodyMedium: TextStyle(color: AppColors.noir),
),
// Personnalisation de l'InputDecorationTheme pour les text fields
inputDecorationTheme: InputDecorationTheme(
focusedBorder: OutlineInputBorder(
// Bordure lorsqu'il est focus
borderSide:
BorderSide(color: AppColors.noir), // Couleur rouge quand focus
title: 'EM2 ERP',
theme: ThemeData(
primarySwatch: Colors.red,
primaryColor: AppColors.noir,
colorScheme:
ColorScheme.fromSwatch().copyWith(secondary: AppColors.rouge),
textTheme: const TextTheme(
bodyMedium: TextStyle(color: AppColors.noir),
),
enabledBorder: OutlineInputBorder(
// Bordure par défaut (non focus)
borderSide:
BorderSide(color: AppColors.gris), // Couleur grise par défaut
),
labelStyle: TextStyle(color: AppColors.noir), // Couleur du label
hintStyle: TextStyle(color: AppColors.gris), // Couleur du hint text
// Tu peux personnaliser d'autres propriétés ici :
// fillColor, filled, iconColor, prefixStyle, suffixStyle, etc.
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
foregroundColor:
AppColors.blanc, // Couleur du texte du bouton (ici blanc)
backgroundColor: AppColors
.noir, // Couleur de fond du bouton (si tu veux aussi changer le fond)
// Autres styles possibles pour les boutons :
// textStyle, padding, shape, elevation, etc.
),
),
),
routes: {
'/login': (context) => const LoginPage(),
'/calendar': (context) => const CalendarPage(),
'/my_account': (context) => const MyAccountPage(),
'/user_management': (context) => const UserManagementPage(),
},
// Conditionally set home based on authentication state
home: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
User? user = snapshot.data;
if (user == null) {
return const LoginPage(); // User not logged in, show LoginPage
}
return const CalendarPage(); // User logged in, show CalendarPage
}
// Checking auth state, show loading indicator
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
// Personnalisation de l'InputDecorationTheme pour les text fields
inputDecorationTheme: InputDecorationTheme(
focusedBorder: OutlineInputBorder(
// Bordure lorsqu'il est focus
borderSide: BorderSide(
color: AppColors.noir), // Couleur rouge quand focus
),
);
enabledBorder: OutlineInputBorder(
// Bordure par défaut (non focus)
borderSide:
BorderSide(color: AppColors.gris), // Couleur grise par défaut
),
labelStyle: TextStyle(color: AppColors.noir), // Couleur du label
hintStyle: TextStyle(color: AppColors.gris), // Couleur du hint text
// Tu peux personnaliser d'autres propriétés ici :
// fillColor, filled, iconColor, prefixStyle, suffixStyle, etc.
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
foregroundColor:
AppColors.blanc, // Couleur du texte du bouton (ici blanc)
backgroundColor: AppColors
.noir, // Couleur de fond du bouton (si tu veux aussi changer le fond)
// Autres styles possibles pour les boutons :
// textStyle, padding, shape, elevation, etc.
),
),
),
routes: {
'/login': (context) => const LoginPage(),
'/calendar': (context) => const CalendarPage(),
'/my_account': (context) => const MyAccountPage(),
'/user_management': (context) => const UserManagementPage(),
},
),
);
// Conditionally set home based on authentication state
home: LoginPage());
}
}

View File

@ -5,27 +5,38 @@ class UserProvider extends ChangeNotifier {
String? _firstName;
String? _lastName;
String? _role;
String? _profilePictureUrl;
// String? _profilePictureUrl;
String? _email;
String? _phoneNumber;
// String? _phoneNumber;
String? get uid => _uid;
String? get firstName => _firstName;
String? get lastName => _lastName;
String? get role => _role;
String? get profilePhotoUrl => _profilePictureUrl;
// String? get profilePhotoUrl => _profilePictureUrl;
String? get email => _email;
String? get phoneNumber => _phoneNumber;
// String? get phoneNumber => _phoneNumber;
// void setUserData(Map<String, dynamic> userData, String uid) {
// _uid = uid;
// _firstName = userData['firstName'];
// _lastName = userData['lastName'];
// _role = userData['role'] ?? 'USER'; // Default role if not provided
// // _profilePictureUrl = userData['profilePhotoUrl'];
// _email = userData['email'];
// // _phoneNumber = userData['phoneNumber'];
// notifyListeners(); // Notify listeners that state has changed
// }
void setUserData(Map<String, dynamic> userData, String uid) {
_uid = uid;
_firstName = userData['firstName'];
_lastName = userData['lastName'];
_role = userData['role'] ?? 'USER'; // Default role if not provided
_profilePictureUrl = userData['profilePhotoUrl'];
_email = userData['email'];
_phoneNumber = userData['phoneNumber'];
notifyListeners(); // Notify listeners that state has changed
print("User data set: $_firstName $_lastName ($_email)");
notifyListeners();
}
void clearUserData() {
@ -33,9 +44,9 @@ class UserProvider extends ChangeNotifier {
_firstName = null;
_lastName = null;
_role = null;
_profilePictureUrl = null;
// _profilePictureUrl = null;
_email = null;
_phoneNumber = null;
// _phoneNumber = null;
notifyListeners();
}
}

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:em2rp/views/widgets/nav/main_drawer.dart';
import 'package:provider/provider.dart'; // Import Provider
import 'package:em2rp/providers/user_provider.dart'; // Import UserProvider

View File

@ -1,6 +1,5 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:em2rp/providers/user_provider.dart';
import 'package:em2rp/views/widgets/auth/forgot_password_dialog.dart';
import 'package:em2rp/views/widgets/auth/mail_textfield.dart';
import 'package:em2rp/views/widgets/error_message.dart';
import 'package:em2rp/views/widgets/auth/forgot_password_button.dart';
@ -11,6 +10,7 @@ import 'package:em2rp/views/widgets/auth/password_textfield.dart';
import 'package:em2rp/views/widgets/auth/welcome_text.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:em2rp/views/widgets/auth/forgot_password_dialog.dart';
import 'package:provider/provider.dart';
class LoginPage extends StatefulWidget {
@ -33,13 +33,11 @@ class _LoginPageState extends State<LoginPage> {
void dispose() {
_emailController.dispose();
_passwordController.dispose();
print("LoginPage DISPOSED");
super.dispose();
}
Future<void> _loginUser() async {
if (!mounted) return; // Early exit if widget is unmounted
Future<void> _signInWithEmailAndPassword() async {
// Pas de vérification mounted ici, le widget reste monté pendant toute l'opération
setState(() {
_errorMessage = '';
_isLoading = true;
@ -47,95 +45,54 @@ class _LoginPageState extends State<LoginPage> {
_highlightEmailField = false;
});
String email = _emailController.text.trim();
String password = _passwordController.text;
try {
// 1. Sign in with Firebase Authentication
UserCredential userCredential = await FirebaseAuth.instance
.signInWithEmailAndPassword(email: email, password: password);
User? user = userCredential.user;
final UserCredential userCredential =
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passwordController.text,
);
final uid = userCredential.user!.uid;
if (user != null) {
// 2. Fetch user data from Firestore
print(
"Fetching user data from Firestore..."); // Log before Firestore fetch
DocumentSnapshot userDoc = await FirebaseFirestore.instance
.collection('users')
.doc(user.uid)
.get();
print("Firestore DocumentSnapshot retrieved."); // Log after fetch
final DocumentSnapshot userDoc =
await FirebaseFirestore.instance.collection('users').doc(uid).get();
if (userDoc.exists) {
print("Firestore Document Exists: true"); // Log doc exists check
print("Document ID: ${userDoc.id}"); // Log document ID
print(
"Before data cast - userDoc.data(): ${userDoc.data()}"); // Log raw data before cast
if (userDoc.exists) {
print("User document found: ${userDoc.data()}");
Provider.of<UserProvider>(context, listen: false)
.setUserData(userDoc.data() as Map<String, dynamic>, uid);
Map<String, dynamic> userData =
userDoc.data() as Map<String, dynamic>;
print(
"After data cast - userData: $userData"); // Log userData after cast
// 3. Store user data in UserProvider
final userProvider =
Provider.of<UserProvider>(context, listen: false);
print(
"Before setUserData call - userProvider: $userProvider"); // Log userProvider instance
userProvider.setUserData(
userData, user.uid); // Store user data in provider
print("After setUserData call"); // Log after setUserData is called
print(
'Login successful! Role: ${userProvider.role}'); // Log success and role
if (mounted) {
Navigator.of(context).pushReplacementNamed(
'/calendar'); // 4. Navigate to CalendarPage
}
} else {
// User document not found in Firestore
print(
"Firestore Document Exists: false"); // Log doc exists check - false
if (mounted) {
setState(() {
_isLoading = false;
_errorMessage =
"Erreur: Utilisateur non trouvé dans la base de données.";
});
}
print('Login Error: Firestore user document not found');
}
// Maintenant que toutes les données sont chargées, naviguer vers CalendarPage.
Navigator.of(context).pushReplacementNamed('/calendar');
} else {
setState(() {
_errorMessage = "Aucune donnée utilisateur trouvée.";
_isLoading = false;
});
}
} on FirebaseAuthException catch (e) {
// Firebase Authentication error
if (mounted) {
setState(() {
_isLoading = false;
if (e.code == 'user-not-found' || e.code == 'wrong-password') {
_errorMessage = "Email ou mot de passe incorrect.";
_highlightPasswordField = true;
_highlightEmailField = true;
} else if (e.code == 'invalid-email') {
_errorMessage = "Adresse email invalide.";
_highlightEmailField = true;
} else {
_errorMessage = "Erreur de connexion. Veuillez réessayer.";
}
});
}
print('Login Error (FirebaseAuth): ${e.code} - ${e.message}');
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
setState(() {
_isLoading = false;
if (e.code == 'user-not-found' || e.code == 'wrong-password') {
_errorMessage = "Email ou mot de passe incorrect.";
_highlightPasswordField = true;
_highlightEmailField = true;
} else {
_errorMessage = "Erreur de connexion. Veuillez réessayer.";
}
});
} catch (e) {
setState(() {
_errorMessage =
"Erreur lors de la récupération des données utilisateur.";
_isLoading = false;
});
}
}
void _togglePasswordVisibility() {
if (mounted) setState(() => _obscurePassword = !_obscurePassword);
setState(() {
_obscurePassword = !_obscurePassword;
});
}
void _forgotPassword() {
@ -151,9 +108,13 @@ class _LoginPageState extends State<LoginPage> {
Widget build(BuildContext context) {
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) => constraints.maxWidth > 900
? _buildLargeScreenLayout(context)
: _buildSmallScreenLayout(context),
builder: (context, constraints) {
if (constraints.maxWidth > 900) {
return _buildLargeScreenLayout(context);
} else {
return _buildSmallScreenLayout(context);
}
},
),
);
}
@ -161,11 +122,15 @@ class _LoginPageState extends State<LoginPage> {
Widget _buildLargeScreenLayout(BuildContext context) {
return Row(
children: <Widget>[
const Expanded(flex: 6, child: ImageSectionWidget()),
Expanded(
flex: 6,
child: const ImageSectionWidget(), // Affiche l'image de gauche
),
Expanded(
flex: 4,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 30),
padding:
const EdgeInsets.symmetric(horizontal: 50.0, vertical: 30.0),
child: _buildLoginForm(context),
),
),
@ -200,26 +165,13 @@ class _LoginPageState extends State<LoginPage> {
passwordController: _passwordController,
obscurePassword: _obscurePassword,
highlightPasswordField: _highlightPasswordField,
onTogglePasswordVisibility: () {
if (!mounted) return;
setState(() {
_obscurePassword = !_obscurePassword;
});
},
onTogglePasswordVisibility: _togglePasswordVisibility,
),
ForgotPasswordButtonWidget(onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return const ForgotPasswordDialogWidget();
},
);
}),
ForgotPasswordButtonWidget(onPressed: _forgotPassword),
const SizedBox(height: 30),
LoginButtonWidget(
isLoading: _isLoading,
onPressed: () => Navigator.of(context).pushNamed(
'/calendar'), //Ici, remplacer par une fonction de connexion avec firebase Auth
onPressed: _signInWithEmailAndPassword,
),
const SizedBox(height: 20),
ErrorMessageWidget(errorMessage: _errorMessage),

View File

@ -4,7 +4,7 @@ import 'package:em2rp/views/calendar_page.dart';
import 'package:em2rp/views/my_account_page.dart';
import 'package:em2rp/views/user_management_page.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; // Import Provider
// Import Provider
class MainDrawer extends StatelessWidget {
final String currentPage;