- Updated AttendanceController to use Rx<DateTime> for date ranges. - Introduced DateRangePickerWidget for selecting date ranges in attendance and expense filters. - Refactored attendance filter bottom sheet to utilize the new DateRangePickerWidget. - Enhanced user document filter bottom sheet with date range selection. - Improved expense filter bottom sheet to include date range selection and refactored UI components for better readability. - Cleaned up unused code and improved overall code structure for maintainability.
214 lines
5.7 KiB
Dart
214 lines
5.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:marco/helpers/services/api_service.dart';
|
|
import 'package:marco/model/document/document_filter_model.dart';
|
|
import 'package:marco/model/document/documents_list_model.dart';
|
|
|
|
class DocumentController extends GetxController {
|
|
// ==================== Observables ====================
|
|
final isLoading = false.obs;
|
|
final documents = <DocumentItem>[].obs;
|
|
final filters = Rxn<DocumentFiltersData>();
|
|
|
|
// Selected filters (multi-select)
|
|
final selectedUploadedBy = <String>[].obs;
|
|
final selectedCategory = <String>[].obs;
|
|
final selectedType = <String>[].obs;
|
|
final selectedTag = <String>[].obs;
|
|
|
|
// Pagination
|
|
final pageNumber = 1.obs;
|
|
final pageSize = 20;
|
|
final hasMore = true.obs;
|
|
|
|
// Error handling
|
|
final errorMessage = ''.obs;
|
|
|
|
// Preferences
|
|
final showInactive = false.obs;
|
|
|
|
// Search
|
|
final searchQuery = ''.obs;
|
|
final searchController = TextEditingController();
|
|
|
|
// Additional filters
|
|
final isUploadedAt = true.obs;
|
|
final isVerified = RxnBool();
|
|
final startDate = Rxn<DateTime>();
|
|
final endDate = Rxn<DateTime>();
|
|
|
|
// ==================== Lifecycle ====================
|
|
|
|
@override
|
|
void onClose() {
|
|
// Don't dispose searchController here - it's managed by the page
|
|
super.onClose();
|
|
}
|
|
|
|
// ==================== API Methods ====================
|
|
|
|
/// Fetch document filters for entity
|
|
Future<void> fetchFilters(String entityTypeId) async {
|
|
try {
|
|
final response = await ApiService.getDocumentFilters(entityTypeId);
|
|
|
|
if (response != null && response.success) {
|
|
filters.value = response.data;
|
|
} else {
|
|
errorMessage.value = response?.message ?? 'Failed to fetch filters';
|
|
_showError('Failed to load filters');
|
|
}
|
|
} catch (e) {
|
|
errorMessage.value = 'Error fetching filters: $e';
|
|
_showError('Error loading filters');
|
|
debugPrint('❌ Error fetching filters: $e');
|
|
}
|
|
}
|
|
|
|
/// Toggle document active/inactive state
|
|
Future<bool> toggleDocumentActive(
|
|
String id, {
|
|
required bool isActive,
|
|
required String entityTypeId,
|
|
required String entityId,
|
|
}) async {
|
|
try {
|
|
isLoading.value = true;
|
|
|
|
final success = await ApiService.deleteDocumentApi(
|
|
id: id,
|
|
isActive: isActive,
|
|
);
|
|
|
|
if (success) {
|
|
// Refresh list after state change
|
|
await fetchDocuments(
|
|
entityTypeId: entityTypeId,
|
|
entityId: entityId,
|
|
reset: true,
|
|
);
|
|
return true;
|
|
} else {
|
|
errorMessage.value = 'Failed to update document state';
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
errorMessage.value = 'Error updating document: $e';
|
|
debugPrint('❌ Error toggling document state: $e');
|
|
return false;
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
}
|
|
|
|
/// Fetch documents for entity with pagination
|
|
Future<void> fetchDocuments({
|
|
required String entityTypeId,
|
|
required String entityId,
|
|
String? filter,
|
|
String? searchString,
|
|
bool reset = false,
|
|
}) async {
|
|
try {
|
|
// Reset pagination if needed
|
|
if (reset) {
|
|
pageNumber.value = 1;
|
|
documents.clear();
|
|
hasMore.value = true;
|
|
}
|
|
|
|
// Don't fetch if no more data
|
|
if (!hasMore.value && !reset) return;
|
|
|
|
// Prevent duplicate requests
|
|
if (isLoading.value) return;
|
|
|
|
isLoading.value = true;
|
|
|
|
final response = await ApiService.getDocumentListApi(
|
|
entityTypeId: entityTypeId,
|
|
entityId: entityId,
|
|
filter: filter ?? '',
|
|
searchString: searchString ?? searchQuery.value,
|
|
pageNumber: pageNumber.value,
|
|
pageSize: pageSize,
|
|
isActive: !showInactive.value,
|
|
);
|
|
|
|
if (response != null && response.success) {
|
|
if (response.data.data.isNotEmpty) {
|
|
documents.addAll(response.data.data);
|
|
pageNumber.value++;
|
|
} else {
|
|
hasMore.value = false;
|
|
}
|
|
errorMessage.value = '';
|
|
} else {
|
|
errorMessage.value = response?.message ?? 'Failed to fetch documents';
|
|
if (documents.isEmpty) {
|
|
_showError('Failed to load documents');
|
|
}
|
|
}
|
|
} catch (e) {
|
|
errorMessage.value = 'Error fetching documents: $e';
|
|
if (documents.isEmpty) {
|
|
_showError('Error loading documents');
|
|
}
|
|
debugPrint('❌ Error fetching documents: $e');
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
}
|
|
|
|
// ==================== Helper Methods ====================
|
|
|
|
/// Clear all selected filters
|
|
void clearFilters() {
|
|
selectedUploadedBy.clear();
|
|
selectedCategory.clear();
|
|
selectedType.clear();
|
|
selectedTag.clear();
|
|
isUploadedAt.value = true;
|
|
isVerified.value = null;
|
|
startDate.value = null;
|
|
endDate.value = null;
|
|
}
|
|
|
|
/// Check if any filters are active
|
|
bool hasActiveFilters() {
|
|
return selectedUploadedBy.isNotEmpty ||
|
|
selectedCategory.isNotEmpty ||
|
|
selectedType.isNotEmpty ||
|
|
selectedTag.isNotEmpty ||
|
|
startDate.value != null ||
|
|
endDate.value != null ||
|
|
isVerified.value != null;
|
|
}
|
|
|
|
/// Show error message
|
|
void _showError(String message) {
|
|
Get.snackbar(
|
|
'Error',
|
|
message,
|
|
snackPosition: SnackPosition.BOTTOM,
|
|
backgroundColor: Colors.red.shade100,
|
|
colorText: Colors.red.shade900,
|
|
margin: const EdgeInsets.all(16),
|
|
borderRadius: 8,
|
|
duration: const Duration(seconds: 3),
|
|
);
|
|
}
|
|
|
|
/// Reset controller state
|
|
void reset() {
|
|
documents.clear();
|
|
clearFilters();
|
|
searchController.clear();
|
|
searchQuery.value = '';
|
|
pageNumber.value = 1;
|
|
hasMore.value = true;
|
|
showInactive.value = false;
|
|
errorMessage.value = '';
|
|
}
|
|
}
|