import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:youmazgestion/Services/stock_managementDatabase.dart'; import 'package:youmazgestion/Services/PermissionCacheService.dart'; // Nouveau import import 'package:youmazgestion/Views/Dashboard.dart'; import 'package:youmazgestion/Views/mobilepage.dart'; import 'package:youmazgestion/Views/particles.dart' show ParticleBackground; import 'package:youmazgestion/accueil.dart'; import '../Models/users.dart'; import '../controller/userController.dart'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @override _LoginPageState createState() => _LoginPageState(); } class _LoginPageState extends State { late TextEditingController _usernameController; late TextEditingController _passwordController; final UserController userController = Get.put(UserController()); final PermissionCacheService _cacheService = PermissionCacheService.instance; // Nouveau bool _isErrorVisible = false; bool _isLoading = false; String _errorMessage = 'Nom d\'utilisateur ou mot de passe invalide'; String _loadingMessage = 'Connexion en cours...'; // Nouveau @override void initState() { super.initState(); _usernameController = TextEditingController(); _passwordController = TextEditingController(); checkUserCount(); } void checkUserCount() async { try { final userCount = await AppDatabase.instance.getUserCount(); print('Nombre d\'utilisateurs trouvés: $userCount'); } catch (error) { print('Erreur lors de la vérification du nombre d\'utilisateurs: $error'); setState(() { _errorMessage = 'Erreur de connexion à la base de données'; _isErrorVisible = true; }); } } @override void dispose() { _usernameController.dispose(); _passwordController.dispose(); super.dispose(); } // /// ✅ OPTIMISÉ: Sauvegarde avec préchargement des permissions // Future saveUserData(Users user, String role, int userId) async { // try { // userController.setUserWithCredentials(user, role, userId); // if (user.pointDeVenteId != null) { // await userController.loadPointDeVenteDesignation(); // } // print('✅ Utilisateur sauvegardé avec point de vente: ${userController.pointDeVenteDesignation}'); // } catch (error) { // print('❌ Erreur lors de la sauvegarde: $error'); // throw Exception('Erreur lors de la sauvegarde des données utilisateur'); // } // } /// ✅ NOUVEAU: Préchargement des permissions en arrière-plan Future _preloadUserPermissions(String username) async { try { setState(() { _loadingMessage = 'Préparation du menu...'; }); // Lancer le préchargement en parallèle avec les autres tâches final permissionFuture = _cacheService.preloadUserData(username); // Attendre maximum 2 secondes pour les permissions await Future.any([ permissionFuture, Future.delayed(const Duration(seconds: 2)) ]); print('✅ Permissions préparées (ou timeout)'); } catch (e) { print('⚠️ Erreur préchargement permissions: $e'); // Continuer même en cas d'erreur } } /// ✅ OPTIMISÉ: Connexion avec préchargement parallèle void _login() async { if (_isLoading) return; final String username = _usernameController.text.trim(); final String password = _passwordController.text.trim(); if (username.isEmpty || password.isEmpty) { setState(() { _errorMessage = 'Veuillez saisir le nom d\'utilisateur et le mot de passe'; _isErrorVisible = true; }); return; } setState(() { _isLoading = true; _isErrorVisible = false; _loadingMessage = 'Connexion...'; }); try { print('🔐 Tentative de connexion pour: $username'); final dbInstance = AppDatabase.instance; // 1. Vérification rapide de la base setState(() { _loadingMessage = 'Vérification...'; }); try { final userCount = await dbInstance.getUserCount(); print('✅ Base accessible, $userCount utilisateurs'); } catch (dbError) { throw Exception('Base de données inaccessible: $dbError'); } // 2. Vérification des identifiants setState(() { _loadingMessage = 'Authentification...'; }); bool isValidUser = await dbInstance.verifyUser(username, password); if (isValidUser) { setState(() { _loadingMessage = 'Chargement du profil...'; }); // 3. Récupération parallèle des données final futures = await Future.wait([ dbInstance.getUser(username), dbInstance.getUserCredentials(username, password), ]); final user = futures[0] as Users; final userCredentials = futures[1] as Map?; if (userCredentials != null) { print('✅ Connexion réussie pour: ${user.username}'); print(' Rôle: ${userCredentials['role']}'); setState(() { _loadingMessage = 'Préparation...'; }); // 4. Sauvegarde des données utilisateur await saveUserData( user, userCredentials['role'] as String, userCredentials['id'] as int, ); // 5. Préchargement des permissions EN PARALLÈLE avec la navigation setState(() { _loadingMessage = 'Finalisation...'; }); // Lancer le préchargement en arrière-plan SANS attendre _cacheService.preloadUserDataAsync(username); // 6. Navigation immédiate if (mounted) { if (userCredentials['role'] == 'commercial' || userCredentials['role'] == 'caisse') { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => const MainLayout()), ); } else { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => DashboardPage()), ); } } // Les permissions se chargeront en arrière-plan après la navigation print('🚀 Navigation immédiate, permissions en arrière-plan'); } else { throw Exception('Erreur lors de la récupération des credentials'); } } else { setState(() { _errorMessage = 'Nom d\'utilisateur ou mot de passe invalide'; _isErrorVisible = true; }); } } catch (error) { setState(() { _errorMessage = 'Erreur de connexion: ${error.toString()}'; _isErrorVisible = true; }); } finally { if (mounted) { setState(() { _isLoading = false; _loadingMessage = 'Connexion en cours...'; }); } } } /// ✅ OPTIMISÉ: Sauvegarde rapide Future saveUserData(Users user, String role, int userId) async { try { userController.setUserWithCredentials(user, role, userId); // Charger le point de vente en parallèle si nécessaire if (user.pointDeVenteId != null) { // Ne pas attendre, charger en arrière-plan unawaited(userController.loadPointDeVenteDesignation()); } print('✅ Utilisateur sauvegardé rapidement'); } catch (error) { print('❌ Erreur lors de la sauvegarde: $error'); throw Exception('Erreur lors de la sauvegarde des données utilisateur'); } } @override Widget build(BuildContext context) { final Color primaryBlue = const Color(0xFF0033A1); final Color accentRed = const Color(0xFFD70000); final Color secondaryBlue = const Color(0xFF1976D2); final Color primaryColor = primaryBlue; final Color accentColor = secondaryBlue; final Color cardColor = Colors.white; return Scaffold( backgroundColor: primaryColor, body: ParticleBackground( child: Center( child: SingleChildScrollView( child: Container( width: MediaQuery.of(context).size.width < 500 ? double.infinity : 400, padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 32.0), decoration: BoxDecoration( color: cardColor.withOpacity(0.98), borderRadius: BorderRadius.circular(30.0), boxShadow: [ BoxShadow( color: primaryColor.withOpacity(0.2), blurRadius: 16, spreadRadius: 4, offset: const Offset(0, 8), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Header Center( child: Column( children: [ CircleAvatar( radius: 38, backgroundColor: accentColor.withOpacity(0.15), child: Icon( Icons.lock_outline, color: accentColor, size: 50, ), ), const SizedBox(height: 14), Text( 'GUYCOM', style: TextStyle( color: primaryColor, fontWeight: FontWeight.bold, fontSize: 28, ), ), const SizedBox(height: 4), Text( 'Connectez-vous à votre compte', style: TextStyle( color: primaryColor.withOpacity(.8), fontSize: 16, ), ), ], ), ), const SizedBox(height: 24), // Username Field TextField( controller: _usernameController, enabled: !_isLoading, decoration: InputDecoration( labelText: 'Nom d\'utilisateur', labelStyle: TextStyle( color: primaryColor.withOpacity(0.7), ), prefixIcon: Icon(Icons.person, color: accentColor), filled: true, fillColor: accentColor.withOpacity(0.045), border: OutlineInputBorder( borderRadius: BorderRadius.circular(30.0), borderSide: BorderSide(color: accentColor, width: 2), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(30.0), borderSide: BorderSide(color: accentColor, width: 2), ), ), ), const SizedBox(height: 18.0), // Password Field TextField( controller: _passwordController, enabled: !_isLoading, obscureText: true, decoration: InputDecoration( labelText: 'Mot de passe', labelStyle: TextStyle( color: primaryColor.withOpacity(0.7), ), prefixIcon: Icon(Icons.lock, color: accentColor), filled: true, fillColor: accentColor.withOpacity(0.045), border: OutlineInputBorder( borderRadius: BorderRadius.circular(30.0), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(30.0), borderSide: BorderSide(color: accentColor, width: 2), ), ), onSubmitted: (_) => _login(), ), if (_isLoading) ...[ const SizedBox(height: 16.0), Column( children: [ // Barre de progression animée Container( height: 4, decoration: BoxDecoration( borderRadius: BorderRadius.circular(2), color: accentColor.withOpacity(0.2), ), child: LayoutBuilder( builder: (context, constraints) { return AnimatedContainer( duration: const Duration(milliseconds: 300), width: constraints.maxWidth * 0.7, decoration: BoxDecoration( borderRadius: BorderRadius.circular(2), gradient: LinearGradient( colors: [accentColor, accentColor.withOpacity(0.7)], ), ), ); }, ), ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( height: 16, width: 16, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(accentColor), ), ), const SizedBox(width: 12), Expanded( child: Text( _loadingMessage, style: TextStyle( color: accentColor, fontSize: 14, fontWeight: FontWeight.w500, ), textAlign: TextAlign.left, ), ), ], ), const SizedBox(height: 4), Text( "Le menu se chargera en arrière-plan", style: TextStyle( color: Colors.grey.shade600, fontSize: 12, ), textAlign: TextAlign.center, ), ], ), ], // Error Message if (_isErrorVisible) ...[ const SizedBox(height: 12.0), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.red.withOpacity(0.1), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.red.withOpacity(0.3)), ), child: Row( children: [ Icon(Icons.error_outline, color: Colors.red, size: 20), const SizedBox(width: 8), Expanded( child: Text( _errorMessage, style: const TextStyle( color: Colors.red, fontSize: 14, fontWeight: FontWeight.w500, ), ), ), ], ), ), ], const SizedBox(height: 26.0), // Login Button ElevatedButton( onPressed: _isLoading ? null : _login, style: ElevatedButton.styleFrom( backgroundColor: accentColor, disabledBackgroundColor: accentColor.withOpacity(0.3), foregroundColor: Colors.white, elevation: 7.0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30.0), ), minimumSize: const Size(double.infinity, 52), ), child: _isLoading ? Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox( height: 20, width: 20, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ), const SizedBox(width: 12), Text( 'Connexion...', style: const TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold, ), ), ], ) : const Text( 'Se connecter', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, letterSpacing: .4, ), ), ), // Debug Button (à enlever en production) if (_isErrorVisible && !_isLoading) ...[ const SizedBox(height: 8), TextButton( onPressed: () async { try { final count = await AppDatabase.instance.getUserCount(); final stats = _cacheService.getCacheStats(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'BDD: $count utilisateurs\n' 'Cache: ${stats['users_cached']} utilisateurs en cache', ), duration: const Duration(seconds: 3), ), ); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Erreur: $e')), ); } }, child: Text( 'Debug: Vérifier BDD & Cache', style: TextStyle( color: primaryColor.withOpacity(0.6), fontSize: 12, ), ), ), ], ], ), ), ), ), ), ); } }