This commit is contained in:
Phuoc Nguyen
2025-11-27 14:59:48 +07:00
parent dc8e60f589
commit ba04576750
25 changed files with 931 additions and 721 deletions

View File

@@ -4,51 +4,84 @@
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';
import 'package:worker/features/projects/domain/usecases/get_submissions.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
SubmissionsRemoteDataSource submissionsRemoteDataSource(Ref ref) {
return SubmissionsRemoteDataSourceImpl();
Future<SubmissionsRemoteDataSource> submissionsRemoteDataSource(Ref ref) async {
final dioClient = await ref.watch(dioClientProvider.future);
return SubmissionsRemoteDataSourceImpl(dioClient);
}
/// Submissions Repository Provider
@riverpod
SubmissionsRepository submissionsRepository(Ref ref) {
final remoteDataSource = ref.watch(submissionsRemoteDataSourceProvider);
return SubmissionsRepositoryImpl(remoteDataSource);
Future<SubmissionsRepository> submissionsRepository(Ref ref) async {
final remoteDataSource = await ref.watch(submissionsRemoteDataSourceProvider.future);
final statusLocalDataSource = ref.watch(projectStatusLocalDataSourceProvider);
return SubmissionsRepositoryImpl(remoteDataSource, statusLocalDataSource);
}
/// Get Submissions Use Case Provider
/// 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
GetSubmissions getSubmissions(Ref ref) {
final repository = ref.watch(submissionsRepositoryProvider);
return GetSubmissions(repository);
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 {
final useCase = ref.watch(getSubmissionsProvider);
return await useCase();
// 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 {
final useCase = ref.read(getSubmissionsProvider);
return await useCase();
// Also refresh status list
await ref.read(projectStatusListProvider.notifier).refresh();
final repository = await ref.read(submissionsRepositoryProvider.future);
return repository.getSubmissions();
});
}
}
@@ -56,10 +89,11 @@ class AllSubmissions extends _$AllSubmissions {
/// 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, SubmissionStatus? selectedStatus}) build() {
({String searchQuery, String? selectedStatus}) build() {
return (searchQuery: '', selectedStatus: null);
}
@@ -68,8 +102,8 @@ class SubmissionsFilter extends _$SubmissionsFilter {
state = (searchQuery: query, selectedStatus: state.selectedStatus);
}
/// Select a status filter
void selectStatus(SubmissionStatus? status) {
/// Select a status filter (uses Vietnamese label from API)
void selectStatus(String? status) {
state = (searchQuery: state.searchQuery, selectedStatus: status);
}
@@ -100,7 +134,7 @@ AsyncValue<List<ProjectSubmission>> filteredSubmissions(Ref ref) {
return dataAsync.whenData((submissions) {
var filtered = submissions;
// Filter by status
// Filter by status (matches Vietnamese label from API)
if (filter.selectedStatus != null) {
filtered = filtered.where((s) => s.status == filter.selectedStatus).toList();
}
@@ -110,12 +144,12 @@ AsyncValue<List<ProjectSubmission>> filteredSubmissions(Ref ref) {
final query = filter.searchQuery.toLowerCase();
filtered = filtered.where((s) {
return s.submissionId.toLowerCase().contains(query) ||
s.projectName.toLowerCase().contains(query);
s.designedArea.toLowerCase().contains(query);
}).toList();
}
// Sort by submitted date (newest first)
filtered.sort((a, b) => b.submittedAt.compareTo(a.submittedAt));
// Sort by request date (newest first)
filtered.sort((a, b) => b.requestDate.compareTo(a.requestDate));
return filtered;
});