marco.pms.mobileapp/lib/controller/task_planing/report_task_controller.dart
Vaibhav Surve e6d05e247e Refactor logging implementation across controllers and services
- Replaced instances of the Logger package with a custom appLogger for consistent logging.
- Introduced app_logger.dart to manage logging with file output and storage permissions.
- Updated all controllers (e.g., DashboardController, EmployeesScreenController, etc.) to use appLogger for logging messages.
- Ensured that logging messages are appropriately categorized (info, warning, error) throughout the application.
- Implemented a file logging mechanism to store logs in a designated directory.
- Cleaned up old log files to maintain only the most recent logs.
2025-06-24 13:11:22 +05:30

323 lines
10 KiB
Dart

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:marco/helpers/services/app_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';
enum ApiStatus { idle, loading, success, failure }
final DailyTaskPlaningController taskController =
Get.put(DailyTaskPlaningController());
final ImagePicker _picker = ImagePicker();
class ReportTaskController extends MyController {
List<PlatformFile> files = [];
MyFormValidator basicValidator = MyFormValidator();
RxBool isLoading = false.obs;
Rx<ApiStatus> reportStatus = ApiStatus.idle.obs;
Rx<ApiStatus> commentStatus = ApiStatus.idle.obs;
RxList<File> selectedImages = <File>[].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();
appLogger.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);
appLogger.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<bool> reportTask({
required String projectId,
required String comment,
required int completedTask,
required List<Map<String, dynamic>> checklist,
required DateTime reportedDate,
List<File>? images,
}) async {
appLogger.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<Map<String, dynamic>>? 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<Map<String, dynamic>>().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) {
appLogger.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<void> commentTask({
required String projectId,
required String comment,
List<File>? images,
}) async {
appLogger.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<Map<String, dynamic>>? 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<Map<String, dynamic>>().toList();
}
final success = await ApiService.commentTask(
id: projectId,
comment: commentField,
images: imageData,
).timeout(const Duration(seconds: 30), onTimeout: () {
appLogger.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) {
appLogger.e("Error commenting task: $e");
showAppSnackbar(
title: "Error",
message: "An error occurred while commenting the task.",
type: SnackbarType.error,
);
} finally {
isLoading.value = false;
}
}
Future<void> 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);
}
}
}