Browse Source

ajout panier

master
Stephane 4 months ago
parent
commit
a23321274a
  1. 212
      lib/pages/commande_item_validation.dart
  2. 59
      lib/pages/commandes_screen.dart

212
lib/pages/commande_item_validation.dart

@ -6,7 +6,8 @@ class ValidateAddItemsPage extends StatefulWidget {
final int commandeId;
final String numeroCommande;
final List<dynamic> newItems; // Nouveaux articles à ajouter
final Map<String, dynamic>? commandeDetails; // Détails de la commande existante
final Map<String, dynamic>?
commandeDetails; // Détails de la commande existante
const ValidateAddItemsPage({
Key? key,
@ -88,14 +89,17 @@ class _ValidateAddItemsPageState extends State<ValidateAddItemsPage> {
// Calculer le total des articles existants
double _calculateExistingItemsTotal() {
if (widget.commandeDetails == null || widget.commandeDetails!['items'] == null) {
if (widget.commandeDetails == null ||
widget.commandeDetails!['items'] == null) {
return 0.0;
}
double total = 0.0;
for (var item in widget.commandeDetails!['items']) {
// Correction: utiliser 'menu_prix_actuel' au lieu de 'menu_prix'
double prix = _parsePrice(item['menu_prix_actuel'] ?? item['prix_unitaire']);
double prix = _parsePrice(
item['menu_prix_actuel'] ?? item['prix_unitaire'],
);
int quantite = item['quantite'] ?? 1;
total += prix * quantite;
}
@ -112,10 +116,14 @@ class _ValidateAddItemsPageState extends State<ValidateAddItemsPage> {
}
int _getTotalExistingArticles() {
if (widget.commandeDetails == null || widget.commandeDetails!['items'] == null) {
if (widget.commandeDetails == null ||
widget.commandeDetails!['items'] == null) {
return 0;
}
return widget.commandeDetails!['items'].fold(0, (sum, item) => sum + (item['quantite'] ?? 1));
return widget.commandeDetails!['items'].fold(
0,
(sum, item) => sum + (item['quantite'] ?? 1),
);
}
void _showConfirmationDialog() {
@ -132,7 +140,9 @@ class _ValidateAddItemsPageState extends State<ValidateAddItemsPage> {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Êtes-vous sûr de vouloir ajouter ces articles à la commande ?'),
Text(
'Êtes-vous sûr de vouloir ajouter ces articles à la commande ?',
),
SizedBox(height: 16),
Text(
'Récapitulatif:',
@ -140,7 +150,9 @@ class _ValidateAddItemsPageState extends State<ValidateAddItemsPage> {
),
Text('• Commande: ${widget.numeroCommande}'),
Text('• Nouveaux articles: ${_getTotalNewArticles()}'),
Text('• Nouveau total: ${_calculateGrandTotal().toStringAsFixed(2)} MGA'),
Text(
'• Nouveau total: ${_calculateGrandTotal().toStringAsFixed(2)} MGA',
),
],
),
actions: [
@ -149,7 +161,8 @@ class _ValidateAddItemsPageState extends State<ValidateAddItemsPage> {
child: Text('Annuler', style: TextStyle(color: Colors.grey[600])),
),
ElevatedButton(
onPressed: _isValidating
onPressed:
_isValidating
? null
: () {
Navigator.of(context).pop();
@ -159,13 +172,16 @@ class _ValidateAddItemsPageState extends State<ValidateAddItemsPage> {
backgroundColor: Colors.green[700],
foregroundColor: Colors.white,
),
child: _isValidating
child:
_isValidating
? SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
valueColor: AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
)
: Text('Confirmer'),
@ -194,7 +210,8 @@ class _ValidateAddItemsPageState extends State<ValidateAddItemsPage> {
try {
// Préparer les données des nouveaux articles
// 1. Construire la liste des items sans commande_id à lintérieur
List<Map<String, dynamic>> items = _newCartItems.map((cartItem) {
List<Map<String, dynamic>> items =
_newCartItems.map((cartItem) {
return {
'menu_id': cartItem.id,
'quantite': cartItem.quantity,
@ -203,16 +220,15 @@ List<Map<String, dynamic>> items = _newCartItems.map((cartItem) {
}).toList();
// 2. Construire le body correctement avec commande_id à la racine
final body = {
'commande_id': widget.commandeId,
'items': items,
};
final body = {'commande_id': widget.commandeId, 'items': items};
print("📦 Données envoyées : ${json.encode(body)}");
// 3. Envoi vers l'API
final response = await http.post(
Uri.parse('https://restaurant.careeracademy.mg/api/commande-items/add-multiple'),
Uri.parse(
'https://restaurant.careeracademy.mg/api/commande-items/add-multiple',
),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
@ -351,58 +367,57 @@ print('✅ Réponse backend : $responseData');
SizedBox(height: 16),
// Résumé des articles existants
if (widget.commandeDetails != null && widget.commandeDetails!['items'] != null) ...[
Text(
'Articles déjà commandés:',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey[700],
),
),
SizedBox(height: 8),
...widget.commandeDetails!['items'].map<Widget>((item) {
final nom = item['menu_nom'] ?? 'Inconnu';
final quantite = item['quantite'] ?? 1;
// Correction: utiliser 'menu_prix_actuel' au lieu de 'menu_prix'
final prix = _parsePrice(item['menu_prix_actuel'] ?? item['prix_unitaire']);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
'$nom x$quantite',
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
),
),
Text(
'${(prix * quantite).toStringAsFixed(2)} MGA',
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
),
],
),
);
}).toList(),
SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Sous-total existant:',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
),
Text(
'${_calculateExistingItemsTotal().toStringAsFixed(2)} MGA',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
),
],
),
Divider(height: 20),
],
// if (widget.commandeDetails != null && widget.commandeDetails!['items'] != null) ...[
// Text(
// 'Articles déjà commandés:',
// style: TextStyle(
// fontSize: 16,
// fontWeight: FontWeight.w600,
// color: Colors.grey[700],
// ),
// ),
// SizedBox(height: 8),
// ...widget.commandeDetails!['items'].map<Widget>((item) {
// final nom = item['menu_nom'] ?? 'Inconnu';
// final quantite = item['quantite'] ?? 1;
// // Correction: utiliser 'menu_prix_actuel' au lieu de 'menu_prix'
// final prix = _parsePrice(item['menu_prix_actuel'] ?? item['prix_unitaire']);
// return Padding(
// padding: const EdgeInsets.symmetric(vertical: 2),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Expanded(
// child: Text(
// '$nom x$quantite',
// style: TextStyle(fontSize: 14, color: Colors.grey[600]),
// ),
// ),
// Text(
// '${(prix * quantite).toStringAsFixed(2)} MGA',
// style: TextStyle(fontSize: 14, color: Colors.grey[600]),
// ),
// ],
// ),
// );
// }).toList(),
// SizedBox(height: 8),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// 'Sous-total existant:',
// style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
// ),
// Text(
// '${_calculateExistingItemsTotal().toStringAsFixed(2)} MGA',
// style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
// ),
// ],
// ),
// Divider(height: 20),
// ],
Text(
'Nouveaux articles à ajouter: ${_getTotalNewArticles()}',
style: TextStyle(
@ -417,7 +432,8 @@ print('✅ Réponse backend : $responseData');
// Liste des nouveaux articles
Expanded(
child: _newCartItems.isEmpty
child:
_newCartItems.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
@ -430,7 +446,10 @@ print('✅ Réponse backend : $responseData');
SizedBox(height: 16),
Text(
'Aucun nouvel article sélectionné',
style: TextStyle(fontSize: 18, color: Colors.grey[600]),
style: TextStyle(
fontSize: 18,
color: Colors.grey[600],
),
),
],
),
@ -438,7 +457,8 @@ print('✅ Réponse backend : $responseData');
: ListView.separated(
padding: EdgeInsets.all(16),
itemCount: _newCartItems.length,
separatorBuilder: (context, index) => SizedBox(height: 12),
separatorBuilder:
(context, index) => SizedBox(height: 12),
itemBuilder: (context, index) {
final item = _newCartItems[index];
return Container(
@ -446,7 +466,10 @@ print('✅ Réponse backend : $responseData');
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.green[200]!, width: 1),
border: Border.all(
color: Colors.green[200]!,
width: 1,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
@ -459,12 +482,17 @@ print('✅ Réponse backend : $responseData');
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Row(
children: [
Icon(Icons.add_circle, color: Colors.green[600], size: 20),
Icon(
Icons.add_circle,
color: Colors.green[600],
size: 20,
),
SizedBox(width: 8),
Expanded(
child: Text(
@ -509,13 +537,15 @@ print('✅ Réponse backend : $responseData');
],
SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
// Contrôles de quantité
Row(
children: [
IconButton(
onPressed: () => _updateQuantity(
onPressed:
() => _updateQuantity(
index,
item.quantity - 1,
),
@ -535,7 +565,8 @@ print('✅ Réponse backend : $responseData');
),
SizedBox(width: 16),
IconButton(
onPressed: () => _updateQuantity(
onPressed:
() => _updateQuantity(
index,
item.quantity + 1,
),
@ -577,10 +608,7 @@ print('✅ Réponse backend : $responseData');
children: [
Text(
'Récapitulatif final',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
SizedBox(height: 16),
Row(
@ -596,7 +624,10 @@ print('✅ Réponse backend : $responseData');
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Sous-total existant:', style: TextStyle(fontSize: 16)),
Text(
'Sous-total existant:',
style: TextStyle(fontSize: 16),
),
Text(
'${_calculateExistingItemsTotal().toStringAsFixed(2)} MGA',
style: TextStyle(fontSize: 16),
@ -607,7 +638,10 @@ print('✅ Réponse backend : $responseData');
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Nouveaux articles:', style: TextStyle(fontSize: 16, color: Colors.green[700])),
Text(
'Nouveaux articles:',
style: TextStyle(fontSize: 16, color: Colors.green[700]),
),
Text(
_getTotalNewArticles().toString(),
style: TextStyle(fontSize: 16, color: Colors.green[700]),
@ -617,7 +651,10 @@ print('✅ Réponse backend : $responseData');
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Sous-total nouveaux:', style: TextStyle(fontSize: 16, color: Colors.green[700])),
Text(
'Sous-total nouveaux:',
style: TextStyle(fontSize: 16, color: Colors.green[700]),
),
Text(
'${_calculateNewItemsTotal().toStringAsFixed(2)} MGA',
style: TextStyle(fontSize: 16, color: Colors.green[700]),
@ -651,7 +688,8 @@ print('✅ Réponse backend : $responseData');
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _newCartItems.isNotEmpty && !_isValidating
onPressed:
_newCartItems.isNotEmpty && !_isValidating
? _showConfirmationDialog
: null,
style: ElevatedButton.styleFrom(
@ -672,7 +710,9 @@ print('✅ Réponse backend : $responseData');
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
valueColor: AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
),
SizedBox(width: 8),

59
lib/pages/commandes_screen.dart

@ -140,8 +140,6 @@ class _OrdersManagementScreenState extends State<OrdersManagementScreen> {
return null;
}
Future<void> updateOrderStatus(
Order order,
String newStatus, {
@ -289,9 +287,6 @@ class _OrdersManagementScreenState extends State<OrdersManagementScreen> {
}
}
List<Order> get activeOrders {
return orders
.where(
@ -347,7 +342,6 @@ class _OrdersManagementScreenState extends State<OrdersManagementScreen> {
style: TextStyle(color: Colors.white),
),
),
],
);
},
@ -357,7 +351,8 @@ class _OrdersManagementScreenState extends State<OrdersManagementScreen> {
}
Future<void> updateTableStatus(int tableId, String newStatus) async {
const String apiUrl = 'https://restaurant.careeracademy.mg/api/tables'; // adapte lURL si besoin
const String apiUrl =
'https://restaurant.careeracademy.mg/api/tables'; // adapte lURL si besoin
final response = await http.put(
Uri.parse('$apiUrl/$tableId'),
@ -603,6 +598,17 @@ class OrderCard extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: onViewDetails,
child: Text(
'Table ${order.tableId}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
@ -621,24 +627,6 @@ class OrderCard extends StatelessWidget {
),
),
),
const SizedBox(width: 4),
if (order.statut == 'en_attente')
IconButton(
icon: const Icon(Icons.add_circle_outline, size: 20, color: Colors.blue),
tooltip: 'Ajouter un article',
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddItemsToOrderPage(
commandeId: order.id,
numeroCommande: order.numero ?? 'Commande #${order.id}',
),
),
);
},
),
],
),
const SizedBox(height: 8),
@ -731,7 +719,8 @@ class OrderCard extends StatelessWidget {
order.statut == 'en_preparation')
Row(
children: [
if (order.statut == 'en_attente')
if (order.statut == 'en_attente' ||
order.statut == 'en_preparation')
Expanded(
child: ElevatedButton(
onPressed:
@ -741,22 +730,28 @@ class OrderCard extends StatelessWidget {
MaterialPageRoute(
builder:
(context) => AddItemsToOrderPage(
commandId: order.id,
commandeId: order.id,
numeroCommande: order.numeroCommande,
),
),
),
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
backgroundColor: const Color.fromARGB(
255,
0,
76,
255,
),
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
),
child: const Text(
'Préparer',
style: TextStyle(color: Colors.white, fontSize: 12),
child: const Icon(
Icons.add,
color: Colors.white,
size: 16,
),
),
),
@ -860,8 +855,6 @@ class OrderCard extends StatelessWidget {
}
}
// Updated Order model to include items
class Order {
final int id;

Loading…
Cancel
Save