diff --git a/lib/core/router/app_router.dart b/lib/core/router/app_router.dart index a2e92fc..5131e8d 100644 --- a/lib/core/router/app_router.dart +++ b/lib/core/router/app_router.dart @@ -350,14 +350,9 @@ final routerProvider = Provider((ref) { name: RouteNames.paymentQr, pageBuilder: (context, state) { final orderId = state.uri.queryParameters['orderId'] ?? ''; - final amountStr = state.uri.queryParameters['amount'] ?? '0'; - final amount = double.tryParse(amountStr) ?? 0.0; return MaterialPage( key: state.pageKey, - child: PaymentQrPage( - orderId: orderId, - amount: amount, - ), + child: PaymentQrPage(orderId: orderId), ); }, ), diff --git a/lib/core/theme/app_theme.dart b/lib/core/theme/app_theme.dart index b6588e3..1a18a06 100644 --- a/lib/core/theme/app_theme.dart +++ b/lib/core/theme/app_theme.dart @@ -14,10 +14,11 @@ class AppTheme { /// Light theme configuration /// [seedColor] - Optional custom seed color, defaults to AppColors.defaultSeedColor static ThemeData lightTheme([Color? seedColor]) { + final seed = seedColor ?? AppColors.defaultSeedColor; final ColorScheme colorScheme = ColorScheme.fromSeed( - seedColor: seedColor ?? AppColors.defaultSeedColor, + seedColor: seed, brightness: Brightness.light, - ); + ).copyWith(primary: seed); return ThemeData( useMaterial3: true, @@ -239,10 +240,11 @@ class AppTheme { /// Dark theme configuration /// [seedColor] - Optional custom seed color, defaults to AppColors.defaultSeedColor static ThemeData darkTheme([Color? seedColor]) { + final seed = seedColor ?? AppColors.defaultSeedColor; final ColorScheme colorScheme = ColorScheme.fromSeed( - seedColor: seedColor ?? AppColors.defaultSeedColor, + seedColor: seed, brightness: Brightness.dark, - ); + ).copyWith(primary: seed); return ThemeData( useMaterial3: true, diff --git a/lib/features/orders/presentation/pages/order_detail_page.dart b/lib/features/orders/presentation/pages/order_detail_page.dart index d611feb..b3ea481 100644 --- a/lib/features/orders/presentation/pages/order_detail_page.dart +++ b/lib/features/orders/presentation/pages/order_detail_page.dart @@ -11,6 +11,7 @@ import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import 'package:worker/core/constants/ui_constants.dart'; import 'package:worker/core/enums/status_color.dart'; +import 'package:worker/core/router/app_router.dart'; import 'package:worker/core/theme/colors.dart'; import 'package:worker/core/utils/extensions.dart'; import 'package:worker/features/account/domain/entities/address.dart'; @@ -38,8 +39,8 @@ class OrderDetailPage extends ConsumerWidget { onPopInvoked: (didPop) { if (didPop) { // Dispose providers when leaving the page - ref.invalidate(updateOrderAddressProvider); - ref.invalidate(cancelOrderProvider); + ref..invalidate(updateOrderAddressProvider) + ..invalidate(cancelOrderProvider); } }, child: Scaffold( @@ -1411,11 +1412,9 @@ class OrderDetailPage extends ConsumerWidget { Expanded( child: ElevatedButton.icon( onPressed: () { - // TODO: Navigate to payment page - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Chức năng thanh toán đang phát triển'), - ), + // Navigate to payment QR page + context.push( + '${RouteNames.paymentQr}?orderId=${orderDetail.order.name}', ); }, icon: const FaIcon(FontAwesomeIcons.creditCard, size: 18), diff --git a/lib/features/orders/presentation/pages/orders_page.dart b/lib/features/orders/presentation/pages/orders_page.dart index 4f0d6ef..ce2177d 100644 --- a/lib/features/orders/presentation/pages/orders_page.dart +++ b/lib/features/orders/presentation/pages/orders_page.dart @@ -37,8 +37,8 @@ class _OrdersPageState extends ConsumerState { @override void dispose() { - _searchController.removeListener(_onSearchChanged); - _searchController.dispose(); + _searchController..removeListener(_onSearchChanged) + ..dispose(); super.dispose(); } @@ -50,8 +50,9 @@ class _OrdersPageState extends ConsumerState { @override Widget build(BuildContext context) { - final filteredOrdersAsync = ref.watch(filteredOrdersProvider); + final ordersAsync = ref.watch(ordersProvider); final selectedStatus = ref.watch(selectedOrderStatusProvider); + final searchQuery = ref.watch(orderSearchQueryProvider); return Scaffold( backgroundColor: const Color(0xFFF4F6F8), @@ -102,22 +103,49 @@ class _OrdersPageState extends ConsumerState { // Orders List SliverPadding( padding: const EdgeInsets.fromLTRB(16, 8, 16, 24), - sliver: filteredOrdersAsync.when( + sliver: ordersAsync.when( data: (orders) { - if (orders.isEmpty) { - return _buildEmptyState(); + // Filter by status + var filtered = orders; + if (selectedStatus != null) { + filtered = filtered + .where((order) => order.status == selectedStatus) + .toList(); } + // Filter by search query + if (searchQuery.isNotEmpty) { + filtered = filtered + .where((order) => order.name + .toLowerCase() + .contains(searchQuery.toLowerCase())) + .toList(); + } + + // Sort by transaction date (newest first) + filtered.sort((a, b) { + try { + final aDate = DateTime.parse(a.transactionDate); + final bDate = DateTime.parse(b.transactionDate); + return bDate.compareTo(aDate); + } catch (e) { + return 0; + } + }); + + if (filtered.isEmpty) { + return _buildEmptyState(); + } return SliverList( delegate: SliverChildBuilderDelegate((context, index) { - final order = orders[index]; + final order = filtered[index]; return OrderCard( order: order, onTap: () { context.push('/orders/${order.name}'); }, ); - }, childCount: orders.length), + }, childCount: filtered.length), ); }, loading: () => _buildLoadingState(), @@ -149,10 +177,13 @@ class _OrdersPageState extends ConsumerState { decoration: InputDecoration( hintText: 'Mã đơn hàng', hintStyle: const TextStyle(color: AppColors.grey500, fontSize: 14), - prefixIcon: const FaIcon( - FontAwesomeIcons.magnifyingGlass, - color: AppColors.grey500, - size: 18, + prefixIcon: const Padding( + padding: EdgeInsets.all(14), + child: FaIcon( + FontAwesomeIcons.magnifyingGlass, + color: AppColors.grey500, + size: 18, + ), ), suffixIcon: _searchController.text.isNotEmpty ? IconButton( diff --git a/lib/features/orders/presentation/pages/payment_qr_page.dart b/lib/features/orders/presentation/pages/payment_qr_page.dart index 2c3b168..f20e45c 100644 --- a/lib/features/orders/presentation/pages/payment_qr_page.dart +++ b/lib/features/orders/presentation/pages/payment_qr_page.dart @@ -2,7 +2,6 @@ /// /// QR code payment screen with bank transfer information. /// Features: -/// - Payment amount display with minimum payment warning /// - QR code for quick payment /// - Bank transfer information with copy buttons /// - Payment confirmation and proof upload buttons @@ -31,12 +30,10 @@ import 'package:worker/features/orders/presentation/providers/order_repository_p /// Displays QR code and bank transfer information for payment. class PaymentQrPage extends HookConsumerWidget { final String orderId; - final double amount; const PaymentQrPage({ super.key, required this.orderId, - required this.amount, }); @override @@ -126,14 +123,8 @@ class PaymentQrPage extends HookConsumerWidget { children: [ const SizedBox(height: AppSpacing.md), - // Payment Amount Card - _buildAmountCard(amount), - - const SizedBox(height: AppSpacing.md), - // QR Code Card _buildQrCodeCard( - amount, orderId, qrCodeData.value?['qr_code'] as String?, isLoadingQr.value, @@ -187,70 +178,8 @@ class PaymentQrPage extends HookConsumerWidget { ); } - /// Build payment amount card - Widget _buildAmountCard(double amount) { - 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( - children: [ - Text( - _formatCurrency(amount), - style: const TextStyle( - fontSize: 28, - fontWeight: FontWeight.bold, - color: AppColors.primaryBlue, - ), - ), - const SizedBox(height: 8), - const Text( - 'Số tiền cần thanh toán', - style: TextStyle(fontSize: 14, color: AppColors.grey500), - ), - const SizedBox(height: 12), - Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: const Color(0xFFFFF8E1), - borderRadius: BorderRadius.circular(AppRadius.card), - border: Border.all(color: const Color(0xFFFFD54F)), - ), - child: Row( - children: [ - const FaIcon(FontAwesomeIcons.circleInfo, color: AppColors.warning, size: 18), - const SizedBox(width: 8), - Expanded( - child: Text( - 'Thanh toán không dưới 20%', - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Colors.orange[900], - ), - ), - ), - ], - ), - ), - ], - ), - ); - } - /// Build QR code card Widget _buildQrCodeCard( - double amount, String orderId, String? qrCodeData, bool isLoading, @@ -956,7 +885,6 @@ class PaymentQrPage extends HookConsumerWidget { RouteNames.orderSuccess, queryParameters: { 'orderNumber': orderId, - 'total': amount.toString(), 'isNegotiation': 'false', }, ); @@ -985,9 +913,4 @@ class PaymentQrPage extends HookConsumerWidget { ); } } - - /// Format currency - String _formatCurrency(double amount) { - return '${amount.toStringAsFixed(0).replaceAllMapped(RegExp(r'(\d)(?=(\d{3})+(?!\d))'), (Match m) => '${m[1]}.')}₫'; - } } diff --git a/lib/features/orders/presentation/providers/orders_provider.dart b/lib/features/orders/presentation/providers/orders_provider.dart index e989ae3..4f64cdf 100644 --- a/lib/features/orders/presentation/providers/orders_provider.dart +++ b/lib/features/orders/presentation/providers/orders_provider.dart @@ -91,55 +91,6 @@ class OrderSearchQuery extends _$OrderSearchQuery { } } -/// Filtered Orders Provider -/// -/// Filters orders by selected status and search query. -@riverpod -Future> filteredOrders(Ref ref) async { - final ordersAsync = ref.watch(ordersProvider); - final selectedStatus = ref.watch(selectedOrderStatusProvider); - final searchQuery = ref.watch(orderSearchQueryProvider); - - return ordersAsync.when( - data: (orders) { - var filtered = orders; - - // Filter by status - if (selectedStatus != null) { - filtered = filtered - .where((order) => order.status == selectedStatus) - .toList(); - } - - // Filter by search query - if (searchQuery.isNotEmpty) { - filtered = filtered - .where( - (order) => order.name.toLowerCase().contains( - searchQuery.toLowerCase(), - ), - ) - .toList(); - } - - // Sort by transaction date (newest first) - filtered.sort((a, b) { - try { - final aDate = DateTime.parse(a.transactionDate); - final bDate = DateTime.parse(b.transactionDate); - return bDate.compareTo(aDate); - } catch (e) { - return 0; // Keep original order if parsing fails - } - }); - - return filtered; - }, - loading: () => [], - error: (error, stack) => [], - ); -} - /// Orders Count by Status Provider /// /// Returns count of orders for each status. diff --git a/lib/features/orders/presentation/providers/orders_provider.g.dart b/lib/features/orders/presentation/providers/orders_provider.g.dart index be10f16..bdc8aa5 100644 --- a/lib/features/orders/presentation/providers/orders_provider.g.dart +++ b/lib/features/orders/presentation/providers/orders_provider.g.dart @@ -205,56 +205,6 @@ abstract class _$OrderSearchQuery extends $Notifier { } } -/// Filtered Orders Provider -/// -/// Filters orders by selected status and search query. - -@ProviderFor(filteredOrders) -const filteredOrdersProvider = FilteredOrdersProvider._(); - -/// Filtered Orders Provider -/// -/// Filters orders by selected status and search query. - -final class FilteredOrdersProvider - extends - $FunctionalProvider< - AsyncValue>, - List, - FutureOr> - > - with $FutureModifier>, $FutureProvider> { - /// Filtered Orders Provider - /// - /// Filters orders by selected status and search query. - const FilteredOrdersProvider._() - : super( - from: null, - argument: null, - retry: null, - name: r'filteredOrdersProvider', - isAutoDispose: true, - dependencies: null, - $allTransitiveDependencies: null, - ); - - @override - String debugGetCreateSourceHash() => _$filteredOrdersHash(); - - @$internal - @override - $FutureProviderElement> $createElement( - $ProviderPointer pointer, - ) => $FutureProviderElement(pointer); - - @override - FutureOr> create(Ref ref) { - return filteredOrders(ref); - } -} - -String _$filteredOrdersHash() => r'04c5c87d7138b66987c8b45f878d445026ec8e19'; - /// Orders Count by Status Provider /// /// Returns count of orders for each status.