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