diff --git a/lib/controller/task_planing/add_task_controller.dart b/lib/controller/task_planing/add_task_controller.dart index 9a8d1d2..3fa9e16 100644 --- a/lib/controller/task_planing/add_task_controller.dart +++ b/lib/controller/task_planing/add_task_controller.dart @@ -1,42 +1,29 @@ import 'package:get/get.dart'; import 'package:logger/logger.dart'; import 'package:marco/helpers/services/api_service.dart'; -import 'package:marco/model/project_model.dart'; -import 'package:marco/model/employee_model.dart'; import 'package:marco/helpers/widgets/my_form_validator.dart'; import 'package:marco/helpers/widgets/my_snackbar.dart'; -import 'package:marco/controller/project_controller.dart'; import 'package:marco/model/dailyTaskPlaning/master_work_category_model.dart'; final Logger log = Logger(); class AddTaskController extends GetxController { - List projects = []; - List employees = []; RxMap uploadingStates = {}.obs; MyFormValidator basicValidator = MyFormValidator(); - RxnInt selectedCategoryId = RxnInt(); + RxnString selectedCategoryId = RxnString(); + RxnString selectedCategoryName = RxnString(); + var categoryIdNameMap = {}.obs; List> roles = []; RxnString selectedRoleId = RxnString(); - RxList selectedEmployees = [].obs; RxBool isLoadingWorkMasterCategories = false.obs; RxList workMasterCategories = [].obs; - void updateSelectedEmployees() { - final selected = - employees.where((e) => uploadingStates[e.id]?.value == true).toList(); - selectedEmployees.value = selected; - } - RxBool isLoading = false.obs; @override void onInit() { super.onInit(); - fetchRoles(); fetchWorkMasterCategories(); - final projectId = Get.find().selectedProject?.id; - fetchEmployeesByProject(projectId); } String? formFieldValidator(String? value, {required String fieldType}) { @@ -56,23 +43,6 @@ class AddTaskController extends GetxController { return null; } - Future fetchRoles() async { - logger.i("Fetching roles..."); - final result = await ApiService.getRoles(); - if (result != null) { - roles = List>.from(result); - logger.i("Roles fetched successfully."); - update(); - } else { - logger.e("Failed to fetch roles."); - } - } - - void onRoleSelected(String? roleId) { - selectedRoleId.value = roleId; - logger.i("Role selected: $roleId"); - } - Future assignDailyTask({ required String workItemId, required int plannedTask, @@ -111,10 +81,11 @@ class AddTaskController extends GetxController { Future createTask({ required String parentTaskId, + required String workAreaId, + required String activityId, required int plannedTask, - required String description, - required List taskTeam, - required String workItemId, + required String comment, + required String categoryId, DateTime? assignmentDate, }) async { logger.i("Creating new task..."); @@ -122,10 +93,12 @@ class AddTaskController extends GetxController { final response = await ApiService.createTask( parentTaskId: parentTaskId, plannedTask: plannedTask, - description: description, - taskTeam: taskTeam, - workItemId: workItemId, + comment: comment, + workAreaId: workAreaId, + activityId: activityId, assignmentDate: assignmentDate, + categoryId: categoryId, + ); if (response == true) { @@ -147,34 +120,6 @@ class AddTaskController extends GetxController { } } - Future fetchEmployeesByProject(String? projectId) async { - if (projectId == null || projectId.isEmpty) { - log.e("Project ID is required but was null or empty."); - return; - } - - isLoading.value = true; - try { - final response = await ApiService.getAllEmployeesByProject(projectId); - if (response != null && response.isNotEmpty) { - employees = - response.map((json) => EmployeeModel.fromJson(json)).toList(); - for (var emp in employees) { - uploadingStates[emp.id] = false.obs; - } - log.i("Employees fetched: ${employees.length} for project $projectId"); - } else { - log.w("No employees found for project $projectId."); - employees = []; - } - } catch (e) { - log.e("Error fetching employees for project $projectId: $e"); - } - - update(); - isLoading.value = false; - } - Future fetchWorkMasterCategories() async { isLoadingWorkMasterCategories.value = true; @@ -182,15 +127,22 @@ class AddTaskController extends GetxController { if (response != null) { try { final dataList = response['data'] ?? []; - workMasterCategories.assignAll( - List.from( - dataList.map((e) => WorkCategoryModel.fromJson(e)), - ), + + final parsedList = List.from( + dataList.map((e) => WorkCategoryModel.fromJson(e)), ); + + workMasterCategories.assignAll(parsedList); + final Map mapped = { + for (var item in parsedList) item.id: item.name, + }; + categoryIdNameMap.assignAll(mapped); + logger.i("Work categories fetched: ${dataList.length}"); } catch (e) { logger.e("Error parsing work categories: $e"); workMasterCategories.clear(); + categoryIdNameMap.clear(); } } else { logger.w("No work categories found or API call failed."); @@ -199,4 +151,9 @@ class AddTaskController extends GetxController { isLoadingWorkMasterCategories.value = false; update(); } + + void selectCategory(String id) { + selectedCategoryId.value = id; + selectedCategoryName.value = categoryIdNameMap[id]; + } } diff --git a/lib/helpers/services/api_endpoints.dart b/lib/helpers/services/api_endpoints.dart index 851f264..87b11cf 100644 --- a/lib/helpers/services/api_endpoints.dart +++ b/lib/helpers/services/api_endpoints.dart @@ -26,6 +26,6 @@ class ApiEndpoints { static const String assignDailyTask = "/task/assign"; static const String getWorkStatus = "/master/work-status"; static const String approveReportAction = "/task/approve"; - static const String assignTask = "/task/assign"; + static const String assignTask = "/project/task"; static const String getmasterWorkCategories = "/Master/work-categories"; } diff --git a/lib/helpers/services/api_service.dart b/lib/helpers/services/api_service.dart index 9c960a1..7ff86d9 100644 --- a/lib/helpers/services/api_service.dart +++ b/lib/helpers/services/api_service.dart @@ -408,20 +408,21 @@ class ApiService { static Future createTask({ required String parentTaskId, required int plannedTask, - required String description, - required List taskTeam, - required String workItemId, + required String comment, + required String workAreaId, + required String activityId, DateTime? assignmentDate, + required String categoryId, }) async { - final body = { - "assignmentDate": - (assignmentDate ?? DateTime.now()).toUtc().toIso8601String(), + final body = [{ "parentTaskId": parentTaskId, - "plannedTask": plannedTask, - "description": description, - "taskTeam": taskTeam, - "workItemId": workItemId, - }; + "plannedWork": plannedTask, + "comment": comment, + "workAreaID": workAreaId, + "activityID": activityId, + "workCategoryId": categoryId, + 'completedWork': 0, + }]; final response = await _postRequest(ApiEndpoints.assignTask, body); if (response == null) return false; diff --git a/lib/model/dailyTaskPlaning/create_task_botom_sheet.dart b/lib/model/dailyTaskPlaning/create_task_botom_sheet.dart index 46ed274..a86b22e 100644 --- a/lib/model/dailyTaskPlaning/create_task_botom_sheet.dart +++ b/lib/model/dailyTaskPlaning/create_task_botom_sheet.dart @@ -2,8 +2,11 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/controller/task_planing/add_task_controller.dart'; import 'package:marco/helpers/widgets/my_text.dart'; +import 'package:logger/logger.dart'; import 'package:marco/helpers/widgets/my_snackbar.dart'; +final Logger log = Logger(); + void showManageTaskBottomSheet({ required String workArea, required String activity, @@ -12,82 +15,15 @@ void showManageTaskBottomSheet({ required Function(String) onCategoryChanged, required String parentTaskId, required int plannedTask, - required String workItemId, + required String activityId, + required String workAreaId, required VoidCallback onSubmit, }) { final controller = Get.put(AddTaskController()); - final ScrollController employeeListScrollController = ScrollController(); final TextEditingController plannedTaskController = TextEditingController(text: plannedTask.toString()); final TextEditingController descriptionController = TextEditingController(); - Widget buildEmployeeList() { - final selectedRoleId = controller.selectedRoleId.value; - final filteredEmployees = selectedRoleId == null - ? controller.employees - : controller.employees - .where((e) => e.jobRoleID.toString() == selectedRoleId) - .toList(); - - if (filteredEmployees.isEmpty) { - return MyText.bodySmall("No employees found for selected role."); - } - - return Scrollbar( - controller: employeeListScrollController, - thumbVisibility: true, - interactive: true, - child: ListView.builder( - controller: employeeListScrollController, - shrinkWrap: true, - physics: const AlwaysScrollableScrollPhysics(), - itemCount: filteredEmployees.length, - itemBuilder: (context, index) { - final employee = filteredEmployees[index]; - final rxBool = controller.uploadingStates[employee.id]; - - return Obx(() => Padding( - padding: const EdgeInsets.symmetric(vertical: 0), - child: Row( - children: [ - Theme( - data: Theme.of(context) - .copyWith(unselectedWidgetColor: Colors.black), - child: Checkbox( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(4), - side: const BorderSide(color: Colors.black), - ), - value: rxBool?.value ?? false, - onChanged: (bool? selected) { - if (rxBool != null) { - rxBool.value = selected ?? false; - controller.updateSelectedEmployees(); - } - }, - fillColor: - WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.selected)) { - return const Color.fromARGB(255, 95, 132, 255); - } - return Colors.transparent; - }), - checkColor: Colors.white, - side: const BorderSide(color: Colors.black), - ), - ), - const SizedBox(width: 8), - Expanded( - child: MyText.bodySmall(employee.name), - ), - ], - ), - )); - }, - ), - ); - } - Get.bottomSheet( StatefulBuilder( builder: (context, setState) { @@ -166,7 +102,7 @@ void showManageTaskBottomSheet({ Icon(Icons.description_outlined, color: Colors.grey[700], size: 18), const SizedBox(width: 8), - MyText.bodyMedium("Description", fontWeight: 600), + MyText.bodyMedium("Comment", fontWeight: 600), ], ), const SizedBox(height: 6), @@ -182,48 +118,59 @@ void showManageTaskBottomSheet({ ), ), const SizedBox(height: 24), - Row( + Row( children: [ - Icon(Icons.group_add_outlined, + Icon(Icons.category_outlined, color: Colors.grey[700], size: 18), const SizedBox(width: 8), - MyText.bodyMedium("Select Team Members", fontWeight: 600), + MyText.bodyMedium("Selected Work Category", + fontWeight: 600), ], ), - const SizedBox(height: 8), + const SizedBox(height: 6), Obx(() { - if (controller.isLoading.value) { - return const Center( - child: CircularProgressIndicator()); - } + final categoryMap = controller.categoryIdNameMap; + final String selectedName = + controller.selectedCategoryId.value != null + ? (categoryMap[ + controller.selectedCategoryId.value!] ?? + 'Select Category') + : 'Select Category'; return Container( - constraints: const BoxConstraints(maxHeight: 150), - child: buildEmployeeList(), - ); - }), - const SizedBox(height: 12), - Obx(() { - if (controller.selectedEmployees.isEmpty) { - return const SizedBox.shrink(); - } - return Wrap( - spacing: 8, - runSpacing: 8, - children: - controller.selectedEmployees.map((employee) { - return Chip( - label: Text(employee.name), - deleteIcon: const Icon(Icons.close), - onDeleted: () { - controller.uploadingStates[employee.id]?.value = - false; - controller.updateSelectedEmployees(); - }, - backgroundColor: Colors.blue.shade100, - labelStyle: const TextStyle(color: Colors.black), - ); - }).toList(), + width: double.infinity, + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 14), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey.shade300), + borderRadius: BorderRadius.circular(8), + ), + child: PopupMenuButton( + padding: EdgeInsets.zero, + onSelected: (val) { + controller.selectCategory(val); + onCategoryChanged(val); + }, + itemBuilder: (context) => categoryMap.entries + .map( + (entry) => PopupMenuItem( + value: entry.key, + child: Text(entry.value), + ), + ) + .toList(), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + selectedName, + style: const TextStyle( + fontSize: 14, color: Colors.black87), + ), + const Icon(Icons.arrow_drop_down), + ], + ), + ), ); }), const SizedBox(height: 24), @@ -232,7 +179,7 @@ void showManageTaskBottomSheet({ Expanded( child: OutlinedButton( onPressed: () { - Get.back(); // Close bottom sheet + Get.back(); }, style: OutlinedButton.styleFrom( side: const BorderSide(color: Colors.grey), @@ -250,30 +197,42 @@ void showManageTaskBottomSheet({ Expanded( child: ElevatedButton( onPressed: () async { - final taskTeam = controller.selectedEmployees - .map((e) => e.id) - .toList(); + final plannedValue = int.tryParse( + plannedTaskController.text.trim()) ?? + 0; + final comment = + descriptionController.text.trim(); + final assignmentDate = DateTime.now(); - if (taskTeam.isEmpty) { + // 🪵 Log the task creation input values + log.i({ + "message": "Creating task with data", + "parentTaskId": parentTaskId, + "plannedTask": plannedValue, + "comment": comment, + "workAreaId": workAreaId, + "activityId": activityId, + }); + + final selectedCategoryId = + controller.selectedCategoryId.value; + if (selectedCategoryId == null) { showAppSnackbar( - title: "Team Required", - message: - "Please select at least one team member.", - type: SnackbarType.warning, + title: "error", + message: "Please select a work category!", + type: SnackbarType.error, ); return; } final success = await controller.createTask( parentTaskId: parentTaskId, - plannedTask: int.tryParse( - plannedTaskController.text.trim()) ?? - 0, - description: - descriptionController.text.trim(), - taskTeam: taskTeam, - workItemId: workItemId, - assignmentDate: DateTime.now(), + plannedTask: plannedValue, + comment: comment, + workAreaId: workAreaId, + activityId: activityId, + categoryId: + selectedCategoryId, ); if (success) { diff --git a/lib/model/dailyTaskPlaning/report_action_bottom_sheet.dart b/lib/model/dailyTaskPlaning/report_action_bottom_sheet.dart index b7a40a8..addb205 100644 --- a/lib/model/dailyTaskPlaning/report_action_bottom_sheet.dart +++ b/lib/model/dailyTaskPlaning/report_action_bottom_sheet.dart @@ -18,14 +18,17 @@ class ReportActionBottomSheet extends StatefulWidget { final Map taskData; final VoidCallback? onCommentSuccess; final String taskDataId; - final String workItemId; + final String workAreaId; + final String activityId; final VoidCallback onReportSuccess; + const ReportActionBottomSheet({ super.key, required this.taskData, this.onCommentSuccess, required this.taskDataId, - required this.workItemId, + required this.workAreaId, + required this.activityId, required this.onReportSuccess, }); @@ -77,8 +80,7 @@ class _ReportActionBottomSheetState extends State controller.basicValidator.getController('comment')?.clear(); controller.basicValidator.getController('task_id')?.text = widget.taskDataId; - controller.basicValidator.getController('work_item_id')?.text = - widget.workItemId; + controller.selectedImages.clear(); WidgetsBinding.instance.addPostFrameCallback((_) { @@ -486,7 +488,10 @@ class _ReportActionBottomSheetState extends State widget.taskData['plannedWork'] ?? '0') ?? 0, - workItemId: widget.workItemId, + activityId: + widget.activityId, + workAreaId: + widget.workAreaId, onSubmit: () { Navigator.of(context).pop(); }, diff --git a/lib/model/dailyTaskPlaning/task_action_buttons.dart b/lib/model/dailyTaskPlaning/task_action_buttons.dart index 933fc3a..4b0127a 100644 --- a/lib/model/dailyTaskPlaning/task_action_buttons.dart +++ b/lib/model/dailyTaskPlaning/task_action_buttons.dart @@ -106,7 +106,8 @@ class TaskActionButtons { required int completed, required VoidCallback refreshCallback, required String parentTaskID, - required String workItemId, + required String activityId, + required String workAreaId, }) { return OutlinedButton.icon( icon: const Icon(Icons.report, size: 18, color: Colors.amber), @@ -131,7 +132,8 @@ class TaskActionButtons { child: ReportActionBottomSheet( taskData: taskData, taskDataId: parentTaskID, - workItemId: workItemId, + workAreaId: workAreaId, + activityId: activityId, onReportSuccess: refreshCallback, ), ), diff --git a/lib/model/daily_task_model.dart b/lib/model/daily_task_model.dart index 57bf8f0..c25dd70 100644 --- a/lib/model/daily_task_model.dart +++ b/lib/model/daily_task_model.dart @@ -32,8 +32,9 @@ class TaskModel { ? DateTime.tryParse(json['reportedDate']) : null, id: json['id'], - workItem: - json['workItem'] != null ? WorkItem.fromJson(json['workItem']) : null, + workItem: json['workItem'] != null + ? WorkItem.fromJson(json['workItem']) + : null, workItemId: json['workItemId'], plannedTask: json['plannedTask'], completedTask: json['completedTask'], @@ -87,25 +88,39 @@ class WorkItem { } class ActivityMaster { + final String? id; // ✅ Added final String activityName; - ActivityMaster({required this.activityName}); + ActivityMaster({ + this.id, + required this.activityName, + }); factory ActivityMaster.fromJson(Map json) { - return ActivityMaster(activityName: json['activityName'] ?? ''); + return ActivityMaster( + id: json['id']?.toString(), + activityName: json['activityName'] ?? '', + ); } } class WorkArea { + final String? id; // ✅ Added final String areaName; final Floor? floor; - WorkArea({required this.areaName, this.floor}); + WorkArea({ + this.id, + required this.areaName, + this.floor, + }); factory WorkArea.fromJson(Map json) { return WorkArea( + id: json['id']?.toString(), areaName: json['areaName'] ?? '', - floor: json['floor'] != null ? Floor.fromJson(json['floor']) : null, + floor: + json['floor'] != null ? Floor.fromJson(json['floor']) : null, ); } } diff --git a/lib/view/taskPlaning/daily_progress.dart b/lib/view/taskPlaning/daily_progress.dart index 2ff80aa..17d4356 100644 --- a/lib/view/taskPlaning/daily_progress.dart +++ b/lib/view/taskPlaning/daily_progress.dart @@ -368,6 +368,9 @@ class _DailyProgressReportScreenState extends State final activityName = task.workItem?.activityMaster?.activityName ?? 'N/A'; + final activityId = task.workItem?.activityMaster?.id ; + final workAreaId = + task.workItem?.workArea?.id; final location = [ task.workItem?.workArea?.floor?.building?.name, task.workItem?.workArea?.floor?.floorName, @@ -380,7 +383,6 @@ class _DailyProgressReportScreenState extends State ? (completed / planned).clamp(0.0, 1.0) : 0.0; final parentTaskID = task.id; - final workItemId = task.workItem?.id; return Column( children: [ Padding( @@ -474,10 +476,12 @@ class _DailyProgressReportScreenState extends State context: context, task: task, parentTaskID: parentTaskID, - workItemId: workItemId.toString(), + workAreaId: workAreaId.toString(), + activityId: activityId.toString(), completed: completed, refreshCallback: _refreshData, ), + const SizedBox(width: 8), ], TaskActionButtons.commentButton(