update address, cancel order

This commit is contained in:
Phuoc Nguyen
2025-11-25 16:39:29 +07:00
parent 039dfb9fb5
commit 84669ac89c
11 changed files with 584 additions and 194 deletions

View File

@@ -58,7 +58,7 @@ class AccountMenuItem extends StatelessWidget {
horizontal: AppSpacing.md,
vertical: AppSpacing.md,
),
decoration: BoxDecoration(
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(color: AppColors.grey100, width: 1.0),
),
@@ -75,10 +75,12 @@ class AccountMenuItem extends StatelessWidget {
AppColors.lightBlue.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: FaIcon(
icon,
size: 18,
color: iconColor ?? AppColors.primaryBlue,
child: Center(
child: FaIcon(
icon,
size: 18,
color: iconColor ?? AppColors.primaryBlue,
),
),
),
const SizedBox(width: AppSpacing.md),

View File

@@ -324,4 +324,48 @@ class OrderRemoteDataSource {
throw Exception('Failed to get order detail: $e');
}
}
/// Update order address
///
/// Calls: POST /api/method/building_material.building_material.api.sales_order.update
/// Body: {
/// "name": "SAL-ORD-2025-00053",
/// "shipping_address_name": "Công ty Tiến Nguyễn 2-thanh toán",
/// "customer_address": "Nguyễn Lê Duy Ti-Billing"
/// }
/// Updates shipping and billing addresses for an existing order
Future<void> updateOrderAddress({
required String orderId,
required String shippingAddressName,
required String customerAddress,
}) async {
try {
await _dioClient.post(
'${ApiConstants.frappeApiMethod}${ApiConstants.updateOrder}',
data: {
'name': orderId,
'shipping_address_name': shippingAddressName,
'customer_address': customerAddress,
},
);
} catch (e) {
throw Exception('Failed to update order address: $e');
}
}
/// Cancel order
///
/// Calls: POST /api/method/building_material.building_material.api.sales_order.cancel
/// Body: { "name": "SAL-ORD-2025-00054" }
/// Cancels an existing order (only allowed for "Chờ phê duyệt" status)
Future<void> cancelOrder(String orderId) async {
try {
await _dioClient.post(
'${ApiConstants.frappeApiMethod}${ApiConstants.cancelOrder}',
data: {'name': orderId},
);
} catch (e) {
throw Exception('Failed to cancel order: $e');
}
}
}

View File

