import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:youmazgestion/Views/Dashboard.dart'; import 'package:youmazgestion/Views/DemandeTransfert.dart'; import 'package:youmazgestion/Views/HandleProduct.dart'; import 'package:youmazgestion/Views/RoleListPage.dart'; import 'package:youmazgestion/Views/commandManagement.dart'; import 'package:youmazgestion/Views/historique.dart'; import 'package:youmazgestion/Views/bilanMois.dart'; import 'package:youmazgestion/Views/gestionStock.dart'; import 'package:youmazgestion/Views/listUser.dart'; import 'package:youmazgestion/Views/loginPage.dart'; import 'package:youmazgestion/Views/newCommand.dart'; import 'package:youmazgestion/Views/registrationPage.dart'; import 'package:youmazgestion/accueil.dart'; import 'package:youmazgestion/controller/userController.dart'; import 'package:youmazgestion/Views/gestion_point_de_vente.dart'; // ✅ NOUVEAU: Imports pour les sorties personnelles import 'package:youmazgestion/Views/demande_sortie_personnelle_page.dart'; import 'package:youmazgestion/Views/approbation_sorties_page.dart'; import 'package:youmazgestion/Views/historique_sorties_personnelles_page.dart'; class CustomDrawer extends StatelessWidget { final UserController userController = Get.find(); Future clearUserData() async { final prefs = await SharedPreferences.getInstance(); await prefs.remove('username'); await prefs.remove('role'); await prefs.remove('user_id'); // ✅ IMPORTANT: Vider le cache de session userController.clearUserData(); } CustomDrawer({super.key}); @override Widget build(BuildContext context) { return Drawer( backgroundColor: Colors.white, child: GetBuilder( builder: (controller) { return ListView( padding: EdgeInsets.zero, children: [ // Header utilisateur _buildUserHeader(controller), // ✅ CORRIGÉ: Construction avec gestion des valeurs null ..._buildDrawerItemsFromSessionCache(), // Déconnexion const Divider(), _buildLogoutItem(), ], ); }, ), ); } /// ✅ CORRIGÉ: Construction avec validation robuste des données List _buildDrawerItemsFromSessionCache() { List drawerItems = []; // Vérifier si le cache est prêt if (!userController.isCacheReady) { return [ const Padding( padding: EdgeInsets.all(16.0), child: Column( children: [ SizedBox( height: 20, width: 20, child: CircularProgressIndicator(strokeWidth: 2), ), SizedBox(height: 8), Text( "Chargement du menu...", style: TextStyle(color: Colors.grey, fontSize: 12), ), ], ), ), ]; } // Récupérer les menus depuis le cache de session final rawUserMenus = userController.getUserMenus(); // 🛡️ VALIDATION: Filtrer les menus valides final validMenus = >[]; final invalidMenus = >[]; for (var menu in rawUserMenus) { // Vérifier que les champs essentiels ne sont pas null final name = menu['name']; final route = menu['route']; final id = menu['id']; if (name != null && route != null && route.toString().isNotEmpty) { validMenus.add({ 'id': id, 'name': name.toString(), 'route': route.toString(), }); } else { invalidMenus.add(menu); // print("⚠️ Menu invalide ignoré dans CustomDrawer: id=$id, name='$name', route='$route'"); } } // ✅ NOUVEAU: Ajouter le menu Demande Assignation pour Super Admin uniquement if (userController.role == 'Super Admin') { // Vérifier si le menu n'existe pas déjà final bool demandeAssignationExists = validMenus.any((menu) => menu['route'] == '/demande-assignation'); if (!demandeAssignationExists) { validMenus.add({ 'id': 'super_admin_demande_assignation', 'name': 'Demande Assignation', 'route': '/demande-assignation', }); // print("✅ Menu 'Demande Assignation' ajouté pour Super Admin"); } } // ✅ NOUVEAU: Ajouter les menus de sorties personnelles _addSortiesPersonnellesMenus(validMenus); // Afficher les statistiques de validation if (invalidMenus.isNotEmpty) { // print( // "📊 CustomDrawer: ${validMenus.length} menus valides, ${invalidMenus.length} invalides"); } if (validMenus.isEmpty) { return [ const Padding( padding: EdgeInsets.all(16.0), child: Text( "Aucun menu accessible", textAlign: TextAlign.center, style: TextStyle(color: Colors.grey), ), ), ]; } // 🔧 DÉDUPLICATION: Éliminer les doublons par route final Map> uniqueMenus = {}; for (var menu in validMenus) { final route = menu['route'] as String; uniqueMenus[route] = menu; } final deduplicatedMenus = uniqueMenus.values.toList(); if (deduplicatedMenus.length != validMenus.length) { // print( // "🔧 CustomDrawer: ${validMenus.length - deduplicatedMenus.length} doublons supprimés"); } // Organiser les menus par catégories final Map>> categorizedMenus = { 'GESTION UTILISATEURS': [], 'GESTION PRODUITS': [], 'GESTION COMMANDES': [], 'GESTION STOCK': [], // ✅ NOUVEAU: Catégorie pour les sorties personnelles 'RAPPORTS': [], 'ADMINISTRATION': [], 'SUPER ADMIN': [], // ✅ NOUVEAU: Catégorie spéciale pour Super Admin }; // Accueil toujours en premier final accueilMenu = deduplicatedMenus .where((menu) => menu['route'] == '/accueil') .firstOrNull; if (accueilMenu != null) { drawerItems.add(_buildDrawerItemFromMenu(accueilMenu)); } // Catégoriser les autres menus avec validation supplémentaire for (var menu in deduplicatedMenus) { final route = menu['route'] as String; // ✅ Validation supplémentaire avant categorisation if (route.isEmpty) { // print("⚠️ Route vide ignorée: ${menu['name']}"); continue; } switch (route) { case '/accueil': // Déjà traité break; case '/ajouter-utilisateur': case '/modifier-utilisateur': case '/pointage': categorizedMenus['GESTION UTILISATEURS']!.add(menu); break; case '/ajouter-produit': categorizedMenus['GESTION PRODUITS']!.add(menu); break; case '/nouvelle-commande': case '/gerer-commandes': categorizedMenus['GESTION COMMANDES']!.add(menu); break; case '/gestion-stock': case '/demande-sortie-personnelle': case '/historique-sorties-personnelles': categorizedMenus['GESTION STOCK']!.add(menu); break; case '/bilan': case '/historique': categorizedMenus['RAPPORTS']!.add(menu); break; case '/gerer-roles': case '/points-de-vente': categorizedMenus['ADMINISTRATION']!.add(menu); break; case '/demande-assignation': case '/approbation-sorties': // ✅ NOUVEAU: Placer dans la catégorie SUPER ADMIN categorizedMenus['SUPER ADMIN']!.add(menu); break; default: // Menu non catégorisé // print("⚠️ Menu non catégorisé: $route"); break; } } // Ajouter les catégories avec leurs menus categorizedMenus.forEach((categoryName, menus) { if (menus.isNotEmpty) { // ✅ Afficher la catégorie SUPER ADMIN seulement pour les Super Admin if (categoryName == 'SUPER ADMIN' && userController.role != 'Super Admin') { return; // Skip cette catégorie } drawerItems.add(_buildCategoryHeader(categoryName)); for (var menu in menus) { drawerItems.add(_buildDrawerItemFromMenu(menu)); } } }); return drawerItems; } /// ✅ NOUVEAU: Méthode pour ajouter les menus de sorties personnelles void _addSortiesPersonnellesMenus(List> validMenus) { // Menu Demande Sortie Personnelle - Accessible à tous les utilisateurs connectés final bool demandeSortieExists = validMenus .any((menu) => menu['route'] == '/demande-sortie-personnelle'); if (!demandeSortieExists) { validMenus.add({ 'id': 'demande_sortie_personnelle', 'name': 'Demande sortie personnelle', 'route': '/demande-sortie-personnelle', }); // print( // "✅ Menu 'Demande sortie personnelle' ajouté pour tous les utilisateurs"); } // Menu Historique Sorties Personnelles - Accessible à tous les utilisateurs connectés final bool historiqueSortiesExists = validMenus .any((menu) => menu['route'] == '/historique-sorties-personnelles'); if (!historiqueSortiesExists) { validMenus.add({ 'id': 'historique_sorties_personnelles', 'name': 'Historique sorties personnelles', 'route': '/historique-sorties-personnelles', }); // print( // "✅ Menu 'Historique sorties personnelles' ajouté pour tous les utilisateurs"); } // Menu Approbation Sorties - Accessible SEULEMENT aux Super Admin if (userController.role == 'Super Admin') { final bool approbationSortiesExists = validMenus.any((menu) => menu['route'] == '/approbation-sorties'); if (!approbationSortiesExists) { validMenus.add({ 'id': 'approbation_sorties', 'name': 'Approuver sorties', 'route': '/approbation-sorties', }); // print("✅ Menu 'Approuver sorties' ajouté pour Super Admin"); } } } /// ✅ CORRIGÉ: Construction d'un item de menu avec validation Widget _buildDrawerItemFromMenu(Map menu) { // 🛡️ VALIDATION: Vérification des types avec gestion des null final nameObj = menu['name']; final routeObj = menu['route']; if (nameObj == null || routeObj == null) { // print( // "⚠️ Menu invalide dans _buildDrawerItemFromMenu: name=$nameObj, route=$routeObj"); return const SizedBox.shrink(); } final String name = nameObj.toString(); final String route = routeObj.toString(); if (name.isEmpty || route.isEmpty) { // print("⚠️ Menu avec valeurs vides: name='$name', route='$route'"); return const SizedBox.shrink(); } // Mapping des routes vers les widgets et icônes final Map> routeMapping = { '/accueil': { 'icon': Icons.home, 'color': Colors.blue, 'widget': DashboardPage(), }, '/ajouter-utilisateur': { 'icon': Icons.person_add, 'color': Colors.green, 'widget': const RegistrationPage(), }, '/modifier-utilisateur': { 'icon': Icons.supervised_user_circle, 'color': const Color.fromARGB(255, 4, 54, 95), 'widget': const ListUserPage(), }, '/pointage': { 'icon': Icons.timer, 'color': const Color.fromARGB(255, 4, 54, 95), 'widget': null, // TODO: Implémenter }, '/ajouter-produit': { 'icon': Icons.inventory, 'color': Colors.indigoAccent, 'widget': const ProductManagementPage(), }, '/gestion-stock': { 'icon': Icons.storage, 'color': Colors.blueAccent, 'widget': const GestionStockPage(), }, '/nouvelle-commande': { 'icon': Icons.add_shopping_cart, 'color': Colors.orange, 'widget': const NouvelleCommandePage(), }, '/gerer-commandes': { 'icon': Icons.list_alt, 'color': Colors.deepPurple, 'widget': const GestionCommandesPage(), }, '/bilan': { 'icon': Icons.bar_chart, 'color': Colors.teal, 'widget': DashboardPage(), }, '/historique': { 'icon': Icons.history, 'color': Colors.blue, 'widget': const HistoriquePage(), }, '/gerer-roles': { 'icon': Icons.admin_panel_settings, 'color': Colors.redAccent, 'widget': const RoleListPage(), }, '/points-de-vente': { 'icon': Icons.store, 'color': Colors.blueGrey, 'widget': const AjoutPointDeVentePage(), }, '/demande-assignation': { 'icon': Icons.assignment_turned_in, 'color': Colors.deepOrange, 'widget': const GestionTransfertsPage(), }, // ✅ NOUVEAU: Routes pour les sorties personnelles '/demande-sortie-personnelle': { 'icon': Icons.person_remove, 'color': Colors.teal, 'widget': const DemandeSortiePersonnellePage(), }, '/approbation-sorties': { 'icon': Icons.approval, 'color': Colors.red, 'widget': const ApprobationSortiesPage(), }, '/historique-sorties-personnelles': { 'icon': Icons.history_edu, 'color': Colors.indigo, 'widget': const HistoriqueSortiesPersonnellesPage(), }, }; final routeData = routeMapping[route]; if (routeData == null) { // print("⚠️ Route non reconnue: '$route' pour le menu '$name'"); return ListTile( leading: const Icon(Icons.help_outline, color: Colors.grey), title: Text(name), subtitle: Text("Route: $route", style: const TextStyle(fontSize: 10, color: Colors.grey)), onTap: () { Get.snackbar( "Route non configurée", "La route '$route' n'est pas encore configurée", snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.orange.shade100, ); }, ); } return ListTile( leading: Icon( routeData['icon'] as IconData, color: routeData['color'] as Color, ), title: Text(name), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ // ✅ NOUVEAU: Badge Super Admin pour les menus réservés if ((route == '/demande-assignation' || route == '/approbation-sorties') && userController.role == 'Super Admin') ...[ Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: Colors.red.shade600, borderRadius: BorderRadius.circular(8), ), child: const Text( 'ADMIN', style: TextStyle( color: Colors.white, fontSize: 8, fontWeight: FontWeight.bold, ), ), ), const SizedBox(width: 4), ], // ✅ NOUVEAU: Badge "Tous" pour les menus accessibles à tous if (route == '/demande-sortie-personnelle' || route == '/historique-sorties-personnelles') ...[ Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: Colors.green.shade600, borderRadius: BorderRadius.circular(8), ), child: const Text( 'TOUS', style: TextStyle( color: Colors.white, fontSize: 8, fontWeight: FontWeight.bold, ), ), ), const SizedBox(width: 4), ], const Icon(Icons.chevron_right, color: Colors.grey), ], ), onTap: () { final widget = routeData['widget']; if (widget != null) { Get.to(widget); } else { Get.snackbar( "Non implémenté", "Cette fonctionnalité sera bientôt disponible", snackPosition: SnackPosition.BOTTOM, ); } }, ); } /// Header de catégorie Widget _buildCategoryHeader(String categoryName) { return Padding( padding: const EdgeInsets.only(left: 20, top: 15, bottom: 5), child: Row( children: [ Text( categoryName, style: const TextStyle( color: Colors.grey, fontSize: 12, fontWeight: FontWeight.bold, ), ), // ✅ NOUVEAU: Icône spéciale pour les catégories importantes if (categoryName == 'SUPER ADMIN') ...[ const SizedBox(width: 8), Icon( Icons.security, size: 14, color: Colors.red.shade600, ), ], if (categoryName == 'GESTION STOCK') ...[ const SizedBox(width: 8), Icon( Icons.inventory_2, size: 14, color: Colors.teal.shade600, ), ], ], ), ); } /// Header utilisateur amélioré Widget _buildUserHeader(UserController controller) { return Container( padding: const EdgeInsets.only(top: 50, left: 20, bottom: 20), decoration: const BoxDecoration( gradient: LinearGradient( colors: [Color.fromARGB(255, 4, 54, 95), Colors.blue], begin: Alignment.topLeft, end: Alignment.bottomRight, ), ), child: Row( children: [ const CircleAvatar( radius: 30, backgroundImage: AssetImage("assets/youmaz2.png"), ), const SizedBox(width: 15), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( controller.name.isNotEmpty ? controller.fullName : 'Utilisateur', style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), Row( children: [ Text( controller.role.isNotEmpty ? controller.role : 'Aucun rôle', style: const TextStyle( color: Colors.white70, fontSize: 12, ), ), // ✅ NOUVEAU: Badge Super Admin dans le header if (controller.role == 'Super Admin') ...[ const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 2), decoration: BoxDecoration( color: Colors.red.shade600, borderRadius: BorderRadius.circular(8), ), child: const Text( 'SA', style: TextStyle( color: Colors.white, fontSize: 8, fontWeight: FontWeight.bold, ), ), ), ], ], ), if (controller.pointDeVenteDesignation.isNotEmpty) ...[ const SizedBox(height: 2), Text( controller.pointDeVenteDesignation, style: const TextStyle( color: Colors.white60, fontSize: 10, ), ), ], // ✅ Indicateur de statut du cache const SizedBox(height: 4), Row( children: [ Icon( controller.isCacheReady ? Icons.check_circle : Icons.hourglass_empty, color: controller.isCacheReady ? Colors.green : Colors.orange, size: 12, ), const SizedBox(width: 4), Text( controller.isCacheReady ? 'Menu prêt' : 'Chargement...', style: const TextStyle( color: Colors.white60, fontSize: 10, ), ), ], ), ], ), ), // ✅ Bouton de rafraîchissement pour les admins if (controller.role == 'Super Admin' || controller.role == 'Admin') ...[ IconButton( icon: const Icon(Icons.refresh, color: Colors.white70, size: 20), onPressed: () async { Get.snackbar( "Cache", "Rechargement des permissions...", snackPosition: SnackPosition.TOP, duration: const Duration(seconds: 1), ); await controller.refreshPermissions(); Get.back(); // Fermer le drawer Get.snackbar( "Cache", "Permissions rechargées avec succès", snackPosition: SnackPosition.TOP, backgroundColor: Colors.green, colorText: Colors.white, ); }, tooltip: "Recharger les permissions", ), ], // 🔧 Bouton de debug (à supprimer en production) if (controller.role == 'Super Admin') ...[ IconButton( icon: const Icon(Icons.bug_report, color: Colors.white70, size: 18), onPressed: () { // Debug des menus final menus = controller.getUserMenus(); String debugInfo = "MENUS DEBUG:\n"; for (var i = 0; i < menus.length; i++) { final menu = menus[i]; debugInfo += "[$i] ID:${menu['id']}, Name:'${menu['name']}', Route:'${menu['route']}'\n"; } // ✅ NOUVEAU: Ajouter info sur les menus sorties personnelles debugInfo += "\n--- MENUS SORTIES PERSONNELLES ---\n"; debugInfo += "Rôle actuel: ${controller.role}\n"; debugInfo += "Menu Demande Sortie: Accessible à tous\n"; debugInfo += "Menu Historique: Accessible à tous\n"; debugInfo += "Menu Approbation: ${controller.role == 'Super Admin' ? 'Accessible (Super Admin)' : 'Non accessible'}\n"; Get.dialog( AlertDialog( title: const Text("Debug Menus"), content: SingleChildScrollView( child: Text(debugInfo, style: const TextStyle(fontSize: 12)), ), actions: [ TextButton( onPressed: () => Get.back(), child: const Text("Fermer"), ), ], ), ); }, tooltip: "Debug menus", ), ], ], ), ); } /// Item de déconnexion Widget _buildLogoutItem() { return ListTile( leading: const Icon(Icons.logout, color: Colors.red), title: const Text("Déconnexion"), onTap: () { Get.dialog( AlertDialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), contentPadding: EdgeInsets.zero, content: Container( constraints: const BoxConstraints(maxWidth: 400), child: Column( mainAxisSize: MainAxisSize.min, children: [ // Header Container( width: double.infinity, padding: const EdgeInsets.all(24), child: Column( children: [ Icon( Icons.logout_rounded, size: 48, color: Colors.orange.shade600, ), const SizedBox(height: 16), const Text( "Déconnexion", style: TextStyle( fontSize: 20, fontWeight: FontWeight.w600, color: Colors.black87, ), ), const SizedBox(height: 12), const Text( "Êtes-vous sûr de vouloir vous déconnecter ?", textAlign: TextAlign.center, style: TextStyle( fontSize: 16, color: Colors.black87, height: 1.4, ), ), const SizedBox(height: 8), Text( "Vos permissions seront rechargées à la prochaine connexion.", textAlign: TextAlign.center, style: TextStyle( fontSize: 14, color: Colors.grey.shade600, height: 1.3, ), ), ], ), ), // Actions Container( width: double.infinity, padding: const EdgeInsets.fromLTRB(24, 0, 24, 24), child: Row( children: [ Expanded( child: OutlinedButton( onPressed: () => Get.back(), style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), side: BorderSide( color: Colors.grey.shade300, width: 1.5, ), ), child: const Text( "Annuler", style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, color: Colors.black87, ), ), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton( onPressed: () async { // ✅ IMPORTANT: Vider le cache de session lors de la déconnexion await clearUserData(); Get.offAll(const LoginPage()); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.red.shade600, foregroundColor: Colors.white, elevation: 2, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: const Text( "Se déconnecter", style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), ), ), ], ), ), ], ), ), ), barrierDismissible: true, ); }, ); } }