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;
});

View File

@@ -8,6 +8,60 @@ part of 'submissions_provider.dart';
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
/// Project Status Local Data Source Provider
@ProviderFor(projectStatusLocalDataSource)
const projectStatusLocalDataSourceProvider =
ProjectStatusLocalDataSourceProvider._();
/// Project Status Local Data Source Provider
final class ProjectStatusLocalDataSourceProvider
extends
$FunctionalProvider<
ProjectStatusLocalDataSource,
ProjectStatusLocalDataSource,
ProjectStatusLocalDataSource
>
with $Provider<ProjectStatusLocalDataSource> {
/// Project Status Local Data Source Provider
const ProjectStatusLocalDataSourceProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'projectStatusLocalDataSourceProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$projectStatusLocalDataSourceHash();
@$internal
@override
$ProviderElement<ProjectStatusLocalDataSource> $createElement(
$ProviderPointer pointer,
) => $ProviderElement(pointer);
@override
ProjectStatusLocalDataSource create(Ref ref) {
return projectStatusLocalDataSource(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(ProjectStatusLocalDataSource value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<ProjectStatusLocalDataSource>(value),
);
}
}
String _$projectStatusLocalDataSourceHash() =>
r'c57291e51bd390f9524369860c241d7a0a90fdbf';
/// Submissions Remote Data Source Provider
@ProviderFor(submissionsRemoteDataSource)
@@ -19,11 +73,13 @@ const submissionsRemoteDataSourceProvider =
final class SubmissionsRemoteDataSourceProvider
extends
$FunctionalProvider<
AsyncValue<SubmissionsRemoteDataSource>,
SubmissionsRemoteDataSource,
SubmissionsRemoteDataSource,
SubmissionsRemoteDataSource
FutureOr<SubmissionsRemoteDataSource>
>
with $Provider<SubmissionsRemoteDataSource> {
with
$FutureModifier<SubmissionsRemoteDataSource>,
$FutureProvider<SubmissionsRemoteDataSource> {
/// Submissions Remote Data Source Provider
const SubmissionsRemoteDataSourceProvider._()
: super(
@@ -41,26 +97,18 @@ final class SubmissionsRemoteDataSourceProvider
@$internal
@override
$ProviderElement<SubmissionsRemoteDataSource> $createElement(
$FutureProviderElement<SubmissionsRemoteDataSource> $createElement(
$ProviderPointer pointer,
) => $ProviderElement(pointer);
) => $FutureProviderElement(pointer);
@override
SubmissionsRemoteDataSource create(Ref ref) {
FutureOr<SubmissionsRemoteDataSource> create(Ref ref) {
return submissionsRemoteDataSource(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(SubmissionsRemoteDataSource value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<SubmissionsRemoteDataSource>(value),
);
}
}
String _$submissionsRemoteDataSourceHash() =>
r'dc2dd71b6ca22d26382c1dfdf13b88d2249bb5ce';
r'ffaa92dd55ef50c8f1166773a83cd5c8cc16ded4';
/// Submissions Repository Provider
@@ -72,11 +120,13 @@ const submissionsRepositoryProvider = SubmissionsRepositoryProvider._();
final class SubmissionsRepositoryProvider
extends
$FunctionalProvider<
AsyncValue<SubmissionsRepository>,
SubmissionsRepository,
SubmissionsRepository,
SubmissionsRepository
FutureOr<SubmissionsRepository>
>
with $Provider<SubmissionsRepository> {
with
$FutureModifier<SubmissionsRepository>,
$FutureProvider<SubmissionsRepository> {
/// Submissions Repository Provider
const SubmissionsRepositoryProvider._()
: super(
@@ -94,76 +144,87 @@ final class SubmissionsRepositoryProvider
@$internal
@override
$ProviderElement<SubmissionsRepository> $createElement(
$FutureProviderElement<SubmissionsRepository> $createElement(
$ProviderPointer pointer,
) => $ProviderElement(pointer);
) => $FutureProviderElement(pointer);
@override
SubmissionsRepository create(Ref ref) {
FutureOr<SubmissionsRepository> create(Ref ref) {
return submissionsRepository(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(SubmissionsRepository value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<SubmissionsRepository>(value),
);
}
}
String _$submissionsRepositoryHash() =>
r'4fa33107966470c07f050b27e669ec1dc4f13fda';
r'd8261cc538c1fdaa47064e4945302b80f49098bb';
/// 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.
@ProviderFor(getSubmissions)
const getSubmissionsProvider = GetSubmissionsProvider._();
@ProviderFor(ProjectStatusList)
const projectStatusListProvider = ProjectStatusListProvider._();
/// Get Submissions Use Case Provider
final class GetSubmissionsProvider
extends $FunctionalProvider<GetSubmissions, GetSubmissions, GetSubmissions>
with $Provider<GetSubmissions> {
/// Get Submissions Use Case Provider
const GetSubmissionsProvider._()
/// 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.
final class ProjectStatusListProvider
extends $AsyncNotifierProvider<ProjectStatusList, List<ProjectStatus>> {
/// 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.
const ProjectStatusListProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'getSubmissionsProvider',
name: r'projectStatusListProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$getSubmissionsHash();
String debugGetCreateSourceHash() => _$projectStatusListHash();
@$internal
@override
$ProviderElement<GetSubmissions> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
GetSubmissions create(Ref ref) {
return getSubmissions(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(GetSubmissions value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<GetSubmissions>(value),
);
}
ProjectStatusList create() => ProjectStatusList();
}
String _$getSubmissionsHash() => r'91b497f826ae6dc72618ba879289fc449a7ef5cb';
String _$projectStatusListHash() => r'69a43b619738dec3a6643a9a780599417403b838';
/// 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.
abstract class _$ProjectStatusList extends $AsyncNotifier<List<ProjectStatus>> {
FutureOr<List<ProjectStatus>> build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref =
this.ref as $Ref<AsyncValue<List<ProjectStatus>>, List<ProjectStatus>>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<List<ProjectStatus>>, List<ProjectStatus>>,
AsyncValue<List<ProjectStatus>>,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
/// All Submissions Provider
///
/// Fetches and manages submissions data from remote.
/// Waits for project status list to be loaded first.
@ProviderFor(AllSubmissions)
const allSubmissionsProvider = AllSubmissionsProvider._();
@@ -171,11 +232,13 @@ const allSubmissionsProvider = AllSubmissionsProvider._();
/// All Submissions Provider
///
/// Fetches and manages submissions data from remote.
/// Waits for project status list to be loaded first.
final class AllSubmissionsProvider
extends $AsyncNotifierProvider<AllSubmissions, List<ProjectSubmission>> {
/// All Submissions Provider
///
/// Fetches and manages submissions data from remote.
/// Waits for project status list to be loaded first.
const AllSubmissionsProvider._()
: super(
from: null,
@@ -195,11 +258,12 @@ final class AllSubmissionsProvider
AllSubmissions create() => AllSubmissions();
}
String _$allSubmissionsHash() => r'40ea0460a8962a4105dabb482bc80573452d4c80';
String _$allSubmissionsHash() => r'a4a7fb0d2953efb21e2e6343429f7550c763ea85';
/// All Submissions Provider
///
/// Fetches and manages submissions data from remote.
/// Waits for project status list to be loaded first.
abstract class _$AllSubmissions
extends $AsyncNotifier<List<ProjectSubmission>> {
@@ -232,6 +296,7 @@ abstract class _$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").
@ProviderFor(SubmissionsFilter)
const submissionsFilterProvider = SubmissionsFilterProvider._();
@@ -239,15 +304,17 @@ const submissionsFilterProvider = SubmissionsFilterProvider._();
/// Submissions Filter State
///
/// Manages search and status filter state.
/// Status filter uses the status label string from API (e.g., "Chờ phê duyệt").
final class SubmissionsFilterProvider
extends
$NotifierProvider<
SubmissionsFilter,
({String searchQuery, SubmissionStatus? selectedStatus})
({String searchQuery, String? selectedStatus})
> {
/// Submissions Filter State
///
/// Manages search and status filter state.
/// Status filter uses the status label string from API (e.g., "Chờ phê duyệt").
const SubmissionsFilterProvider._()
: super(
from: null,
@@ -268,28 +335,28 @@ final class SubmissionsFilterProvider
/// {@macro riverpod.override_with_value}
Override overrideWithValue(
({String searchQuery, SubmissionStatus? selectedStatus}) value,
({String searchQuery, String? selectedStatus}) value,
) {
return $ProviderOverride(
origin: this,
providerOverride:
$SyncValueProvider<
({String searchQuery, SubmissionStatus? selectedStatus})
>(value),
$SyncValueProvider<({String searchQuery, String? selectedStatus})>(
value,
),
);
}
}
String _$submissionsFilterHash() => r'049dd9fa4f6f1bff0d49c6cba0975f9714621883';
String _$submissionsFilterHash() => r'b3c59003922b1786b71f68726f97b210eed94c89';
/// Submissions Filter State
///
/// Manages search and status filter state.
/// Status filter uses the status label string from API (e.g., "Chờ phê duyệt").
abstract class _$SubmissionsFilter
extends
$Notifier<({String searchQuery, SubmissionStatus? selectedStatus})> {
({String searchQuery, SubmissionStatus? selectedStatus}) build();
extends $Notifier<({String searchQuery, String? selectedStatus})> {
({String searchQuery, String? selectedStatus}) build();
@$mustCallSuper
@override
void runBuild() {
@@ -297,17 +364,17 @@ abstract class _$SubmissionsFilter
final ref =
this.ref
as $Ref<
({String searchQuery, SubmissionStatus? selectedStatus}),
({String searchQuery, SubmissionStatus? selectedStatus})
({String searchQuery, String? selectedStatus}),
({String searchQuery, String? selectedStatus})
>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<
({String searchQuery, SubmissionStatus? selectedStatus}),
({String searchQuery, SubmissionStatus? selectedStatus})
({String searchQuery, String? selectedStatus}),
({String searchQuery, String? selectedStatus})
>,
({String searchQuery, SubmissionStatus? selectedStatus}),
({String searchQuery, String? selectedStatus}),
Object?,
Object?
>;
@@ -374,4 +441,4 @@ final class FilteredSubmissionsProvider
}
String _$filteredSubmissionsHash() =>
r'd0a07ab78a0d98596f01d0ed0a25016d573db5aa';
r'5be22b3242426c6b0c2f9778eaee5c7cf23e4814';