@@ -3,6 +3,7 @@
/// Data model for order detail API response.
library;
import 'package:worker/features/account/data/models/address_model.dart';
import 'package:worker/features/orders/domain/entities/order_detail.dart';
/// Order Detail Model
@@ -19,8 +20,8 @@ class OrderDetailModel {
});
final OrderDetailInfoModel order;
final AddressInfoModel billingAddress;
final AddressInfoModel shippingAddress;
final AddressModel billingAddress;
final AddressModel shippingAddress;
final List<OrderItemDetailModel> items;
final PaymentTermsInfoModel paymentTerms;
final List<TimelineItemModel> timeline;
@@ -33,10 +34,10 @@ class OrderDetailModel {
order: OrderDetailInfoModel.fromJson(
json['order'] as Map<String, dynamic>,
),
billingAddress: AddressInfoModel.fromJson(
billingAddress: AddressModel.fromJson(
json['billing_address'] as Map<String, dynamic>,
),
shippingAddress: AddressInfoModel.fromJson(
shippingAddress: AddressModel.fromJson(
json['shipping_address'] as Map<String, dynamic>,
),
items: (json['items'] as List<dynamic>)
@@ -93,8 +94,8 @@ class OrderDetailModel {
factory OrderDetailModel.fromEntity(OrderDetail entity) {
return OrderDetailModel(
order: OrderDetailInfoModel.fromEntity(entity.order),
billingAddress: AddressInfoModel.fromEntity(entity.billingAddress),
shippingAddress: AddressInfoModel.fromEntity(entity.shippingAddress),
billingAddress: AddressModel.fromEntity(entity.billingAddress),
shippingAddress: AddressModel.fromEntity(entity.shippingAddress),
items: entity.items
.map((item) => OrderItemDetailModel.fromEntity(item))
.toList(),
@@ -229,105 +230,6 @@ class OrderDetailInfoModel {
}
}
/// Address Info Model
class AddressInfoModel {
const AddressInfoModel({
required this.name,
required this.addressTitle,
required this.addressLine1,
required this.phone,
required this.email,
this.fax,
required this.taxCode,
required this.cityCode,
required this.wardCode,
required this.cityName,
required this.wardName,
required this.isAllowEdit,
});
final String name;
final String addressTitle;
final String addressLine1;
final String phone;
final String email;
final String? fax;
final String taxCode;
final String cityCode;
final String wardCode;
final String cityName;
final String wardName;
final bool isAllowEdit;
factory AddressInfoModel.fromJson(Map<String, dynamic> json) {
return AddressInfoModel(
name: json['name'] as String,
addressTitle: json['address_title'] as String,
addressLine1: json['address_line1'] as String,
phone: json['phone'] as String,
email: json['email'] as String,
fax: json['fax'] as String?,
taxCode: json['tax_code'] as String,
cityCode: json['city_code'] as String,
wardCode: json['ward_code'] as String,
cityName: json['city_name'] as String,
wardName: json['ward_name'] as String,
isAllowEdit: json['is_allow_edit'] as bool,
);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'address_title': addressTitle,
'address_line1': addressLine1,
'phone': phone,
'email': email,
'fax': fax,
'tax_code': taxCode,
'city_code': cityCode,
'ward_code': wardCode,
'city_name': cityName,
'ward_name': wardName,
'is_allow_edit': isAllowEdit,
};
}
AddressInfo toEntity() {
return AddressInfo(
name: name,
addressTitle: addressTitle,
addressLine1: addressLine1,
phone: phone,
email: email,
fax: fax,
taxCode: taxCode,
cityCode: cityCode,
wardCode: wardCode,
cityName: cityName,
wardName: wardName,
isAllowEdit: isAllowEdit,
);
}
factory AddressInfoModel.fromEntity(AddressInfo entity) {
return AddressInfoModel(
name: entity.name,
addressTitle: entity.addressTitle,
addressLine1: entity.addressLine1,
phone: entity.phone,
email: entity.email,
fax: entity.fax,
taxCode: entity.taxCode,
cityCode: entity.cityCode,
wardCode: entity.wardCode,
cityName: entity.cityName,
wardName: entity.wardName,
isAllowEdit: entity.isAllowEdit,
);
}
}
/// Order Item Detail Model
class OrderItemDetailModel {
const OrderItemDetailModel({

View File

@@ -140,4 +140,30 @@ class OrderRepositoryImpl implements OrderRepository {
throw Exception('Failed to upload bill: $e');
}
}
@override
Future<void> updateOrderAddress({
required String orderId,
required String shippingAddressName,
required String customerAddress,
}) async {
try {
await _remoteDataSource.updateOrderAddress(
orderId: orderId,
shippingAddressName: shippingAddressName,
customerAddress: customerAddress,
);
} catch (e) {
throw Exception('Failed to update order address: $e');
}
}
@override
Future<void> cancelOrder(String orderId) async {
try {
await _remoteDataSource.cancelOrder(orderId);
} catch (e) {
throw Exception('Failed to cancel order: $e');
}
}
}

View File

@@ -4,6 +4,7 @@
library;
import 'package:equatable/equatable.dart';
import 'package:worker/features/account/domain/entities/address.dart';
/// Order Detail Entity
class OrderDetail extends Equatable {
@@ -19,8 +20,8 @@ class OrderDetail extends Equatable {
});
final OrderDetailInfo order;
final AddressInfo billingAddress;
final AddressInfo shippingAddress;
final Address billingAddress;
final Address shippingAddress;
final List<OrderItemDetail> items;
final PaymentTermsInfo paymentTerms;
final List<TimelineItem> timeline;
@@ -96,53 +97,6 @@ class OrderDetailInfo extends Equatable {
];
}
/// Address Info
class AddressInfo extends Equatable {
const AddressInfo({
required this.name,
required this.addressTitle,
required this.addressLine1,
required this.phone,
required this.email,
this.fax,
required this.taxCode,
required this.cityCode,
required this.wardCode,
required this.cityName,
required this.wardName,
required this.isAllowEdit,
});
final String name;
final String addressTitle;
final String addressLine1;
final String phone;
final String email;
final String? fax;
final String taxCode;
final String cityCode;
final String wardCode;
final String cityName;
final String wardName;
final bool isAllowEdit;
@override
List<Object?> get props => [
name,
addressTitle,
addressLine1,
phone,
email,
fax,
taxCode,
cityCode,
wardCode,
cityName,
wardName,
isAllowEdit,
];
}
/// Order Item Detail
class OrderItemDetail extends Equatable {
const OrderItemDetail({

View File

@@ -44,4 +44,14 @@ abstract class OrderRepository {
required String filePath,
required String orderId,
});
/// Update order address
Future<void> updateOrderAddress({
required String orderId,
required String shippingAddressName,
required String customerAddress,
});
/// Cancel order
Future<void> cancelOrder(String orderId);
}

View File

@@ -13,6 +13,7 @@ import 'package:worker/core/constants/ui_constants.dart';
import 'package:worker/core/enums/status_color.dart';
import 'package:worker/core/theme/colors.dart';
import 'package:worker/core/utils/extensions.dart';
import 'package:worker/features/account/domain/entities/address.dart';
import 'package:worker/features/orders/domain/entities/order_detail.dart';
import 'package:worker/features/orders/presentation/providers/orders_provider.dart';
@@ -33,13 +34,21 @@ class OrderDetailPage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final orderDetailAsync = ref.watch(orderDetailProvider(orderId));
return Scaffold(
backgroundColor: const Color(0xFFF4F6F8),
appBar: AppBar(
leading: IconButton(
icon: const FaIcon(FontAwesomeIcons.arrowLeft, color: Colors.black, size: 20),
onPressed: () => context.pop(),
),
return PopScope(
onPopInvoked: (didPop) {
if (didPop) {
// Dispose providers when leaving the page
ref.invalidate(updateOrderAddressProvider);
ref.invalidate(cancelOrderProvider);
}
},
child: Scaffold(
backgroundColor: const Color(0xFFF4F6F8),
appBar: AppBar(
leading: IconButton(
icon: const FaIcon(FontAwesomeIcons.arrowLeft, color: Colors.black, size: 20),
onPressed: () => context.pop(),
),
title: const Text(
'Chi tiết đơn hàng',
style: TextStyle(color: Colors.black),
@@ -83,10 +92,10 @@ class OrderDetailPage extends ConsumerWidget {
_buildStatusTimelineCard(orderDetail),
// Delivery/Address Information Card
_buildAddressInfoCard(context, orderDetail),
_buildAddressInfoCard(context, ref, orderDetail),
// Invoice Information Card
_buildInvoiceInfoCard(context, orderDetail),
_buildInvoiceInfoCard(context, ref, orderDetail),
// Invoices List Card
_buildInvoicesListCard(context, orderDetail),
@@ -98,7 +107,15 @@ class OrderDetailPage extends ConsumerWidget {
_buildOrderSummaryCard(orderDetail),
// Payment History Card
_buildPaymentHistoryCard(context, orderDetail),
if (orderDetail.order.totalRemaining > 0) ...[
_buildPaymentHistoryCard(context, orderDetail),
],
// Cancel Order Button (only show for "Chờ phê duyệt" status)
if (orderDetail.order.status == 'Chờ phê duyệt') ...[
const SizedBox(height: 16),
_buildCancelOrderButton(context, ref, orderDetail),
],
const SizedBox(height: 16),
],
@@ -163,6 +180,7 @@ class OrderDetailPage extends ConsumerWidget {
),
),
),
),
);
}
@@ -336,7 +354,11 @@ class OrderDetailPage extends ConsumerWidget {
}
/// Build Address Info Card
Widget _buildAddressInfoCard(BuildContext context, OrderDetail orderDetail) {
Widget _buildAddressInfoCard(
BuildContext context,
WidgetRef ref,
OrderDetail orderDetail,
) {
final order = orderDetail.order;
final shippingAddress = orderDetail.shippingAddress;
final dateFormatter = DateFormat('dd/MM/yyyy');
@@ -384,11 +406,71 @@ class OrderDetailPage extends ConsumerWidget {
),
),
TextButton(
onPressed: () {
// TODO: Navigate to address update
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Chức năng đang phát triển')),
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,
);
// 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: () {},
);
}
},
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
@@ -501,7 +583,11 @@ class OrderDetailPage extends ConsumerWidget {
}
/// Build Invoice Info Card
Widget _buildInvoiceInfoCard(BuildContext context, OrderDetail orderDetail) {
Widget _buildInvoiceInfoCard(
BuildContext context,
WidgetRef ref,
OrderDetail orderDetail,
) {
final billingAddress = orderDetail.billingAddress;
return Card(
@@ -536,17 +622,77 @@ class OrderDetailPage extends ConsumerWidget {
],
),
TextButton(
onPressed: () {
// TODO: Navigate to invoice update
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Chức năng đang phát triển')),
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,
);
// 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: () {},
);
}
},
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
side: BorderSide(color: AppColors.grey100),
side: const BorderSide(color: AppColors.grey100),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
@@ -583,7 +729,8 @@ class OrderDetailPage extends ConsumerWidget {
color: AppColors.grey900,
),
),
if (billingAddress.taxCode.isNotEmpty) ...[
if (billingAddress.taxCode != null &&
billingAddress.taxCode!.isNotEmpty) ...[
const SizedBox(height: 2),
Text(
'Mã số thuế: ${billingAddress.taxCode}',
@@ -949,15 +1096,15 @@ class OrderDetailPage extends ConsumerWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
const Row(
children: [
const FaIcon(
FaIcon(
FontAwesomeIcons.receipt,
color: AppColors.primaryBlue,
size: 18,
),
const SizedBox(width: 8),
const Text(
SizedBox(width: 8),
Text(
'Tổng kết đơn hàng',
style: TextStyle(
fontSize: 16,
@@ -993,15 +1140,15 @@ class OrderDetailPage extends ConsumerWidget {
const Divider(height: 24),
// Payment Terms
Row(
const Row(
children: [
const FaIcon(
FaIcon(
FontAwesomeIcons.creditCard,
size: 14,
color: AppColors.grey500,
),
const SizedBox(width: 6),
const Text(
SizedBox(width: 6),
Text(
'Điều khoản thanh toán:',
style: TextStyle(fontSize: 14, color: AppColors.grey500),
),
@@ -1295,4 +1442,116 @@ class OrderDetailPage extends ConsumerWidget {
),
);
}
/// Build Cancel Order Button
Widget _buildCancelOrderButton(
BuildContext context,
WidgetRef ref,
OrderDetail orderDetail,
) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: OutlinedButton.icon(
onPressed: () async {
// Show confirmation dialog
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Xác nhận hủy đơn'),
content: Text(
'Bạn có chắc chắn muốn hủy đơn hàng ${orderDetail.order.name}?\n\nHành động này không thể hoàn tác.',
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('Không'),
),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(true),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.danger,
foregroundColor: Colors.white,
),
child: const Text('Hủy đơn'),
),
],
),
);
// If user confirmed, proceed with cancellation
if (confirmed == true && context.mounted) {
// Show loading indicator
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Row(
children: [
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
SizedBox(width: 12),
Text('Đang hủy đơn hàng...'),
],
),
duration: Duration(seconds: 2),
),
);
// Call cancel order API
await ref.read(cancelOrderProvider.notifier).cancel(orderId);
// Check result
final cancelState = ref.read(cancelOrderProvider);
if (context.mounted) {
cancelState.when(
data: (_) {
// Success: Invalidate order providers and show success message
ref.invalidate(ordersProvider);
ref.invalidate(orderDetailProvider(orderId));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Đã hủy đơn hàng thành công'),
backgroundColor: AppColors.success,
),
);
// Pop back to orders list
context.pop();
},
error: (error, _) {
// Error: Show error message
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Lỗi: ${error.toString()}'),
backgroundColor: AppColors.danger,
),
);
},
loading: () {},
);
}
}
},
icon: const FaIcon(FontAwesomeIcons.xmark, size: 18),
label: const Text('Hủy đơn hàng'),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 14),
side: const BorderSide(
color: AppColors.danger,
width: 2,
),
foregroundColor: AppColors.danger,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
minimumSize: const Size(double.infinity, 48),
),
),
);
}
}

View File

@@ -189,3 +189,51 @@ Future<OrderDetail> orderDetail(Ref ref, String orderId) async {
final repository = await ref.watch(orderRepositoryProvider.future);
return await repository.getOrderDetail(orderId);
}
/// Update Order Address Action
///
/// Updates the shipping and billing addresses for an order.
@Riverpod(keepAlive: true)
class UpdateOrderAddress extends _$UpdateOrderAddress {
@override
FutureOr<void> build() {
// No initial state needed
}
/// Update order address
Future<void> updateAddress({
required String orderId,
required String shippingAddressName,
required String customerAddress,
}) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
final repository = await ref.read(orderRepositoryProvider.future);
await repository.updateOrderAddress(
orderId: orderId,
shippingAddressName: shippingAddressName,
customerAddress: customerAddress,
);
});
}
}
/// Cancel Order Action
///
/// Cancels an order (only allowed for "Chờ phê duyệt" status).
@Riverpod(keepAlive: true)
class CancelOrder extends _$CancelOrder {
@override
FutureOr<void> build() {
// No initial state needed
}
/// Cancel order
Future<void> cancel(String orderId) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
final repository = await ref.read(orderRepositoryProvider.future);
await repository.cancelOrder(orderId);
});
}
}

