create submission
This commit is contained in:
@@ -5,9 +5,12 @@ library;
|
||||
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:worker/core/network/dio_client.dart';
|
||||
import 'package:worker/features/projects/data/datasources/project_progress_local_datasource.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/models/project_submission_request.dart';
|
||||
import 'package:worker/features/projects/data/repositories/submissions_repository_impl.dart';
|
||||
import 'package:worker/features/projects/domain/entities/project_progress.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';
|
||||
@@ -20,6 +23,12 @@ ProjectStatusLocalDataSource projectStatusLocalDataSource(Ref ref) {
|
||||
return ProjectStatusLocalDataSource();
|
||||
}
|
||||
|
||||
/// Project Progress Local Data Source Provider
|
||||
@riverpod
|
||||
ProjectProgressLocalDataSource projectProgressLocalDataSource(Ref ref) {
|
||||
return ProjectProgressLocalDataSource();
|
||||
}
|
||||
|
||||
/// Submissions Remote Data Source Provider
|
||||
@riverpod
|
||||
Future<SubmissionsRemoteDataSource> submissionsRemoteDataSource(Ref ref) async {
|
||||
@@ -32,7 +41,12 @@ Future<SubmissionsRemoteDataSource> submissionsRemoteDataSource(Ref ref) async {
|
||||
Future<SubmissionsRepository> submissionsRepository(Ref ref) async {
|
||||
final remoteDataSource = await ref.watch(submissionsRemoteDataSourceProvider.future);
|
||||
final statusLocalDataSource = ref.watch(projectStatusLocalDataSourceProvider);
|
||||
return SubmissionsRepositoryImpl(remoteDataSource, statusLocalDataSource);
|
||||
final progressLocalDataSource = ref.watch(projectProgressLocalDataSourceProvider);
|
||||
return SubmissionsRepositoryImpl(
|
||||
remoteDataSource,
|
||||
statusLocalDataSource,
|
||||
progressLocalDataSource,
|
||||
);
|
||||
}
|
||||
|
||||
/// Project Status List Provider
|
||||
@@ -57,16 +71,39 @@ class ProjectStatusList extends _$ProjectStatusList {
|
||||
}
|
||||
}
|
||||
|
||||
/// Project Progress List Provider
|
||||
///
|
||||
/// Fetches construction progress stages from API with cache-first pattern.
|
||||
/// Used for dropdown selection when creating/updating project submissions.
|
||||
@riverpod
|
||||
class ProjectProgressList extends _$ProjectProgressList {
|
||||
@override
|
||||
Future<List<ProjectProgress>> build() async {
|
||||
final repository = await ref.watch(submissionsRepositoryProvider.future);
|
||||
return repository.getProjectProgressList();
|
||||
}
|
||||
|
||||
/// Refresh progress 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.getProjectProgressList(forceRefresh: true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// All Submissions Provider
|
||||
///
|
||||
/// Fetches and manages submissions data from remote.
|
||||
/// Waits for project status list to be loaded first.
|
||||
/// Waits for project status list and progress 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)
|
||||
// Ensure status list and progress list are loaded first (for filter options)
|
||||
await ref.watch(projectStatusListProvider.future);
|
||||
await ref.watch(projectProgressListProvider.future);
|
||||
|
||||
// Then fetch submissions
|
||||
final repository = await ref.watch(submissionsRepositoryProvider.future);
|
||||
@@ -77,8 +114,9 @@ class AllSubmissions extends _$AllSubmissions {
|
||||
Future<void> refresh() async {
|
||||
state = const AsyncValue.loading();
|
||||
state = await AsyncValue.guard(() async {
|
||||
// Also refresh status list
|
||||
// Also refresh status list and progress list
|
||||
await ref.read(projectStatusListProvider.notifier).refresh();
|
||||
await ref.read(projectProgressListProvider.notifier).refresh();
|
||||
|
||||
final repository = await ref.read(submissionsRepositoryProvider.future);
|
||||
return repository.getSubmissions();
|
||||
@@ -154,3 +192,152 @@ AsyncValue<List<ProjectSubmission>> filteredSubmissions(Ref ref) {
|
||||
return filtered;
|
||||
});
|
||||
}
|
||||
|
||||
/// Save Submission Provider
|
||||
///
|
||||
/// Handles creating new project submissions via API.
|
||||
@riverpod
|
||||
class SaveSubmission extends _$SaveSubmission {
|
||||
@override
|
||||
AsyncValue<void> build() {
|
||||
return const AsyncValue.data(null);
|
||||
}
|
||||
|
||||
/// Save a new project submission
|
||||
///
|
||||
/// Returns the project name (ID) if successful, throws exception on failure.
|
||||
Future<String> save(ProjectSubmissionRequest request) async {
|
||||
state = const AsyncValue.loading();
|
||||
try {
|
||||
final repository = await ref.read(submissionsRepositoryProvider.future);
|
||||
if (!ref.mounted) throw Exception('Provider disposed');
|
||||
|
||||
final projectName = await repository.saveSubmission(request);
|
||||
if (!ref.mounted) return projectName;
|
||||
|
||||
state = const AsyncValue.data(null);
|
||||
|
||||
// Refresh submissions list after successful save
|
||||
ref.invalidate(allSubmissionsProvider);
|
||||
|
||||
return projectName;
|
||||
} catch (e, st) {
|
||||
if (ref.mounted) {
|
||||
state = AsyncValue.error(e, st);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Upload state for tracking individual file uploads
|
||||
class FileUploadState {
|
||||
final String filePath;
|
||||
final bool isUploading;
|
||||
final bool isUploaded;
|
||||
final String? fileUrl;
|
||||
final String? error;
|
||||
|
||||
const FileUploadState({
|
||||
required this.filePath,
|
||||
this.isUploading = false,
|
||||
this.isUploaded = false,
|
||||
this.fileUrl,
|
||||
this.error,
|
||||
});
|
||||
|
||||
FileUploadState copyWith({
|
||||
bool? isUploading,
|
||||
bool? isUploaded,
|
||||
String? fileUrl,
|
||||
String? error,
|
||||
}) {
|
||||
return FileUploadState(
|
||||
filePath: filePath,
|
||||
isUploading: isUploading ?? this.isUploading,
|
||||
isUploaded: isUploaded ?? this.isUploaded,
|
||||
fileUrl: fileUrl ?? this.fileUrl,
|
||||
error: error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Upload Project Files Provider
|
||||
///
|
||||
/// Handles uploading multiple files for a project submission.
|
||||
/// Tracks upload state for each file individually.
|
||||
@riverpod
|
||||
class UploadProjectFiles extends _$UploadProjectFiles {
|
||||
@override
|
||||
List<FileUploadState> build() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/// Initialize with file paths
|
||||
void initFiles(List<String> filePaths) {
|
||||
state = filePaths
|
||||
.map((path) => FileUploadState(filePath: path))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Upload all files for a project
|
||||
/// Returns list of uploaded file URLs
|
||||
Future<List<String>> uploadAll(String projectName) async {
|
||||
final uploadedUrls = <String>[];
|
||||
|
||||
for (var i = 0; i < state.length; i++) {
|
||||
if (!ref.mounted) break;
|
||||
|
||||
// Mark as uploading
|
||||
state = [
|
||||
...state.sublist(0, i),
|
||||
state[i].copyWith(isUploading: true),
|
||||
...state.sublist(i + 1),
|
||||
];
|
||||
|
||||
try {
|
||||
final repository = await ref.read(submissionsRepositoryProvider.future);
|
||||
if (!ref.mounted) break;
|
||||
|
||||
final fileUrl = await repository.uploadProjectFile(
|
||||
projectName: projectName,
|
||||
filePath: state[i].filePath,
|
||||
);
|
||||
|
||||
if (!ref.mounted) break;
|
||||
|
||||
// Mark as uploaded
|
||||
state = [
|
||||
...state.sublist(0, i),
|
||||
state[i].copyWith(
|
||||
isUploading: false,
|
||||
isUploaded: true,
|
||||
fileUrl: fileUrl,
|
||||
),
|
||||
...state.sublist(i + 1),
|
||||
];
|
||||
|
||||
uploadedUrls.add(fileUrl);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) break;
|
||||
|
||||
// Mark as failed
|
||||
state = [
|
||||
...state.sublist(0, i),
|
||||
state[i].copyWith(
|
||||
isUploading: false,
|
||||
error: e.toString(),
|
||||
),
|
||||
...state.sublist(i + 1),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return uploadedUrls;
|
||||
}
|
||||
|
||||
/// Clear all files
|
||||
void clear() {
|
||||
state = [];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user