feat: Enhance daily task planning by fetching infra details and associated tasks; update API service for new endpoints

This commit is contained in:
Vaibhav Surve 2025-09-20 16:32:11 +05:30
parent ae7ce851ee
commit 04cbdab277
5 changed files with 151 additions and 39 deletions

View File

@ -18,7 +18,6 @@ class DailyTaskPlanningController extends GetxController {
MyFormValidator basicValidator = MyFormValidator();
List<Map<String, dynamic>> 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<void> 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<void> 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<dynamic>?;
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<dynamic>).map((floorJson) {
return Floor(
id: floorJson['id'],
floorName: floorJson['floorName'],
workAreas: (floorJson['workAreas'] as List<dynamic>).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<dynamic>? ?? [];
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();

View File

@ -1880,7 +1880,7 @@ class ApiService {
_getRequest(ApiEndpoints.getRoles).then(
(res) => res != null ? _parseResponse(res, label: 'Roles') : null);
static Future<Map<String, dynamic>?> 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<Map<String, dynamic>?> 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<String, dynamic>?
: null;
/// Fetch infra details for a given project
static Future<Map<String, dynamic>?> 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<String, dynamic>?;
}
/// Fetch work items for a given work area
static Future<Map<String, dynamic>?> 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<String, dynamic>?;
}
static Future<bool> assignDailyTask({

View File

@ -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<String, dynamic> data) {
final contactId = data['ContactId'];
@ -299,8 +302,6 @@ class NotificationActionHandler {
}
static void _handleContactNoteModified(Map<String, dynamic> 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<String>
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();

View File

@ -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?,

View File

@ -116,7 +116,7 @@ class _AttendanceLogsTabState extends State<AttendanceLogsTab> {
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;