import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:youmazgestion/Components/app_bar.dart'; import 'package:youmazgestion/Components/appDrawer.dart'; import 'package:youmazgestion/Services/stock_managementDatabase.dart'; import 'package:youmazgestion/controller/userController.dart'; class ApprobationSortiesPage extends StatefulWidget { const ApprobationSortiesPage({super.key}); @override _ApprobationSortiesPageState createState() => _ApprobationSortiesPageState(); } class _ApprobationSortiesPageState extends State { final AppDatabase _database = AppDatabase.instance; final UserController _userController = Get.find(); List> _sortiesEnAttente = []; bool _isLoading = false; @override void initState() { super.initState(); _loadSortiesEnAttente(); } Future _loadSortiesEnAttente() async { setState(() => _isLoading = true); try { final sorties = await _database.getSortiesPersonnellesEnAttente(); setState(() { _sortiesEnAttente = sorties; _isLoading = false; }); } catch (e) { setState(() => _isLoading = false); Get.snackbar('Erreur', 'Impossible de charger les demandes: $e'); } } Future _approuverSortie(Map sortie) async { final confirm = await Get.dialog( AlertDialog( title: const Text('Approuver la demande'), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Produit: ${sortie['produit_nom']}'), Text('Quantité: ${sortie['quantite']}'), Text('Demandeur: ${sortie['admin_nom']}'), Text('Motif: ${sortie['motif']}'), const SizedBox(height: 16), const Text( 'Confirmer l\'approbation de cette demande de sortie personnelle ?', style: TextStyle(fontWeight: FontWeight.w600), ), ], ), actions: [ TextButton( onPressed: () => Get.back(result: false), child: const Text('Annuler'), ), ElevatedButton( onPressed: () => Get.back(result: true), style: ElevatedButton.styleFrom(backgroundColor: Colors.green), child: const Text('Approuver', style: TextStyle(color: Colors.white)), ), ], ), ); if (confirm == true) { try { await _database.approuverSortiePersonnelle( sortie['id'] as int, _userController.userId, ); Get.snackbar( 'Demande approuvée', 'La sortie personnelle a été approuvée et le stock mis à jour', backgroundColor: Colors.green, colorText: Colors.white, ); _loadSortiesEnAttente(); } catch (e) { Get.snackbar( 'Erreur', 'Impossible d\'approuver la demande: $e', backgroundColor: Colors.red, colorText: Colors.white, ); } } } Future _refuserSortie(Map sortie) async { final motifController = TextEditingController(); final confirm = await Get.dialog( AlertDialog( title: const Text('Refuser la demande'), content: Column( mainAxisSize: MainAxisSize.min, children: [ Text('Demande de: ${sortie['admin_nom']}'), Text('Produit: ${sortie['produit_nom']}'), const SizedBox(height: 16), TextField( controller: motifController, decoration: const InputDecoration( labelText: 'Motif du refus *', border: OutlineInputBorder(), ), maxLines: 3, ), ], ), actions: [ TextButton( onPressed: () => Get.back(result: false), child: const Text('Annuler'), ), ElevatedButton( onPressed: () { if (motifController.text.trim().isNotEmpty) { Get.back(result: true); } else { Get.snackbar('Erreur', 'Veuillez indiquer un motif de refus'); } }, style: ElevatedButton.styleFrom(backgroundColor: Colors.red), child: const Text('Refuser', style: TextStyle(color: Colors.white)), ), ], ), ); if (confirm == true && motifController.text.trim().isNotEmpty) { try { await _database.refuserSortiePersonnelle( sortie['id'] as int, _userController.userId, motifController.text.trim(), ); Get.snackbar( 'Demande refusée', 'La sortie personnelle a été refusée', backgroundColor: Colors.orange, colorText: Colors.white, ); _loadSortiesEnAttente(); } catch (e) { Get.snackbar( 'Erreur', 'Impossible de refuser la demande: $e', backgroundColor: Colors.red, colorText: Colors.white, ); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: CustomAppBar(title: 'Approbation sorties personnelles'), drawer: CustomDrawer(), body: _isLoading ? const Center(child: CircularProgressIndicator()) : RefreshIndicator( onRefresh: _loadSortiesEnAttente, child: _sortiesEnAttente.isEmpty ? const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.inbox, size: 64, color: Colors.grey), SizedBox(height: 16), Text( 'Aucune demande en attente', style: TextStyle(fontSize: 18, color: Colors.grey), ), ], ), ) : ListView.builder( padding: const EdgeInsets.all(16), itemCount: _sortiesEnAttente.length, itemBuilder: (context, index) { final sortie = _sortiesEnAttente[index]; return _buildSortieCard(sortie); }, ), ), ); } Widget _buildSortieCard(Map sortie) { final dateSortie = DateTime.parse(sortie['date_sortie'].toString()); return Card( margin: const EdgeInsets.only(bottom: 12), elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // En-tête avec statut Row( children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Colors.orange.shade100, borderRadius: BorderRadius.circular(8), ), child: Text( 'EN ATTENTE', style: TextStyle( fontSize: 12, fontWeight: FontWeight.bold, color: Colors.orange.shade700, ), ), ), const Spacer(), Text( DateFormat('dd/MM/yyyy HH:mm').format(dateSortie), style: TextStyle( fontSize: 12, color: Colors.grey.shade600, ), ), ], ), const SizedBox(height: 12), // Informations du produit Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.inventory, color: Colors.blue.shade700, size: 16), const SizedBox(width: 8), Text( 'Produit demandé', style: TextStyle( fontWeight: FontWeight.w600, color: Colors.blue.shade700, ), ), ], ), const SizedBox(height: 8), Text( sortie['produit_nom'].toString(), style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Row( children: [ Text('Référence: ${sortie['produit_reference'] ?? 'N/A'}'), const SizedBox(width: 16), Text('Stock actuel: ${sortie['stock_actuel']}'), const SizedBox(width: 16), Text( 'Quantité demandée: ${sortie['quantite']}', style: const TextStyle(fontWeight: FontWeight.w600), ), ], ), ], ), ), const SizedBox(height: 12), // Informations du demandeur Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.green.shade50, borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.person, color: Colors.green.shade700, size: 16), const SizedBox(width: 8), Text( 'Demandeur', style: TextStyle( fontWeight: FontWeight.w600, color: Colors.green.shade700, ), ), ], ), const SizedBox(height: 8), Text( '${sortie['admin_nom']} ${sortie['admin_nom_famille'] ?? ''}', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), if (sortie['point_vente_nom'] != null) Text('Point de vente: ${sortie['point_vente_nom']}'), ], ), ), const SizedBox(height: 12), // Motif Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.purple.shade50, borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.note, color: Colors.purple.shade700, size: 16), const SizedBox(width: 8), Text( 'Motif', style: TextStyle( fontWeight: FontWeight.w600, color: Colors.purple.shade700, ), ), ], ), const SizedBox(height: 8), Text( sortie['motif'].toString(), style: const TextStyle(fontSize: 14), ), if (sortie['notes'] != null && sortie['notes'].toString().isNotEmpty) ...[ const SizedBox(height: 8), Text( 'Notes: ${sortie['notes']}', style: TextStyle( fontSize: 12, color: Colors.grey.shade600, fontStyle: FontStyle.italic, ), ), ], ], ), ), const SizedBox(height: 16), // Vérification de stock if ((sortie['stock_actuel'] as int) < (sortie['quantite'] as int)) Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.red.shade50, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.red.shade300), ), child: Row( children: [ Icon(Icons.warning, color: Colors.red.shade700), const SizedBox(width: 8), Expanded( child: Text( 'ATTENTION: Stock insuffisant pour cette demande', style: TextStyle( color: Colors.red.shade700, fontWeight: FontWeight.w600, ), ), ), ], ), ), const SizedBox(height: 16), // Boutons d'action Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: () => _refuserSortie(sortie), icon: const Icon(Icons.close, size: 18), label: const Text('Refuser'), style: ElevatedButton.styleFrom( backgroundColor: Colors.red.shade600, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton.icon( onPressed: ((sortie['stock_actuel'] as int) >= (sortie['quantite'] as int)) ? () => _approuverSortie(sortie) : null, icon: const Icon(Icons.check, size: 18), label: const Text('Approuver'), style: ElevatedButton.styleFrom( backgroundColor: Colors.green.shade600, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), ), ), ], ), ], ), ), ); } }