From 646efaad64117a829619716b95445139d053a305 Mon Sep 17 00:00:00 2001 From: Stephane Date: Sun, 3 Aug 2025 13:31:34 +0300 Subject: [PATCH] tsy haiko hoe inona no nataoko teto --- lib/pages/PlatEdit_screen.dart | 14 ++++ lib/pages/commandes_screen.dart | 53 +++++++++++++-- lib/pages/menu.dart | 110 ++++++++++++++++++-------------- lib/pages/menus_screen.dart | 8 +-- 4 files changed, 129 insertions(+), 56 deletions(-) diff --git a/lib/pages/PlatEdit_screen.dart b/lib/pages/PlatEdit_screen.dart index 69cf6c5..bc1e8e3 100644 --- a/lib/pages/PlatEdit_screen.dart +++ b/lib/pages/PlatEdit_screen.dart @@ -21,6 +21,20 @@ class MenuPlat { required this.disponible, required this.categories, }); + factory MenuPlat.fromJson(Map json) { + return MenuPlat( + id: json['id'], + nom: json['nom'], + commentaire: json['commentaire'], + prix: (json['prix'] as num).toDouble(), + imageUrl: json['image_url'], + disponible: json['disponible'] ?? true, + categories: + (json['categories'] as List) + .map((c) => MenuCategory.fromJson(c)) + .toList(), + ); + } } class MenuCategory { diff --git a/lib/pages/commandes_screen.dart b/lib/pages/commandes_screen.dart index 0b8d2bb..86757d2 100644 --- a/lib/pages/commandes_screen.dart +++ b/lib/pages/commandes_screen.dart @@ -3,6 +3,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; +import './PlatEdit_screen.dart'; class OrdersManagementScreen extends StatefulWidget { const OrdersManagementScreen({super.key}); @@ -16,11 +17,18 @@ class _OrdersManagementScreenState extends State { bool isLoading = true; String? errorMessage; final String baseUrl = 'https://restaurant.careeracademy.mg/api'; - + Map? menuMap; @override void initState() { super.initState(); loadOrders(); + loadMenuMap(); + } + + Future loadMenuMap() async { + final loadedMenuMap = await fetchMenuMap(); + + menuMap = loadedMenuMap; } Future loadOrders() async { @@ -521,6 +529,7 @@ class _OrdersManagementScreenState extends State { onProcessPayment: processPayment, onDelete: deleteOrder, onViewDetails: () => getOrderById(order.id), + menuMap: menuMap, ); }, ), @@ -528,6 +537,30 @@ class _OrdersManagementScreenState extends State { } } +/// Récupère la liste des menus et la stocke dans un [Map] +/// où la clé est l'ID du menu et la valeur est le [MenuPlat] correspondant. +/// +/// Si la requête échoue, lève une [Exception] avec un message d'erreur. +Future> fetchMenuMap() async { + final response = await http.get( + Uri.parse('https://restaurant.careeracademy.mg/api/menus'), + ); + + if (response.statusCode == 200) { + final List menuList = json.decode(response.body); + final Map menuMap = {}; + + for (var menu in menuList) { + final menuPlat = MenuPlat.fromJson(menu); + menuMap[menuPlat.id] = menuPlat; + } + + return menuMap; + } else { + throw Exception('Échec de la récupération des menus'); + } +} + class OrderCard extends StatelessWidget { final Order order; final Function(Order, String, {String? modePaiement}) onStatusUpdate; @@ -535,14 +568,19 @@ class OrderCard extends StatelessWidget { final Function(Order) onDelete; final VoidCallback onViewDetails; - const OrderCard({ - Key? key, + // Add menuMap field to have access to menu names + + // late Future> menuMapFuture = fetchMenuMap(); + final Map menuMap; + OrderCard({ + super.key, required this.order, required this.onStatusUpdate, required this.onProcessPayment, required this.onDelete, required this.onViewDetails, - }) : super(key: key); + required this.menuMap, // Require menuMap in constructor + }); String _formatTime(DateTime dateTime) { return '${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')} •${dateTime.day.toString().padLeft(2, '0')}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.year} •1 personne'; @@ -661,7 +699,7 @@ class OrderCard extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - '${item.quantite}x ${item.menuId}', // You might want to resolve menu name + '${item.quantite}x ${item.getNomById(menuMap)}', style: const TextStyle( fontSize: 14, color: Colors.black87, @@ -904,4 +942,9 @@ class OrderItem { commentaires: json['commentaires'], ); } + + // Pass a Map of menus to get the name + String getNomById(Map menuMap) { + return menuMap[menuId]?.nom ?? 'Menu Item $menuId'; + } } diff --git a/lib/pages/menu.dart b/lib/pages/menu.dart index 2be56a7..f948be6 100644 --- a/lib/pages/menu.dart +++ b/lib/pages/menu.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; @@ -9,8 +10,7 @@ class MenuPage extends StatefulWidget { final int tableId; final int personne; - const MenuPage({Key? key, required this.tableId, required this.personne}) - : super(key: key); + const MenuPage({super.key, required this.tableId, required this.personne}); @override State createState() => _MenuPageState(); @@ -20,7 +20,7 @@ class _MenuPageState extends State { int? _selectedCategory; List _categories = []; List _menus = []; - List _cart = []; + final List _cart = []; @override void initState() { @@ -49,10 +49,14 @@ class _MenuPageState extends State { } }); } else { - print("Erreur API catégories: ${response.statusCode}"); + if (kDebugMode) { + print("Erreur API catégories: ${response.statusCode}"); + } } } catch (e) { - print("Exception fetchCategories: $e"); + if (kDebugMode) { + print("Exception fetchCategories: $e"); + } } } @@ -73,10 +77,14 @@ class _MenuPageState extends State { _menus = menusList; }); } else { - print("Erreur API menus: ${response.statusCode}"); + if (kDebugMode) { + print("Erreur API menus: ${response.statusCode}"); + } } } catch (e) { - print("Exception fetchMenus: $e"); + if (kDebugMode) { + print("Exception fetchMenus: $e"); + } } } @@ -145,13 +153,13 @@ class _MenuPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Menu"), + title: const Text("Menu"), actions: [ IconButton( onPressed: () { if (_selectedCategory != null) fetchMenus(_selectedCategory!); }, - icon: Icon(Icons.refresh), + icon: const Icon(Icons.refresh), ), ], ), @@ -162,7 +170,7 @@ class _MenuPageState extends State { padding: const EdgeInsets.all(8.0), child: Text( "Table ${widget.tableId} • ${widget.personne} personne${widget.personne > 1 ? 's' : ''}", - style: TextStyle(fontSize: 16), + style: const TextStyle(fontSize: 16), ), ), if (_categories.isNotEmpty) @@ -174,7 +182,7 @@ class _MenuPageState extends State { }).toList(), ) else - Center(child: CircularProgressIndicator()), + const Center(child: CircularProgressIndicator()), Expanded( child: _menus.isNotEmpty @@ -183,7 +191,7 @@ class _MenuPageState extends State { itemBuilder: (context, index) { final item = _menus[index]; return Card( - margin: EdgeInsets.all(8), + margin: const EdgeInsets.all(8), child: ListTile( onTap: () => showAddToCartModal( @@ -204,7 +212,9 @@ class _MenuPageState extends State { ), title: Text( item['nom'] ?? 'Nom non disponible', - style: TextStyle(fontWeight: FontWeight.bold), + style: const TextStyle( + fontWeight: FontWeight.bold, + ), ), subtitle: Text(item['commentaire'] ?? ''), trailing: Text( @@ -219,18 +229,18 @@ class _MenuPageState extends State { ); }, ) - : Center(child: Text("Aucun menu disponible")), + : const Center(child: Text("Aucun menu disponible")), ), Container( width: double.infinity, - padding: EdgeInsets.symmetric(vertical: 12), + padding: const EdgeInsets.symmetric(vertical: 12), color: Colors.green[700], child: Center( child: TextButton( onPressed: _navigateToCart, // Navigation vers la page panier child: Text( "Voir le panier (${_cart.length})", - style: TextStyle(color: Colors.white, fontSize: 16), + style: const TextStyle(color: Colors.white, fontSize: 16), ), ), ), @@ -246,7 +256,7 @@ class _MenuPageState extends State { child: GestureDetector( onTap: () => changeCategory(id), child: Container( - padding: EdgeInsets.symmetric(vertical: 10), + padding: const EdgeInsets.symmetric(vertical: 10), color: selected ? Colors.grey[300] : Colors.grey[100], child: Center( child: Text( @@ -268,10 +278,10 @@ class AddToCartModal extends StatefulWidget { final Function(dynamic, int, String) onAddToCart; const AddToCartModal({ - Key? key, + super.key, required this.item, required this.onAddToCart, - }) : super(key: key); + }); @override State createState() => _AddToCartModalState(); @@ -307,8 +317,8 @@ class _AddToCartModalState extends State { return Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Container( - padding: EdgeInsets.all(20), - constraints: BoxConstraints(maxWidth: 400), + padding: const EdgeInsets.all(20), + constraints: const BoxConstraints(maxWidth: 400), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -319,17 +329,20 @@ class _AddToCartModalState extends State { children: [ Text( widget.item['nom'] ?? 'Menu', - style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), ), IconButton( onPressed: () => Navigator.of(context).pop(), - icon: Icon(Icons.close), + icon: const Icon(Icons.close), padding: EdgeInsets.zero, - constraints: BoxConstraints(), + constraints: const BoxConstraints(), ), ], ), - SizedBox(height: 16), + const SizedBox(height: 16), // Image placeholder Container( @@ -345,7 +358,7 @@ class _AddToCartModalState extends State { color: Colors.green[700], ), ), - SizedBox(height: 16), + const SizedBox(height: 16), // Description if (widget.item['commentaire'] != null && @@ -354,13 +367,13 @@ class _AddToCartModalState extends State { widget.item['commentaire'], style: TextStyle(fontSize: 14, color: Colors.grey[600]), ), - SizedBox(height: 16), + const SizedBox(height: 16), // Prix unitaire Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( + const Text( "Prix unitaire", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), ), @@ -374,14 +387,14 @@ class _AddToCartModalState extends State { ), ], ), - SizedBox(height: 20), + const SizedBox(height: 20), // Quantité - Text( + const Text( "Quantité", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), ), - SizedBox(height: 8), + const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -394,38 +407,41 @@ class _AddToCartModalState extends State { }); } : null, - icon: Icon(Icons.remove), + icon: const Icon(Icons.remove), style: IconButton.styleFrom( backgroundColor: Colors.grey[200], ), ), - SizedBox(width: 20), + const SizedBox(width: 20), Text( _quantity.toString(), - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), ), - SizedBox(width: 20), + const SizedBox(width: 20), IconButton( onPressed: () { setState(() { _quantity++; }); }, - icon: Icon(Icons.add), + icon: const Icon(Icons.add), style: IconButton.styleFrom( backgroundColor: Colors.grey[200], ), ), ], ), - SizedBox(height: 20), + const SizedBox(height: 20), // Notes - Text( + const Text( "Notes (optionnel)", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), ), - SizedBox(height: 8), + const SizedBox(height: 8), TextField( controller: _notesController, maxLines: 3, @@ -434,15 +450,15 @@ class _AddToCartModalState extends State { border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), - contentPadding: EdgeInsets.all(12), + contentPadding: const EdgeInsets.all(12), ), ), - SizedBox(height: 24), + const SizedBox(height: 24), // Total et bouton d'ajout Container( width: double.infinity, - padding: EdgeInsets.all(16), + padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.grey[50], borderRadius: BorderRadius.circular(12), @@ -452,7 +468,7 @@ class _AddToCartModalState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( + const Text( "Total", style: TextStyle( fontSize: 18, @@ -469,7 +485,7 @@ class _AddToCartModalState extends State { ), ], ), - SizedBox(height: 16), + const SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton( @@ -488,19 +504,19 @@ class _AddToCartModalState extends State { "${widget.item['nom']} ajouté au panier", ), backgroundColor: Colors.green, - duration: Duration(seconds: 2), + duration: const Duration(seconds: 2), ), ); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.green[700], foregroundColor: Colors.white, - padding: EdgeInsets.symmetric(vertical: 16), + padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), - child: Text( + child: const Text( "Ajouter au panier", style: TextStyle( fontSize: 16, diff --git a/lib/pages/menus_screen.dart b/lib/pages/menus_screen.dart index 283101e..1441263 100644 --- a/lib/pages/menus_screen.dart +++ b/lib/pages/menus_screen.dart @@ -74,12 +74,13 @@ class _PlatsManagementScreenState extends State { Future _deletePlat(int id) async { final res = await http.delete(Uri.parse('$_baseUrl/menus/$id')); - if (res.statusCode == 200) + if (res.statusCode == 200) { _fetchPlats(); - else { + } else { if (kDebugMode) print("Error deleting plat: ${res.body}"); + // ignore: use_build_context_synchronously ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Des commandes sont liées à ce plat.')), + const SnackBar(content: Text('Des commandes sont liées à ce plat.')), ); } } @@ -467,7 +468,6 @@ class _EditPlatDialogState extends State { commentaire = widget.plat.commentaire ?? ''; ingredients = widget.plat.ingredients ?? ''; prix = widget.plat.prix; - var disponible = widget.plat.disponible; // cat = (widget.plat.categories) as MenuCategory?; cat = widget.plat.category; }