andrymodeste 4 months ago
parent
commit
b1759601fe
  1. 14
      lib/pages/PlatEdit_screen.dart
  2. 53
      lib/pages/commandes_screen.dart
  3. 110
      lib/pages/menu.dart
  4. 8
      lib/pages/menus_screen.dart

14
lib/pages/PlatEdit_screen.dart

@ -21,6 +21,20 @@ class MenuPlat {
required this.disponible,
required this.categories,
});
factory MenuPlat.fromJson(Map<String, dynamic> 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 {

53
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';
import 'commande_item_screen.dart';
@ -18,11 +19,18 @@ class _OrdersManagementScreenState extends State<OrdersManagementScreen> {
bool isLoading = true;
String? errorMessage;
final String baseUrl = 'https://restaurant.careeracademy.mg/api';
Map<int, MenuPlat>? menuMap;
@override
void initState() {
super.initState();
loadOrders();
loadMenuMap();
}
Future<void> loadMenuMap() async {
final loadedMenuMap = await fetchMenuMap();
menuMap = loadedMenuMap;
}
Future<void> loadOrders() async {
@ -528,6 +536,7 @@ class _OrdersManagementScreenState extends State<OrdersManagementScreen> {
onProcessPayment: processPayment,
onDelete: deleteOrder,
onViewDetails: () => getOrderById(order.id),
menuMap: menuMap,
);
},
),
@ -535,6 +544,30 @@ class _OrdersManagementScreenState extends State<OrdersManagementScreen> {
}
}
/// Récupère la liste des menus et la stocke dans un [Map]
/// 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<Map<int, MenuPlat>> fetchMenuMap() async {
final response = await http.get(
Uri.parse('https://restaurant.careeracademy.mg/api/menus'),
);
if (response.statusCode == 200) {
final List<dynamic> menuList = json.decode(response.body);
final Map<int, MenuPlat> 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;
@ -542,14 +575,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<Map<int, MenuPlat>> menuMapFuture = fetchMenuMap();
final Map<int, MenuPlat> 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';
@ -675,7 +713,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,
@ -922,4 +960,9 @@ class OrderItem {
commentaires: json['commentaires'],
);
}
// Pass a Map of menus to get the name
String getNomById(Map<int, MenuPlat> menuMap) {
return menuMap[menuId]?.nom ?? 'Menu Item $menuId';
}
}

110
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<MenuPage> createState() => _MenuPageState();
@ -20,7 +20,7 @@ class _MenuPageState extends State<MenuPage> {
int? _selectedCategory;
List<dynamic> _categories = [];
List<dynamic> _menus = [];
List<dynamic> _cart = [];
final List<dynamic> _cart = [];
@override
void initState() {
@ -49,10 +49,14 @@ class _MenuPageState extends State<MenuPage> {
}
});
} 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<MenuPage> {
_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<MenuPage> {
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<MenuPage> {
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<MenuPage> {
}).toList(),
)
else
Center(child: CircularProgressIndicator()),
const Center(child: CircularProgressIndicator()),
Expanded(
child:
_menus.isNotEmpty
@ -183,7 +191,7 @@ class _MenuPageState extends State<MenuPage> {
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<MenuPage> {
),
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<MenuPage> {
);
},
)
: 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<MenuPage> {
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<AddToCartModal> createState() => _AddToCartModalState();
@ -307,8 +317,8 @@ class _AddToCartModalState extends State<AddToCartModal> {
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<AddToCartModal> {
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<AddToCartModal> {
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<AddToCartModal> {
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<AddToCartModal> {
),
],
),
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<AddToCartModal> {
});
}
: 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<AddToCartModal> {
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<AddToCartModal> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
const Text(
"Total",
style: TextStyle(
fontSize: 18,
@ -469,7 +485,7 @@ class _AddToCartModalState extends State<AddToCartModal> {
),
],
),
SizedBox(height: 16),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: ElevatedButton(
@ -488,19 +504,19 @@ class _AddToCartModalState extends State<AddToCartModal> {
"${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,

8
lib/pages/menus_screen.dart

@ -74,12 +74,13 @@ class _PlatsManagementScreenState extends State<PlatsManagementScreen> {
Future<void> _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<EditPlatDialog> {
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;
}

Loading…
Cancel
Save