import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; // Import de la page de validation (à ajuster selon votre structure de dossiers) import 'commande_item_validation.dart'; class AddItemsToOrderPage extends StatefulWidget { final int commandeId; final String numeroCommande; const AddItemsToOrderPage({ Key? key, required this.commandeId, required this.numeroCommande }) : super(key: key); @override State createState() => _AddItemsToOrderPageState(); } class _AddItemsToOrderPageState extends State { int? _selectedCategory; List _categories = []; List _menus = []; List _cart = []; bool _isLoading = false; Map? _commandeDetails; @override void initState() { super.initState(); fetchCategories(); fetchCommandeDetails(); } Future fetchCategories() async { try { final url = Uri.parse( "https://restaurant.careeracademy.mg/api/menu-categories", ); final response = await http.get(url); if (response.statusCode == 200) { final jsonResponse = json.decode(response.body); final categoriesList = (jsonResponse['data']?['categories'] ?? []) as List; setState(() { _categories = categoriesList; if (_categories.isNotEmpty) { _selectedCategory = _categories[0]['id']; fetchMenus(_selectedCategory!); } }); } else { print("Erreur API catégories: ${response.statusCode}"); } } catch (e) { print("Exception fetchCategories: $e"); } } Future fetchMenus(int categoryId) async { try { final url = Uri.parse( "https://restaurant.careeracademy.mg/api/menus/category/$categoryId?disponible=true", ); final response = await http.get(url); if (response.statusCode == 200) { final jsonResponse = json.decode(response.body); final List menusList = jsonResponse is List ? jsonResponse : (jsonResponse['data'] ?? []); setState(() { _menus = menusList; }); } else { print("Erreur API menus: ${response.statusCode}"); } } catch (e) { print("Exception fetchMenus: $e"); } } Future fetchCommandeDetails() async { try { final url = Uri.parse("https://restaurant.careeracademy.mg/api/commandes/${widget.commandeId}"); final response = await http.get(url); if (response.statusCode == 200) { final jsonResponse = json.decode(response.body); final data = jsonResponse['data']; print(data); setState(() { _commandeDetails = data; }); } else { print("Erreur chargement commande: ${response.statusCode}"); } } catch (e) { print("Exception fetchCommandeDetails: $e"); } } void changeCategory(int id) { setState(() { _selectedCategory = id; _menus = []; }); fetchMenus(id); } void addToCart(dynamic item, int quantity, String notes) { setState(() { for (int i = 0; i < quantity; i++) { Map cartItem = Map.from(item); if (notes.isNotEmpty) { cartItem['notes'] = notes; } _cart.add(cartItem); } }); } void showAddToCartModal(dynamic item) { showDialog( context: context, builder: (BuildContext context) { return AddToCartModal(item: item, onAddToCart: addToCart); }, ); } // NOUVELLE FONCTION: Navigation vers la page de validation Future _navigateToValidationPage() async { if (_cart.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Aucun article à ajouter'), backgroundColor: Colors.orange, ), ); return; } // Naviguer vers la page de validation final result = await Navigator.of(context).push( MaterialPageRoute( builder: (context) => ValidateAddItemsPage( commandeId: widget.commandeId, numeroCommande: widget.numeroCommande, newItems: _cart, commandeDetails: _commandeDetails, ), ), ); // Si l'ajout a été effectué avec succès, retourner à la page précédente if (result == true) { Navigator.of(context).pop(true); } } /// Conversion sécurisée du prix en string avec 2 décimales String formatPrix(dynamic prix) { if (prix == null) return ""; double? val; if (prix is num) { val = prix.toDouble(); } else if (prix is String) { val = double.tryParse(prix); } return val != null ? val.toStringAsFixed(2) : ""; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Ajouter des articles"), backgroundColor: Colors.white, elevation: 1, actions: [ IconButton( onPressed: () { if (_selectedCategory != null) fetchMenus(_selectedCategory!); }, icon: const Icon(Icons.refresh), ), ], ), body: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header avec info commande Container( width: double.infinity, padding: const EdgeInsets.all(16.0), color: Colors.grey[50], child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Commande: ${widget.numeroCommande}", style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black87, ), ), const SizedBox(height: 4), const Text( "Sélectionnez les articles à ajouter", style: TextStyle( fontSize: 14, color: Colors.grey, ), ), const SizedBox(height: 12), if (_commandeDetails != null && _commandeDetails!['items'] != null) Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "Articles déjà commandés:", style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: Colors.grey, ), ), const SizedBox(height: 8), ..._commandeDetails!['items'].map((item) { final nom = item['menu_nom'] ?? 'Inconnu'; final quantite = item['quantite'] ?? 1; final description = item['menu_description'] ?? ''; // Correction: utiliser 'menu_prix_actuel' pour afficher le prix final prix = item['menu_prix_actuel'] ?? item['prix_unitaire'] ?? 0; return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( nom, style: const TextStyle(fontSize: 14) ), ), Text( "x$quantite - ${formatPrix(prix)} MGA", style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500) ), ], ), if (description.isNotEmpty) Text( description, style: const TextStyle(fontSize: 12, color: Colors.grey), ), const Divider(height: 8), ], ), ); }).toList(), ], ) else const Text( "Aucun article encore commandé.", style: TextStyle(fontSize: 14, color: Colors.grey), ), ], ), ), // Catégories if (_categories.isNotEmpty) Container( color: Colors.white, child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: _categories.map((cat) { return buildCategoryButton(cat['nom'], cat['id']); }).toList(), ), ) else const Center(child: CircularProgressIndicator()), // Liste des menus Expanded( child: _menus.isNotEmpty ? ListView.builder( itemCount: _menus.length, itemBuilder: (context, index) { final item = _menus[index]; return Card( margin: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), child: ListTile( onTap: () => showAddToCartModal(item), leading: Container( width: 60, height: 60, decoration: BoxDecoration( color: Colors.green[100], borderRadius: BorderRadius.circular(8), ), child: Icon( Icons.restaurant_menu, color: Colors.green[700], size: 30, ), ), title: Text( item['nom'] ?? 'Nom non disponible', style: const TextStyle(fontWeight: FontWeight.bold), ), subtitle: Text(item['commentaire'] ?? ''), trailing: Text( "${formatPrix(item['prix'])} MGA", style: TextStyle( color: Colors.green[700], fontWeight: FontWeight.bold, fontSize: 16, ), ), ), ); }, ) : const Center(child: Text("Aucun menu disponible")), ), // Bouton pour valider l'ajout - MODIFIÉ Container( width: double.infinity, padding: const EdgeInsets.all(16), color: Colors.white, child: Column( children: [ if (_cart.isNotEmpty) Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 8), child: Text( "${_cart.length} article(s) sélectionné(s)", textAlign: TextAlign.center, style: const TextStyle( fontSize: 14, color: Colors.grey, ), ), ), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _cart.isEmpty ? null : _navigateToValidationPage, // CHANGÉ ICI style: ElevatedButton.styleFrom( backgroundColor: Colors.green[700], foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), disabledBackgroundColor: Colors.grey[300], // Ajouté pour le style désactivé ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( _cart.isEmpty ? Icons.shopping_cart_outlined : Icons.preview, size: 20, ), const SizedBox(width: 8), Text( _cart.isEmpty ? "Sélectionner des articles" : "Valider l'ajout (${_cart.length})", // CHANGÉ LE TEXTE style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ], ), ), ), ], ), ), ], ), ); } Widget buildCategoryButton(String label, int id) { final selected = _selectedCategory == id; return Expanded( child: GestureDetector( onTap: () => changeCategory(id), child: Container( padding: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: selected ? Colors.green[700] : Colors.grey[100], border: Border( bottom: BorderSide( color: selected ? Colors.green[700]! : Colors.transparent, width: 2, ), ), ), child: Center( child: Text( label, style: TextStyle( fontWeight: selected ? FontWeight.bold : FontWeight.normal, color: selected ? Colors.white : Colors.black87, ), ), ), ), ), ); } } // Modal pour ajouter au panier (même que MenuPage) class AddToCartModal extends StatefulWidget { final dynamic item; final Function(dynamic, int, String) onAddToCart; const AddToCartModal({ Key? key, required this.item, required this.onAddToCart, }) : super(key: key); @override State createState() => _AddToCartModalState(); } class _AddToCartModalState extends State { int _quantity = 1; final TextEditingController _notesController = TextEditingController(); String formatPrix(dynamic prix) { if (prix == null) return "0.00"; double? val; if (prix is num) { val = prix.toDouble(); } else if (prix is String) { val = double.tryParse(prix); } return val != null ? val.toStringAsFixed(2) : "0.00"; } double calculateTotal() { double prix = 0.0; if (widget.item['prix'] is num) { prix = widget.item['prix'].toDouble(); } else if (widget.item['prix'] is String) { prix = double.tryParse(widget.item['prix']) ?? 0.0; } return prix * _quantity; } @override Widget build(BuildContext context) { return Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Container( padding: const EdgeInsets.all(20), constraints: const BoxConstraints(maxWidth: 400), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( widget.item['nom'] ?? 'Menu', style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), ), IconButton( onPressed: () => Navigator.of(context).pop(), icon: const Icon(Icons.close), padding: EdgeInsets.zero, constraints: const BoxConstraints(), ), ], ), const SizedBox(height: 16), // Image placeholder Container( width: double.infinity, height: 150, decoration: BoxDecoration( color: Colors.green[100], borderRadius: BorderRadius.circular(12), ), child: Icon( Icons.restaurant_menu, size: 60, color: Colors.green[700], ), ), const SizedBox(height: 16), // Description if (widget.item['commentaire'] != null && widget.item['commentaire'].toString().isNotEmpty) Text( widget.item['commentaire'], style: TextStyle(fontSize: 14, color: Colors.grey[600]), ), const SizedBox(height: 16), // Prix unitaire Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( "Prix unitaire", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), ), Text( "${formatPrix(widget.item['prix'])} MGA", style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.green[700], ), ), ], ), const SizedBox(height: 20), // Quantité const Text( "Quantité", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), ), const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( onPressed: _quantity > 1 ? () { setState(() { _quantity--; }); } : null, icon: const Icon(Icons.remove), style: IconButton.styleFrom( backgroundColor: Colors.grey[200], ), ), const SizedBox(width: 20), Text( _quantity.toString(), style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(width: 20), IconButton( onPressed: () { setState(() { _quantity++; }); }, icon: const Icon(Icons.add), style: IconButton.styleFrom( backgroundColor: Colors.grey[200], ), ), ], ), const SizedBox(height: 20), // Notes const Text( "Notes (optionnel)", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), ), const SizedBox(height: 8), TextField( controller: _notesController, maxLines: 3, decoration: InputDecoration( hintText: "Commentaires spéciaux (sans oignons, bien cuit...)", border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), contentPadding: const EdgeInsets.all(12), ), ), const SizedBox(height: 24), // Total et bouton d'ajout Container( width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.grey[50], borderRadius: BorderRadius.circular(12), ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( "Total", style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), Text( "${calculateTotal().toStringAsFixed(2)} MGA", style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.green[700], ), ), ], ), const SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () { widget.onAddToCart( widget.item, _quantity, _notesController.text, ); Navigator.of(context).pop(); // Afficher un snackbar de confirmation ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( "${widget.item['nom']} ajouté au panier", ), backgroundColor: Colors.green, duration: const Duration(seconds: 2), ), ); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.green[700], foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: const Text( "Ajouter au panier", style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ), ], ), ), ], ), ), ); } }