import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:marco/controller/my_controller.dart'; import 'package:marco/helpers/widgets/my_form_validator.dart'; import 'package:marco/helpers/services/api_service.dart'; import 'package:get/get.dart'; import 'package:logger/logger.dart'; import 'package:marco/helpers/widgets/my_snackbar.dart'; import 'package:marco/controller/task_planing/daily_task_planing_controller.dart'; import 'package:image_picker/image_picker.dart'; import 'dart:io'; import 'dart:convert'; import 'package:marco/helpers/widgets/my_image_compressor.dart'; final Logger logger = Logger(); enum ApiStatus { idle, loading, success, failure } final DailyTaskPlaningController taskController = Get.put(DailyTaskPlaningController()); final ImagePicker _picker = ImagePicker(); class ReportTaskController extends MyController { List files = []; MyFormValidator basicValidator = MyFormValidator(); RxBool isLoading = false.obs; Rx reportStatus = ApiStatus.idle.obs; Rx commentStatus = ApiStatus.idle.obs; RxList selectedImages = [].obs; // Controllers for each form field final assignedDateController = TextEditingController(); final workAreaController = TextEditingController(); final activityController = TextEditingController(); final teamSizeController = TextEditingController(); final taskIdController = TextEditingController(); final assignedController = TextEditingController(); final completedWorkController = TextEditingController(); final commentController = TextEditingController(); final assignedByController = TextEditingController(); final teamMembersController = TextEditingController(); final plannedWorkController = TextEditingController(); @override void onInit() { super.onInit(); logger.i("Initializing ReportTaskController..."); basicValidator.addField('assigned_date', label: "Assigned Date", controller: assignedDateController); basicValidator.addField('work_area', label: "Work Area", controller: workAreaController); basicValidator.addField('activity', label: "Activity", controller: activityController); basicValidator.addField('team_size', label: "Team Size", controller: teamSizeController); basicValidator.addField('task_id', label: "Task Id", controller: taskIdController); basicValidator.addField('assigned', label: "Assigned", controller: assignedController); basicValidator.addField('completed_work', label: "Completed Work", required: true, controller: completedWorkController); basicValidator.addField('comment', label: "Comment", required: true, controller: commentController); basicValidator.addField('assigned_by', label: "Assigned By", controller: assignedByController); basicValidator.addField('team_members', label: "Team Members", controller: teamMembersController); basicValidator.addField('planned_work', label: "Planned Work", controller: plannedWorkController); logger.i( "Fields initialized for assigned_date, work_area, activity, team_size, assigned, completed_work, and comment."); } @override void onClose() { assignedDateController.dispose(); workAreaController.dispose(); activityController.dispose(); teamSizeController.dispose(); taskIdController.dispose(); assignedController.dispose(); completedWorkController.dispose(); commentController.dispose(); assignedByController.dispose(); teamMembersController.dispose(); plannedWorkController.dispose(); super.onClose(); } Future reportTask({ required String projectId, required String comment, required int completedTask, required List> checklist, required DateTime reportedDate, List? images, }) async { logger.i("Starting task report..."); final completedWork = completedWorkController.text.trim(); if (completedWork.isEmpty) { showAppSnackbar( title: "Error", message: "Completed work is required.", type: SnackbarType.error, ); return false; } final completedWorkInt = int.tryParse(completedWork); if (completedWorkInt == null || completedWorkInt < 0) { showAppSnackbar( title: "Error", message: "Completed work must be a positive integer.", type: SnackbarType.error, ); return false; } final commentField = commentController.text.trim(); if (commentField.isEmpty) { showAppSnackbar( title: "Error", message: "Comment is required.", type: SnackbarType.error, ); return false; } try { reportStatus.value = ApiStatus.loading; isLoading.value = true; List>? imageData; if (images != null && images.isNotEmpty) { final imageFutures = images.map((file) async { final compressedBytes = await compressImageToUnder100KB(file); if (compressedBytes == null) return null; final base64Image = base64Encode(compressedBytes); final fileName = file.path.split('/').last; final contentType = _getContentTypeFromFileName(fileName); return { "fileName": fileName, "base64Data": base64Image, "contentType": contentType, "fileSize": compressedBytes.lengthInBytes, "description": "Image uploaded for task report", }; }).toList(); final results = await Future.wait(imageFutures); imageData = results.whereType>().toList(); } final success = await ApiService.reportTask( id: projectId, comment: commentField, completedTask: completedWorkInt, checkList: checklist, images: imageData, ); if (success) { reportStatus.value = ApiStatus.success; showAppSnackbar( title: "Success", message: "Task reported successfully!", type: SnackbarType.success, ); await taskController.fetchTaskData(projectId); return true; } else { reportStatus.value = ApiStatus.failure; showAppSnackbar( title: "Error", message: "Failed to report task.", type: SnackbarType.error, ); return false; } } catch (e) { logger.e("Error reporting task: $e"); reportStatus.value = ApiStatus.failure; showAppSnackbar( title: "Error", message: "An error occurred while reporting the task.", type: SnackbarType.error, ); return false; } finally { isLoading.value = false; Future.delayed(const Duration(milliseconds: 500), () { reportStatus.value = ApiStatus.idle; }); } } String _getContentTypeFromFileName(String fileName) { final ext = fileName.split('.').last.toLowerCase(); switch (ext) { case 'jpg': case 'jpeg': return 'image/jpeg'; case 'png': return 'image/png'; case 'webp': return 'image/webp'; case 'gif': return 'image/gif'; default: return 'application/octet-stream'; } } Future commentTask({ required String projectId, required String comment, List? images, }) async { logger.i("Starting task comment..."); final commentField = commentController.text.trim(); if (commentField.isEmpty) { showAppSnackbar( title: "Error", message: "Comment is required.", type: SnackbarType.error, ); return; } try { isLoading.value = true; List>? imageData; if (images != null && images.isNotEmpty) { final imageFutures = images.map((file) async { final compressedBytes = await compressImageToUnder100KB(file); if (compressedBytes == null) return null; final base64Image = base64Encode(compressedBytes); final fileName = file.path.split('/').last; final contentType = _getContentTypeFromFileName(fileName); return { "fileName": fileName, "base64Data": base64Image, "contentType": contentType, "fileSize": compressedBytes.lengthInBytes, "description": "Image uploaded for task comment", }; }).toList(); final results = await Future.wait(imageFutures); imageData = results.whereType>().toList(); } final success = await ApiService.commentTask( id: projectId, comment: commentField, images: imageData, ).timeout(const Duration(seconds: 30), onTimeout: () { logger.e("Request timed out."); throw Exception("Request timed out."); }); if (success) { showAppSnackbar( title: "Success", message: "Task commented successfully!", type: SnackbarType.success, ); await taskController.fetchTaskData(projectId); } else { showAppSnackbar( title: "Error", message: "Failed to comment task.", type: SnackbarType.error, ); } } catch (e) { logger.e("Error commenting task: $e"); showAppSnackbar( title: "Error", message: "An error occurred while commenting the task.", type: SnackbarType.error, ); } finally { isLoading.value = false; } } Future pickImages({required bool fromCamera}) async { if (fromCamera) { final pickedFile = await _picker.pickImage( source: ImageSource.camera, imageQuality: 75, ); if (pickedFile != null) { selectedImages.add(File(pickedFile.path)); } } else { final pickedFiles = await _picker.pickMultiImage(imageQuality: 75); if (pickedFiles.isNotEmpty) { selectedImages.addAll(pickedFiles.map((xfile) => File(xfile.path))); } } } void removeImageAt(int index) { if (index >= 0 && index < selectedImages.length) { selectedImages.removeAt(index); } } }