diff --git a/em2rp/assets/images/tshirt-incrust.webp b/em2rp/assets/images/tshirt-incrust.webp new file mode 100644 index 0000000..cb5a13e Binary files /dev/null and b/em2rp/assets/images/tshirt-incrust.webp differ diff --git a/em2rp/lib/main.dart b/em2rp/lib/main.dart index cd58dc6..2bbfb63 100644 --- a/em2rp/lib/main.dart +++ b/em2rp/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:em2rp/views/calendar_page.dart'; import 'package:em2rp/views/login_page.dart'; import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; @@ -19,6 +20,13 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( title: 'EM2 ERP', + initialRoute: '/', + routes: { + '/': (context) => + LoginPage(), // Remplacez HomePage par votre page d'accueil + '/calendar': (context) => + CalendarPage(), // Ajoutez cette ligne pour la route /calendar + }, theme: ThemeData( primarySwatch: Colors.red, primaryColor: AppColors.noir, @@ -32,7 +40,7 @@ class MyApp extends StatelessWidget { focusedBorder: OutlineInputBorder( // Bordure lorsqu'il est focus borderSide: - BorderSide(color: AppColors.rouge), // Couleur rouge quand focus + BorderSide(color: AppColors.noir), // Couleur rouge quand focus ), enabledBorder: OutlineInputBorder( // Bordure par défaut (non focus) @@ -55,7 +63,6 @@ class MyApp extends StatelessWidget { ), ), ), - home: const LoginPage(), ); } } diff --git a/em2rp/lib/views/calendar_page.dart b/em2rp/lib/views/calendar_page.dart new file mode 100644 index 0000000..580a4ff --- /dev/null +++ b/em2rp/lib/views/calendar_page.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:firebase_auth/firebase_auth.dart'; + +class CalendarPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + final User? user = FirebaseAuth.instance.currentUser; + final String email = user?.email ?? 'Guest'; + + return Scaffold( + appBar: AppBar( + title: Text('Calendar Page'), + ), + body: Center( + child: Text( + 'Hello $email, welcome to EM2RP', + style: TextStyle(fontSize: 20), + ), + ), + ); + } +} diff --git a/em2rp/lib/views/login_page.dart b/em2rp/lib/views/login_page.dart index 94c47ca..4f2a70e 100644 --- a/em2rp/lib/views/login_page.dart +++ b/em2rp/lib/views/login_page.dart @@ -1,5 +1,13 @@ -import 'package:flutter/material.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'; +import 'package:em2rp/views/widgets/image/big_image_left.dart'; +import 'package:em2rp/views/widgets/auth/login_button.dart'; +import 'package:em2rp/views/widgets/image/em2_logo_n_sur_b.dart'; +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'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @@ -13,6 +21,9 @@ class _LoginPageState extends State { final _passwordController = TextEditingController(); String _errorMessage = ''; bool _isLoading = false; + bool _obscurePassword = true; + bool _highlightPasswordField = false; + bool _highlightEmailField = false; @override void dispose() { @@ -25,82 +36,118 @@ class _LoginPageState extends State { setState(() { _errorMessage = ''; _isLoading = true; + _highlightPasswordField = false; + _highlightEmailField = false; }); try { await FirebaseAuth.instance.signInWithEmailAndPassword( email: _emailController.text.trim(), password: _passwordController.text, ); - // Navigation vers la page principale après connexion réussie (à implémenter) - // Par exemple : Navigator.of(context).pushReplacementNamed('/home'); + Navigator.of(context).pushReplacementNamed('/calendar'); + print('Connexion réussie!'); } on FirebaseAuthException catch (e) { setState(() { - _errorMessage = "Erreur de connexion: ${e.message}"; _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('Erreur de connexion: ${e.code} - ${e.message}'); } } + void _togglePasswordVisibility() { + setState(() { + _obscurePassword = !_obscurePassword; + }); + } + + void _forgotPassword() { + // TODO: Implémenter la logique de mot de passe oublié + print("Mot de passe oublié cliqué !"); + } + @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('Connexion'), - ), - body: Center( - child: SingleChildScrollView( - // Pour éviter le débordement sur les petits écrans - padding: const EdgeInsets.all(20.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.asset( - 'assets/EM2_NsurB.jpg', - height: 150, - ), - const SizedBox(height: 20), - const Text( - 'Bienvenue !', - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 20), - TextField( - controller: _emailController, - keyboardType: TextInputType.emailAddress, - decoration: const InputDecoration( - labelText: 'Email', - border: OutlineInputBorder(), - ), - ), - const SizedBox(height: 10), - TextField( - controller: _passwordController, - obscureText: true, - decoration: const InputDecoration( - labelText: 'Mot de passe', - border: OutlineInputBorder(), - ), - ), - const SizedBox(height: 20), - ElevatedButton( - onPressed: _isLoading - ? null - : _signInWithEmailAndPassword, // Désactiver pendant le chargement - child: _isLoading - ? const CircularProgressIndicator() // Indicateur de chargement - : const Text('Se connecter'), - ), - const SizedBox(height: 10), - const SizedBox(height: 10), - if (_errorMessage.isNotEmpty) - Text( - _errorMessage, - style: const TextStyle(color: Colors.red), - ), - ], - ), - ), + body: LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxWidth > 900) { + return _buildLargeScreenLayout(context); + } else { + return _buildSmallScreenLayout(context); + } + }, ), ); } + + Widget _buildLargeScreenLayout(BuildContext context) { + return Row( + children: [ + Expanded( + flex: 6, + child: + const ImageSectionWidget(), // Utilise le composant ImageSection + ), + Expanded( + flex: 4, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 50.0, vertical: 30.0), + child: _buildLoginForm(context), + ), + ), + ], + ); + } + + Widget _buildSmallScreenLayout(BuildContext context) { + return Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(20.0), + child: _buildLoginForm(context), + ), + ); + } + + Widget _buildLoginForm(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const LogoWidget(), // Utilise le widget LogoWidget + const SizedBox(height: 30), + const WelcomeTextWidget(), // Utilise le widget WelcomeTextWidget + const SizedBox(height: 40), + EmailTextFieldWidget( + emailController: _emailController, + highlightEmailField: _highlightEmailField, + ), + const SizedBox(height: 20), + PasswordTextFieldWidget( + passwordController: _passwordController, + obscurePassword: _obscurePassword, + highlightPasswordField: _highlightPasswordField, + onTogglePasswordVisibility: _togglePasswordVisibility, + ), + ForgotPasswordButtonWidget(onPressed: _forgotPassword), + const SizedBox(height: 30), + LoginButtonWidget( + isLoading: _isLoading, + onPressed: _signInWithEmailAndPassword, + ), + const SizedBox(height: 20), + ErrorMessageWidget(errorMessage: _errorMessage), + ], + ); + } } diff --git a/em2rp/lib/views/widgets/auth/forgot_password_button.dart b/em2rp/lib/views/widgets/auth/forgot_password_button.dart new file mode 100644 index 0000000..ad3a46e --- /dev/null +++ b/em2rp/lib/views/widgets/auth/forgot_password_button.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class ForgotPasswordButtonWidget extends StatelessWidget { + final VoidCallback onPressed; + + const ForgotPasswordButtonWidget({super.key, required this.onPressed}); + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.centerRight, + child: TextButton( + onPressed: onPressed, + child: const Text( + 'Mot de passe oublié ?', + style: TextStyle(color: Colors.grey), + ), + ), + ); + } +} diff --git a/em2rp/lib/views/widgets/auth/login_button.dart b/em2rp/lib/views/widgets/auth/login_button.dart new file mode 100644 index 0000000..c6d846b --- /dev/null +++ b/em2rp/lib/views/widgets/auth/login_button.dart @@ -0,0 +1,29 @@ +import 'package:em2rp/utils/colors.dart'; +import 'package:flutter/material.dart'; + +class LoginButtonWidget extends StatelessWidget { + final bool isLoading; + final VoidCallback onPressed; + + const LoginButtonWidget({ + super.key, + required this.isLoading, + required this.onPressed, + }); + + @override + Widget build(BuildContext context) { + return ElevatedButton( + onPressed: isLoading ? null : onPressed, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 15), + textStyle: const TextStyle(fontSize: 18), + ), + child: isLoading + ? const CircularProgressIndicator( + color: AppColors.blanc, + ) + : const Text('Se connecter'), + ); + } +} diff --git a/em2rp/lib/views/widgets/auth/mail_textfield.dart b/em2rp/lib/views/widgets/auth/mail_textfield.dart new file mode 100644 index 0000000..35386e5 --- /dev/null +++ b/em2rp/lib/views/widgets/auth/mail_textfield.dart @@ -0,0 +1,31 @@ +import 'package:em2rp/utils/colors.dart'; +import 'package:flutter/material.dart'; + +class EmailTextFieldWidget extends StatelessWidget { + final TextEditingController emailController; + final bool highlightEmailField; + + const EmailTextFieldWidget({ + super.key, + required this.emailController, + required this.highlightEmailField, + }); + + @override + Widget build(BuildContext context) { + return TextField( + controller: emailController, + keyboardType: TextInputType.emailAddress, + decoration: InputDecoration( + labelText: 'Email', + border: OutlineInputBorder( + borderSide: highlightEmailField + ? const BorderSide(color: Colors.red) + : const BorderSide(), + ), + filled: true, + fillColor: AppColors.blanc, + ), + ); + } +} diff --git a/em2rp/lib/views/widgets/auth/password_textfield.dart b/em2rp/lib/views/widgets/auth/password_textfield.dart new file mode 100644 index 0000000..adf7349 --- /dev/null +++ b/em2rp/lib/views/widgets/auth/password_textfield.dart @@ -0,0 +1,43 @@ +import 'package:em2rp/utils/colors.dart'; +import 'package:flutter/material.dart'; +import 'package:em2rp/utils/colors.dart'; + +class PasswordTextFieldWidget extends StatelessWidget { + final TextEditingController passwordController; + final bool obscurePassword; + final bool highlightPasswordField; + final VoidCallback onTogglePasswordVisibility; + + const PasswordTextFieldWidget({ + super.key, + required this.passwordController, + required this.obscurePassword, + required this.highlightPasswordField, + required this.onTogglePasswordVisibility, + }); + + @override + Widget build(BuildContext context) { + return TextField( + controller: passwordController, + obscureText: obscurePassword, + decoration: InputDecoration( + labelText: 'Mot de passe', + border: OutlineInputBorder( + borderSide: highlightPasswordField + ? const BorderSide(color: Colors.red) + : const BorderSide(), + ), + filled: true, + fillColor: AppColors.blanc, + suffixIcon: IconButton( + icon: Icon( + obscurePassword ? Icons.visibility_off : Icons.visibility, + color: AppColors.gris, + ), + onPressed: onTogglePasswordVisibility, + ), + ), + ); + } +} diff --git a/em2rp/lib/views/widgets/auth/welcome_text.dart b/em2rp/lib/views/widgets/auth/welcome_text.dart new file mode 100644 index 0000000..c2fc3f5 --- /dev/null +++ b/em2rp/lib/views/widgets/auth/welcome_text.dart @@ -0,0 +1,16 @@ +import 'package:em2rp/utils/colors.dart'; +import 'package:flutter/material.dart'; + +class WelcomeTextWidget extends StatelessWidget { + const WelcomeTextWidget({super.key}); + + @override + Widget build(BuildContext context) { + return Text( + 'Bienvenue !', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 28, fontWeight: FontWeight.bold, color: AppColors.noir), + ); + } +} diff --git a/em2rp/lib/views/widgets/error_message.dart b/em2rp/lib/views/widgets/error_message.dart new file mode 100644 index 0000000..af49064 --- /dev/null +++ b/em2rp/lib/views/widgets/error_message.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class ErrorMessageWidget extends StatelessWidget { + final String errorMessage; + + const ErrorMessageWidget({super.key, required this.errorMessage}); + + @override + Widget build(BuildContext context) { + return Visibility( + visible: errorMessage.isNotEmpty, + child: Padding( + padding: const EdgeInsets.only(top: 20.0), + child: Text( + errorMessage, + textAlign: TextAlign.center, + style: const TextStyle(color: Colors.red), + ), + ), + ); + } +} diff --git a/em2rp/lib/views/widgets/image/big_image_left.dart b/em2rp/lib/views/widgets/image/big_image_left.dart new file mode 100644 index 0000000..eaf634d --- /dev/null +++ b/em2rp/lib/views/widgets/image/big_image_left.dart @@ -0,0 +1,23 @@ +import 'package:em2rp/utils/colors.dart'; +import 'package:flutter/material.dart'; + +class ImageSectionWidget extends StatelessWidget { + const ImageSectionWidget({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + color: AppColors.gris.withOpacity(0.1), + child: ClipRRect( + borderRadius: BorderRadius.zero, + child: Image.asset( + 'assets/images/tshirt-incrust.webp', + fit: BoxFit.cover, + height: double.infinity, + width: double.infinity, + alignment: Alignment.centerLeft, + ), + ), + ); + } +} diff --git a/em2rp/lib/views/widgets/image/em2_logo_n_sur_b.dart b/em2rp/lib/views/widgets/image/em2_logo_n_sur_b.dart new file mode 100644 index 0000000..841b253 --- /dev/null +++ b/em2rp/lib/views/widgets/image/em2_logo_n_sur_b.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +class LogoWidget extends StatelessWidget { + const LogoWidget({super.key}); + + @override + Widget build(BuildContext context) { + return Image.asset( + 'assets/EM2_NsurB.jpg', + height: 100, + ); + } +} diff --git a/em2rp/pubspec.yaml b/em2rp/pubspec.yaml index 27bd93f..0bee70c 100644 --- a/em2rp/pubspec.yaml +++ b/em2rp/pubspec.yaml @@ -23,3 +23,4 @@ flutter: uses-material-design: true assets: - assets/ + - assets/images/