import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../app/app_state.dart'; import '../models/cart.dart'; import '../models/menu_item.dart'; import '../services/api.dart'; import '../services/order_polling_service.dart'; class CartViewScreen extends StatefulWidget { const CartViewScreen({super.key}); @override State createState() => _CartViewScreenState(); } class _CartViewScreenState extends State { Cart? _cart; bool _isLoading = true; String? _error; Map _menuItemsById = {}; @override void initState() { super.initState(); _loadCart(); } Future _loadCart() async { setState(() { _isLoading = true; _error = null; }); try { final appState = context.read(); final cartOrderId = appState.cartOrderId; if (cartOrderId == null) { setState(() { _isLoading = false; _cart = null; }); return; } // Load cart final cart = await Api.getCart(orderId: cartOrderId); // Load menu items to get names and prices final businessId = appState.selectedBusinessId; if (businessId != null) { final menuItems = await Api.listMenuItems(businessId: businessId); _menuItemsById = {for (var item in menuItems) item.itemId: item}; } setState(() { _cart = cart; _isLoading = false; }); // Update item count in app state appState.updateCartItemCount(cart.itemCount); } catch (e) { setState(() { _error = e.toString(); _isLoading = false; }); } } Future _removeLineItem(OrderLineItem lineItem) async { try { final appState = context.read(); final cartOrderId = appState.cartOrderId; if (cartOrderId == null) return; setState(() => _isLoading = true); // Set IsSelected=false to remove the item await Api.setLineItem( orderId: cartOrderId, parentOrderLineItemId: lineItem.parentOrderLineItemId, itemId: lineItem.itemId, isSelected: false, ); // Reload cart await _loadCart(); } catch (e) { setState(() { _error = e.toString(); _isLoading = false; }); } } Future _updateQuantity(OrderLineItem lineItem, int newQuantity) async { if (newQuantity < 1) return; try { final appState = context.read(); final cartOrderId = appState.cartOrderId; if (cartOrderId == null) return; setState(() => _isLoading = true); await Api.setLineItem( orderId: cartOrderId, parentOrderLineItemId: lineItem.parentOrderLineItemId, itemId: lineItem.itemId, isSelected: true, quantity: newQuantity, remark: lineItem.remark, ); // Reload cart await _loadCart(); } catch (e) { setState(() { _error = e.toString(); _isLoading = false; }); } } Future _submitOrder() async { try { final appState = context.read(); final cartOrderId = appState.cartOrderId; if (cartOrderId == null) return; setState(() => _isLoading = true); await Api.submitOrder(orderId: cartOrderId); // Set active order for polling (status 1 = submitted) appState.setActiveOrder(orderId: cartOrderId, statusId: 1); // Start polling for status updates OrderPollingService.startPolling( orderId: cartOrderId, initialStatusId: 1, onStatusUpdate: (update) { // Update app state appState.updateActiveOrderStatus(update.statusId); // Show notification if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(update.message), backgroundColor: _getStatusColor(update.statusId), duration: const Duration(seconds: 5), behavior: SnackBarBehavior.floating, ), ); } }, ); // Clear cart state appState.clearCart(); if (!mounted) return; // Show success message ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Order submitted successfully! You'll receive notifications as your order is prepared."), backgroundColor: Colors.green, duration: Duration(seconds: 5), ), ); // Navigate back Navigator.of(context).pop(); } catch (e) { setState(() { _error = e.toString(); _isLoading = false; }); } } Color _getStatusColor(int statusId) { switch (statusId) { case 1: // Submitted return Colors.blue; case 2: // Preparing return Colors.orange; case 3: // Ready return Colors.green; case 4: // Completed return Colors.purple; default: return Colors.grey; } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Cart"), backgroundColor: Colors.black, foregroundColor: Colors.white, ), body: _isLoading ? const Center(child: CircularProgressIndicator()) : _error != null ? Center( child: Padding( padding: const EdgeInsets.all(16), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error, color: Colors.red, size: 48), const SizedBox(height: 16), Text( "Error loading cart", style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 8), Text(_error!, textAlign: TextAlign.center), const SizedBox(height: 16), ElevatedButton( onPressed: _loadCart, child: const Text("Retry"), ), ], ), ), ) : _cart == null || _cart!.itemCount == 0 ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.shopping_cart_outlined, size: 80, color: Colors.grey, ), const SizedBox(height: 16), Text( "Your cart is empty", style: Theme.of(context).textTheme.titleLarge, ), ], ), ) : Column( children: [ Expanded( child: ListView( padding: const EdgeInsets.all(16), children: _buildCartItems(), ), ), _buildCartSummary(), ], ), ); } List _buildCartItems() { if (_cart == null) return []; // Group line items by root items final rootItems = _cart!.lineItems .where((item) => item.parentOrderLineItemId == 0 && !item.isDeleted) .toList(); final widgets = []; for (final rootItem in rootItems) { widgets.add(_buildRootItemCard(rootItem)); widgets.add(const SizedBox(height: 12)); } return widgets; } Widget _buildRootItemCard(OrderLineItem rootItem) { final menuItem = _menuItemsById[rootItem.itemId]; final itemName = menuItem?.name ?? "Item #${rootItem.itemId}"; // Find all modifiers for this root item final modifiers = _cart!.lineItems .where((item) => item.parentOrderLineItemId == rootItem.orderLineItemId && !item.isDeleted) .toList(); return Card( child: Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( itemName, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ), IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () => _confirmRemoveItem(rootItem, itemName), ), ], ), if (modifiers.isNotEmpty) ...[ const SizedBox(height: 8), ...modifiers.map((mod) => _buildModifierRow(mod)), ], const SizedBox(height: 8), Row( children: [ IconButton( icon: const Icon(Icons.remove_circle_outline), onPressed: rootItem.quantity > 1 ? () => _updateQuantity(rootItem, rootItem.quantity - 1) : null, ), Text( "${rootItem.quantity}", style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), IconButton( icon: const Icon(Icons.add_circle_outline), onPressed: () => _updateQuantity(rootItem, rootItem.quantity + 1), ), const Spacer(), Text( "\$${rootItem.price.toStringAsFixed(2)}", style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ], ), ], ), ), ); } Widget _buildModifierRow(OrderLineItem modifier) { final menuItem = _menuItemsById[modifier.itemId]; final modName = menuItem?.name ?? "Modifier #${modifier.itemId}"; return Padding( padding: const EdgeInsets.only(left: 16, top: 4), child: Row( children: [ const Icon(Icons.add, size: 12, color: Colors.grey), const SizedBox(width: 4), Expanded( child: Text( modName, style: const TextStyle( fontSize: 14, color: Colors.grey, ), ), ), if (modifier.price > 0) Text( "+\$${modifier.price.toStringAsFixed(2)}", style: const TextStyle( fontSize: 14, color: Colors.grey, ), ), ], ), ); } Widget _buildCartSummary() { if (_cart == null) return const SizedBox.shrink(); return Container( decoration: BoxDecoration( color: Colors.grey[100], boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 4, offset: const Offset(0, -2), ), ], ), padding: const EdgeInsets.all(16), child: SafeArea( child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( "Subtotal", style: TextStyle(fontSize: 16), ), Text( "\$${_cart!.subtotal.toStringAsFixed(2)}", style: const TextStyle(fontSize: 16), ), ], ), if (_cart!.deliveryFee > 0) ...[ const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( "Delivery Fee", style: TextStyle(fontSize: 16), ), Text( "\$${_cart!.deliveryFee.toStringAsFixed(2)}", style: const TextStyle(fontSize: 16), ), ], ), ], const Divider(height: 24), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( "Total", style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), Text( "\$${_cart!.total.toStringAsFixed(2)}", style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _cart!.itemCount > 0 ? _submitOrder : null, style: ElevatedButton.styleFrom( backgroundColor: Colors.black, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), ), child: const Text( "Submit Order", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ), ), ], ), ), ); } void _confirmRemoveItem(OrderLineItem item, String itemName) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text("Remove Item"), content: Text("Remove $itemName from cart?"), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text("Cancel"), ), TextButton( onPressed: () { Navigator.of(context).pop(); _removeLineItem(item); }, style: TextButton.styleFrom(foregroundColor: Colors.red), child: const Text("Remove"), ), ], ), ); } }