fix order, update qr

This commit is contained in:
Phuoc Nguyen
2025-12-01 15:28:07 +07:00
parent 250c453413
commit e62c466155
7 changed files with 56 additions and 205 deletions

View File

@@ -350,14 +350,9 @@ final routerProvider = Provider<GoRouter>((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),
);
},
),

View File

@@ -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,

View File

@@ -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),

View File

@@ -37,8 +37,8 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
@override
void dispose() {
_searchController.removeListener(_onSearchChanged);
_searchController.dispose();
_searchController..removeListener(_onSearchChanged)
..dispose();
super.dispose();
}
@@ -50,8 +50,9 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
@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<OrdersPage> {
// 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,11 +177,14 @@ class _OrdersPageState extends ConsumerState<OrdersPage> {
decoration: InputDecoration(
hintText: 'Mã đơn hàng',
hintStyle: const TextStyle(color: AppColors.grey500, fontSize: 14),
prefixIcon: const FaIcon(
prefixIcon: const Padding(
padding: EdgeInsets.all(14),
child: FaIcon(
FontAwesomeIcons.magnifyingGlass,
color: AppColors.grey500,
size: 18,
),
),
suffixIcon: _searchController.text.isNotEmpty
? IconButton(
icon: const FaIcon(

View File

@@ -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]}.')}';
}
}

View File

@@ -91,55 +91,6 @@ class OrderSearchQuery extends _$OrderSearchQuery {
}
}
/// Filtered Orders Provider
///
/// Filters orders by selected status and search query.
@riverpod
Future<List<Order>> 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.

View File

@@ -205,56 +205,6 @@ abstract class _$OrderSearchQuery extends $Notifier<String> {
}
}
/// 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<Order>>,
List<Order>,
FutureOr<List<Order>>
>
with $FutureModifier<List<Order>>, $FutureProvider<List<Order>> {
/// 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<List<Order>> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<List<Order>> create(Ref ref) {
return filteredOrders(ref);
}
}
String _$filteredOrdersHash() => r'04c5c87d7138b66987c8b45f878d445026ec8e19';
/// Orders Count by Status Provider
///
/// Returns count of orders for each status.