import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../../core/auth/bloc/temp_auth_bloc.dart'; import '../../../../core/auth/bloc/auth_event.dart'; import '../../../../core/auth/models/auth_state.dart'; import '../../../../core/auth/models/login_request.dart'; import '../../../../shared/theme/app_theme.dart'; import '../widgets/login_header.dart'; import '../widgets/login_footer.dart'; /// Écran de connexion temporaire simplifié class TempLoginPage extends StatefulWidget { const TempLoginPage({super.key}); @override State createState() => _TempLoginPageState(); } class _TempLoginPageState extends State with TickerProviderStateMixin { late AnimationController _animationController; late AnimationController _shakeController; late Animation _fadeAnimation; late Animation _slideAnimation; late Animation _shakeAnimation; final _formKey = GlobalKey(); final _emailController = TextEditingController(text: 'admin@unionflow.dev'); final _passwordController = TextEditingController(text: 'admin123'); bool _obscurePassword = true; bool _rememberMe = false; bool _isLoading = false; @override void initState() { super.initState(); _setupAnimations(); _startEntryAnimation(); } void _setupAnimations() { _animationController = AnimationController( duration: const Duration(milliseconds: 1200), vsync: this, ); _shakeController = AnimationController( duration: const Duration(milliseconds: 600), vsync: this, ); _fadeAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _animationController, curve: const Interval(0.0, 0.6, curve: Curves.easeOut), )); _slideAnimation = Tween( begin: 50.0, end: 0.0, ).animate(CurvedAnimation( parent: _animationController, curve: const Interval(0.2, 0.8, curve: Curves.easeOut), )); _shakeAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _shakeController, curve: Curves.elasticInOut, )); } void _startEntryAnimation() { Future.delayed(const Duration(milliseconds: 100), () { if (mounted) { _animationController.forward(); } }); } @override void dispose() { _animationController.dispose(); _shakeController.dispose(); _emailController.dispose(); _passwordController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppTheme.backgroundLight, body: BlocListener( listener: _handleAuthStateChange, child: SafeArea( child: AnimatedBuilder( animation: _animationController, builder: (context, child) { return FadeTransition( opacity: _fadeAnimation, child: Transform.translate( offset: Offset(0, _slideAnimation.value), child: _buildLoginContent(), ), ); }, ), ), ), ); } Widget _buildLoginContent() { return SingleChildScrollView( padding: const EdgeInsets.all(24), child: Column( children: [ const SizedBox(height: 60), // Header avec logo et titre const LoginHeader(), const SizedBox(height: 60), // Formulaire de connexion AnimatedBuilder( animation: _shakeAnimation, builder: (context, child) { return Transform.translate( offset: Offset( _shakeAnimation.value * 10 * (1 - _shakeAnimation.value) * (1 - _shakeAnimation.value), 0, ), child: _buildLoginForm(), ); }, ), const SizedBox(height: 40), // Footer avec liens et informations const LoginFooter(), const SizedBox(height: 20), ], ), ); } Widget _buildLoginForm() { return Form( key: _formKey, child: Column( children: [ // Champ email _buildEmailField(), const SizedBox(height: 20), // Champ mot de passe _buildPasswordField(), const SizedBox(height: 16), // Options _buildOptionsRow(), const SizedBox(height: 32), // Bouton de connexion _buildLoginButton(), ], ), ); } Widget _buildEmailField() { return TextFormField( controller: _emailController, keyboardType: TextInputType.emailAddress, textInputAction: TextInputAction.next, enabled: !_isLoading, decoration: InputDecoration( labelText: 'Adresse email', hintText: 'votre.email@exemple.com', prefixIcon: Icon( Icons.email_outlined, color: AppTheme.primaryColor, ), filled: true, fillColor: Colors.white, border: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none, ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide( color: AppTheme.primaryColor, width: 2, ), ), contentPadding: const EdgeInsets.symmetric( horizontal: 20, vertical: 16, ), ), validator: (value) { if (value == null || value.isEmpty) { return 'Veuillez saisir votre email'; } if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) { return 'Format d\'email invalide'; } return null; }, ); } Widget _buildPasswordField() { return TextFormField( controller: _passwordController, obscureText: _obscurePassword, textInputAction: TextInputAction.done, enabled: !_isLoading, onFieldSubmitted: (_) => _handleLogin(), decoration: InputDecoration( labelText: 'Mot de passe', hintText: 'Saisissez votre mot de passe', prefixIcon: Icon( Icons.lock_outlined, color: AppTheme.primaryColor, ), suffixIcon: IconButton( onPressed: () { setState(() { _obscurePassword = !_obscurePassword; }); HapticFeedback.selectionClick(); }, icon: Icon( _obscurePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined, color: AppTheme.primaryColor, ), ), filled: true, fillColor: Colors.white, border: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none, ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide( color: AppTheme.primaryColor, width: 2, ), ), contentPadding: const EdgeInsets.symmetric( horizontal: 20, vertical: 16, ), ), validator: (value) { if (value == null || value.isEmpty) { return 'Veuillez saisir votre mot de passe'; } if (value.length < 6) { return 'Le mot de passe doit contenir au moins 6 caractères'; } return null; }, ); } Widget _buildOptionsRow() { return Row( children: [ // Se souvenir de moi Expanded( child: GestureDetector( onTap: () { setState(() { _rememberMe = !_rememberMe; }); HapticFeedback.selectionClick(); }, child: Row( children: [ Container( width: 20, height: 20, decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all( color: _rememberMe ? AppTheme.primaryColor : AppTheme.textSecondary, width: 2, ), color: _rememberMe ? AppTheme.primaryColor : Colors.transparent, ), child: _rememberMe ? const Icon( Icons.check, size: 14, color: Colors.white, ) : null, ), const SizedBox(width: 8), Text( 'Se souvenir de moi', style: TextStyle( fontSize: 14, color: AppTheme.textSecondary, fontWeight: FontWeight.w500, ), ), ], ), ), ), // Compte de test Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), decoration: BoxDecoration( color: AppTheme.infoColor.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Text( 'Compte de test', style: TextStyle( fontSize: 12, color: AppTheme.infoColor, fontWeight: FontWeight.w600, ), ), ), ], ); } Widget _buildLoginButton() { return SizedBox( width: double.infinity, height: 56, child: ElevatedButton( onPressed: _isLoading ? null : _handleLogin, style: ElevatedButton.styleFrom( backgroundColor: AppTheme.primaryColor, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), elevation: 4, ), child: _isLoading ? const SizedBox( width: 24, height: 24, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.login, size: 20), const SizedBox(width: 8), const Text( 'Se connecter', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), ], ), ), ); } void _handleAuthStateChange(BuildContext context, AuthState state) { setState(() { _isLoading = state.isLoading; }); if (state.status == AuthStatus.authenticated) { _showSuccessMessage(); HapticFeedback.heavyImpact(); } else if (state.status == AuthStatus.error) { _handleLoginError(state.errorMessage ?? 'Erreur inconnue'); } } void _handleLogin() { if (!_formKey.currentState!.validate()) { _triggerShakeAnimation(); HapticFeedback.mediumImpact(); return; } final email = _emailController.text.trim(); final password = _passwordController.text; final loginRequest = LoginRequest( email: email, password: password, rememberMe: _rememberMe, ); context.read().add(AuthLoginRequested(loginRequest)); HapticFeedback.lightImpact(); } void _handleLoginError(String errorMessage) { _showErrorMessage(errorMessage); _triggerShakeAnimation(); HapticFeedback.mediumImpact(); } void _triggerShakeAnimation() { _shakeController.reset(); _shakeController.forward(); } void _showSuccessMessage() { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Row( children: [ Icon(Icons.check_circle, color: Colors.white), SizedBox(width: 12), Text('Connexion réussie !'), ], ), backgroundColor: AppTheme.successColor, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ); } void _showErrorMessage(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Row( children: [ const Icon(Icons.error_outline, color: Colors.white), const SizedBox(width: 12), Expanded(child: Text(message)), ], ), backgroundColor: AppTheme.errorColor, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ); } }