update
This commit is contained in:
@@ -26,9 +26,9 @@ class OrderStatusLocalDataSource {
|
||||
/// Get cached order status list
|
||||
List<OrderStatusModel> getCachedStatusList() {
|
||||
try {
|
||||
final values = _box.values.whereType<OrderStatusModel>().toList();
|
||||
final values = _box.values.whereType<OrderStatusModel>().toList()
|
||||
// Sort by index
|
||||
values.sort((a, b) => a.index.compareTo(b.index));
|
||||
..sort((a, b) => a.index.compareTo(b.index));
|
||||
return values;
|
||||
} catch (e) {
|
||||
return [];
|
||||
|
||||
@@ -111,8 +111,9 @@ class OrderDetailPage extends ConsumerWidget {
|
||||
_buildPaymentHistoryCard(context, orderDetail),
|
||||
],
|
||||
|
||||
// Cancel Order Button (only show for "Chờ phê duyệt" status)
|
||||
if (orderDetail.order.status == 'Chờ phê duyệt') ...[
|
||||
// Cancel Order Button
|
||||
// Use API flag to determine if cancellation is allowed
|
||||
if (orderDetail.order.isAllowCancel) ...[
|
||||
const SizedBox(height: 16),
|
||||
_buildCancelOrderButton(context, ref, orderDetail),
|
||||
],
|
||||
@@ -405,90 +406,98 @@ class OrderDetailPage extends ConsumerWidget {
|
||||
color: AppColors.grey500,
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
// Navigate to address selection and wait for result
|
||||
final result = await context.push<Address>(
|
||||
'/account/addresses',
|
||||
extra: {
|
||||
'selectMode': true,
|
||||
'currentAddress': shippingAddress,
|
||||
},
|
||||
);
|
||||
|
||||
// If user selected an address, update the order
|
||||
if (result != null && context.mounted) {
|
||||
// Show loading indicator
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Đang cập nhật địa chỉ...'),
|
||||
duration: Duration(seconds: 1),
|
||||
),
|
||||
);
|
||||
|
||||
// Update shipping address (keep billing address the same)
|
||||
await ref
|
||||
.read(updateOrderAddressProvider.notifier)
|
||||
.updateAddress(
|
||||
orderId: orderId,
|
||||
shippingAddressName: result.name,
|
||||
customerAddress: orderDetail.billingAddress.name,
|
||||
Builder(
|
||||
builder: (context) {
|
||||
// Use API flag to determine if editing is allowed
|
||||
if (orderDetail.shippingAddress.isAllowEdit) {
|
||||
return TextButton(
|
||||
onPressed: () async {
|
||||
// Navigate to address selection and wait for result
|
||||
final result = await context.push<Address>(
|
||||
'/account/addresses',
|
||||
extra: {
|
||||
'selectMode': true,
|
||||
'currentAddress': shippingAddress,
|
||||
},
|
||||
);
|
||||
|
||||
// Check if update was successful
|
||||
final updateState =
|
||||
ref.read(updateOrderAddressProvider)
|
||||
..when(
|
||||
data: (_) {
|
||||
// Refresh order detail to show updated address
|
||||
ref.invalidate(orderDetailProvider(orderId));
|
||||
// If user selected an address, update the order
|
||||
if (result != null && context.mounted) {
|
||||
// Show loading indicator
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Đang cập nhật địa chỉ...'),
|
||||
duration: Duration(seconds: 1),
|
||||
),
|
||||
);
|
||||
|
||||
// Show success message
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'Cập nhật địa chỉ giao hàng thành công',
|
||||
),
|
||||
backgroundColor: AppColors.success,
|
||||
),
|
||||
// Update shipping address (keep billing address the same)
|
||||
await ref
|
||||
.read(updateOrderAddressProvider.notifier)
|
||||
.updateAddress(
|
||||
orderId: orderId,
|
||||
shippingAddressName: result.name,
|
||||
customerAddress: orderDetail.billingAddress.name,
|
||||
);
|
||||
|
||||
// Check if update was successful
|
||||
final updateState =
|
||||
ref.read(updateOrderAddressProvider)
|
||||
..when(
|
||||
data: (_) {
|
||||
// Refresh order detail to show updated address
|
||||
ref.invalidate(orderDetailProvider(orderId));
|
||||
|
||||
// Show success message
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'Cập nhật địa chỉ giao hàng thành công',
|
||||
),
|
||||
backgroundColor: AppColors.success,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
error: (error, _) {
|
||||
// Show error message
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
'Lỗi: ${error.toString()}',
|
||||
),
|
||||
backgroundColor: AppColors.danger,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
loading: () {},
|
||||
);
|
||||
}
|
||||
},
|
||||
error: (error, _) {
|
||||
// Show error message
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
'Lỗi: ${error.toString()}',
|
||||
),
|
||||
backgroundColor: AppColors.danger,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
loading: () {},
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
minimumSize: Size.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
side: const BorderSide(color: AppColors.grey100),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'Cập nhật',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.primaryBlue,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
minimumSize: Size.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
side: const BorderSide(color: AppColors.grey100),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'Cập nhật',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.primaryBlue,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -621,91 +630,100 @@ class OrderDetailPage extends ConsumerWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
// Navigate to address selection and wait for result
|
||||
final result = await context.push<Address>(
|
||||
'/account/addresses',
|
||||
extra: {
|
||||
'selectMode': true,
|
||||
'currentAddress': billingAddress,
|
||||
},
|
||||
);
|
||||
|
||||
// If user selected an address, update the order
|
||||
if (result != null && context.mounted) {
|
||||
// Show loading indicator
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Đang cập nhật địa chỉ...'),
|
||||
duration: Duration(seconds: 1),
|
||||
),
|
||||
);
|
||||
|
||||
// Update billing address (keep shipping address the same)
|
||||
await ref
|
||||
.read(updateOrderAddressProvider.notifier)
|
||||
.updateAddress(
|
||||
orderId: orderId,
|
||||
shippingAddressName: orderDetail.shippingAddress.name,
|
||||
customerAddress: result.name,
|
||||
Builder(
|
||||
builder: (context) {
|
||||
// Use API flag to determine if editing is allowed
|
||||
if (orderDetail.billingAddress.isAllowEdit) {
|
||||
return TextButton(
|
||||
onPressed: () async {
|
||||
// Navigate to address selection and wait for result
|
||||
final result = await context.push<Address>(
|
||||
'/account/addresses',
|
||||
extra: {
|
||||
'selectMode': true,
|
||||
'currentAddress': billingAddress,
|
||||
},
|
||||
);
|
||||
|
||||
// Check if update was successful
|
||||
final updateState =
|
||||
ref.read(updateOrderAddressProvider)
|
||||
..when(
|
||||
data: (_) {
|
||||
// Refresh order detail to show updated address
|
||||
ref.invalidate(orderDetailProvider(orderId));
|
||||
// If user selected an address, update the order
|
||||
if (result != null && context.mounted) {
|
||||
// Show loading indicator
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Đang cập nhật địa chỉ...'),
|
||||
duration: Duration(seconds: 1),
|
||||
),
|
||||
);
|
||||
|
||||
// Show success message
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'Cập nhật địa chỉ hóa đơn thành công',
|
||||
),
|
||||
backgroundColor: AppColors.success,
|
||||
),
|
||||
// Update billing address (keep shipping address the same)
|
||||
await ref
|
||||
.read(updateOrderAddressProvider.notifier)
|
||||
.updateAddress(
|
||||
orderId: orderId,
|
||||
shippingAddressName: orderDetail.shippingAddress.name,
|
||||
customerAddress: result.name,
|
||||
);
|
||||
|
||||
// Check if update was successful
|
||||
final updateState =
|
||||
ref.read(updateOrderAddressProvider)
|
||||
..when(
|
||||
data: (_) {
|
||||
// Refresh order detail to show updated address
|
||||
ref.invalidate(orderDetailProvider(orderId));
|
||||
|
||||
// Show success message
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'Cập nhật địa chỉ hóa đơn thành công',
|
||||
),
|
||||
backgroundColor: AppColors.success,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
error: (error, _) {
|
||||
// Show error message
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
'Lỗi: ${error.toString()}',
|
||||
),
|
||||
backgroundColor: AppColors.danger,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
loading: () {},
|
||||
);
|
||||
}
|
||||
},
|
||||
error: (error, _) {
|
||||
// Show error message
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
'Lỗi: ${error.toString()}',
|
||||
),
|
||||
backgroundColor: AppColors.danger,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
loading: () {},
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
minimumSize: Size.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
side: const BorderSide(color: AppColors.grey100),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'Cập nhật',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.primaryBlue,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
minimumSize: Size.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
side: const BorderSide(color: AppColors.grey100),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'Cập nhật',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.primaryBlue,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
/// Riverpod providers for managing orders state.
|
||||
library;
|
||||
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:worker/core/constants/storage_constants.dart';
|
||||
import 'package:worker/features/orders/data/datasources/order_status_local_datasource.dart';
|
||||
import 'package:worker/features/orders/data/models/order_status_model.dart';
|
||||
import 'package:worker/features/orders/domain/entities/order.dart';
|
||||
import 'package:worker/features/orders/domain/entities/order_detail.dart';
|
||||
import 'package:worker/features/orders/domain/entities/order_status.dart';
|
||||
@@ -171,6 +175,37 @@ Future<int> totalOrdersCount(Ref ref) async {
|
||||
);
|
||||
}
|
||||
|
||||
/// Order Status Hive Box Provider
|
||||
///
|
||||
/// Provides direct access to the Hive box for order statuses.
|
||||
/// Use this to read data directly from Hive in the UI.
|
||||
@riverpod
|
||||
Box<dynamic> orderStatusHiveBox(Ref ref) {
|
||||
return Hive.box(HiveBoxNames.orderStatusBox);
|
||||
}
|
||||
|
||||
/// Helper: Get Order Status by Label
|
||||
///
|
||||
/// Returns the OrderStatusModel for a given label (e.g., "Chờ phê duyệt").
|
||||
/// Returns null if not found.
|
||||
OrderStatusModel? getOrderStatusByLabel(Box<dynamic> box, String label) {
|
||||
final statuses = box.values.whereType<OrderStatusModel>();
|
||||
try {
|
||||
return statuses.firstWhere((status) => status.label == label);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper: Get Order Status by Index
|
||||
///
|
||||
/// Returns the OrderStatusModel for a given index.
|
||||
/// Returns null if not found.
|
||||
OrderStatusModel? getOrderStatusByIndex(Box<dynamic> box, int index) {
|
||||
final status = box.get(index);
|
||||
return status is OrderStatusModel ? status : null;
|
||||
}
|
||||
|
||||
/// Order Status List Provider
|
||||
///
|
||||
/// Provides cached order status list with automatic refresh.
|
||||
|
||||
@@ -344,6 +344,62 @@ final class TotalOrdersCountProvider
|
||||
|
||||
String _$totalOrdersCountHash() => r'ec1ab3a8d432033aa1f02d28e841e78eba06d63e';
|
||||
|
||||
/// Order Status Hive Box Provider
|
||||
///
|
||||
/// Provides direct access to the Hive box for order statuses.
|
||||
/// Use this to read data directly from Hive in the UI.
|
||||
|
||||
@ProviderFor(orderStatusHiveBox)
|
||||
const orderStatusHiveBoxProvider = OrderStatusHiveBoxProvider._();
|
||||
|
||||
/// Order Status Hive Box Provider
|
||||
///
|
||||
/// Provides direct access to the Hive box for order statuses.
|
||||
/// Use this to read data directly from Hive in the UI.
|
||||
|
||||
final class OrderStatusHiveBoxProvider
|
||||
extends $FunctionalProvider<Box<dynamic>, Box<dynamic>, Box<dynamic>>
|
||||
with $Provider<Box<dynamic>> {
|
||||
/// Order Status Hive Box Provider
|
||||
///
|
||||
/// Provides direct access to the Hive box for order statuses.
|
||||
/// Use this to read data directly from Hive in the UI.
|
||||
const OrderStatusHiveBoxProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'orderStatusHiveBoxProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$orderStatusHiveBoxHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$ProviderElement<Box<dynamic>> $createElement($ProviderPointer pointer) =>
|
||||
$ProviderElement(pointer);
|
||||
|
||||
@override
|
||||
Box<dynamic> create(Ref ref) {
|
||||
return orderStatusHiveBox(ref);
|
||||
}
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(Box<dynamic> value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<Box<dynamic>>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$orderStatusHiveBoxHash() =>
|
||||
r'49b681c9cabb60f58e049dd4d89872c827d7db9a';
|
||||
|
||||
/// Order Status List Provider
|
||||
///
|
||||
/// Provides cached order status list with automatic refresh.
|
||||
|
||||
Reference in New Issue
Block a user