import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:em2rp/utils/colors.dart'; import 'package:em2rp/utils/permission_gate.dart'; import 'package:em2rp/views/widgets/nav/main_drawer.dart'; import 'package:em2rp/views/widgets/nav/custom_app_bar.dart'; import 'package:em2rp/providers/container_provider.dart'; import 'package:em2rp/providers/local_user_provider.dart'; import 'package:em2rp/models/container_model.dart'; import 'package:em2rp/models/equipment_model.dart'; import 'package:em2rp/views/widgets/common/qr_code_format_selector_dialog.dart'; import 'package:em2rp/mixins/selection_mode_mixin.dart'; import 'package:em2rp/views/widgets/management/management_search_bar.dart'; import 'package:em2rp/views/widgets/management/management_card.dart'; import 'package:em2rp/views/widgets/management/management_list.dart'; class ContainerManagementPage extends StatefulWidget { const ContainerManagementPage({super.key}); @override State createState() => _ContainerManagementPageState(); } class _ContainerManagementPageState extends State with SelectionModeMixin { final TextEditingController _searchController = TextEditingController(); ContainerType? _selectedType; EquipmentStatus? _selectedStatus; List? _cachedContainers; // Cache pour éviter le rebuild @override void dispose() { _searchController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final isMobile = MediaQuery.of(context).size.width < 800; return PermissionGate( requiredPermissions: const ['view_equipment'], fallback: Scaffold( appBar: const CustomAppBar(title: 'Accès refusé'), drawer: const MainDrawer(currentPage: '/container_management'), body: const Center( child: Padding( padding: EdgeInsets.all(24.0), child: Text( 'Vous n\'avez pas les permissions nécessaires pour accéder à la gestion des containers.', textAlign: TextAlign.center, style: TextStyle(fontSize: 16), ), ), ), ), child: Scaffold( appBar: isSelectionMode ? AppBar( backgroundColor: AppColors.rouge, leading: IconButton( icon: const Icon(Icons.close, color: Colors.white), onPressed: toggleSelectionMode, ), title: Text( '$selectedCount sélectionné(s)', style: const TextStyle(color: Colors.white), ), actions: [ if (hasSelection) ...[ IconButton( icon: const Icon(Icons.qr_code, color: Colors.white), tooltip: 'Générer QR Codes', onPressed: _generateQRCodesForSelected, ), IconButton( icon: const Icon(Icons.delete, color: Colors.white), tooltip: 'Supprimer', onPressed: _deleteSelectedContainers, ), ], ], ) : AppBar( title: const Text('Gestion des Containers'), backgroundColor: AppColors.rouge, leading: IconButton( icon: const Icon(Icons.arrow_back), tooltip: 'Retour à la gestion des équipements', onPressed: () => Navigator.pushReplacementNamed(context, '/equipment_management'), ), actions: [ IconButton( icon: const Icon(Icons.logout, color: Colors.white), onPressed: () async { final shouldLogout = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Déconnexion'), content: const Text('Voulez-vous vraiment vous déconnecter ?'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Annuler'), ), TextButton( onPressed: () => Navigator.pop(context, true), child: const Text('Déconnexion'), ), ], ), ); if (shouldLogout == true && context.mounted) { await context.read().signOut(); if (context.mounted) { Navigator.pushReplacementNamed(context, '/login'); } } }, ), ], ), drawer: const MainDrawer(currentPage: '/container_management'), floatingActionButton: !isSelectionMode ? FloatingActionButton.extended( onPressed: () => _navigateToForm(context), backgroundColor: AppColors.rouge, icon: const Icon(Icons.add, color: Colors.white), label: const Text( 'Nouveau Container', style: TextStyle(color: Colors.white), ), ) : null, body: isMobile ? _buildMobileLayout() : _buildDesktopLayout(), ), ); } Widget _buildMobileLayout() { return Column( children: [ _buildSearchBar(), _buildMobileFilters(), Expanded(child: _buildContainerList()), ], ); } Widget _buildDesktopLayout() { return Row( children: [ SizedBox( width: 250, child: _buildSidebar(), ), const VerticalDivider(width: 1, thickness: 1), Expanded( child: Column( children: [ _buildSearchBar(), Expanded(child: _buildContainerList()), ], ), ), ], ); } Widget _buildSearchBar() { return ManagementSearchBar( controller: _searchController, hintText: 'Rechercher un container...', onChanged: (value) { context.read().setSearchQuery(value); }, onSelectionModeToggle: isSelectionMode ? null : toggleSelectionMode, showSelectionModeButton: !isSelectionMode, ); } Widget _buildMobileFilters() { return Container( padding: const EdgeInsets.symmetric(vertical: 8), color: Colors.grey.shade50, child: SingleChildScrollView( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( children: [ _buildTypeChip(null, 'Tous'), const SizedBox(width: 8), ...ContainerType.values.map((type) { return Padding( padding: const EdgeInsets.only(right: 8), child: _buildTypeChip(type, type.label), ); }), ], ), ), ); } Widget _buildTypeChip(ContainerType? type, String label) { final isSelected = _selectedType == type; final color = isSelected ? Colors.white : AppColors.noir; return ChoiceChip( label: Row( mainAxisSize: MainAxisSize.min, children: [ if (type != null) ...[ type.getIcon(size: 16, color: color), const SizedBox(width: 8), ], Text(label), ], ), selected: isSelected, onSelected: (selected) { setState(() { _selectedType = selected ? type : null; context.read().setSelectedType(_selectedType); }); }, selectedColor: AppColors.rouge, labelStyle: TextStyle( color: color, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ); } Widget _buildSidebar() { return Container( color: Colors.grey.shade50, child: ListView( padding: const EdgeInsets.all(16), children: [ Text( 'Filtres', style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, color: AppColors.noir, ), ), const SizedBox(height: 16), // Filtre par type Text( 'Type de container', style: Theme.of(context).textTheme.titleSmall?.copyWith( fontWeight: FontWeight.bold, color: AppColors.noir, ), ), const SizedBox(height: 8), _buildFilterOption(null, 'Tous les types'), ...ContainerType.values.map((type) { return _buildFilterOption(type, type.label); }), const Divider(height: 32), // Filtre par statut Text( 'Statut', style: Theme.of(context).textTheme.titleSmall?.copyWith( fontWeight: FontWeight.bold, color: AppColors.noir, ), ), const SizedBox(height: 8), _buildStatusFilter(null, 'Tous les statuts'), _buildStatusFilter(EquipmentStatus.available, 'Disponible'), _buildStatusFilter(EquipmentStatus.inUse, 'En prestation'), _buildStatusFilter(EquipmentStatus.maintenance, 'En maintenance'), _buildStatusFilter(EquipmentStatus.outOfService, 'Hors service'), ], ), ); } Widget _buildFilterOption(ContainerType? type, String label) { final isSelected = _selectedType == type; return RadioListTile( title: Text(label), value: type, groupValue: _selectedType, activeColor: AppColors.rouge, dense: true, contentPadding: EdgeInsets.zero, onChanged: (value) { setState(() { _selectedType = value; context.read().setSelectedType(_selectedType); }); }, ); } Widget _buildStatusFilter(EquipmentStatus? status, String label) { final isSelected = _selectedStatus == status; return RadioListTile( title: Text(label), value: status, groupValue: _selectedStatus, activeColor: AppColors.rouge, dense: true, contentPadding: EdgeInsets.zero, onChanged: (value) { setState(() { _selectedStatus = value; context.read().setSelectedStatus(_selectedStatus); }); }, ); } Widget _buildContainerList() { return Consumer( builder: (context, provider, child) { return ManagementList( stream: provider.containersStream, cachedItems: _cachedContainers, emptyMessage: 'Aucun container trouvé', emptyIcon: Icons.inventory_2_outlined, onDataReceived: (items) { _cachedContainers = items; }, itemBuilder: (container) => _buildContainerCard(container), ); }, ); } Widget _buildContainerCard(ContainerModel container) { final isSelected = isItemSelected(container.id); return ManagementCard( item: container, getId: (c) => c.id, getTitle: (c) => c.id, getSubtitle: (c) => c.name, getIcon: (c) => c.type.getIcon( size: 40, color: AppColors.rouge, ), getInfoChips: (c) => [ InfoChip( label: c.type.label, icon: Icons.category, ), InfoChip( label: '${c.itemCount} items', icon: Icons.inventory, ), ], getStatusBadge: (c) => StatusBadge( label: c.status.label, color: c.status.color, ), actions: const [ CardAction( id: 'view', label: 'Voir détails', icon: Icons.visibility, ), CardAction( id: 'edit', label: 'Modifier', icon: Icons.edit, ), CardAction( id: 'qr', label: 'QR Code', icon: Icons.qr_code, ), CardAction( id: 'delete', label: 'Supprimer', icon: Icons.delete, color: Colors.red, ), ], onActionSelected: _handleMenuAction, onTap: () { if (isSelectionMode) { toggleItemSelection(container.id); } else { _viewContainerDetails(container); } }, onLongPress: () { if (!isSelectionMode) { toggleSelectionMode(); toggleItemSelection(container.id); } }, isSelectionMode: isSelectionMode, isSelected: isSelected, onSelectionChanged: (value) { toggleItemSelection(container.id); }, ); } void _handleMenuAction(String action, ContainerModel container) { switch (action) { case 'view': _viewContainerDetails(container); break; case 'edit': _editContainer(container); break; case 'qr': // Non utilisé - les QR codes multiples sont générés via _generateQRCodesForSelected break; case 'delete': _deleteContainer(container); break; } } void _navigateToForm(BuildContext context) async { final result = await Navigator.pushNamed(context, '/container_form'); if (result == true) { // Rafraîchir la liste } } void _viewContainerDetails(ContainerModel container) async { await Navigator.pushNamed( context, '/container_detail', arguments: container, ); } void _editContainer(ContainerModel container) async { await Navigator.pushNamed( context, '/container_form', arguments: container, ); } Future _generateQRCodesForSelected() async { if (!hasSelection) return; // Récupérer les containers sélectionnés final containerProvider = context.read(); final List selectedContainers = []; final Map> containerEquipmentMap = {}; for (final id in selectedIds) { final container = await containerProvider.getContainerById(id); if (container != null) { selectedContainers.add(container); // Charger les équipements pour ce container final equipment = await containerProvider.getContainerEquipment(id); containerEquipmentMap[id] = equipment; } } if (selectedContainers.isEmpty) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Aucun container trouvé')), ); } return; } // Afficher le dialogue de sélection de format avec le widget générique if (mounted) { showDialog( context: context, builder: (context) => QRCodeFormatSelectorDialog( itemList: selectedContainers, getId: (c) => c.id, getTitle: (c) => c.name, getDetails: (ContainerModel c) { final equipment = containerEquipmentMap[c.id] ?? []; return [ 'Contenu (${equipment.length}):', ...equipment.take(5).map((eq) => '- ${eq.id}'), if (equipment.length > 5) '... +${equipment.length - 5}', ]; }, dialogTitle: 'Générer ${selectedContainers.length} QR Code(s)', ), ); } } Future _deleteContainer(ContainerModel container) async { final confirm = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Confirmer la suppression'), content: Text( 'Êtes-vous sûr de vouloir supprimer le container "${container.name}" ?\n\n' 'Cette action est irréversible.', ), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Annuler'), ), ElevatedButton( onPressed: () => Navigator.pop(context, true), style: ElevatedButton.styleFrom(backgroundColor: Colors.red), child: const Text('Supprimer'), ), ], ), ); if (confirm == true && mounted) { try { await context.read().deleteContainer(container.id); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Container supprimé avec succès')), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Erreur lors de la suppression: $e')), ); } } } } Future _deleteSelectedContainers() async { final confirm = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Confirmer la suppression'), content: Text( 'Êtes-vous sûr de vouloir supprimer $selectedCount container(s) ?\n\n' 'Cette action est irréversible.', ), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Annuler'), ), ElevatedButton( onPressed: () => Navigator.pop(context, true), style: ElevatedButton.styleFrom(backgroundColor: Colors.red), child: const Text('Supprimer'), ), ], ), ); if (confirm == true && mounted) { try { final provider = context.read(); for (final id in selectedIds) { await provider.deleteContainer(id); } disableSelectionMode(); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Containers supprimés avec succès')), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Erreur lors de la suppression: $e')), ); } } } } }