import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../models/order_detail.dart'; import '../services/api.dart'; class OrderDetailScreen extends StatefulWidget { final int orderId; const OrderDetailScreen({super.key, required this.orderId}); @override State createState() => _OrderDetailScreenState(); } class _OrderDetailScreenState extends State { OrderDetail? _order; bool _isLoading = true; String? _error; @override void initState() { super.initState(); _loadOrder(); } Future _loadOrder() async { setState(() { _isLoading = true; _error = null; }); try { final order = await Api.getOrderDetail(orderId: widget.orderId); if (mounted) { setState(() { _order = order; _isLoading = false; }); } } catch (e) { debugPrint('Error loading order detail: $e'); if (mounted) { setState(() { _error = e.toString(); _isLoading = false; }); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Order #${widget.orderId}'), ), body: _buildBody(), ); } Widget _buildBody() { if (_isLoading) { return const Center(child: CircularProgressIndicator()); } if (_error != null) { return Center( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.error_outline, size: 48, color: Theme.of(context).colorScheme.error, ), const SizedBox(height: 16), Text( 'Failed to load order', style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 8), Text( _error!, textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodySmall, ), const SizedBox(height: 24), FilledButton.icon( onPressed: _loadOrder, icon: const Icon(Icons.refresh), label: const Text('Retry'), ), ], ), ), ); } final order = _order; if (order == null) { return const Center(child: Text('Order not found')); } return RefreshIndicator( onRefresh: _loadOrder, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildOrderHeader(order), const SizedBox(height: 16), _buildStatusCard(order), const SizedBox(height: 16), _buildItemsCard(order), const SizedBox(height: 16), _buildTotalsCard(order), if (order.notes.isNotEmpty) ...[ const SizedBox(height: 16), _buildNotesCard(order), ], const SizedBox(height: 32), ], ), ), ); } Widget _buildOrderHeader(OrderDetail order) { final dateFormat = DateFormat('MMM d, yyyy'); final timeFormat = DateFormat('h:mm a'); final displayDate = order.submittedOn ?? order.createdOn; return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( order.businessName.isNotEmpty ? order.businessName : 'Order', style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, ), ), ), _buildOrderTypeChip(order.orderTypeId, order.orderTypeName), ], ), const SizedBox(height: 8), Row( children: [ Icon( Icons.calendar_today, size: 14, color: Theme.of(context).colorScheme.onSurfaceVariant, ), const SizedBox(width: 4), Text( '${dateFormat.format(displayDate)} at ${timeFormat.format(displayDate)}', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), ], ), if (order.servicePoint.name.isNotEmpty) ...[ const SizedBox(height: 4), Row( children: [ Icon( Icons.table_restaurant, size: 14, color: Theme.of(context).colorScheme.onSurfaceVariant, ), const SizedBox(width: 4), Text( order.servicePoint.name, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), ], ), ], ], ), ), ); } Widget _buildOrderTypeChip(int typeId, String typeName) { IconData icon; Color color; switch (typeId) { case 1: // Dine-in icon = Icons.restaurant; color = Colors.orange; break; case 2: // Takeaway icon = Icons.shopping_bag; color = Colors.blue; break; case 3: // Delivery icon = Icons.delivery_dining; color = Colors.green; break; default: icon = Icons.receipt; color = Colors.grey; } return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: Border.all(color: color.withOpacity(0.3)), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 14, color: color), const SizedBox(width: 4), Text( typeName, style: TextStyle( color: color, fontSize: 12, fontWeight: FontWeight.w500, ), ), ], ), ); } Widget _buildStatusCard(OrderDetail order) { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Row( children: [ _buildStatusIcon(order.status), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Status', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), Text( order.statusText, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ), ), ], ), ), ], ), ), ); } Widget _buildStatusIcon(int status) { IconData icon; Color color; switch (status) { case 1: // Submitted icon = Icons.send; color = Colors.blue; break; case 2: // In Progress icon = Icons.pending; color = Colors.orange; break; case 3: // Ready icon = Icons.check_circle_outline; color = Colors.green; break; case 4: // Completed icon = Icons.check_circle; color = Colors.green; break; case 5: // Cancelled icon = Icons.cancel; color = Colors.red; break; default: // Cart or Unknown icon = Icons.shopping_cart; color = Colors.grey; } return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: color.withOpacity(0.1), shape: BoxShape.circle, ), child: Icon(icon, color: color, size: 24), ); } Widget _buildItemsCard(OrderDetail order) { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Items', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ), ), const SizedBox(height: 12), ...order.lineItems.map((item) => _buildLineItem(item)), ], ), ), ); } Widget _buildLineItem(OrderLineItemDetail item) { final nonDefaultMods = item.nonDefaultModifiers; return Padding( padding: const EdgeInsets.only(bottom: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Quantity badge Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: Theme.of(context).colorScheme.primaryContainer, borderRadius: BorderRadius.circular(4), ), child: Text( '${item.quantity}x', style: TextStyle( color: Theme.of(context).colorScheme.onPrimaryContainer, fontSize: 12, fontWeight: FontWeight.bold, ), ), ), const SizedBox(width: 8), // Item name Expanded( child: Text( item.itemName, style: Theme.of(context).textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, ), ), ), // Price Text( '\$${item.totalPrice.toStringAsFixed(2)}', style: Theme.of(context).textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, ), ), ], ), // Non-default modifiers if (nonDefaultMods.isNotEmpty) ...[ const SizedBox(height: 4), Padding( padding: const EdgeInsets.only(left: 36), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: nonDefaultMods.map((mod) { final modPrice = mod.unitPrice > 0 ? ' (+\$${mod.unitPrice.toStringAsFixed(2)})' : ''; return Padding( padding: const EdgeInsets.only(top: 2), child: Text( '+ ${mod.itemName}$modPrice', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), ); }).toList(), ), ), ], // Item remarks/notes if (item.remarks.isNotEmpty) ...[ const SizedBox(height: 4), Padding( padding: const EdgeInsets.only(left: 36), child: Text( '"${item.remarks}"', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Colors.orange[700], fontStyle: FontStyle.italic, ), ), ), ], const Divider(height: 16), ], ), ); } Widget _buildTotalsCard(OrderDetail order) { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ _buildTotalRow('Subtotal', order.subtotal), const SizedBox(height: 8), _buildTotalRow('Tax', order.tax), if (order.tip > 0) ...[ const SizedBox(height: 8), _buildTotalRow('Tip', order.tip), ], const Divider(height: 16), _buildTotalRow('Total', order.total, isTotal: true), ], ), ), ); } Widget _buildTotalRow(String label, double amount, {bool isTotal = false}) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, style: isTotal ? Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ) : Theme.of(context).textTheme.bodyMedium, ), Text( '\$${amount.toStringAsFixed(2)}', style: isTotal ? Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.primary, ) : Theme.of(context).textTheme.bodyMedium, ), ], ); } Widget _buildNotesCard(OrderDetail order) { return Card( color: Colors.amber[50], child: Padding( padding: const EdgeInsets.all(16), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(Icons.note, color: Colors.amber[700], size: 20), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Order Notes', style: TextStyle( fontSize: 12, color: Colors.amber[800], fontWeight: FontWeight.w600, ), ), const SizedBox(height: 4), Text( order.notes, style: TextStyle( fontSize: 14, color: Colors.amber[900], ), ), ], ), ), ], ), ), ); } }