/// Providers: Project Submissions /// /// Riverpod providers for managing project submissions state. library; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:worker/core/network/dio_client.dart'; import 'package:worker/features/projects/data/datasources/project_status_local_datasource.dart'; import 'package:worker/features/projects/data/datasources/submissions_remote_datasource.dart'; import 'package:worker/features/projects/data/repositories/submissions_repository_impl.dart'; import 'package:worker/features/projects/domain/entities/project_status.dart'; import 'package:worker/features/projects/domain/entities/project_submission.dart'; import 'package:worker/features/projects/domain/repositories/submissions_repository.dart'; part 'submissions_provider.g.dart'; /// Project Status Local Data Source Provider @riverpod ProjectStatusLocalDataSource projectStatusLocalDataSource(Ref ref) { return ProjectStatusLocalDataSource(); } /// Submissions Remote Data Source Provider @riverpod Future submissionsRemoteDataSource(Ref ref) async { final dioClient = await ref.watch(dioClientProvider.future); return SubmissionsRemoteDataSourceImpl(dioClient); } /// Submissions Repository Provider @riverpod Future submissionsRepository(Ref ref) async { final remoteDataSource = await ref.watch(submissionsRemoteDataSourceProvider.future); final statusLocalDataSource = ref.watch(projectStatusLocalDataSourceProvider); return SubmissionsRepositoryImpl(remoteDataSource, statusLocalDataSource); } /// Project Status List Provider /// /// Fetches project status options from API with cache-first pattern. /// This is loaded before submissions to ensure filter options are available. @riverpod class ProjectStatusList extends _$ProjectStatusList { @override Future> build() async { final repository = await ref.watch(submissionsRepositoryProvider.future); return repository.getProjectStatusList(); } /// Refresh status list from remote (force refresh) Future refresh() async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() async { final repository = await ref.read(submissionsRepositoryProvider.future); return repository.getProjectStatusList(forceRefresh: true); }); } } /// All Submissions Provider /// /// Fetches and manages submissions data from remote. /// Waits for project status list to be loaded first. @riverpod class AllSubmissions extends _$AllSubmissions { @override Future> build() async { // Ensure status list is loaded first (for filter options) await ref.watch(projectStatusListProvider.future); // Then fetch submissions final repository = await ref.watch(submissionsRepositoryProvider.future); return repository.getSubmissions(); } /// Refresh submissions from remote Future refresh() async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() async { // Also refresh status list await ref.read(projectStatusListProvider.notifier).refresh(); final repository = await ref.read(submissionsRepositoryProvider.future); return repository.getSubmissions(); }); } } /// Submissions Filter State /// /// Manages search and status filter state. /// Status filter uses the status label string from API (e.g., "Chờ phê duyệt"). @riverpod class SubmissionsFilter extends _$SubmissionsFilter { @override ({String searchQuery, String? selectedStatus}) build() { return (searchQuery: '', selectedStatus: null); } /// Update search query void updateSearchQuery(String query) { state = (searchQuery: query, selectedStatus: state.selectedStatus); } /// Select a status filter (uses Vietnamese label from API) void selectStatus(String? status) { state = (searchQuery: state.searchQuery, selectedStatus: status); } /// Clear status filter void clearStatusFilter() { state = (searchQuery: state.searchQuery, selectedStatus: null); } /// Clear search query void clearSearchQuery() { state = (searchQuery: '', selectedStatus: state.selectedStatus); } /// Clear all filters void clearAllFilters() { state = (searchQuery: '', selectedStatus: null); } } /// Filtered Submissions Provider /// /// Combines submissions data with filter state to return filtered results. @riverpod AsyncValue> filteredSubmissions(Ref ref) { final dataAsync = ref.watch(allSubmissionsProvider); final filter = ref.watch(submissionsFilterProvider); return dataAsync.whenData((submissions) { var filtered = submissions; // Filter by status (matches Vietnamese label from API) if (filter.selectedStatus != null) { filtered = filtered.where((s) => s.status == filter.selectedStatus).toList(); } // Filter by search query if (filter.searchQuery.isNotEmpty) { final query = filter.searchQuery.toLowerCase(); filtered = filtered.where((s) { return s.submissionId.toLowerCase().contains(query) || s.designedArea.toLowerCase().contains(query); }).toList(); } // Sort by request date (newest first) filtered.sort((a, b) => b.requestDate.compareTo(a.requestDate)); return filtered; }); }