157 lines
5.2 KiB
Dart
157 lines
5.2 KiB
Dart
/// 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> submissionsRemoteDataSource(Ref ref) async {
|
|
final dioClient = await ref.watch(dioClientProvider.future);
|
|
return SubmissionsRemoteDataSourceImpl(dioClient);
|
|
}
|
|
|
|
/// Submissions Repository Provider
|
|
@riverpod
|
|
Future<SubmissionsRepository> 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<List<ProjectStatus>> build() async {
|
|
final repository = await ref.watch(submissionsRepositoryProvider.future);
|
|
return repository.getProjectStatusList();
|
|
}
|
|
|
|
/// Refresh status list from remote (force refresh)
|
|
Future<void> 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<List<ProjectSubmission>> 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<void> 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<List<ProjectSubmission>> 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;
|
|
});
|
|
}
|