240 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			240 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'package:flutter/material.dart';
 | |
| import 'package:get/get.dart';
 | |
| import 'package:marco/helpers/services/app_logger.dart';
 | |
| import 'package:marco/helpers/services/api_service.dart';
 | |
| import 'package:marco/model/document/master_document_type_model.dart';
 | |
| import 'package:marco/model/document/master_document_tags.dart';
 | |
| import 'package:marco/helpers/widgets/my_snackbar.dart';
 | |
| 
 | |
| class DocumentUploadController extends GetxController {
 | |
|   // Observables
 | |
|   var isLoading = false.obs;
 | |
|   var isUploading = false.obs;
 | |
| 
 | |
|   var categories = <DocumentType>[].obs;
 | |
|   var tags = <TagItem>[].obs;
 | |
| 
 | |
|   DocumentType? selectedCategory;
 | |
| 
 | |
|   /// --- FILE HANDLING ---
 | |
|   String? selectedFileName;
 | |
|   String? selectedFileBase64;
 | |
|   String? selectedFileContentType;
 | |
|   int? selectedFileSize;
 | |
| 
 | |
|   /// --- TAG HANDLING ---
 | |
|   final tagCtrl = TextEditingController();
 | |
|   final enteredTags = <String>[].obs;
 | |
|   final filteredSuggestions = <String>[].obs;
 | |
|   var documentTypes = <DocumentType>[].obs;
 | |
|   DocumentType? selectedType;
 | |
|   @override
 | |
|   void onInit() {
 | |
|     super.onInit();
 | |
|     fetchCategories();
 | |
|     fetchTags();
 | |
|   }
 | |
| 
 | |
|   /// Fetch available document categories
 | |
|   Future<void> fetchCategories() async {
 | |
|     try {
 | |
|       isLoading.value = true;
 | |
|       final response = await ApiService.getMasterDocumentTypesApi();
 | |
|       if (response != null && response.data.isNotEmpty) {
 | |
|         categories.assignAll(response.data);
 | |
|         logSafe("Fetched categories: ${categories.length}");
 | |
|       } else {
 | |
|         logSafe("No categories fetched", level: LogLevel.warning);
 | |
|       }
 | |
|     } finally {
 | |
|       isLoading.value = false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Future<void> fetchDocumentTypes(String categoryId) async {
 | |
|     try {
 | |
|       isLoading.value = true;
 | |
|       final response =
 | |
|           await ApiService.getDocumentTypesByCategoryApi(categoryId);
 | |
|       if (response != null && response.data.isNotEmpty) {
 | |
|         documentTypes.assignAll(response.data);
 | |
|         selectedType = null; // reset previous type
 | |
|       } else {
 | |
|         documentTypes.clear();
 | |
|         selectedType = null;
 | |
|       }
 | |
|     } finally {
 | |
|       isLoading.value = false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Future<String?> fetchPresignedUrl(String versionId) async {
 | |
|     return await ApiService.getPresignedUrlApi(versionId);
 | |
|   }
 | |
| 
 | |
|   /// Fetch available document tags
 | |
|   Future<void> fetchTags() async {
 | |
|     try {
 | |
|       isLoading.value = true;
 | |
|       final response = await ApiService.getMasterDocumentTagsApi();
 | |
|       if (response != null) {
 | |
|         tags.assignAll(response.data);
 | |
|         logSafe("Fetched tags: ${tags.length}");
 | |
|       } else {
 | |
|         logSafe("No tags fetched", level: LogLevel.warning);
 | |
|       }
 | |
|     } finally {
 | |
|       isLoading.value = false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /// --- TAG LOGIC ---
 | |
|   void filterSuggestions(String query) {
 | |
|     if (query.isEmpty) {
 | |
|       filteredSuggestions.clear();
 | |
|       return;
 | |
|     }
 | |
|     filteredSuggestions.assignAll(
 | |
|       tags.map((t) => t.name).where(
 | |
|             (tag) => tag.toLowerCase().contains(query.toLowerCase()),
 | |
|           ),
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   void addEnteredTag(String tag) {
 | |
|     if (tag.trim().isEmpty) return;
 | |
|     if (!enteredTags.contains(tag.trim())) {
 | |
|       enteredTags.add(tag.trim());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void removeEnteredTag(String tag) {
 | |
|     enteredTags.remove(tag);
 | |
|   }
 | |
| 
 | |
|   void clearSuggestions() {
 | |
|     filteredSuggestions.clear();
 | |
|   }
 | |
| 
 | |
|   /// Upload document
 | |
|   Future<bool> uploadDocument({
 | |
|     required String documentId,
 | |
|     required String name,
 | |
|     required String entityId,
 | |
|     required String documentTypeId,
 | |
|     required String fileName,
 | |
|     required String base64Data,
 | |
|     required String contentType,
 | |
|     required int fileSize,
 | |
|     String? description,
 | |
|   }) async {
 | |
|     try {
 | |
|       isUploading.value = true;
 | |
| 
 | |
|       final payloadTags =
 | |
|           enteredTags.map((t) => {"name": t, "isActive": true}).toList();
 | |
| 
 | |
|       final payload = {
 | |
|         "documentId": documentId,
 | |
|         "name": name,
 | |
|         "description": description,
 | |
|         "entityId": entityId,
 | |
|         "documentTypeId": documentTypeId,
 | |
|         "fileName": fileName,
 | |
|         "base64Data":
 | |
|             base64Data.isNotEmpty ? "<base64-string-truncated>" : null,
 | |
|         "contentType": contentType,
 | |
|         "fileSize": fileSize,
 | |
|         "tags": payloadTags,
 | |
|       };
 | |
| 
 | |
|       // Log the payload (hide long base64 string for readability)
 | |
|       logSafe("Upload payload: $payload");
 | |
| 
 | |
|       final success = await ApiService.uploadDocumentApi(
 | |
|         documentId: documentId,
 | |
|         name: name,
 | |
|         description: description,
 | |
|         entityId: entityId,
 | |
|         documentTypeId: documentTypeId,
 | |
|         fileName: fileName,
 | |
|         base64Data: base64Data,
 | |
|         contentType: contentType,
 | |
|         fileSize: fileSize,
 | |
|         tags: payloadTags,
 | |
|       );
 | |
| 
 | |
|       if (success) {
 | |
|         showAppSnackbar(
 | |
|           title: "Success",
 | |
|           message: "Document uploaded successfully",
 | |
|           type: SnackbarType.success,
 | |
|         );
 | |
|       } else {
 | |
|         showAppSnackbar(
 | |
|           title: "Error",
 | |
|           message: "Could not upload document",
 | |
|           type: SnackbarType.error,
 | |
|         );
 | |
|       }
 | |
| 
 | |
|       return success;
 | |
|     } catch (e, stack) {
 | |
|       logSafe("Upload error: $e", level: LogLevel.error);
 | |
|       logSafe("Stacktrace: $stack", level: LogLevel.debug);
 | |
|       showAppSnackbar(
 | |
|         title: "Error",
 | |
|         message: "An unexpected error occurred",
 | |
|         type: SnackbarType.error,
 | |
|       );
 | |
|       return false;
 | |
|     } finally {
 | |
|       isUploading.value = false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Future<bool> editDocument(Map<String, dynamic> payload) async {
 | |
|     try {
 | |
|       isUploading.value = true;
 | |
| 
 | |
|       final attachment = payload["attachment"];
 | |
| 
 | |
|       final success = await ApiService.editDocumentApi(
 | |
|         id: payload["id"],
 | |
|         name: payload["name"],
 | |
|         documentId: payload["documentId"],
 | |
|         description: payload["description"],
 | |
|         tags: (payload["tags"] as List).cast<Map<String, dynamic>>(),
 | |
|         attachment: attachment, 
 | |
|       );
 | |
| 
 | |
|       if (success) {
 | |
|         showAppSnackbar(
 | |
|           title: "Success",
 | |
|           message: "Document updated successfully",
 | |
|           type: SnackbarType.success,
 | |
|         );
 | |
|       } else {
 | |
|         showAppSnackbar(
 | |
|           title: "Error",
 | |
|           message: "Failed to update document",
 | |
|           type: SnackbarType.error,
 | |
|         );
 | |
|       }
 | |
| 
 | |
|       return success;
 | |
|     } catch (e, stack) {
 | |
|       logSafe("Edit error: $e", level: LogLevel.error);
 | |
|       logSafe("Stacktrace: $stack", level: LogLevel.debug);
 | |
|       showAppSnackbar(
 | |
|         title: "Error",
 | |
|         message: "An unexpected error occurred",
 | |
|         type: SnackbarType.error,
 | |
|       );
 | |
|       return false;
 | |
|     } finally {
 | |
|       isUploading.value = false;
 | |
|     }
 | |
|   }
 | |
| }
 |