From 7ce4239772d4a22f8a5b3bf6ef1d8a486fca1ead Mon Sep 17 00:00:00 2001 From: Phuoc Nguyen Date: Mon, 27 Oct 2025 15:01:34 +0700 Subject: [PATCH] order detail --- lib/core/router/app_router.dart | 14 + .../presentation/pages/order_detail_page.dart | 1057 +++++++++++++++++ .../presentation/pages/orders_page.dart | 10 +- .../presentation/widgets/order_card.dart | 114 +- 4 files changed, 1124 insertions(+), 71 deletions(-) create mode 100644 lib/features/orders/presentation/pages/order_detail_page.dart diff --git a/lib/core/router/app_router.dart b/lib/core/router/app_router.dart index 9866192..86c4430 100644 --- a/lib/core/router/app_router.dart +++ b/lib/core/router/app_router.dart @@ -10,6 +10,7 @@ import 'package:worker/features/cart/presentation/pages/cart_page.dart'; import 'package:worker/features/favorites/presentation/pages/favorites_page.dart'; import 'package:worker/features/loyalty/presentation/pages/rewards_page.dart'; import 'package:worker/features/main/presentation/pages/main_scaffold.dart'; +import 'package:worker/features/orders/presentation/pages/order_detail_page.dart'; import 'package:worker/features/orders/presentation/pages/orders_page.dart'; import 'package:worker/features/orders/presentation/pages/payment_detail_page.dart'; import 'package:worker/features/orders/presentation/pages/payments_page.dart'; @@ -119,6 +120,19 @@ class AppRouter { ), ), + // Order Detail Route + GoRoute( + path: RouteNames.orderDetail, + name: RouteNames.orderDetail, + pageBuilder: (context, state) { + final orderId = state.pathParameters['id']; + return MaterialPage( + key: state.pageKey, + child: OrderDetailPage(orderId: orderId ?? ''), + ); + }, + ), + // Payments Route GoRoute( path: RouteNames.payments, diff --git a/lib/features/orders/presentation/pages/order_detail_page.dart b/lib/features/orders/presentation/pages/order_detail_page.dart new file mode 100644 index 0000000..8271872 --- /dev/null +++ b/lib/features/orders/presentation/pages/order_detail_page.dart @@ -0,0 +1,1057 @@ +/// Page: Order Detail Page +/// +/// Displays detailed order information including status timeline, delivery info, and products. +library; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:intl/intl.dart'; +import 'package:worker/core/constants/ui_constants.dart'; +import 'package:worker/core/database/models/enums.dart'; +import 'package:worker/core/theme/colors.dart'; + +/// Order Detail Page +/// +/// Features: +/// - Status timeline with progress tracking +/// - Delivery information +/// - Customer details +/// - Product list +/// - Order summary +/// - Action buttons (Contact customer, Update status) +class OrderDetailPage extends ConsumerWidget { + + const OrderDetailPage({ + required this.orderId, + super.key, + }); + final String orderId; + + @override + Widget build(BuildContext context, WidgetRef ref) { + // TODO: Replace with actual order data from provider + // For now using mock data based on HTML reference + final mockOrder = _getMockOrder(); + + return Scaffold( + backgroundColor: const Color(0xFFF4F6F8), + appBar: AppBar( + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.black), + onPressed: () => context.pop(), + ), + title: const Text( + 'Chi tiết đơn hàng', + style: TextStyle(color: Colors.black), + ), + actions: [ + IconButton( + icon: const Icon(Icons.share, color: Colors.black), + onPressed: () { + // TODO: Implement share functionality + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Chức năng chia sẻ đang phát triển')), + ); + }, + ), + IconButton( + icon: const Icon(Icons.print, color: Colors.black), + onPressed: () { + // TODO: Implement print functionality + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Chức năng in đang phát triển')), + ); + }, + ), + ], + elevation: AppBarSpecs.elevation, + backgroundColor: AppColors.white, + foregroundColor: AppColors.grey900, + centerTitle: false, + ), + body: Stack( + children: [ + SingleChildScrollView( + padding: const EdgeInsets.only(bottom: 100), + child: Column( + children: [ + // Status Timeline Card + _buildStatusTimelineCard( + mockOrder['orderNumber']! as String, + mockOrder['status']! as OrderStatus, + mockOrder['statusHistory']! as List>, + ), + + // Delivery Information Card + _buildDeliveryInfoCard( + mockOrder['deliveryMethod']! as String, + mockOrder['warehouseDate']! as DateTime, + mockOrder['deliveryDate']! as DateTime, + mockOrder['deliveryAddress']! as String, + mockOrder['receiverName']! as String, + mockOrder['receiverPhone']! as String, + ), + + // Customer Information Card + _buildCustomerInfoCard( + mockOrder['customerName']! as String, + mockOrder['customerPhone']! as String, + mockOrder['customerEmail']! as String, + mockOrder['customerType']! as String, + ), + + // Products List Card + _buildProductsListCard(), + + // Order Summary Card + _buildOrderSummaryCard( + mockOrder['subtotal']! as double, + mockOrder['shippingFee']! as double, + mockOrder['discount']! as double, + mockOrder['total']! as double, + mockOrder['paymentMethod']! as String, + mockOrder['notes'] as String?, + ), + ], + ), + ), + + // Fixed Action Buttons + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Container( + decoration: BoxDecoration( + color: AppColors.white, + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.1), + blurRadius: 15, + offset: const Offset(0, -4), + ), + ], + ), + padding: const EdgeInsets.all(16), + child: Row( + spacing: 12, + children: [ + Expanded( + child: OutlinedButton.icon( + onPressed: () { + // TODO: Implement contact customer + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Gọi điện cho khách hàng...')), + ); + }, + icon: const Icon(Icons.phone), + label: const Text('Liên hệ khách hàng'), + style: OutlinedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 12), + side: const BorderSide(color: AppColors.grey100, width: 2), + foregroundColor: AppColors.grey900, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ), + Expanded( + child: ElevatedButton.icon( + onPressed: () { + // TODO: Implement update status + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Cập nhật trạng thái...')), + ); + }, + icon: const Icon(Icons.edit), + label: const Text('Cập nhật trạng thái'), + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 12), + backgroundColor: AppColors.primaryBlue, + foregroundColor: Colors.white, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ), + ), + ], + ), + ), + ), + ], + ), + ); + } + + /// Build Status Timeline Card + Widget _buildStatusTimelineCard( + String orderNumber, + OrderStatus currentStatus, + List> statusHistory, + ) { + return Card( + margin: const EdgeInsets.all(16), + elevation: 1, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Order Number and Status Badge + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '#$orderNumber', + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + color: AppColors.primaryBlue, + ), + ), + _buildStatusBadge(currentStatus), + ], + ), + + const SizedBox(height: 24), + + // Status Timeline + ...statusHistory.asMap().entries.map((entry) { + final index = entry.key; + final item = entry.value; + final isLast = index == statusHistory.length - 1; + + return _buildTimelineItem( + title: item['title']! as String, + date: item['date'] as String?, + status: item['status']! as String, + isLast: isLast, + ); + }), + ], + ), + ), + ); + } + + /// Build Timeline Item + Widget _buildTimelineItem({ + required String title, + String? date, + required String status, // 'completed', 'active', 'pending' + required bool isLast, + }) { + Color iconColor; + Color iconBgColor; + IconData iconData; + + switch (status) { + case 'completed': + iconColor = Colors.white; + iconBgColor = AppColors.success; + iconData = Icons.check; + break; + case 'active': + iconColor = Colors.white; + iconBgColor = AppColors.warning; + iconData = Icons.settings; + break; + default: // pending + iconColor = AppColors.grey500; + iconBgColor = AppColors.grey100; + iconData = _getIconForTitle(title); + } + + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Timeline icon and line + Column( + children: [ + Container( + width: 24, + height: 24, + decoration: BoxDecoration( + color: iconBgColor, + shape: BoxShape.circle, + ), + child: Icon( + iconData, + size: 12, + color: iconColor, + ), + ), + if (!isLast) + Container( + width: 2, + height: 40, + color: AppColors.grey100, + margin: const EdgeInsets.symmetric(vertical: 4), + ), + ], + ), + + const SizedBox(width: 16), + + // Timeline content + Expanded( + child: Padding( + padding: EdgeInsets.only(bottom: isLast ? 0 : 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: AppColors.grey900, + ), + ), + const SizedBox(height: 2), + Text( + date ?? 'Đang cập nhật', + style: const TextStyle( + fontSize: 12, + color: AppColors.grey500, + ), + ), + ], + ), + ), + ), + ], + ); + } + + /// Get icon for timeline title + IconData _getIconForTitle(String title) { + if (title.contains('Vận chuyển')) { + return Icons.local_shipping; + } else if (title.contains('Giao hàng')) { + return Icons.inventory_2; + } + return Icons.circle; + } + + /// Build Status Badge + Widget _buildStatusBadge(OrderStatus status) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: _getStatusColor(status).withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: _getStatusColor(status).withValues(alpha: 0.3), + width: 1, + ), + ), + child: Text( + _getStatusText(status), + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: _getStatusColor(status), + ), + ), + ); + } + + /// Get status color + Color _getStatusColor(OrderStatus status) { + switch (status) { + case OrderStatus.draft: + return AppColors.grey500; + case OrderStatus.pending: + return AppColors.info; + case OrderStatus.confirmed: + return AppColors.info; + case OrderStatus.processing: + return AppColors.warning; + case OrderStatus.shipped: + return AppColors.primaryBlue; + case OrderStatus.delivered: + return AppColors.success; + case OrderStatus.completed: + return AppColors.success; + case OrderStatus.cancelled: + return AppColors.danger; + case OrderStatus.refunded: + return const Color(0xFFF97316); + } + } + + /// Get status text + String _getStatusText(OrderStatus status) { + switch (status) { + case OrderStatus.draft: + return 'Nháp'; + case OrderStatus.pending: + return 'Chờ xác nhận'; + case OrderStatus.confirmed: + return 'Đã xác nhận'; + case OrderStatus.processing: + return 'Đang xử lý'; + case OrderStatus.shipped: + return 'Đang vận chuyển'; + case OrderStatus.delivered: + return 'Đã giao hàng'; + case OrderStatus.completed: + return 'Hoàn thành'; + case OrderStatus.cancelled: + return 'Đã hủy'; + case OrderStatus.refunded: + return 'Đã hoàn tiền'; + } + } + + /// Build Delivery Info Card + Widget _buildDeliveryInfoCard( + String deliveryMethod, + DateTime warehouseDate, + DateTime deliveryDate, + String deliveryAddress, + String receiverName, + String receiverPhone, + ) { + final dateFormatter = DateFormat('dd/MM/yyyy'); + + return Card( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + elevation: 1, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon(Icons.local_shipping, color: AppColors.primaryBlue, size: 20), + const SizedBox(width: 8), + const Text( + 'Thông tin giao hàng', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.grey900, + ), + ), + ], + ), + + const SizedBox(height: 16), + + // Delivery Method + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.grey50, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: AppColors.primaryBlue, + borderRadius: BorderRadius.circular(8), + ), + child: const Icon( + Icons.local_shipping, + color: Colors.white, + size: 20, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + deliveryMethod, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: AppColors.grey900, + ), + ), + const SizedBox(height: 2), + const Text( + 'Giao trong 3-5 ngày làm việc', + style: TextStyle( + fontSize: 12, + color: AppColors.grey500, + ), + ), + ], + ), + ), + ], + ), + ), + + const SizedBox(height: 16), + + // Delivery Details + _buildInfoRow( + icon: Icons.calendar_today, + label: 'Ngày xuất kho', + value: dateFormatter.format(warehouseDate), + valueColor: AppColors.success, + ), + + const SizedBox(height: 12), + + _buildInfoRow( + icon: Icons.access_time, + label: 'Thời gian giao hàng', + value: '${dateFormatter.format(deliveryDate)}, 8:00 - 17:00', + ), + + const SizedBox(height: 12), + + _buildInfoRow( + icon: Icons.location_on, + label: 'Địa chỉ giao hàng', + value: deliveryAddress, + ), + + const SizedBox(height: 12), + + _buildInfoRow( + icon: Icons.person, + label: 'Người nhận', + value: '$receiverName - $receiverPhone', + ), + ], + ), + ), + ); + } + + /// Build Info Row + Widget _buildInfoRow({ + required IconData icon, + required String label, + required String value, + Color? valueColor, + }) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 2, + child: Row( + children: [ + Icon(icon, size: 16, color: AppColors.grey500), + const SizedBox(width: 6), + Expanded( + child: Text( + label, + style: const TextStyle( + fontSize: 14, + color: AppColors.grey500, + ), + ), + ), + ], + ), + ), + const SizedBox(width: 12), + Expanded( + flex: 2, + child: Text( + value, + textAlign: TextAlign.right, + style: TextStyle( + fontSize: 14, + fontWeight: valueColor != null ? FontWeight.w600 : FontWeight.w500, + color: valueColor ?? AppColors.grey900, + ), + ), + ), + ], + ); + } + + /// Build Customer Info Card + Widget _buildCustomerInfoCard( + String customerName, + String customerPhone, + String customerEmail, + String customerType, + ) { + return Card( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + elevation: 1, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon(Icons.person_outline, color: AppColors.primaryBlue, size: 20), + const SizedBox(width: 8), + const Text( + 'Thông tin khách hàng', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.grey900, + ), + ), + ], + ), + + const SizedBox(height: 16), + + _buildCustomerRow('Tên khách hàng:', customerName), + const SizedBox(height: 12), + + _buildCustomerRow('Số điện thoại:', customerPhone), + const SizedBox(height: 12), + + _buildCustomerRow('Email:', customerEmail), + const SizedBox(height: 12), + + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Loại khách hàng:', + style: TextStyle( + fontSize: 14, + color: AppColors.grey500, + ), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), + decoration: BoxDecoration( + gradient: const LinearGradient( + colors: [Color(0xFFFFD700), Color(0xFFFFA500)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(12), + ), + child: Text( + customerType, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Colors.white, + ), + ), + ), + ], + ), + ], + ), + ), + ); + } + + /// Build Customer Row + Widget _buildCustomerRow(String label, String value) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + label, + style: const TextStyle( + fontSize: 14, + color: AppColors.grey500, + ), + ), + Text( + value, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: AppColors.grey900, + ), + ), + ], + ); + } + + /// Build Products List Card + Widget _buildProductsListCard() { + final products = [ + { + 'name': 'Gạch Eurotile MỘC LAM E03', + 'size': '60x60cm', + 'sku': 'ET-ML-E03-60x60', + 'quantity': '30 m²', + 'unitPrice': '285.000đ/m²', + 'totalPrice': '8.550.000đ', + }, + { + 'name': 'Gạch Eurotile STONE GREY S02', + 'size': '80x80cm', + 'sku': 'ET-SG-S02-80x80', + 'quantity': '20 m²', + 'unitPrice': '217.500đ/m²', + 'totalPrice': '4.350.000đ', + }, + ]; + + return Card( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + elevation: 1, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon(Icons.inventory_2, color: AppColors.primaryBlue, size: 20), + const SizedBox(width: 8), + const Text( + 'Sản phẩm đặt hàng', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.grey900, + ), + ), + ], + ), + + const SizedBox(height: 16), + + ...products.map((product) => Container( + margin: const EdgeInsets.only(bottom: 12), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + border: Border.all(color: AppColors.grey100), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Product Image + Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: AppColors.grey50, + borderRadius: BorderRadius.circular(6), + ), + child: const Icon( + Icons.image, + color: AppColors.grey500, + size: 30, + ), + ), + + const SizedBox(width: 12), + + // Product Info + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + product['name']!, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: AppColors.grey900, + ), + ), + const SizedBox(height: 4), + Text( + 'Kích thước: ${product['size']}', + style: const TextStyle( + fontSize: 12, + color: AppColors.grey500, + ), + ), + Text( + 'SKU: ${product['sku']}', + style: const TextStyle( + fontSize: 12, + color: AppColors.grey500, + ), + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Số lượng:', + style: TextStyle( + fontSize: 11, + color: AppColors.grey500, + ), + ), + Text( + product['quantity']!, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: AppColors.grey900, + ), + ), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + product['unitPrice']!, + style: const TextStyle( + fontSize: 12, + color: AppColors.grey500, + ), + ), + Text( + product['totalPrice']!, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: AppColors.danger, + ), + ), + ], + ), + ], + ), + ], + ), + ), + ], + ), + )), + ], + ), + ), + ); + } + + /// Build Order Summary Card + Widget _buildOrderSummaryCard( + double subtotal, + double shippingFee, + double discount, + double total, + String paymentMethod, + String? notes, + ) { + final currencyFormatter = NumberFormat.currency( + locale: 'vi_VN', + symbol: 'đ', + decimalDigits: 0, + ); + + return Card( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + elevation: 1, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon(Icons.receipt_long, color: AppColors.primaryBlue, size: 20), + const SizedBox(width: 8), + const Text( + 'Tổng kết đơn hàng', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.grey900, + ), + ), + ], + ), + + const SizedBox(height: 16), + + _buildSummaryRow('Tạm tính:', currencyFormatter.format(subtotal)), + const SizedBox(height: 8), + + _buildSummaryRow( + 'Phí vận chuyển:', + shippingFee == 0 ? 'Miễn phí' : currencyFormatter.format(shippingFee), + valueColor: shippingFee == 0 ? AppColors.success : null, + ), + const SizedBox(height: 8), + + _buildSummaryRow( + 'Giảm giá VIP:', + '-${currencyFormatter.format(discount)}', + valueColor: AppColors.success, + ), + + const Divider(height: 24), + + _buildSummaryRow( + 'Tổng cộng:', + currencyFormatter.format(total), + isTotal: true, + ), + + const Divider(height: 24), + + // Payment Method + Row( + children: [ + const Icon(Icons.credit_card, size: 16, color: AppColors.grey500), + const SizedBox(width: 6), + const Text( + 'Phương thức thanh toán:', + style: TextStyle( + fontSize: 14, + color: AppColors.grey500, + ), + ), + ], + ), + const SizedBox(height: 4), + Text( + paymentMethod, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: AppColors.grey900, + ), + ), + + if (notes != null) ...[ + const Divider(height: 24), + + // Order Notes + Row( + children: [ + const Icon(Icons.note, size: 16, color: AppColors.grey500), + const SizedBox(width: 6), + const Text( + 'Ghi chú đơn hàng:', + style: TextStyle( + fontSize: 14, + color: AppColors.grey500, + ), + ), + ], + ), + const SizedBox(height: 4), + Text( + notes, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: AppColors.grey900, + height: 1.4, + ), + ), + ], + ], + ), + ), + ); + } + + /// Build Summary Row + Widget _buildSummaryRow(String label, String value, {bool isTotal = false, Color? valueColor}) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + label, + style: TextStyle( + fontSize: isTotal ? 14 : 14, + fontWeight: isTotal ? FontWeight.w600 : FontWeight.normal, + color: isTotal ? AppColors.grey900 : AppColors.grey500, + ), + ), + Text( + value, + style: TextStyle( + fontSize: isTotal ? 16 : 14, + fontWeight: isTotal ? FontWeight.w700 : FontWeight.w500, + color: valueColor ?? (isTotal ? AppColors.danger : AppColors.grey900), + ), + ), + ], + ); + } + + /// Get mock order data for development + Map _getMockOrder() { + return { + 'orderNumber': 'DH001234', + 'status': OrderStatus.processing, + 'statusHistory': [ + { + 'title': 'Đơn hàng được tạo', + 'date': '03/08/2023 - 09:30', + 'status': 'completed', + }, + { + 'title': 'Đã xác nhận đơn hàng', + 'date': '03/08/2023 - 10:15', + 'status': 'completed', + }, + { + 'title': 'Đang chuẩn bị hàng', + 'date': 'Đang thực hiện', + 'status': 'active', + }, + { + 'title': 'Vận chuyển', + 'date': 'Dự kiến: 05/08/2023', + 'status': 'pending', + }, + { + 'title': 'Giao hàng thành công', + 'date': 'Dự kiến: 07/08/2023', + 'status': 'pending', + }, + ], + 'deliveryMethod': 'Giao hàng tiêu chuẩn', + 'warehouseDate': DateTime(2023, 8, 5), + 'deliveryDate': DateTime(2023, 8, 7), + 'deliveryAddress': '123 Đường Lê Văn Lương, Phường Tân Hưng,\nQuận 7, TP. Hồ Chí Minh', + 'receiverName': 'Nguyễn Văn A', + 'receiverPhone': '0901234567', + 'customerName': 'Nguyễn Văn A', + 'customerPhone': '0901234567', + 'customerEmail': 'nguyenvana@email.com', + 'customerType': 'Khách VIP', + 'subtotal': 12900000.0, + 'shippingFee': 0.0, + 'discount': 129000.0, + 'total': 12771000.0, + 'paymentMethod': 'Chuyển khoản ngân hàng', + 'notes': 'Giao hàng trong giờ hành chính. Vui lòng gọi trước 30 phút khi đến giao hàng.', + }; + } +} diff --git a/lib/features/orders/presentation/pages/orders_page.dart b/lib/features/orders/presentation/pages/orders_page.dart index 5f72961..4f32b78 100644 --- a/lib/features/orders/presentation/pages/orders_page.dart +++ b/lib/features/orders/presentation/pages/orders_page.dart @@ -107,15 +107,7 @@ class _OrdersPageState extends ConsumerState { return OrderCard( order: order, onTap: () { - // TODO: Navigate to order detail page - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - 'Order ${order.orderNumber} tapped', - ), - duration: const Duration(seconds: 1), - ), - ); + context.push('/orders/${order.orderId}'); }, ); }, diff --git a/lib/features/orders/presentation/widgets/order_card.dart b/lib/features/orders/presentation/widgets/order_card.dart index 2275089..bba5fa8 100644 --- a/lib/features/orders/presentation/widgets/order_card.dart +++ b/lib/features/orders/presentation/widgets/order_card.dart @@ -45,74 +45,64 @@ class OrderCard extends StatelessWidget { clipBehavior: Clip.antiAlias, child: InkWell( onTap: onTap, - child: Container( - decoration: BoxDecoration( - border: Border( - left: BorderSide( - color: _getStatusColor(order.status), - width: 4, + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Order number and amount row + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Order number + Text( + '#${order.orderNumber}', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + color: AppColors.grey900, + ), + ), + + // Amount + Text( + currencyFormatter.format(order.finalAmount), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + color: AppColors.primaryBlue, + ), + ), + ], ), - ), - ), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Order number and amount row - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Order number - Text( - '#${order.orderNumber}', - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w700, - color: AppColors.grey900, - ), - ), - // Amount - Text( - currencyFormatter.format(order.finalAmount), - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w700, - color: AppColors.primaryBlue, - ), - ), - ], - ), + const SizedBox(height: 12), - const SizedBox(height: 12), + // Order details + _buildDetailRow( + 'Ngày đặt:', + _formatDate(order.createdAt), + ), + const SizedBox(height: 6), - // Order details - _buildDetailRow( - 'Ngày đặt:', - _formatDate(order.createdAt), - ), - const SizedBox(height: 6), + _buildDetailRow( + 'Ngày giao:', + order.expectedDeliveryDate != null + ? _formatDate(order.expectedDeliveryDate!) + : 'Chưa xác định', + ), + const SizedBox(height: 6), - _buildDetailRow( - 'Ngày giao:', - order.expectedDeliveryDate != null - ? _formatDate(order.expectedDeliveryDate!) - : 'Chưa xác định', - ), - const SizedBox(height: 6), + _buildDetailRow( + 'Địa chỉ:', + _getShortAddress(), + ), + const SizedBox(height: 12), - _buildDetailRow( - 'Địa chỉ:', - _getShortAddress(), - ), - const SizedBox(height: 12), - - // Status badge - _buildStatusBadge(), - ], - ), + // Status badge + _buildStatusBadge(), + ], ), ), ),