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

59
lib/pages/commandes_screen.dart

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

Loading…
Cancel
Save