import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'package:intl/intl.dart'; import '../services/pdf_impression_commande.dart'; import 'commandes_screen.dart'; class ValidateAddItemsPage extends StatefulWidget { final int commandeId; final String numeroCommande; final List newItems; // Nouveaux articles à ajouter final Map? commandeDetails; // Détails de la commande existante const ValidateAddItemsPage({ Key? key, required this.commandeId, required this.numeroCommande, required this.newItems, required this.commandeDetails, }) : super(key: key); @override State createState() => _ValidateAddItemsPageState(); } class _ValidateAddItemsPageState extends State { List _newCartItems = []; bool _isValidating = false; @override void initState() { super.initState(); _processNewItems(); } void _processNewItems() { // Grouper les nouveaux articles identiques Map groupedItems = {}; for (var item in widget.newItems) { String key = "${item['id']}_${item['notes'] ?? ''}"; if (groupedItems.containsKey(key)) { groupedItems[key]!.quantity++; } else { groupedItems[key] = CartItemModel( id: item['id'], nom: item['nom'] ?? 'Article', prix: _parsePrice(item['prix']), quantity: 1, notes: item['notes'] ?? '', ); } } setState(() { _newCartItems = groupedItems.values.toList(); }); } double _parsePrice(dynamic prix) { if (prix == null) return 0.0; if (prix is num) return prix.toDouble(); if (prix is String) return double.tryParse(prix) ?? 0.0; return 0.0; } void _updateQuantity(int index, int newQuantity) { setState(() { if (newQuantity > 0) { _newCartItems[index].quantity = newQuantity; } else { _newCartItems.removeAt(index); } }); } void _removeItem(int index) { setState(() { _newCartItems.removeAt(index); }); } // Calculer le total des nouveaux articles double _calculateNewItemsTotal() { return _newCartItems.fold( 0.0, (sum, item) => sum + (item.prix * item.quantity), ); } // Calculer le total des articles existants double _calculateExistingItemsTotal() { if (widget.commandeDetails == null || widget.commandeDetails!['items'] == null) { return 0.0; } double total = 0.0; for (var item in widget.commandeDetails!['items']) { // Correction: utiliser 'menu_prix_actuel' au lieu de 'menu_prix' double prix = _parsePrice( item['menu_prix_actuel'] ?? item['prix_unitaire'], ); int quantite = item['quantite'] ?? 1; total += prix * quantite; } return total; } // Calculer le total général double _calculateGrandTotal() { return _calculateExistingItemsTotal() + _calculateNewItemsTotal(); } int _getTotalNewArticles() { return _newCartItems.fold(0, (sum, item) => sum + item.quantity); } int _getTotalExistingArticles() { if (widget.commandeDetails == null || widget.commandeDetails!['items'] == null) { return 0; } return widget.commandeDetails!['items'].fold( 0, (sum, item) => sum + (item['quantite'] ?? 1), ); } void _showConfirmationDialog() { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: Text( 'Confirmer l\'ajout', style: TextStyle(fontWeight: FontWeight.bold), ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Êtes-vous sûr de vouloir ajouter ces articles à la commande ?', ), SizedBox(height: 16), Text( 'Récapitulatif:', style: TextStyle(fontWeight: FontWeight.bold), ), Text('• Commande: ${widget.numeroCommande}'), Text('• Nouveaux articles: ${_getTotalNewArticles()}'), Text( '• Nouveau total: ${NumberFormat("#,##0.00", "fr_FR").format(_calculateGrandTotal())} MGA', ), ], ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: Text('Annuler', style: TextStyle(color: Colors.grey[600])), ), ElevatedButton( onPressed: _isValidating ? null : () { Navigator.of(context).pop(); _validateAddItems(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.green[700], foregroundColor: Colors.white, ), child: _isValidating ? SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( Colors.white, ), ), ) : Text('Confirmer'), ), ], ); }, ); } Future _validateAddItems() async { if (_newCartItems.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Aucun article à ajouter'), backgroundColor: Colors.orange, ), ); return; } setState(() { _isValidating = true; }); try { // Préparer les données des nouveaux articles // 1. Construire la liste des items sans commande_id à l'intérieur List> items = _newCartItems.map((cartItem) { return { 'menu_id': cartItem.id, 'quantite': cartItem.quantity, 'commentaires': cartItem.notes.isNotEmpty ? cartItem.notes : null, }; }).toList(); // 2. Construire le body correctement avec commande_id à la racine final body = {'commande_id': widget.commandeId, 'items': items}; print("📦 Données envoyées : ${json.encode(body)}"); // 3. Envoi vers l'API final response = await http.post( Uri.parse( 'https://restaurant.careeracademy.mg/api/commande-items/add-multiple', ), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: json.encode(body), ); print(body); if (response.statusCode == 200 || response.statusCode == 201) { final responseData = json.decode(response.body); if (responseData['success'] == true) { // Récupérer les détails de la commande mise à jour pour l'impression await _getUpdatedOrderAndPrint(); } else { throw Exception('API returned success: false'); } } else { throw Exception('Failed to add items: ${response.statusCode}'); } } catch (e) { _showErrorDialog('Erreur lors de l\'ajout: $e'); } finally { setState(() { _isValidating = false; }); } } // Nouvelle méthode pour récupérer la commande mise à jour et imprimer Future _getUpdatedOrderAndPrint() async { try { // Récupérer les détails mis à jour de la commande final response = await http.get( Uri.parse( 'https://restaurant.careeracademy.mg/api/commandes/${widget.commandeId}', ), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); if (response.statusCode == 200) { final responseData = json.decode(response.body); if (responseData['success'] == true && responseData['data'] != null) { // Créer l'objet Order à partir des données mises à jour final order = Order.fromJson(responseData['data']); // Afficher le dialog de succès avec impression _showSuccessDialogWithPrint(order); } else { // Si on ne peut pas récupérer la commande mise à jour, afficher le dialog sans impression _showSuccessDialog(); } } else { // Si erreur, afficher le dialog simple _showSuccessDialog(); } } catch (e) { print('Erreur lors de la récupération de la commande mise à jour: $e'); // En cas d'erreur, afficher le dialog simple _showSuccessDialog(); } } // Nouveau dialog de succès avec option d'impression void _showSuccessDialogWithPrint(Order order) { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: Row( children: [ Icon(Icons.check_circle, color: Colors.green[700]), SizedBox(width: 8), Text('Articles ajoutés'), ], ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Les nouveaux articles ont été ajoutés à la commande avec succès !', ), SizedBox(height: 16), Text( 'Voulez-vous imprimer la commande mise à jour ?', style: TextStyle(fontWeight: FontWeight.w600), ), ], ), actions: [ TextButton( onPressed: () { // Fermer le dialog et retourner sans impression Navigator.of(context).pop(); _navigateBackWithSuccess(); }, child: Text('Non merci', style: TextStyle(color: Colors.grey[600])), ), ElevatedButton( onPressed: () { // Fermer le dialog et imprimer Navigator.of(context).pop(); _printAndNavigateBack(order); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.green[700], foregroundColor: Colors.white, ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.print, size: 16), SizedBox(width: 4), Text('Imprimer'), ], ), ), ], ); }, ); } // Méthode pour imprimer et naviguer Future _printAndNavigateBack(Order order) async { // Afficher un indicateur de chargement showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( content: Row( mainAxisSize: MainAxisSize.min, children: [ CircularProgressIndicator(), SizedBox(width: 16), Text('Impression en cours...'), ], ), ), ); try { // Lancer l'impression avec feedback await printOrderWithFeedback(order, (message, isSuccess) { print('$message - Success: $isSuccess'); print(order); }); // Fermer le dialog d'impression Navigator.of(context).pop(); // Afficher un message de succès ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Commande imprimée avec succès'), backgroundColor: Colors.green[700], duration: Duration(seconds: 2), ), ); // Attendre un peu puis naviguer await Future.delayed(Duration(milliseconds: 500)); _navigateBackWithSuccess(); } catch (e) { // Fermer le dialog d'impression Navigator.of(context).pop(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Erreur d\'impression: ${e.toString()}'), backgroundColor: Colors.orange, duration: Duration(seconds: 3), ), ); // Naviguer quand même await Future.delayed(Duration(milliseconds: 500)); _navigateBackWithSuccess(); } } // Méthode pour naviguer avec succès void _navigateBackWithSuccess() { // Fermer toutes les pages précédentes et rediriger vers OrdersManagementScreen Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (context) => OrdersManagementScreen(), ), (Route route) => false, // Supprime toutes les pages précédentes ); // Optionnel : petit délai pour laisser le temps à la navigation Future.delayed(Duration(milliseconds: 300), () { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Articles ajoutés avec succès à la commande ${widget.numeroCommande}', ), backgroundColor: Colors.green[700], ), ); }); } // Ancien dialog de succès (sans impression) - garde comme fallback void _showSuccessDialog() { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: Row( children: [ Icon(Icons.check_circle, color: Colors.green[700]), SizedBox(width: 8), Text('Articles ajoutés'), ], ), content: Text( 'Les nouveaux articles ont été ajoutés à la commande avec succès !', ), actions: [ ElevatedButton( onPressed: () { Navigator.of(context).pop(); _navigateBackWithSuccess(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.green[700], foregroundColor: Colors.white, ), child: Text('OK'), ), ], ); }, ); } void _showErrorDialog(String message) { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Row( children: [ Icon(Icons.error, color: Colors.red), SizedBox(width: 8), Text('Erreur'), ], ), content: Text(message), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: Text('OK'), ), ], ); }, ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.grey[50], appBar: AppBar( backgroundColor: Colors.white, elevation: 0, leading: IconButton( onPressed: () => Navigator.pop(context), icon: Icon(Icons.arrow_back, color: Colors.black), ), title: Text( 'Valider l\'ajout', style: TextStyle( color: Colors.black, fontSize: 24, fontWeight: FontWeight.bold, ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text( 'Annuler', style: TextStyle(color: Colors.black, fontSize: 16), ), ), SizedBox(width: 16), ], ), body: Column( children: [ // Header avec infos commande et totaux Container( width: double.infinity, padding: EdgeInsets.all(20), color: Colors.white, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Commande: ${widget.numeroCommande}', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black87, ), ), SizedBox(height: 16), Text( 'Nouveaux articles à ajouter: ${_getTotalNewArticles()}', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.green[700], ), ), ], ), ), // Liste des nouveaux articles Expanded( child: _newCartItems.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.shopping_cart_outlined, size: 80, color: Colors.grey[400], ), SizedBox(height: 16), Text( 'Aucun nouvel article sélectionné', style: TextStyle( fontSize: 18, color: Colors.grey[600], ), ), ], ), ) : ListView.separated( padding: EdgeInsets.all(16), itemCount: _newCartItems.length, separatorBuilder: (context, index) => SizedBox(height: 12), itemBuilder: (context, index) { final item = _newCartItems[index]; return Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all( color: Colors.green[200]!, width: 1, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 5, offset: Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Row( children: [ Icon( Icons.add_circle, color: Colors.green[600], size: 20, ), SizedBox(width: 8), Expanded( child: Text( item.nom, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), ), ], ), ), IconButton( onPressed: () => _removeItem(index), icon: Icon( Icons.delete_outline, color: Colors.red, ), constraints: BoxConstraints(), padding: EdgeInsets.zero, ), ], ), Text( '${NumberFormat("#,##0.00", "fr_FR").format(item.prix)} MGA l\'unité', style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), if (item.notes.isNotEmpty) ...[ SizedBox(height: 8), Text( 'Notes: ${item.notes}', style: TextStyle( fontSize: 14, color: Colors.grey[600], fontStyle: FontStyle.italic, ), ), ], SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Contrôles de quantité Row( children: [ IconButton( onPressed: () => _updateQuantity( index, item.quantity - 1, ), icon: Icon(Icons.remove), style: IconButton.styleFrom( backgroundColor: Colors.grey[200], minimumSize: Size(40, 40), ), ), SizedBox(width: 16), Text( item.quantity.toString(), style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), SizedBox(width: 16), IconButton( onPressed: () => _updateQuantity( index, item.quantity + 1, ), icon: Icon(Icons.add), style: IconButton.styleFrom( backgroundColor: Colors.grey[200], minimumSize: Size(40, 40), ), ), ], ), // Prix total de l'article Text( '${NumberFormat("#,##0.00", "fr_FR").format(item.prix * item.quantity)} MGA', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.green[700], ), ), ], ), ], ), ); }, ), ), // Récapitulatif final Container( padding: EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, border: Border(top: BorderSide(color: Colors.grey[200]!)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Récapitulatif final', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Articles existants:', style: TextStyle(fontSize: 16)), Text( _getTotalExistingArticles().toString(), style: TextStyle(fontSize: 16), ), ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Sous-total existant:', style: TextStyle(fontSize: 16), ), Text( '${NumberFormat("#,##0.00", "fr_FR").format(_calculateExistingItemsTotal())} MGA', style: TextStyle(fontSize: 16), ), ], ), SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Nouveaux articles:', style: TextStyle(fontSize: 16, color: Colors.green[700]), ), Text( _getTotalNewArticles().toString(), style: TextStyle(fontSize: 16, color: Colors.green[700]), ), ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Sous-total nouveaux:', style: TextStyle(fontSize: 16, color: Colors.green[700]), ), Text( '${NumberFormat("#,##0.00", "fr_FR").format(_calculateNewItemsTotal())} MGA', style: TextStyle(fontSize: 16, color: Colors.green[700]), ), ], ), SizedBox(height: 16), Divider(), SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Total général:', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), Text( '${NumberFormat("#,##0.00", "fr_FR").format(_calculateGrandTotal())} MGA', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.green[700], ), ), ], ), SizedBox(height: 20), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _newCartItems.isNotEmpty && !_isValidating ? _showConfirmationDialog : null, style: ElevatedButton.styleFrom( backgroundColor: Colors.green[700], foregroundColor: Colors.white, padding: EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), disabledBackgroundColor: Colors.grey[300], ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (_isValidating) ...[ SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( Colors.white, ), ), ), SizedBox(width: 8), Text( 'Ajout en cours...', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ] else ...[ Icon(Icons.add_shopping_cart, size: 20), SizedBox(width: 8), Text( _newCartItems.isEmpty ? 'Aucun article à ajouter' : 'Ajouter à la commande (${_getTotalNewArticles()})', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ], ], ), ), ), ], ), ), ], ), ); } } // Classe CartItemModel class CartItemModel { final int id; final String nom; final double prix; int quantity; final String notes; CartItemModel({ required this.id, required this.nom, required this.prix, required this.quantity, required this.notes, }); }