import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:em2rp/models/maintenance_model.dart'; import 'package:em2rp/providers/maintenance_provider.dart'; import 'package:em2rp/providers/equipment_provider.dart'; import 'package:em2rp/views/maintenance_form_page.dart'; import 'package:em2rp/views/widgets/nav/custom_app_bar.dart'; import 'package:em2rp/views/widgets/nav/main_drawer.dart'; import 'package:em2rp/utils/permission_gate.dart'; import 'package:intl/intl.dart'; import 'package:em2rp/utils/colors.dart'; /// Page de gestion des maintenances class MaintenanceManagementPage extends StatefulWidget { const MaintenanceManagementPage({super.key}); @override State createState() => _MaintenanceManagementPageState(); } class _MaintenanceManagementPageState extends State { String _filterType = 'all'; // all, upcoming, overdue, completed @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { _loadMaintenances(); }); } Future _loadMaintenances() async { final maintenanceProvider = context.read(); final equipmentProvider = context.read(); await Future.wait([ maintenanceProvider.loadMaintenances(), equipmentProvider.ensureLoaded(), ]); } List _getFilteredMaintenances(List maintenances) { switch (_filterType) { case 'upcoming': return maintenances.where((m) => !m.isCompleted && !m.isOverdue).toList(); case 'overdue': return maintenances.where((m) => m.isOverdue).toList(); case 'completed': return maintenances.where((m) => m.isCompleted).toList(); default: return maintenances; } } @override Widget build(BuildContext context) { return PermissionGate( requiredPermissions: const ['manage_maintenances'], fallback: Scaffold( appBar: const CustomAppBar(title: 'Accès refusé'), drawer: const MainDrawer(currentPage: '/maintenance_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 maintenances.', textAlign: TextAlign.center, style: TextStyle(fontSize: 16), ), ), ), ), child: Scaffold( appBar: const CustomAppBar( title: 'Gestion des maintenances', ), drawer: const MainDrawer(currentPage: '/maintenance_management'), body: Consumer( builder: (context, maintenanceProvider, _) { if (maintenanceProvider.isLoading) { return const Center(child: CircularProgressIndicator()); } final filteredMaintenances = _getFilteredMaintenances( maintenanceProvider.maintenances, ); return Column( children: [ // Filtres _buildFilterChips(), // Statistiques _buildStatsCards(maintenanceProvider), // Liste des maintenances Expanded( child: filteredMaintenances.isEmpty ? _buildEmptyState() : _buildMaintenanceList(filteredMaintenances), ), ], ); }, ), floatingActionButton: FloatingActionButton.extended( onPressed: () => _navigateToForm(null), backgroundColor: AppColors.bleuFonce, icon: const Icon(Icons.add), label: const Text('Nouvelle maintenance'), ), ), ); } Widget _buildFilterChips() { return Container( padding: const EdgeInsets.all(16), child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: [ _buildFilterChip('Toutes', 'all'), const SizedBox(width: 8), _buildFilterChip('À venir', 'upcoming'), const SizedBox(width: 8), _buildFilterChip('En retard', 'overdue'), const SizedBox(width: 8), _buildFilterChip('Complétées', 'completed'), ], ), ), ); } Widget _buildFilterChip(String label, String filterValue) { final isSelected = _filterType == filterValue; return FilterChip( label: Text(label), selected: isSelected, onSelected: (selected) { setState(() { _filterType = filterValue; }); }, selectedColor: AppColors.bleuFonce.withValues(alpha: 0.2), checkmarkColor: AppColors.bleuFonce, ); } Widget _buildStatsCards(MaintenanceProvider provider) { final upcoming = provider.maintenances.where((m) => !m.isCompleted && !m.isOverdue).length; final overdue = provider.maintenances.where((m) => m.isOverdue).length; final completed = provider.maintenances.where((m) => m.isCompleted).length; return Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( children: [ Expanded( child: _buildStatCard( 'À venir', upcoming.toString(), Icons.schedule, Colors.blue, ), ), const SizedBox(width: 8), Expanded( child: _buildStatCard( 'En retard', overdue.toString(), Icons.warning, Colors.orange, ), ), const SizedBox(width: 8), Expanded( child: _buildStatCard( 'Complétées', completed.toString(), Icons.check_circle, Colors.green, ), ), ], ), ); } Widget _buildStatCard(String label, String value, IconData icon, Color color) { return Card( child: Padding( padding: const EdgeInsets.all(12), child: Column( children: [ Icon(icon, color: color, size: 28), const SizedBox(height: 4), Text( value, style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: color, ), ), Text( label, style: const TextStyle(fontSize: 12), textAlign: TextAlign.center, ), ], ), ), ); } Widget _buildEmptyState() { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.build_outlined, size: 64, color: Colors.grey[400]), const SizedBox(height: 16), Text( 'Aucune maintenance', style: TextStyle(fontSize: 18, color: Colors.grey[600]), ), const SizedBox(height: 8), Text( 'Créez votre première maintenance', style: TextStyle(color: Colors.grey[500]), ), ], ), ); } Widget _buildMaintenanceList(List maintenances) { // Trier par date (les plus récentes/urgentes en premier) final sortedMaintenances = List.from(maintenances) ..sort((a, b) { if (a.isCompleted && !b.isCompleted) return 1; if (!a.isCompleted && b.isCompleted) return -1; return a.scheduledDate.compareTo(b.scheduledDate); }); return ListView.builder( padding: const EdgeInsets.all(16), itemCount: sortedMaintenances.length, itemBuilder: (context, index) { return _buildMaintenanceCard(sortedMaintenances[index]); }, ); } Widget _buildMaintenanceCard(MaintenanceModel maintenance) { final equipmentProvider = context.read(); final equipmentNames = maintenance.equipmentIds .map((id) => equipmentProvider.allEquipment .cast() .firstWhere((e) => e.id == id, orElse: () => null) ?.name ?? 'Inconnu') .toList(); final typeInfo = _getMaintenanceTypeInfo(maintenance.type); final statusInfo = _getStatusInfo(maintenance); return Card( margin: const EdgeInsets.only(bottom: 12), child: InkWell( onTap: () => _navigateToForm(maintenance), borderRadius: BorderRadius.circular(4), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // En-tête Row( children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: typeInfo.$3.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(4), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(typeInfo.$2, size: 16, color: typeInfo.$3), const SizedBox(width: 4), Text( typeInfo.$1, style: TextStyle( color: typeInfo.$3, fontWeight: FontWeight.bold, fontSize: 12, ), ), ], ), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: statusInfo.$2.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(4), ), child: Text( statusInfo.$1, style: TextStyle( color: statusInfo.$2, fontWeight: FontWeight.bold, fontSize: 12, ), ), ), const Spacer(), IconButton( icon: const Icon(Icons.more_vert), onPressed: () => _showMaintenanceMenu(maintenance), ), ], ), const SizedBox(height: 12), // Nom Text( maintenance.name, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), // Description if (maintenance.description.isNotEmpty) Text( maintenance.description, style: TextStyle(color: Colors.grey[700]), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 12), // Équipements Wrap( spacing: 4, runSpacing: 4, children: equipmentNames.map((name) { return Chip( label: Text(name, style: const TextStyle(fontSize: 12)), backgroundColor: Colors.grey[200], padding: EdgeInsets.zero, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ); }).toList(), ), const SizedBox(height: 12), // Dates Row( children: [ Icon(Icons.event, size: 16, color: Colors.grey[600]), const SizedBox(width: 4), Text( maintenance.isCompleted ? 'Complétée le ${DateFormat('dd/MM/yyyy').format(maintenance.completedDate!)}' : 'Planifiée le ${DateFormat('dd/MM/yyyy').format(maintenance.scheduledDate)}', style: TextStyle(fontSize: 14, color: Colors.grey[700]), ), ], ), // Coût if (maintenance.cost != null) ...[ const SizedBox(height: 8), Row( children: [ Icon(Icons.euro, size: 16, color: Colors.grey[600]), const SizedBox(width: 4), Text( '${maintenance.cost!.toStringAsFixed(2)} €', style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), ), ], ), ], ], ), ), ), ); } (String, IconData, Color) _getMaintenanceTypeInfo(MaintenanceType type) { switch (type) { case MaintenanceType.preventive: return ('Préventive', Icons.schedule, Colors.blue); case MaintenanceType.corrective: return ('Corrective', Icons.build, Colors.orange); case MaintenanceType.inspection: return ('Inspection', Icons.search, Colors.purple); } } (String, Color) _getStatusInfo(MaintenanceModel maintenance) { if (maintenance.isCompleted) { return ('Complétée', Colors.green); } else if (maintenance.isOverdue) { return ('En retard', Colors.red); } else { return ('À venir', Colors.blue); } } void _showMaintenanceMenu(MaintenanceModel maintenance) { showModalBottomSheet( context: context, builder: (context) { return SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ if (!maintenance.isCompleted) ListTile( leading: const Icon(Icons.check_circle, color: Colors.green), title: const Text('Marquer comme complétée'), onTap: () { Navigator.pop(context); _completeMaintenance(maintenance); }, ), ListTile( leading: const Icon(Icons.edit, color: AppColors.bleuFonce), title: const Text('Modifier'), onTap: () { Navigator.pop(context); _navigateToForm(maintenance); }, ), ListTile( leading: const Icon(Icons.delete, color: Colors.red), title: const Text('Supprimer'), onTap: () { Navigator.pop(context); _deleteMaintenance(maintenance); }, ), ], ), ); }, ); } Future _completeMaintenance(MaintenanceModel maintenance) async { final result = await showDialog>( context: context, builder: (context) => _CompleteMaintenanceDialog(maintenance: maintenance), ); if (result != null && mounted) { try { await context.read().completeMaintenance( maintenance.id, performedBy: result['performedBy'], cost: result['cost'], ); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Maintenance marquée comme complétée'), backgroundColor: Colors.green, ), ); _loadMaintenances(); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Erreur: $e'), backgroundColor: Colors.red, ), ); } } } } Future _deleteMaintenance(MaintenanceModel maintenance) async { final confirm = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Supprimer la maintenance'), content: Text('Êtes-vous sûr de vouloir supprimer "${maintenance.name}" ?'), 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().deleteMaintenance(maintenance.id); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Maintenance supprimée'), backgroundColor: Colors.green, ), ); _loadMaintenances(); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Erreur: $e'), backgroundColor: Colors.red, ), ); } } } } Future _navigateToForm(MaintenanceModel? maintenance) async { final result = await Navigator.push( context, MaterialPageRoute( builder: (context) => MaintenanceFormPage(maintenance: maintenance), ), ); if (result == true && mounted) { _loadMaintenances(); } } } /// Dialog pour compléter une maintenance class _CompleteMaintenanceDialog extends StatefulWidget { final MaintenanceModel maintenance; const _CompleteMaintenanceDialog({required this.maintenance}); @override State<_CompleteMaintenanceDialog> createState() => _CompleteMaintenanceDialogState(); } class _CompleteMaintenanceDialogState extends State<_CompleteMaintenanceDialog> { final _costController = TextEditingController(); final _notesController = TextEditingController(); @override void dispose() { _costController.dispose(); _notesController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AlertDialog( title: const Text('Compléter la maintenance'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: _costController, decoration: const InputDecoration( labelText: 'Coût (€)', hintText: 'Ex: 150.00', prefixIcon: Icon(Icons.euro), ), keyboardType: TextInputType.number, ), const SizedBox(height: 16), TextField( controller: _notesController, decoration: const InputDecoration( labelText: 'Notes (optionnel)', hintText: 'Commentaires sur l\'intervention', prefixIcon: Icon(Icons.notes), ), maxLines: 3, ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Annuler'), ), ElevatedButton( onPressed: () { final cost = double.tryParse(_costController.text); Navigator.pop(context, { 'cost': cost, 'notes': _notesController.text.trim(), }); }, style: ElevatedButton.styleFrom(backgroundColor: Colors.green), child: const Text('Valider'), ), ], ); } }