View File

@@ -492,3 +492,122 @@ final class OrderDetailFamily extends $Family
@override
String toString() => r'orderDetailProvider';
}
/// Update Order Address Action
///
/// Updates the shipping and billing addresses for an order.
@ProviderFor(UpdateOrderAddress)
const updateOrderAddressProvider = UpdateOrderAddressProvider._();
/// Update Order Address Action
///
/// Updates the shipping and billing addresses for an order.
final class UpdateOrderAddressProvider
extends $AsyncNotifierProvider<UpdateOrderAddress, void> {
/// Update Order Address Action
///
/// Updates the shipping and billing addresses for an order.
const UpdateOrderAddressProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'updateOrderAddressProvider',
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$updateOrderAddressHash();
@$internal
@override
UpdateOrderAddress create() => UpdateOrderAddress();
}
String _$updateOrderAddressHash() =>
r'1913c2ccc2ba232debb4368f350f64c3d08cccec';
/// Update Order Address Action
///
/// Updates the shipping and billing addresses for an order.
abstract class _$UpdateOrderAddress extends $AsyncNotifier<void> {
FutureOr<void> build();
@$mustCallSuper
@override
void runBuild() {
build();
final ref = this.ref as $Ref<AsyncValue<void>, void>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<void>, void>,
AsyncValue<void>,
Object?,
Object?
>;
element.handleValue(ref, null);
}
}
/// Cancel Order Action
///
/// Cancels an order (only allowed for "Chờ phê duyệt" status).
@ProviderFor(CancelOrder)
const cancelOrderProvider = CancelOrderProvider._();
/// Cancel Order Action
///
/// Cancels an order (only allowed for "Chờ phê duyệt" status).
final class CancelOrderProvider
extends $AsyncNotifierProvider<CancelOrder, void> {
/// Cancel Order Action
///
/// Cancels an order (only allowed for "Chờ phê duyệt" status).
const CancelOrderProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'cancelOrderProvider',
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$cancelOrderHash();
@$internal
@override
CancelOrder create() => CancelOrder();
}
String _$cancelOrderHash() => r'201624156f3ae3c05fe438ea7e266cb8fa2a5bd6';
/// Cancel Order Action
///
/// Cancels an order (only allowed for "Chờ phê duyệt" status).
abstract class _$CancelOrder extends $AsyncNotifier<void> {
FutureOr<void> build();
@$mustCallSuper
@override
void runBuild() {
build();
final ref = this.ref as $Ref<AsyncValue<void>, void>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<void>, void>,
AsyncValue<void>,
Object?,
Object?
>;
element.handleValue(ref, null);
}
}