Merge branch 'master' of https://git.c4m.mg/Andrii/CARING_RESTORANT
This commit is contained in:
commit
b1759601fe
@ -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 {
|
||||
|
||||
@ -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]
|
||||
/// 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<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';
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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…
Reference in New Issue
Block a user