import 'package:flutter/material.dart'; import 'package:youmazgestion/Components/app_bar.dart'; import 'package:youmazgestion/Models/Permission.dart'; import 'package:youmazgestion/Models/role.dart'; import 'package:youmazgestion/Services/stock_managementDatabase.dart'; class RolePermissionsPage extends StatefulWidget { final Role role; const RolePermissionsPage({super.key, required this.role}); @override State createState() => _RolePermissionsPageState(); } class _RolePermissionsPageState extends State { final db = AppDatabase.instance; List permissions = []; List> menus = []; Map> menuPermissionsMap = {}; bool isLoading = true; String? errorMessage; @override void initState() { super.initState(); _initData(); } Future _initData() async { try { setState(() { isLoading = true; errorMessage = null; }); final perms = await db.getAllPermissions(); final menuList = await db.getAllMenus(); // Utilise la nouvelle méthode Map> tempMenuPermissionsMap = {}; for (var menu in menuList) { final menuId = menu['id'] as int; final menuPerms = await db.getPermissionsForRoleAndMenu( widget.role.id!, menuId); tempMenuPermissionsMap[menuId] = { for (var perm in perms) perm.name: menuPerms.any((mp) => mp.name == perm.name) }; } setState(() { permissions = perms; menus = menuList; menuPermissionsMap = tempMenuPermissionsMap; isLoading = false; }); } catch (e) { setState(() { errorMessage = 'Erreur lors du chargement des données: $e'; isLoading = false; }); print("Erreur lors de l'initialisation des données: $e"); } } Future _onPermissionToggle( int menuId, String permission, bool enabled) async { try { final perm = permissions.firstWhere((p) => p.name == permission); if (enabled) { await db.assignRoleMenuPermission( widget.role.id!, menuId, perm.id!); } else { await db.removeRoleMenuPermission( widget.role.id!, menuId, perm.id!); } setState(() { menuPermissionsMap[menuId]![permission] = enabled; }); // Afficher un message de confirmation ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( enabled ? 'Permission "$permission" accordée' : 'Permission "$permission" révoquée', ), backgroundColor: enabled ? Colors.green : Colors.orange, duration: const Duration(seconds: 2), ), ); } catch (e) { print("Erreur lors de la modification de la permission: $e"); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Erreur lors de la modification: $e'), backgroundColor: Colors.red, ), ); } } void _toggleAllPermissions(int menuId, bool enabled) { for (var permission in permissions) { _onPermissionToggle(menuId, permission.name, enabled); } } int _getSelectedPermissionsCount(int menuId) { return menuPermissionsMap[menuId]?.values.where((selected) => selected).length ?? 0; } double _getPermissionPercentage(int menuId) { if (permissions.isEmpty) return 0.0; return _getSelectedPermissionsCount(menuId) / permissions.length; } Widget _buildPermissionSummary() { int totalPermissions = menus.length * permissions.length; int selectedPermissions = 0; for (var menuId in menuPermissionsMap.keys) { selectedPermissions += _getSelectedPermissionsCount(menuId); } double percentage = totalPermissions > 0 ? selectedPermissions / totalPermissions : 0.0; return Card( elevation: 4, margin: const EdgeInsets.only(bottom: 16), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.analytics, color: Colors.blue.shade600), const SizedBox(width: 8), Text( 'Résumé des permissions', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.blue.shade700, ), ), ], ), const SizedBox(height: 12), LinearProgressIndicator( value: percentage, backgroundColor: Colors.grey.shade300, valueColor: AlwaysStoppedAnimation( percentage > 0.7 ? Colors.green : percentage > 0.3 ? Colors.orange : Colors.red, ), ), const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '$selectedPermissions / $totalPermissions permissions', style: const TextStyle(fontWeight: FontWeight.w500), ), Text( '${(percentage * 100).toStringAsFixed(1)}%', style: const TextStyle( fontWeight: FontWeight.bold, color: Colors.blue, ), ), ], ), ], ), ), ); } Widget _buildMenuCard(Map menu) { final menuId = menu['id'] as int; final menuName = menu['name'] as String; final menuRoute = menu['route'] as String; final selectedCount = _getSelectedPermissionsCount(menuId); final percentage = _getPermissionPercentage(menuId); return Card( margin: const EdgeInsets.only(bottom: 16), elevation: 3, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: ExpansionTile( leading: CircleAvatar( backgroundColor: percentage == 1.0 ? Colors.green : percentage > 0 ? Colors.orange : Colors.red.shade100, child: Icon( Icons.menu, color: percentage == 1.0 ? Colors.white : percentage > 0 ? Colors.white : Colors.red, ), ), title: Text( menuName, style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( menuRoute, style: TextStyle( fontSize: 12, color: Colors.grey.shade600, ), ), const SizedBox(height: 4), Row( children: [ Expanded( child: LinearProgressIndicator( value: percentage, backgroundColor: Colors.grey.shade300, valueColor: AlwaysStoppedAnimation( percentage == 1.0 ? Colors.green : percentage > 0 ? Colors.orange : Colors.red, ), ), ), const SizedBox(width: 8), Text( '$selectedCount/${permissions.length}', style: const TextStyle( fontSize: 12, fontWeight: FontWeight.w500, ), ), ], ), ], ), trailing: PopupMenuButton( onSelected: (value) { if (value == 'all') { _toggleAllPermissions(menuId, true); } else if (value == 'none') { _toggleAllPermissions(menuId, false); } }, itemBuilder: (context) => [ const PopupMenuItem( value: 'all', child: Row( children: [ Icon(Icons.select_all, color: Colors.green), SizedBox(width: 8), Text('Tout sélectionner'), ], ), ), const PopupMenuItem( value: 'none', child: Row( children: [ Icon(Icons.deselect, color: Colors.red), SizedBox(width: 8), Text('Tout désélectionner'), ], ), ), ], child: const Icon(Icons.more_vert), ), children: [ Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Permissions disponibles:', style: TextStyle( fontWeight: FontWeight.w500, fontSize: 14, ), ), const SizedBox(height: 12), Wrap( spacing: 8, runSpacing: 8, children: permissions.map((perm) { final isChecked = menuPermissionsMap[menuId]?[perm.name] ?? false; return CustomFilterChip( label: perm.name, selected: isChecked, onSelected: (bool value) { _onPermissionToggle(menuId, perm.name, value); }, ); }).toList(), ), ], ), ), ], ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: CustomAppBar( title: "Permissions - ${widget.role.designation}", ), body: isLoading ? const Center(child: CircularProgressIndicator()) : errorMessage != null ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.error_outline, size: 64, color: Colors.red.shade400, ), const SizedBox(height: 16), Text( 'Erreur de chargement', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.red.shade600, ), ), const SizedBox(height: 8), Padding( padding: const EdgeInsets.symmetric(horizontal: 32), child: Text( errorMessage!, textAlign: TextAlign.center, style: TextStyle(color: Colors.grey.shade600), ), ), const SizedBox(height: 16), ElevatedButton.icon( onPressed: _initData, icon: const Icon(Icons.refresh), label: const Text('Réessayer'), ), ], ), ) : Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // En-tête avec informations du rôle Card( elevation: 4, margin: const EdgeInsets.only(bottom: 16), child: Padding( padding: const EdgeInsets.all(16.0), child: Row( children: [ CircleAvatar( backgroundColor: widget.role.designation == 'Super Admin' ? Colors.red.shade100 : Colors.blue.shade100, radius: 24, child: Icon( Icons.person, color: widget.role.designation == 'Super Admin' ? Colors.red.shade700 : Colors.blue.shade700, ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Gestion des permissions', style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, ), ), Text( 'Rôle: ${widget.role.designation}', style: TextStyle( fontSize: 16, color: Colors.grey.shade700, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 4), Text( 'Configurez les accès pour chaque menu', style: TextStyle( fontSize: 14, color: Colors.grey.shade600, ), ), ], ), ), ], ), ), ), // Résumé des permissions if (permissions.isNotEmpty && menus.isNotEmpty) _buildPermissionSummary(), // Liste des menus et permissions if (permissions.isNotEmpty && menus.isNotEmpty) Expanded( child: ListView.builder( itemCount: menus.length, itemBuilder: (context, index) { return _buildMenuCard(menus[index]); }, ), ) else Expanded( child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.inbox, size: 64, color: Colors.grey.shade400, ), const SizedBox(height: 16), Text( 'Aucune donnée disponible', style: TextStyle( fontSize: 18, color: Colors.grey.shade600, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 8), Text( 'Permissions: ${permissions.length} | Menus: ${menus.length}', style: TextStyle( fontSize: 14, color: Colors.grey.shade500, ), ), const SizedBox(height: 16), ElevatedButton.icon( onPressed: _initData, icon: const Icon(Icons.refresh), label: const Text('Actualiser'), ), ], ), ), ), ], ), ), floatingActionButton: !isLoading && errorMessage == null ? FloatingActionButton.extended( onPressed: () { Navigator.of(context).pop(); }, icon: const Icon(Icons.save), label: const Text('Enregistrer'), backgroundColor: Colors.green, ) : null, ); } } class CustomFilterChip extends StatelessWidget { final String label; final bool selected; final ValueChanged onSelected; const CustomFilterChip({ super.key, required this.label, required this.selected, required this.onSelected, }); Color _getChipColor(String label) { switch (label.toLowerCase()) { case 'view': case 'read': return Colors.blue; case 'create': return Colors.green; case 'update': return Colors.orange; case 'delete': return Colors.red; case 'admin': return Colors.purple; case 'manage': return Colors.indigo; default: return Colors.grey; } } IconData _getChipIcon(String label) { switch (label.toLowerCase()) { case 'view': case 'read': return Icons.visibility; case 'create': return Icons.add; case 'update': return Icons.edit; case 'delete': return Icons.delete; case 'admin': return Icons.admin_panel_settings; case 'manage': return Icons.settings; default: return Icons.security; } } @override Widget build(BuildContext context) { final color = _getChipColor(label); final icon = _getChipIcon(label); return FilterChip( label: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( icon, size: 16, color: selected ? Colors.white : color, ), const SizedBox(width: 4), Text( label, style: TextStyle( color: selected ? Colors.white : color, fontWeight: FontWeight.w500, ), ), ], ), selected: selected, onSelected: onSelected, selectedColor: color, backgroundColor: color.withOpacity(0.1), checkmarkColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), side: BorderSide( color: selected ? color : color.withOpacity(0.3), width: 1, ), ), elevation: selected ? 4 : 1, pressElevation: 8, ); } }