diff --git a/lib/controller/task_planning/daily_task_planning_controller.dart b/lib/controller/task_planning/daily_task_planning_controller.dart index fc680e2..540b7c7 100644 --- a/lib/controller/task_planning/daily_task_planning_controller.dart +++ b/lib/controller/task_planning/daily_task_planning_controller.dart @@ -18,7 +18,6 @@ class DailyTaskPlanningController extends GetxController { MyFormValidator basicValidator = MyFormValidator(); List> roles = []; RxBool isAssigningTask = false.obs; - RxnString selectedRoleId = RxnString(); RxBool isLoading = false.obs; @@ -50,18 +49,12 @@ class DailyTaskPlanningController extends GetxController { final selected = employees.where((e) => uploadingStates[e.id]?.value == true).toList(); selectedEmployees.value = selected; - logSafe( - "Updated selected employees", - level: LogLevel.debug, - ); + logSafe("Updated selected employees", level: LogLevel.debug); } void onRoleSelected(String? roleId) { selectedRoleId.value = roleId; - logSafe( - "Role selected", - level: LogLevel.info, - ); + logSafe("Role selected", level: LogLevel.info); } Future fetchRoles() async { @@ -94,7 +87,7 @@ class DailyTaskPlanningController extends GetxController { assignmentDate: assignmentDate, ); - isAssigningTask.value = false; + isAssigningTask.value = false; if (response == true) { logSafe("Task assigned successfully", level: LogLevel.info); @@ -137,6 +130,7 @@ class DailyTaskPlanningController extends GetxController { } } + /// Fetch Infra details and then tasks per work area Future fetchTaskData(String? projectId) async { if (projectId == null) { logSafe("Project ID is null", level: LogLevel.warning); @@ -145,20 +139,89 @@ class DailyTaskPlanningController extends GetxController { isLoading.value = true; try { - final response = await ApiService.getDailyTasksDetails(projectId); - final data = response?['data']; - if (data != null) { - dailyTasks = [TaskPlanningDetailsModel.fromJson(data)]; - logSafe( - "Daily task Planning Details fetched", - level: LogLevel.info, - ); - } else { - logSafe("Data field is null", level: LogLevel.warning); + final infraResponse = await ApiService.getInfraDetails(projectId); + final infraData = infraResponse?['data'] as List?; + + if (infraData == null || infraData.isEmpty) { + logSafe("No infra data found for project $projectId", + level: LogLevel.warning); + dailyTasks = []; + return; } + + // Map infra to dailyTasks structure + dailyTasks = infraData.map((buildingJson) { + final building = Building( + id: buildingJson['id'], + name: buildingJson['buildingName'], + description: buildingJson['description'], + floors: (buildingJson['floors'] as List).map((floorJson) { + return Floor( + id: floorJson['id'], + floorName: floorJson['floorName'], + workAreas: (floorJson['workAreas'] as List).map((areaJson) { + return WorkArea( + id: areaJson['id'], + areaName: areaJson['areaName'], + workItems: [], // Initially empty, will fill after tasks API + ); + }).toList(), + ); + }).toList(), + ); + + return TaskPlanningDetailsModel( + id: building.id, + name: building.name, + projectAddress: "", + contactPerson: "", + startDate: DateTime.now(), + endDate: DateTime.now(), + projectStatusId: "", + buildings: [building], + ); + }).toList(); + + // Fetch tasks for each work area + await Future.wait(dailyTasks.expand((task) => task.buildings) + .expand((b) => b.floors) + .expand((f) => f.workAreas) + .map((area) async { + try { + final taskResponse = await ApiService.getWorkItemsByWorkArea(area.id); + final taskData = taskResponse?['data'] as List? ?? []; + + area.workItems.addAll(taskData.map((taskJson) { + return WorkItemWrapper( + workItemId: taskJson['id'], + workItem: WorkItem( + id: taskJson['id'], + activityMaster: taskJson['activityMaster'] != null + ? ActivityMaster.fromJson(taskJson['activityMaster']) + : null, + workCategoryMaster: taskJson['workCategoryMaster'] != null + ? WorkCategoryMaster.fromJson(taskJson['workCategoryMaster']) + : null, + plannedWork: (taskJson['plannedWork'] as num?)?.toDouble(), + completedWork: (taskJson['completedWork'] as num?)?.toDouble(), + todaysAssigned: (taskJson['todaysAssigned'] as num?)?.toDouble(), + description: taskJson['description'] as String?, + taskDate: taskJson['taskDate'] != null + ? DateTime.tryParse(taskJson['taskDate']) + : null, + ), + ); + })); + } catch (e, stack) { + logSafe("Error fetching tasks for work area ${area.id}", + level: LogLevel.error, error: e, stackTrace: stack); + } + })); + + logSafe("Fetched infra and tasks for project $projectId", + level: LogLevel.info); } catch (e, stack) { - logSafe("Error fetching daily task data", - level: LogLevel.error, error: e, stackTrace: stack); + logSafe("Error fetching daily task data", level: LogLevel.error, error: e, stackTrace: stack); } finally { isLoading.value = false; update(); diff --git a/lib/helpers/services/api_service.dart b/lib/helpers/services/api_service.dart index 68f58c9..b660075 100644 --- a/lib/helpers/services/api_service.dart +++ b/lib/helpers/services/api_service.dart @@ -1880,7 +1880,7 @@ class ApiService { _getRequest(ApiEndpoints.getRoles).then( (res) => res != null ? _parseResponse(res, label: 'Roles') : null); static Future?> createEmployee({ - String? id, // Optional, for editing + String? id, required String firstName, required String lastName, required String phoneNumber, @@ -1889,7 +1889,7 @@ class ApiService { required String joiningDate, }) async { final body = { - if (id != null) "id": id, // Include id only if editing + if (id != null) "id": id, "firstName": firstName, "lastName": lastName, "phoneNumber": phoneNumber, @@ -1991,14 +1991,35 @@ class ApiService { return response.statusCode == 200 && json['success'] == true; } - static Future?> getDailyTasksDetails( - String projectId) async { - final url = "${ApiEndpoints.dailyTaskDetails}/$projectId"; - final response = await _getRequest(url); - return response != null - ? _parseResponseForAllData(response, label: 'Daily Task Details') - as Map? - : null; + /// Fetch infra details for a given project + static Future?> getInfraDetails(String projectId) async { + final endpoint = "/project/infra-details/$projectId"; + + final res = await _getRequest(endpoint); + if (res == null) { + logSafe('Infra Details API returned null'); + return null; + } + + logSafe('Infra Details raw response: ${res.body}'); + return _parseResponseForAllData(res, label: 'Infra Details') + as Map?; + } + + /// Fetch work items for a given work area + static Future?> getWorkItemsByWorkArea( + String workAreaId) async { + final endpoint = "/project/tasks/$workAreaId"; + + final res = await _getRequest(endpoint); + if (res == null) { + logSafe('Work Items API returned null'); + return null; + } + + logSafe('Work Items raw response: ${res.body}'); + return _parseResponseForAllData(res, label: 'Work Items') + as Map?; } static Future assignDailyTask({ diff --git a/lib/helpers/services/notification_action_handler.dart b/lib/helpers/services/notification_action_handler.dart index a84f4ee..d21cb32 100644 --- a/lib/helpers/services/notification_action_handler.dart +++ b/lib/helpers/services/notification_action_handler.dart @@ -68,6 +68,10 @@ class NotificationActionHandler { _handleDashboardUpdate(data); } break; + case 'Team_Modified': + // Call method to handle team modifications and dashboard update + _handleDashboardUpdate(data); + break; /// 🔹 Tasks case 'Report_Task': @@ -270,7 +274,6 @@ class NotificationActionHandler { } } - /// ---------------------- DIRECTORY HANDLERS ---------------------- /// ---------------------- DIRECTORY HANDLERS ---------------------- static void _handleContactModified(Map data) { final contactId = data['ContactId']; @@ -299,8 +302,6 @@ class NotificationActionHandler { } static void _handleContactNoteModified(Map data) { - final contactId = data['ContactId']; - // Refresh both contacts and notes when a note is modified _handleContactModified(data); } @@ -330,14 +331,38 @@ class NotificationActionHandler { case 'attendance_updated': await controller.fetchRoleWiseAttendance(); break; + case 'task_updated': await controller.fetchDashboardTasks( - projectId: - controller.projectController.selectedProjectId.value); + projectId: controller.projectController.selectedProjectId.value, + ); break; + case 'project_progress_update': await controller.fetchProjectProgress(); break; + + case 'Employee_Suspend': + final currentProjectId = + controller.projectController.selectedProjectId.value; + final projectIdsString = data['ProjectIds'] ?? ''; + + // Convert comma-separated string to List + final notificationProjectIds = + projectIdsString.split(',').map((e) => e.trim()).toList(); + + // Refresh only if current project ID is in the list + if (notificationProjectIds.contains(currentProjectId)) { + await controller.fetchDashboardTeams(projectId: currentProjectId); + } + break; + + case 'Team_Modified': + final projectId = data['ProjectId'] ?? + controller.projectController.selectedProjectId.value; + await controller.fetchDashboardTeams(projectId: projectId); + break; + case 'full_dashboard_refresh': default: await controller.refreshDashboard(); diff --git a/lib/model/dailyTaskPlanning/daily_task_planning_model.dart b/lib/model/dailyTaskPlanning/daily_task_planning_model.dart index 1adf51c..7609903 100644 --- a/lib/model/dailyTaskPlanning/daily_task_planning_model.dart +++ b/lib/model/dailyTaskPlanning/daily_task_planning_model.dart @@ -129,6 +129,7 @@ class WorkItem { final WorkCategoryMaster? workCategoryMaster; final double? plannedWork; final double? completedWork; + final String? description; final double? todaysAssigned; final DateTime? taskDate; final String? tenantId; @@ -142,6 +143,7 @@ class WorkItem { this.workArea, this.activityMaster, this.workCategoryMaster, + this.description, this.plannedWork, this.completedWork, this.todaysAssigned, @@ -175,7 +177,8 @@ class WorkItem { : null, todaysAssigned: json['todaysAssigned'] != null ? (json['todaysAssigned'] as num).toDouble() - : null, // ✅ added parsing + : null, + description: json['description'] as String?, taskDate: json['taskDate'] != null ? DateTime.tryParse(json['taskDate']) : null, tenantId: json['tenantId'] as String?, diff --git a/lib/view/Attendence/attendance_logs_tab.dart b/lib/view/Attendence/attendance_logs_tab.dart index 24fbd2b..852e851 100644 --- a/lib/view/Attendence/attendance_logs_tab.dart +++ b/lib/view/Attendence/attendance_logs_tab.dart @@ -116,7 +116,7 @@ class _AttendanceLogsTabState extends State { final showPendingOnly = widget.controller.showPendingOnly.value; final filteredLogs = showPendingOnly ? allLogs - .where((emp) => emp.activity == 1 || emp.activity == 2) + .where((emp) => emp.activity == 1 ) .toList() : allLogs;