asdasdasd
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import 'package:dropdown_search/dropdown_search.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../core/di/providers.dart';
|
||||
import '../../../../core/services/print_service.dart';
|
||||
import '../../../../core/utils/text_utils.dart';
|
||||
import '../../../users/domain/entities/user_entity.dart';
|
||||
import '../../data/models/create_product_warehouse_request.dart';
|
||||
import '../../domain/entities/product_stage_entity.dart';
|
||||
@@ -332,7 +334,7 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
|
||||
|
||||
return SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.all(16),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 8,
|
||||
@@ -821,45 +823,116 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
DropdownButtonFormField<UserEntity>(
|
||||
value: value,
|
||||
decoration: InputDecoration(
|
||||
labelText: label,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color: theme.colorScheme.outline,
|
||||
DropdownSearch<UserEntity>(
|
||||
items: (filter, infiniteScrollProps) => users,
|
||||
selectedItem: value,
|
||||
itemAsString: (UserEntity user) {
|
||||
return user.name.isNotEmpty
|
||||
? '${user.name} ${user.firstName}'
|
||||
: user.email;
|
||||
},
|
||||
compareFn: (item1, item2) => item1.id == item2.id,
|
||||
// Custom filter function for Vietnamese-aware search
|
||||
filterFn: (user, filter) {
|
||||
if (filter.isEmpty) return true;
|
||||
|
||||
// Search in name, firstName, and email
|
||||
final searchTexts = [
|
||||
user.name,
|
||||
user.firstName,
|
||||
user.email,
|
||||
'${user.name} ${user.firstName}', // Full name
|
||||
];
|
||||
|
||||
// Use Vietnamese-aware search
|
||||
return TextUtils.containsVietnameseSearchInAny(searchTexts, filter);
|
||||
},
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: true,
|
||||
searchFieldProps: TextFieldProps(
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Tìm kiếm',
|
||||
hintText: 'Nhập tên hoặc email...',
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color: theme.colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
menuProps: const MenuProps(
|
||||
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||
elevation: 8,
|
||||
),
|
||||
filled: true,
|
||||
fillColor: theme.colorScheme.surface,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 12,
|
||||
itemBuilder: (context, item, isDisabled, isSelected) {
|
||||
return ListTile(
|
||||
selected: isSelected,
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 4,
|
||||
),
|
||||
title: Text(
|
||||
item.name.isNotEmpty
|
||||
? '${item.name} ${item.firstName}'
|
||||
: item.email,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
subtitle: item.email.isNotEmpty && item.name.isNotEmpty
|
||||
? Text(
|
||||
item.email,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
},
|
||||
emptyBuilder: (context, searchEntry) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'Không tìm thấy kết quả',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
decoratorProps: DropDownDecoratorProps(
|
||||
decoration: InputDecoration(
|
||||
labelText: label,
|
||||
hintText: 'Chọn $label',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color: theme.colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: theme.colorScheme.surface,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
hint: Text('Chọn $label'),
|
||||
items: users.map((user) {
|
||||
return DropdownMenuItem<UserEntity>(
|
||||
value: user,
|
||||
child: Text(
|
||||
user.name.isNotEmpty ? '${user.name} ${user.firstName}' : user.email,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: onChanged,
|
||||
isExpanded: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -56,21 +56,23 @@ class _ProductsPageState extends ConsumerState<ProductsPage>
|
||||
_isTabSwitching = true; // Mark that tab is switching
|
||||
});
|
||||
|
||||
// Load products for new operation type
|
||||
// Load products for new operation type from cache (forceRefresh: false)
|
||||
ref.read(productsProvider.notifier).loadProducts(
|
||||
widget.warehouseId,
|
||||
widget.warehouseName,
|
||||
_currentOperationType,
|
||||
forceRefresh: false, // Load from cache when switching tabs
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Load products when page is initialized
|
||||
// Load products from cache when page is initialized (forceRefresh: false)
|
||||
Future.microtask(() {
|
||||
ref.read(productsProvider.notifier).loadProducts(
|
||||
widget.warehouseId,
|
||||
widget.warehouseName,
|
||||
_currentOperationType,
|
||||
forceRefresh: false, // Load from cache on initial load
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,11 +52,13 @@ class ProductsNotifier extends StateNotifier<ProductsState> {
|
||||
/// [warehouseId] - The ID of the warehouse
|
||||
/// [warehouseName] - The name of the warehouse (for display)
|
||||
/// [type] - The operation type ('import' or 'export')
|
||||
/// [forceRefresh] - If true, bypass cache and fetch from API
|
||||
Future<void> loadProducts(
|
||||
int warehouseId,
|
||||
String warehouseName,
|
||||
String type,
|
||||
) async {
|
||||
String type, {
|
||||
bool forceRefresh = false,
|
||||
}) async {
|
||||
// Set loading state
|
||||
state = state.copyWith(
|
||||
isLoading: true,
|
||||
@@ -66,8 +68,12 @@ class ProductsNotifier extends StateNotifier<ProductsState> {
|
||||
operationType: type,
|
||||
);
|
||||
|
||||
// Call the use case
|
||||
final result = await getProductsUseCase(warehouseId, type);
|
||||
// Call the use case with forceRefresh flag
|
||||
final result = await getProductsUseCase(
|
||||
warehouseId,
|
||||
type,
|
||||
forceRefresh: forceRefresh,
|
||||
);
|
||||
|
||||
// Handle the result
|
||||
result.fold(
|
||||
@@ -95,13 +101,14 @@ class ProductsNotifier extends StateNotifier<ProductsState> {
|
||||
state = const ProductsState();
|
||||
}
|
||||
|
||||
/// Refresh products
|
||||
/// Refresh products - forces fetch from API
|
||||
Future<void> refreshProducts() async {
|
||||
if (state.warehouseId != null) {
|
||||
await loadProducts(
|
||||
state.warehouseId!,
|
||||
state.warehouseName ?? '',
|
||||
state.operationType,
|
||||
forceRefresh: true, // Always force refresh when explicitly requested
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user