update
This commit is contained in:
@@ -197,7 +197,50 @@ class HiveKeys {
|
|||||||
static const String lastSyncTime = 'last_sync_time';
|
static const String lastSyncTime = 'last_sync_time';
|
||||||
static const String schemaVersion = 'schema_version';
|
static const String schemaVersion = 'schema_version';
|
||||||
static const String encryptionEnabled = 'encryption_enabled';
|
static const String encryptionEnabled = 'encryption_enabled';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Order Status Indices
|
||||||
|
///
|
||||||
|
/// Index values for order statuses stored in Hive.
|
||||||
|
/// These correspond to the index field in OrderStatusModel.
|
||||||
|
/// Use these constants to compare order status by index instead of hardcoded strings.
|
||||||
|
///
|
||||||
|
/// API Response Structure:
|
||||||
|
/// - status: "Pending approval" (English status name)
|
||||||
|
/// - label: "Chờ phê duyệt" (Vietnamese display label)
|
||||||
|
/// - color: "Warning" (Status color indicator)
|
||||||
|
/// - index: 1 (Unique identifier)
|
||||||
|
class OrderStatusIndex {
|
||||||
|
// Private constructor to prevent instantiation
|
||||||
|
OrderStatusIndex._();
|
||||||
|
|
||||||
|
/// Pending approval - "Chờ phê duyệt"
|
||||||
|
/// Color: Warning
|
||||||
|
static const int pendingApproval = 1;
|
||||||
|
|
||||||
|
/// Manager Review - "Manager Review"
|
||||||
|
/// Color: Warning
|
||||||
|
static const int managerReview = 2;
|
||||||
|
|
||||||
|
/// Processing - "Đang xử lý"
|
||||||
|
/// Color: Info
|
||||||
|
static const int processing = 3;
|
||||||
|
|
||||||
|
/// Completed - "Hoàn thành"
|
||||||
|
/// Color: Success
|
||||||
|
static const int completed = 4;
|
||||||
|
|
||||||
|
/// Rejected - "Từ chối"
|
||||||
|
/// Color: Danger
|
||||||
|
static const int rejected = 5;
|
||||||
|
|
||||||
|
/// Cancelled - "HỦY BỎ"
|
||||||
|
/// Color: Danger
|
||||||
|
static const int cancelled = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hive Keys (continued)
|
||||||
|
extension HiveKeysContinued on HiveKeys {
|
||||||
// Cache Box Keys
|
// Cache Box Keys
|
||||||
static const String productsCacheKey = 'products_cache';
|
static const String productsCacheKey = 'products_cache';
|
||||||
static const String categoriesCacheKey = 'categories_cache';
|
static const String categoriesCacheKey = 'categories_cache';
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ class AddressModel extends HiveObject {
|
|||||||
@HiveField(11)
|
@HiveField(11)
|
||||||
String? wardName;
|
String? wardName;
|
||||||
|
|
||||||
|
/// Whether editing this address is allowed
|
||||||
|
@HiveField(12)
|
||||||
|
bool isAllowEdit;
|
||||||
|
|
||||||
AddressModel({
|
AddressModel({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.addressTitle,
|
required this.addressTitle,
|
||||||
@@ -75,6 +79,7 @@ class AddressModel extends HiveObject {
|
|||||||
this.isDefault = false,
|
this.isDefault = false,
|
||||||
this.cityName,
|
this.cityName,
|
||||||
this.wardName,
|
this.wardName,
|
||||||
|
this.isAllowEdit = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Create from JSON (API response)
|
/// Create from JSON (API response)
|
||||||
@@ -92,6 +97,7 @@ class AddressModel extends HiveObject {
|
|||||||
isDefault: json['is_default'] == 1 || json['is_default'] == true,
|
isDefault: json['is_default'] == 1 || json['is_default'] == true,
|
||||||
cityName: json['city_name'] as String?,
|
cityName: json['city_name'] as String?,
|
||||||
wardName: json['ward_name'] as String?,
|
wardName: json['ward_name'] as String?,
|
||||||
|
isAllowEdit: json['is_allow_edit'] == 1 || json['is_allow_edit'] == true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,6 +117,7 @@ class AddressModel extends HiveObject {
|
|||||||
'is_default': isDefault,
|
'is_default': isDefault,
|
||||||
if (cityName != null && cityName!.isNotEmpty) 'city_name': cityName,
|
if (cityName != null && cityName!.isNotEmpty) 'city_name': cityName,
|
||||||
if (wardName != null && wardName!.isNotEmpty) 'ward_name': wardName,
|
if (wardName != null && wardName!.isNotEmpty) 'ward_name': wardName,
|
||||||
|
'is_allow_edit': isAllowEdit,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +136,7 @@ class AddressModel extends HiveObject {
|
|||||||
isDefault: isDefault,
|
isDefault: isDefault,
|
||||||
cityName: cityName,
|
cityName: cityName,
|
||||||
wardName: wardName,
|
wardName: wardName,
|
||||||
|
isAllowEdit: isAllowEdit,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,12 +155,14 @@ class AddressModel extends HiveObject {
|
|||||||
isDefault: entity.isDefault,
|
isDefault: entity.isDefault,
|
||||||
cityName: entity.cityName,
|
cityName: entity.cityName,
|
||||||
wardName: entity.wardName,
|
wardName: entity.wardName,
|
||||||
|
isAllowEdit: entity.isAllowEdit,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'AddressModel(name: $name, addressTitle: $addressTitle, '
|
return 'AddressModel(name: $name, addressTitle: $addressTitle, '
|
||||||
'addressLine1: $addressLine1, phone: $phone, isDefault: $isDefault)';
|
'addressLine1: $addressLine1, phone: $phone, isDefault: $isDefault, '
|
||||||
|
'isAllowEdit: $isAllowEdit)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,13 +29,14 @@ class AddressModelAdapter extends TypeAdapter<AddressModel> {
|
|||||||
isDefault: fields[9] == null ? false : fields[9] as bool,
|
isDefault: fields[9] == null ? false : fields[9] as bool,
|
||||||
cityName: fields[10] as String?,
|
cityName: fields[10] as String?,
|
||||||
wardName: fields[11] as String?,
|
wardName: fields[11] as String?,
|
||||||
|
isAllowEdit: fields[12] == null ? true : fields[12] as bool,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, AddressModel obj) {
|
void write(BinaryWriter writer, AddressModel obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(12)
|
..writeByte(13)
|
||||||
..writeByte(0)
|
..writeByte(0)
|
||||||
..write(obj.name)
|
..write(obj.name)
|
||||||
..writeByte(1)
|
..writeByte(1)
|
||||||
@@ -59,7 +60,9 @@ class AddressModelAdapter extends TypeAdapter<AddressModel> {
|
|||||||
..writeByte(10)
|
..writeByte(10)
|
||||||
..write(obj.cityName)
|
..write(obj.cityName)
|
||||||
..writeByte(11)
|
..writeByte(11)
|
||||||
..write(obj.wardName);
|
..write(obj.wardName)
|
||||||
|
..writeByte(12)
|
||||||
|
..write(obj.isAllowEdit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class Address extends Equatable {
|
|||||||
final bool isDefault;
|
final bool isDefault;
|
||||||
final String? cityName;
|
final String? cityName;
|
||||||
final String? wardName;
|
final String? wardName;
|
||||||
|
final bool isAllowEdit;
|
||||||
|
|
||||||
const Address({
|
const Address({
|
||||||
required this.name,
|
required this.name,
|
||||||
@@ -36,6 +37,7 @@ class Address extends Equatable {
|
|||||||
this.isDefault = false,
|
this.isDefault = false,
|
||||||
this.cityName,
|
this.cityName,
|
||||||
this.wardName,
|
this.wardName,
|
||||||
|
this.isAllowEdit = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -52,6 +54,7 @@ class Address extends Equatable {
|
|||||||
isDefault,
|
isDefault,
|
||||||
cityName,
|
cityName,
|
||||||
wardName,
|
wardName,
|
||||||
|
isAllowEdit,
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Get full address display string
|
/// Get full address display string
|
||||||
@@ -81,6 +84,7 @@ class Address extends Equatable {
|
|||||||
bool? isDefault,
|
bool? isDefault,
|
||||||
String? cityName,
|
String? cityName,
|
||||||
String? wardName,
|
String? wardName,
|
||||||
|
bool? isAllowEdit,
|
||||||
}) {
|
}) {
|
||||||
return Address(
|
return Address(
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
@@ -95,11 +99,12 @@ class Address extends Equatable {
|
|||||||
isDefault: isDefault ?? this.isDefault,
|
isDefault: isDefault ?? this.isDefault,
|
||||||
cityName: cityName ?? this.cityName,
|
cityName: cityName ?? this.cityName,
|
||||||
wardName: wardName ?? this.wardName,
|
wardName: wardName ?? this.wardName,
|
||||||
|
isAllowEdit: isAllowEdit ?? this.isAllowEdit,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'Address(name: $name, addressTitle: $addressTitle, addressLine1: $addressLine1, phone: $phone, isDefault: $isDefault)';
|
return 'Address(name: $name, addressTitle: $addressTitle, addressLine1: $addressLine1, phone: $phone, isDefault: $isDefault, isAllowEdit: $isAllowEdit)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ class OrderStatusLocalDataSource {
|
|||||||
/// Get cached order status list
|
/// Get cached order status list
|
||||||
List<OrderStatusModel> getCachedStatusList() {
|
List<OrderStatusModel> getCachedStatusList() {
|
||||||
try {
|
try {
|
||||||
final values = _box.values.whereType<OrderStatusModel>().toList();
|
final values = _box.values.whereType<OrderStatusModel>().toList()
|
||||||
// Sort by index
|
// Sort by index
|
||||||
values.sort((a, b) => a.index.compareTo(b.index));
|
..sort((a, b) => a.index.compareTo(b.index));
|
||||||
return values;
|
return values;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@@ -111,8 +111,9 @@ class OrderDetailPage extends ConsumerWidget {
|
|||||||
_buildPaymentHistoryCard(context, orderDetail),
|
_buildPaymentHistoryCard(context, orderDetail),
|
||||||
],
|
],
|
||||||
|
|
||||||
// Cancel Order Button (only show for "Chờ phê duyệt" status)
|
// Cancel Order Button
|
||||||
if (orderDetail.order.status == 'Chờ phê duyệt') ...[
|
// Use API flag to determine if cancellation is allowed
|
||||||
|
if (orderDetail.order.isAllowCancel) ...[
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
_buildCancelOrderButton(context, ref, orderDetail),
|
_buildCancelOrderButton(context, ref, orderDetail),
|
||||||
],
|
],
|
||||||
@@ -405,90 +406,98 @@ class OrderDetailPage extends ConsumerWidget {
|
|||||||
color: AppColors.grey500,
|
color: AppColors.grey500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextButton(
|
Builder(
|
||||||
onPressed: () async {
|
builder: (context) {
|
||||||
// Navigate to address selection and wait for result
|
// Use API flag to determine if editing is allowed
|
||||||
final result = await context.push<Address>(
|
if (orderDetail.shippingAddress.isAllowEdit) {
|
||||||
'/account/addresses',
|
return TextButton(
|
||||||
extra: {
|
onPressed: () async {
|
||||||
'selectMode': true,
|
// Navigate to address selection and wait for result
|
||||||
'currentAddress': shippingAddress,
|
final result = await context.push<Address>(
|
||||||
},
|
'/account/addresses',
|
||||||
);
|
extra: {
|
||||||
|
'selectMode': true,
|
||||||
// If user selected an address, update the order
|
'currentAddress': shippingAddress,
|
||||||
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
|
// If user selected an address, update the order
|
||||||
final updateState =
|
if (result != null && context.mounted) {
|
||||||
ref.read(updateOrderAddressProvider)
|
// Show loading indicator
|
||||||
..when(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
data: (_) {
|
const SnackBar(
|
||||||
// Refresh order detail to show updated address
|
content: Text('Đang cập nhật địa chỉ...'),
|
||||||
ref.invalidate(orderDetailProvider(orderId));
|
duration: Duration(seconds: 1),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// Show success message
|
// Update shipping address (keep billing address the same)
|
||||||
if (context.mounted) {
|
await ref
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
.read(updateOrderAddressProvider.notifier)
|
||||||
const SnackBar(
|
.updateAddress(
|
||||||
content: Text(
|
orderId: orderId,
|
||||||
'Cập nhật địa chỉ giao hàng thành công',
|
shippingAddressName: result.name,
|
||||||
),
|
customerAddress: orderDetail.billingAddress.name,
|
||||||
backgroundColor: AppColors.success,
|
);
|
||||||
),
|
|
||||||
|
// 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, _) {
|
style: TextButton.styleFrom(
|
||||||
// Show error message
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
if (context.mounted) {
|
minimumSize: Size.zero,
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
SnackBar(
|
side: const BorderSide(color: AppColors.grey100),
|
||||||
content: Text(
|
shape: RoundedRectangleBorder(
|
||||||
'Lỗi: ${error.toString()}',
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
backgroundColor: AppColors.danger,
|
),
|
||||||
),
|
child: const Text(
|
||||||
);
|
'Cập nhật',
|
||||||
}
|
style: TextStyle(
|
||||||
},
|
fontSize: 14,
|
||||||
loading: () {},
|
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(
|
Builder(
|
||||||
onPressed: () async {
|
builder: (context) {
|
||||||
// Navigate to address selection and wait for result
|
// Use API flag to determine if editing is allowed
|
||||||
final result = await context.push<Address>(
|
if (orderDetail.billingAddress.isAllowEdit) {
|
||||||
'/account/addresses',
|
return TextButton(
|
||||||
extra: {
|
onPressed: () async {
|
||||||
'selectMode': true,
|
// Navigate to address selection and wait for result
|
||||||
'currentAddress': billingAddress,
|
final result = await context.push<Address>(
|
||||||
},
|
'/account/addresses',
|
||||||
);
|
extra: {
|
||||||
|
'selectMode': true,
|
||||||
// If user selected an address, update the order
|
'currentAddress': billingAddress,
|
||||||
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
|
// If user selected an address, update the order
|
||||||
final updateState =
|
if (result != null && context.mounted) {
|
||||||
ref.read(updateOrderAddressProvider)
|
// Show loading indicator
|
||||||
..when(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
data: (_) {
|
const SnackBar(
|
||||||
// Refresh order detail to show updated address
|
content: Text('Đang cập nhật địa chỉ...'),
|
||||||
ref.invalidate(orderDetailProvider(orderId));
|
duration: Duration(seconds: 1),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// Show success message
|
// Update billing address (keep shipping address the same)
|
||||||
if (context.mounted) {
|
await ref
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
.read(updateOrderAddressProvider.notifier)
|
||||||
const SnackBar(
|
.updateAddress(
|
||||||
content: Text(
|
orderId: orderId,
|
||||||
'Cập nhật địa chỉ hóa đơn thành công',
|
shippingAddressName: orderDetail.shippingAddress.name,
|
||||||
),
|
customerAddress: result.name,
|
||||||
backgroundColor: AppColors.success,
|
);
|
||||||
),
|
|
||||||
|
// 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, _) {
|
style: TextButton.styleFrom(
|
||||||
// Show error message
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
if (context.mounted) {
|
minimumSize: Size.zero,
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
SnackBar(
|
side: const BorderSide(color: AppColors.grey100),
|
||||||
content: Text(
|
shape: RoundedRectangleBorder(
|
||||||
'Lỗi: ${error.toString()}',
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
backgroundColor: AppColors.danger,
|
),
|
||||||
),
|
child: const Text(
|
||||||
);
|
'Cập nhật',
|
||||||
}
|
style: TextStyle(
|
||||||
},
|
fontSize: 14,
|
||||||
loading: () {},
|
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.
|
/// Riverpod providers for managing orders state.
|
||||||
library;
|
library;
|
||||||
|
|
||||||
|
import 'package:hive_ce/hive.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.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.dart';
|
||||||
import 'package:worker/features/orders/domain/entities/order_detail.dart';
|
import 'package:worker/features/orders/domain/entities/order_detail.dart';
|
||||||
import 'package:worker/features/orders/domain/entities/order_status.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
|
/// Order Status List Provider
|
||||||
///
|
///
|
||||||
/// Provides cached order status list with automatic refresh.
|
/// Provides cached order status list with automatic refresh.
|
||||||
|
|||||||
@@ -344,6 +344,62 @@ final class TotalOrdersCountProvider
|
|||||||
|
|
||||||
String _$totalOrdersCountHash() => r'ec1ab3a8d432033aa1f02d28e841e78eba06d63e';
|
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
|
/// Order Status List Provider
|
||||||
///
|
///
|
||||||
/// Provides cached order status list with automatic refresh.
|
/// Provides cached order status list with automatic refresh.
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.0.1+12
|
version: 1.0.1+15
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.10.0
|
sdk: ^3.10.0
|
||||||
|
|||||||
Reference in New Issue
Block a user