/// Checkout Page /// /// Complete checkout flow with delivery info, invoice options, payment methods. /// Features: /// - Delivery information form with province/ward dropdowns /// - Invoice toggle section (checkbox reveals invoice fields) /// - Payment method selection (bank transfer vs COD) /// - Order summary with items list /// - Price negotiation option (hides payment, changes button) /// - Form validation and submission library; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:worker/core/constants/ui_constants.dart'; import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/account/domain/entities/address.dart'; import 'package:worker/features/cart/presentation/providers/cart_state.dart'; import 'package:worker/features/cart/presentation/widgets/checkout_submit_button.dart'; import 'package:worker/features/cart/presentation/widgets/delivery_information_section.dart'; import 'package:worker/features/cart/presentation/widgets/invoice_section.dart'; import 'package:worker/features/cart/presentation/widgets/order_summary_section.dart'; import 'package:worker/features/cart/presentation/widgets/payment_method_section.dart'; import 'package:worker/features/cart/presentation/widgets/price_negotiation_section.dart'; import 'package:worker/features/orders/presentation/providers/order_status_provider.dart'; import 'package:worker/features/orders/presentation/providers/payment_terms_provider.dart'; /// Checkout Page /// /// Full checkout flow for placing orders. class CheckoutPage extends HookConsumerWidget { const CheckoutPage({ super.key, this.checkoutData, }); final Map? checkoutData; @override Widget build(BuildContext context, WidgetRef ref) { // Form key for validation final formKey = useMemoized(() => GlobalKey()); // Delivery information final notesController = useTextEditingController(); final selectedPickupDate = useState(null); final selectedAddress = useState(null); // Invoice section final needsInvoice = useState(false); // Payment method (will be set to first payment term name from API) final paymentMethod = useState(''); // Price negotiation final needsNegotiation = useState(false); final needsContract = useState(false); // Watch API provider for payment terms final paymentTermsListAsync = ref.watch(paymentTermsListProvider); // Get CartItemData from navigation final cartItemsData = checkoutData?['cartItems'] as List? ?? []; // Convert CartItemData to Map format for OrderSummarySection // Use all data directly from cart (no API calls needed) final checkoutItems = cartItemsData.map((itemData) { final cartItem = itemData as CartItemData; return { 'id': cartItem.product.productId, 'name': cartItem.product.name, 'sku': cartItem.product.erpnextItemCode ?? cartItem.product.productId, 'quantity': cartItem.quantity, 'quantityConverted': cartItem.quantityConverted, 'boxes': cartItem.boxes, 'price': cartItem.product.basePrice, 'image': cartItem.product.images.isNotEmpty ? cartItem.product.images.first : null, }; }).toList(); // Calculate totals from cart data final subtotal = checkoutItems.fold( 0.0, (sum, item) => sum + (item['price'] as double) * (item['quantityConverted'] as double), ); // TODO: Fetch member discount from user profile API const memberDiscountPercent = 0.0; // Temporarily disabled (was 15.0 for Diamond tier) final memberDiscount = subtotal * (memberDiscountPercent / 100); // TODO: Fetch shipping fee from API based on address const shipping = 0.0; // Free shipping (temporary) final total = subtotal - memberDiscount + shipping; return Scaffold( backgroundColor: const Color(0xFFF4F6F8), appBar: AppBar( backgroundColor: Colors.white, elevation: 0, leading: IconButton( icon: const FaIcon( FontAwesomeIcons.arrowLeft, color: Colors.black, size: 20, ), onPressed: () => context.pop(), ), title: const Text( 'Thanh toán', style: TextStyle( color: Colors.black, fontSize: 18, fontWeight: FontWeight.bold, ), ), centerTitle: false, actions: const [SizedBox(width: AppSpacing.sm)], ), body: Form( key: formKey, child: SingleChildScrollView( child: Column( children: [ const SizedBox(height: AppSpacing.md), // Delivery Information Section DeliveryInformationSection( notesController: notesController, selectedPickupDate: selectedPickupDate, selectedAddress: selectedAddress, ), const SizedBox(height: AppSpacing.md), // Invoice Section InvoiceSection(needsInvoice: needsInvoice), const SizedBox(height: AppSpacing.md), // Payment Method Section (hidden if negotiation is checked) if (!needsNegotiation.value) paymentTermsListAsync.when( data: (paymentTerms) { // Set default payment method to first term if not set if (paymentMethod.value.isEmpty && paymentTerms.isNotEmpty) { WidgetsBinding.instance.addPostFrameCallback((_) { paymentMethod.value = paymentTerms.first.name; }); } return PaymentMethodSection( paymentMethod: paymentMethod, paymentTerms: paymentTerms, ); }, loading: () => Container( margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md), padding: const EdgeInsets.all(AppSpacing.md), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(AppRadius.card), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: const Center( child: CircularProgressIndicator(), ), ), error: (error, stack) => Container( margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md), padding: const EdgeInsets.all(AppSpacing.md), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(AppRadius.card), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Column( children: [ const Icon( FontAwesomeIcons.triangleExclamation, color: AppColors.danger, size: 32, ), const SizedBox(height: 12), Text( 'Không thể tải phương thức thanh toán', style: const TextStyle( fontSize: 14, color: AppColors.grey500, ), textAlign: TextAlign.center, ), const SizedBox(height: 8), TextButton( onPressed: () { ref.invalidate(paymentTermsListProvider); }, child: const Text('Thử lại'), ), ], ), ), ), if (!needsNegotiation.value) const SizedBox(height: AppSpacing.md), // Discount Code Section _buildDiscountCodeSection(), const SizedBox(height: AppSpacing.md), // Order Summary Section OrderSummarySection( cartItems: checkoutItems, subtotal: subtotal, discount: memberDiscount, shipping: shipping, total: total, ), const SizedBox(height: AppSpacing.md), // Price Negotiation Section PriceNegotiationSection(needsNegotiation: needsNegotiation), const SizedBox(height: AppSpacing.md), Container( margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md), padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md), decoration: BoxDecoration( color: const Color(0xFFFFF8E1), borderRadius: BorderRadius.circular(AppRadius.card), border: Border.all(color: const Color(0xFFFFD54F)), ), child: Row( children: [ Checkbox( value: needsContract.value, onChanged: (value) { needsContract.value = value ?? false; }, activeColor: AppColors.warning, ), const Expanded( child: Text( 'Yêu cầu hợp đồng', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, color: Color(0xFF212121), ), ), ), ], ), ), const SizedBox(height: AppSpacing.md), // Terms and Conditions const Padding( padding: EdgeInsets.symmetric(horizontal: AppSpacing.md), child: Text.rich( TextSpan( text: 'Bằng cách đặt hàng, bạn đồng ý với ', style: TextStyle( fontSize: 14, color: Color(0xFF6B7280), ), children: [ TextSpan( text: 'Điều khoản & Điều kiện', style: TextStyle( color: AppColors.primaryBlue, decoration: TextDecoration.underline, ), ), ], ), textAlign: TextAlign.center, ), ), const SizedBox(height: AppSpacing.md), // Place Order Button CheckoutSubmitButton( formKey: formKey, needsNegotiation: needsNegotiation.value, needsInvoice: needsInvoice.value, selectedAddress: selectedAddress.value, paymentMethod: paymentMethod.value, total: total, cartItems: checkoutItems, notes: notesController.text.trim().isEmpty ? null : notesController.text.trim(), ), const SizedBox(height: AppSpacing.lg), ], ), ), ), ); } /// Build Discount Code Section (Card 4 from HTML) Widget _buildDiscountCodeSection() { return Container( margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md), padding: const EdgeInsets.all(AppSpacing.md), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(AppRadius.card), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Section Title Row( children: [ Icon( FontAwesomeIcons.ticket, color: AppColors.primaryBlue, size: 20, ), const SizedBox(width: 8), const Text( 'Mã giảm giá', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF212121), ), ), ], ), const SizedBox(height: 12), // Input field with Apply button Row( children: [ Expanded( child: TextField( decoration: InputDecoration( hintText: 'Nhập mã giảm giá', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: Color(0xFFD1D5DB)), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: const BorderSide(color: Color(0xFFD1D5DB)), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: const BorderSide( color: AppColors.primaryBlue, width: 2, ), ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 12, ), ), ), ), const SizedBox(width: 8), ElevatedButton( onPressed: () { // TODO: Apply discount code }, style: ElevatedButton.styleFrom( backgroundColor: AppColors.primaryBlue, padding: const EdgeInsets.symmetric( horizontal: 24, vertical: 12, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), elevation: 0, ), child: const Text( 'Áp dụng', style: TextStyle( color: Colors.white, fontWeight: FontWeight.w600, ), ), ), ], ), const SizedBox(height: 12), // Success banner (Diamond discount) Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFFF0FDF4), border: Border.all(color: const Color(0xFFBBF7D0)), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Icon( FontAwesomeIcons.circleCheck, color: AppColors.success, size: 18, ), const SizedBox(width: 8), const Expanded( child: Text( 'Bạn được giảm 15% (hạng Diamond)', style: TextStyle( fontSize: 14, color: Color(0xFF166534), fontWeight: FontWeight.w500, ), ), ), ], ), ), ], ), ); } }