From f0fbbaf9866bc9dc879a5c9374af2ba6d1fb9a4d Mon Sep 17 00:00:00 2001 From: Stephane Date: Sat, 2 Aug 2025 11:27:39 +0300 Subject: [PATCH] commande --- lib/pages/cart_page.dart | 612 ++++++++++++++++++++------------------- 1 file changed, 316 insertions(+), 296 deletions(-) diff --git a/lib/pages/cart_page.dart b/lib/pages/cart_page.dart index 548e477..2d716b8 100644 --- a/lib/pages/cart_page.dart +++ b/lib/pages/cart_page.dart @@ -31,10 +31,10 @@ class _CartPageState extends State { void _processCartItems() { // Grouper les articles identiques Map groupedItems = {}; - + for (var item in widget.cartItems) { String key = "${item['id']}_${item['notes'] ?? ''}"; - + if (groupedItems.containsKey(key)) { groupedItems[key]!.quantity++; } else { @@ -47,7 +47,7 @@ class _CartPageState extends State { ); } } - + setState(() { _cartItems = groupedItems.values.toList(); }); @@ -77,7 +77,10 @@ class _CartPageState extends State { } double _calculateTotal() { - return _cartItems.fold(0.0, (sum, item) => sum + (item.prix * item.quantity)); + return _cartItems.fold( + 0.0, + (sum, item) => sum + (item.prix * item.quantity), + ); } int _getTotalArticles() { @@ -113,30 +116,33 @@ class _CartPageState extends State { actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), - child: Text( - 'Annuler', - style: TextStyle(color: Colors.grey[600]), - ), + child: Text('Annuler', style: TextStyle(color: Colors.grey[600])), ), ElevatedButton( - onPressed: _isValidating ? null : () { - Navigator.of(context).pop(); - _validateOrder(); - }, + onPressed: + _isValidating + ? null + : () { + Navigator.of(context).pop(); + _validateOrder(); + }, 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('Valider'), + child: + _isValidating + ? SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation( + Colors.white, + ), + ), + ) + : Text('Valider'), ), ], ); @@ -157,29 +163,36 @@ class _CartPageState extends State { "reservation_id": 1, // Peut être null si pas de réservation "serveur": "Serveur par défaut", // Valeur par défaut comme demandé "commentaires": _getOrderComments(), - "items": _cartItems.map((item) => { - "menu_id": item.id, - "quantite": item.quantity, - "commentaires": item.notes.isNotEmpty ? item.notes : null, - }).toList(), + "items": + _cartItems + .map( + (item) => { + "menu_id": item.id, + "quantite": item.quantity, + "commentaires": item.notes.isNotEmpty ? item.notes : null, + }, + ) + .toList(), }; // Appel API pour créer la commande final response = await http.post( - Uri.parse('https://restaurant.careeracademy.mg/api/commandes'), // Remplacez par votre URL d'API - headers: { - 'Content-Type': 'application/json', - }, + Uri.parse( + 'https://restaurant.careeracademy.mg/api/commandes', + ), // Remplacez par votre URL d'API + headers: {'Content-Type': 'application/json'}, body: json.encode(orderData), ); - print(orderData); + print('response body: ${response.body}'); if (response.statusCode == 200 || response.statusCode == 201) { // Succès _showSuccessDialog(); } else { // Erreur - _showErrorDialog('Erreur lors de l\'enregistrement de la commande (${response.statusCode})'); + _showErrorDialog( + 'Erreur lors de l\'enregistrement de la commande (${response.statusCode})', + ); } } catch (e) { _showErrorDialog('Erreur de connexion: $e'); @@ -192,11 +205,12 @@ class _CartPageState extends State { String _getOrderComments() { // Concaténer toutes les notes des articles pour les commentaires généraux - List allNotes = _cartItems - .where((item) => item.notes.isNotEmpty) - .map((item) => '${item.nom}: ${item.notes}') - .toList(); - + List allNotes = + _cartItems + .where((item) => item.notes.isNotEmpty) + .map((item) => '${item.nom}: ${item.notes}') + .toList(); + return allNotes.join('; '); } @@ -213,7 +227,9 @@ class _CartPageState extends State { Text('Commande validée'), ], ), - content: Text('Votre commande a été envoyée en cuisine avec succès !'), + content: Text( + 'Votre commande a été envoyée en cuisine avec succès !', + ), actions: [ ElevatedButton( onPressed: () { @@ -281,299 +297,303 @@ class _CartPageState extends State { onPressed: () => Navigator.pop(context), child: Text( 'Retour au menu', - style: TextStyle( - color: Colors.black, - fontSize: 16, - ), + style: TextStyle(color: Colors.black, fontSize: 16), ), ), SizedBox(width: 16), ], ), - body: _cartItems.isEmpty - ? Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.shopping_cart_outlined, - size: 80, - color: Colors.grey[400], - ), - SizedBox(height: 16), - Text( - 'Votre panier est vide', - style: TextStyle( - fontSize: 18, - color: Colors.grey[600], + body: + _cartItems.isEmpty + ? Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.shopping_cart_outlined, + size: 80, + color: Colors.grey[400], ), - ), - ], - ), - ) - : Column( - children: [ - // Header avec infos table - Container( - width: double.infinity, - padding: EdgeInsets.all(20), - color: Colors.white, - child: Text( - 'Table ${widget.tableId} • ${widget.personne} personne${widget.personne > 1 ? 's' : ''}', - style: TextStyle( - fontSize: 16, - color: Colors.grey[600], + SizedBox(height: 16), + Text( + 'Votre panier est vide', + style: TextStyle(fontSize: 18, color: Colors.grey[600]), ), - ), + ], ), - - // Liste des articles - Expanded( - child: ListView.separated( - padding: EdgeInsets.all(16), - itemCount: _cartItems.length, - separatorBuilder: (context, index) => SizedBox(height: 12), - itemBuilder: (context, index) { - final item = _cartItems[index]; - return Container( - padding: EdgeInsets.all(16), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(12), - 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: Text( - item.nom, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, + ) + : Column( + children: [ + // Header avec infos table + Container( + width: double.infinity, + padding: EdgeInsets.all(20), + color: Colors.white, + child: Text( + 'Table ${widget.tableId} • ${widget.personne} personne${widget.personne > 1 ? 's' : ''}', + style: TextStyle(fontSize: 16, color: Colors.grey[600]), + ), + ), + + // Liste des articles + Expanded( + child: ListView.separated( + padding: EdgeInsets.all(16), + itemCount: _cartItems.length, + separatorBuilder: + (context, index) => SizedBox(height: 12), + itemBuilder: (context, index) { + final item = _cartItems[index]; + return Container( + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + 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: Text( + item.nom, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), ), ), - ), - IconButton( - onPressed: () => _removeItem(index), - icon: Icon( - Icons.delete_outline, - color: Colors.red, + IconButton( + onPressed: () => _removeItem(index), + icon: Icon( + Icons.delete_outline, + color: Colors.red, + ), + constraints: BoxConstraints(), + padding: EdgeInsets.zero, ), - constraints: BoxConstraints(), - padding: EdgeInsets.zero, - ), - ], - ), - Text( - '${item.prix.toStringAsFixed(2)} € l\'unité', - style: TextStyle( - fontSize: 14, - color: Colors.grey[600], + ], ), - ), - if (item.notes.isNotEmpty) ...[ - SizedBox(height: 8), Text( - 'Notes: ${item.notes}', + '${item.prix.toStringAsFixed(2)} € l\'unité', 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 + if (item.notes.isNotEmpty) ...[ + SizedBox(height: 8), Text( - '${(item.prix * item.quantity).toStringAsFixed(2)} €', + 'Notes: ${item.notes}', style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.green[700], + 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( + '${(item.prix * item.quantity).toStringAsFixed(2)} €', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.green[700], + ), + ), + ], + ), + ], + ), + ); + }, + ), + ), + + // Récapitulatif + 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', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Articles:', style: TextStyle(fontSize: 16)), + Text( + _getTotalArticles().toString(), + style: TextStyle(fontSize: 16), ), ], ), - ); - }, - ), - ), - - // Récapitulatif - 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', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, + SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Table:', style: TextStyle(fontSize: 16)), + Text( + widget.tableId.toString(), + style: TextStyle(fontSize: 16), + ), + ], ), - ), - SizedBox(height: 16), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('Articles:', style: TextStyle(fontSize: 16)), - Text( - _getTotalArticles().toString(), - style: TextStyle(fontSize: 16), - ), - ], - ), - SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('Table:', style: TextStyle(fontSize: 16)), - Text( - widget.tableId.toString(), - style: TextStyle(fontSize: 16), - ), - ], - ), - SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('Personnes:', style: TextStyle(fontSize: 16)), - Text( - widget.personne.toString(), - style: TextStyle(fontSize: 16), - ), - ], - ), - SizedBox(height: 16), - Divider(), - SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Total:', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, + SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Personnes:', style: TextStyle(fontSize: 16)), + Text( + widget.personne.toString(), + style: TextStyle(fontSize: 16), ), - ), - Text( - '${_calculateTotal().toStringAsFixed(2)} €', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, + ], + ), + SizedBox(height: 16), + Divider(), + SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Total:', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), ), - ), - ], - ), - SizedBox(height: 20), - SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: _cartItems.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), + Text( + '${_calculateTotal().toStringAsFixed(2)} €', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), ), - 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(height: 20), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: + _cartItems.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( - 'Validation en cours...', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, + SizedBox(width: 8), + Text( + 'Validation en cours...', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), ), - ), - ] else ...[ - Icon(Icons.check, size: 20), - SizedBox(width: 8), - Text( - 'Valider la commande', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, + ] else ...[ + Icon(Icons.check, size: 20), + SizedBox(width: 8), + Text( + 'Valider la commande', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), ), - ), + ], ], - ], + ), ), ), - ), - ], + ], + ), ), - ), - ], - ), + ], + ), ); } } @@ -592,4 +612,4 @@ class CartItemModel { required this.quantity, required this.notes, }); -} \ No newline at end of file +}