diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 059e36ba..9857c9a5 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -91,10 +91,15 @@ class URLs { //Task request apis ... static get getTaskTypeByUserUrl=> '$_baseUrl/TaskTypes/GetTaskTypesByUser'; static get addTaskUrl=> '$_baseUrl/TaskJobs/AddTaskJob'; + static get getTaskByIdUrl=> '$_baseUrl/TaskJobs/GetTaskJobById'; + static get updateTaskByEngineerUrl=> '$_baseUrl/TaskJobs/UpdateByEngineer'; + + // Task LookUps static get taskJobTypeOfAlert => "$_baseUrl/Lookups/GetLookup?lookupEnum=1260"; static get taskJobRiskLevel => "$_baseUrl/Lookups/GetLookup?lookupEnum=1261"; static get taskJobResource => "$_baseUrl/Lookups/GetLookup?lookupEnum=1262"; static get taskJobActionNeeded => "$_baseUrl/Lookups/GetLookup?lookupEnum=1263"; + static get taskJobImpactStatus => "$_baseUrl/Lookups/GetLookup?lookupEnum=1264"; diff --git a/lib/controllers/providers/api/status_drop_down/report/service_report_assistants_employee_provider.dart b/lib/controllers/providers/api/status_drop_down/report/service_report_assistants_employee_provider.dart index d84a2738..65970f78 100644 --- a/lib/controllers/providers/api/status_drop_down/report/service_report_assistants_employee_provider.dart +++ b/lib/controllers/providers/api/status_drop_down/report/service_report_assistants_employee_provider.dart @@ -50,11 +50,17 @@ class ServiceReportAssistantsEmployeeProvider extends ChangeNotifier { /// lib\controllers\http_status_manger\http_status_manger.dart /// - Future getAssistantEmployees(num assetId) async { + Future getAssistantEmployees({num? assetId}) async { if (_loading) return -2; _loading = true; + String url = ''; + if(assetId!=null){ + url = "${URLs.getEngineers}&assetId=$assetId"; + }else{ + url = "${URLs.getEngineers}"; + } notifyListeners(); - try {final response = await ApiManager.instance.get("${URLs.getEngineers}&assetId=$assetId"); + try {final response = await ApiManager.instance.get(url); _stateCode = response.statusCode; if (response.statusCode >= 200 && response.statusCode < 300) { final usersListJson = json.decode(response.body) as List; diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb index 4ad10fdb..526e0647 100644 --- a/lib/l10n/app_ar.arb +++ b/lib/l10n/app_ar.arb @@ -555,6 +555,11 @@ "createTaskRequest": "إنشاء طلب مهمة", "taskRequest": "طلب مهمة", "completedActions": "الإجراءات المكتملة", - "impactStatus": "حالة التأثير" + "impactStatus": "حالة التأثير", + "installationSite": "موقع التركيب", + "installationBuilding": "مبنى التركيب", + "installationFloor": "طابق التركيب", + "installationDepartment": "قسم التركيب", + "acknowledge": "إقرار" } \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index eb92835a..89ce0bcd 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -558,5 +558,10 @@ "createTaskRequest": "Create Task Request", "taskRequest": "Task Request", "completedActions": "Completed Actions", - "impactStatus": "Impact Status" + "impactStatus": "Impact Status", + "installationSite": "Installation Site", + "installationBuilding": "Installation Building", + "installationFloor": "Installation Floor", + "installationDepartment": "Installation Department", + "acknowledge": "Acknowledge" } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index a340691b..5f5ccd1a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -253,6 +253,7 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (_) => TaskJobResourceProvider()), ChangeNotifierProvider(create: (_) => TaskJobActionNeededProvider()), ChangeNotifierProvider(create: (_) => TaskEvaluatorUserProvider()), + ChangeNotifierProvider(create: (_) => TaskJobImpactStatusProvider()), ///todo deleted //ChangeNotifierProvider(create: (_) => AssetTypesProvider()), diff --git a/lib/models/new_models/task_request/task_request_model.dart b/lib/models/new_models/task_request/task_request_model.dart index 7493690b..bff6152f 100644 --- a/lib/models/new_models/task_request/task_request_model.dart +++ b/lib/models/new_models/task_request/task_request_model.dart @@ -1,13 +1,16 @@ import 'package:test_sa/models/base.dart'; import 'package:test_sa/models/lookup.dart'; +import 'package:test_sa/models/new_models/assistant_employee.dart'; import 'package:test_sa/models/new_models/building.dart'; import 'package:test_sa/models/new_models/floor.dart'; import 'package:test_sa/models/new_models/site.dart'; import 'package:test_sa/models/new_models/department.dart'; import 'package:test_sa/models/new_models/room_model.dart'; +import 'package:test_sa/models/new_models/work_order_detail_model.dart'; +import 'package:test_sa/models/timer_model.dart'; class TaskRequestModel { - final Data? data; + final TaskData? data; final String? message; final String? title; final String? innerMessage; @@ -24,7 +27,7 @@ class TaskRequestModel { }); factory TaskRequestModel.fromJson(Map json) => TaskRequestModel( - data: json['data'] != null ? Data.fromJson(json['data']) : null, + data: json['data'] != null ? TaskData.fromJson(json['data']) : null, message: json['message'], title: json['title'], innerMessage: json['innerMessage'], @@ -42,45 +45,52 @@ class TaskRequestModel { }; } -class Data { - final int? id; - final String? taskJobNo; - final User? userCreated; - final List? taskJobContactPersons; - final TaskTypeModel? taskType; - final TaskJobStatus? taskJobStatus; - final dynamic asset; - final dynamic site; - final dynamic building; - final dynamic floor; - final dynamic department; - final dynamic room; - final String? callComment; - final List? taskJobAttachments; - final User? assignedEngineer; - final List? taskJobAssistantEmployees; - final List? taskJobActivityEngineerTimers; - final List? taskJobHistories; - final BuildingData? installationBuilding; - final dynamic installationFloor; - final dynamic installationDepartment; - final String? serialNo; - final String? installationDate; - final dynamic completedAction; - final dynamic impactStatus; - final bool? isUserAcknowledge; - final dynamic typeOfAlert; - final dynamic riskLevel; - final dynamic resource; - final dynamic actionNeeded; - final dynamic alertNo; - final String? estimationDeliveryDate; - final dynamic reasonOfFSCA; - final dynamic correctiveActionDescription; - final User? evaluatorUser; +class TaskData { + int? id; + int? statusValue; + String? taskJobNo; + User? userCreated; + List? taskJobContactPersons; + TaskTypeModel? taskType; + TaskJobStatus? taskJobStatus; + TaskAsset? asset; + Site? site; + Building? building; + Floor? floor; + Department? department; + Rooms? room; + String? callComment; + List? taskJobAttachments; + User? assignedEngineer; + List? taskJobAssistantEmployees; + TaskJobAssistantEmployees? modelAssistantEmployees; + List? assistantEmployees; + List? taskJobHistories; + Lookup? installationBuilding; + Lookup? installationFloor; + Lookup? installationDepartment; + String? serialNo; + String? installationDate; + Lookup? completedAction; + Lookup? impactStatus; + bool? isUserAcknowledge; + Lookup? typeOfAlert; + dynamic riskLevel; + dynamic resource; + Lookup? actionNeeded; + String? alertNo; + String? estimationDeliveryDate; + String? reasonOfFSCA; + String? correctiveActionDescription; + User? evaluatorUser; + List? taskJobActivityEngineerTimers = []; + TimerModel? taskTimerModel = TimerModel(); + double? totalWorkingHours = 0.0; + List? timerModelList = []; - Data({ + TaskData({ this.id, + this.statusValue, this.taskJobNo, this.userCreated, this.taskJobContactPersons, @@ -96,7 +106,8 @@ class Data { this.taskJobAttachments, this.assignedEngineer, this.taskJobAssistantEmployees, - this.taskJobActivityEngineerTimers, + this.modelAssistantEmployees, + this.assistantEmployees, this.taskJobHistories, this.installationBuilding, this.installationFloor, @@ -117,119 +128,217 @@ class Data { this.evaluatorUser, }); - factory Data.fromJson(Map json) => Data( - id: json['id'], - taskJobNo: json['taskJobNo'], - userCreated: json['userCreated'] != null ? User.fromJson(json['userCreated']) : null, - taskJobContactPersons: (json['taskJobContactPersons'] as List?)?.map((e) => TaskJobContactPerson.fromJson(e)).toList(), - taskType: json['taskType'] != null ? TaskTypeModel.fromJson(json['taskType']) : null, - taskJobStatus: json['taskJobStatus'] != null ? TaskJobStatus.fromJson(json['taskJobStatus']) : null, - asset: json['asset'], - site: json['site'], - building: json['building'], - floor: json['floor'], - department: json['department'], - room: json['room'], - callComment: json['callComment'], - taskJobAttachments: (json['taskJobAttachments'] as List?)?.map((e) => TaskJobAttachment.fromJson(e)).toList(), - assignedEngineer: json['assignedEngineer'] != null ? User.fromJson(json['assignedEngineer']) : null, - taskJobAssistantEmployees: json['taskJobAssistantEmployees'], - taskJobActivityEngineerTimers: json['taskJobActivityEngineerTimers'], - taskJobHistories: (json['taskJobHistories'] as List?)?.map((e) => TaskJobHistory.fromJson(e)).toList(), - installationBuilding: json['installationBuilding'] != null ? BuildingData.fromJson(json['installationBuilding']) : null, - installationFloor: json['installationFloor'], - installationDepartment: json['installationDepartment'], - serialNo: json['serialNo'], - installationDate: json['installationDate'], - completedAction: json['completedAction'], - impactStatus: json['impactStatus'], - isUserAcknowledge: json['isUserAcknowledge'], - typeOfAlert: json['typeOfAlert'], - riskLevel: json['riskLevel'], - resource: json['resource'], - actionNeeded: json['actionNeeded'], - alertNo: json['alertNo'], - estimationDeliveryDate: json['estimationDeliveryDate'], - reasonOfFSCA: json['reasonOfFSCA'], - correctiveActionDescription: json['correctiveActionDescription'], - evaluatorUser: json['evaluatorUser'] != null ? User.fromJson(json['evaluatorUser']) : null, - ); + TaskData.fromJson(Map json) { + id = json['id']; + taskJobNo = json['taskJobNo']; + userCreated = json['userCreated'] != null ? User.fromJson(json['userCreated']) : null; + if (json['taskJobContactPersons'] != null) { + taskJobContactPersons = []; + json['taskJobContactPersons'].forEach((v) { + taskJobContactPersons!.add(TaskJobContactPerson.fromJson(v)); + }); + } + if (json['attachments'] != null) { + taskJobAttachments = []; + json['attachments'].forEach((v) { + taskJobAttachments!.add(TaskJobAttachment.fromJson(v)); + }); + } + taskType = json['taskType'] != null ? TaskTypeModel.fromJson(json['taskType']) : null; + taskJobStatus = json['taskJobStatus'] != null ? TaskJobStatus.fromJson(json['taskJobStatus']) : null; + callComment = json['callComment']; + if (json['taskJobAttachments'] != null) { + taskJobAttachments = []; + json['taskJobAttachments'].forEach((v) { + taskJobAttachments!.add(TaskJobAttachment.fromJson(v)); + }); + } + assignedEngineer = json['assignedEngineer'] != null ? User.fromJson(json['assignedEngineer']) : null; + asset = json['asset'] != null ? TaskAsset.fromJson(json['asset']) : null; + if (json['taskJobAssistantEmployees'] != null) { + taskJobAssistantEmployees = []; + json['taskJobAssistantEmployees'].forEach((v) { + taskJobAssistantEmployees!.add(TaskJobAssistantEmployees.fromJson(v)); + }); + + if (taskJobAssistantEmployees != null && taskJobAssistantEmployees!.isNotEmpty) { + assistantEmployees = [ + AssistantEmployees.fromJson({ + 'id': null, + 'user': { + 'id': taskJobAssistantEmployees?[0].assistantEngineer?.userId, + 'name': taskJobAssistantEmployees?[0].assistantEngineer?.userName, + } + }) + ]; + } + modelAssistantEmployees = taskJobAssistantEmployees!.isNotEmpty ? taskJobAssistantEmployees!.first : TaskJobAssistantEmployees(); + } + if (json['taskJobActivityEngineerTimers'] != null) { + taskJobActivityEngineerTimers = []; + json['taskJobActivityEngineerTimers'].forEach((v) { + taskJobActivityEngineerTimers!.add(TaskJobActivityEngineerTimer.fromJson(v)); + }); + } + + if (json['taskJobHistories'] != null) { + taskJobHistories = []; + json['taskJobHistories'].forEach((v) { + taskJobHistories!.add(TaskJobHistory.fromJson(v)); + }); + } + serialNo = json['serialNo']; + installationDate = json['installationDate']; + completedAction = json["completedAction"] == null ? null : Lookup.fromJson(json["completedAction"]); + impactStatus = json["impactStatus"] == null ? null : Lookup.fromJson(json["impactStatus"]); + actionNeeded = json["actionNeeded"] == null ? null : Lookup.fromJson(json["actionNeeded"]); + typeOfAlert = json["typeOfAlert"] == null ? null : Lookup.fromJson(json["typeOfAlert"]); + installationBuilding = json["installationBuilding"] == null ? null : Lookup.fromJson(json["installationBuilding"]); + installationDepartment = json["installationDepartment"] == null ? null : Lookup.fromJson(json["installationDepartment"]); + installationFloor = json["installationFloor"] == null ? null : Lookup.fromJson(json["installationFloor"]); + isUserAcknowledge = json['isUserAcknowledge'] ?? false; + riskLevel = json['riskLevel']; + resource = json['resource']; + alertNo = json['alertNo']; + estimationDeliveryDate = json['estimationDeliveryDate']; + reasonOfFSCA = json['reasonOfFSCA']; + correctiveActionDescription = json['correctiveActionDescription']; + evaluatorUser = json['evaluatorUser'] != null ? User.fromJson(json['evaluatorUser']) : null; + } - Map toJson() => { - 'id': id, - 'taskJobNo': taskJobNo, - 'userCreated': userCreated?.toJson(), - 'taskJobContactPersons': taskJobContactPersons?.map((e) => e.toJson()).toList(), - 'taskType': taskType?.toJson(), - 'taskJobStatus': taskJobStatus?.toJson(), - 'asset': asset, - 'site': site, - 'building': building, - 'floor': floor, - 'department': department, - 'room': room, - 'callComment': callComment, - 'taskJobAttachments': taskJobAttachments?.map((e) => e.toJson()).toList(), - 'assignedEngineer': assignedEngineer?.toJson(), - 'taskJobAssistantEmployees': taskJobAssistantEmployees, - 'taskJobActivityEngineerTimers': taskJobActivityEngineerTimers, - 'taskJobHistories': taskJobHistories?.map((e) => e.toJson()).toList(), - 'installationBuilding': installationBuilding?.toJson(), - 'installationFloor': installationFloor, - 'installationDepartment': installationDepartment, - 'serialNo': serialNo, - 'installationDate': installationDate, - 'completedAction': completedAction, - 'impactStatus': impactStatus, - 'isUserAcknowledge': isUserAcknowledge, - 'typeOfAlert': typeOfAlert, - 'riskLevel': riskLevel, - 'resource': resource, - 'actionNeeded': actionNeeded, - 'alertNo': alertNo, - 'estimationDeliveryDate': estimationDeliveryDate, - 'reasonOfFSCA': reasonOfFSCA, - 'correctiveActionDescription': correctiveActionDescription, - 'evaluatorUser': evaluatorUser?.toJson(), - }; + Map toJson() { + final Map data = {}; - Map createTaskJson() => { - 'id': id, - 'taskJobNo': taskJobNo, - 'userCreated': userCreated?.toJson(), - 'taskJobContactPersons': taskJobContactPersons?.map((e) => e.toJson()).toList(), - 'taskType': taskType?.toJson(), - 'taskJobStatus': taskJobStatus?.toJson(), - 'asset': asset, - 'site': site, - 'building': building, - 'floor': floor, - 'department': department, - 'room': room, - 'callComment': callComment, - 'taskJobAttachments': taskJobAttachments?.map((e) => e.toJson()).toList(), - 'assignedEngineer': assignedEngineer?.toJson(), - 'taskJobAssistantEmployees': taskJobAssistantEmployees, - 'taskJobActivityEngineerTimers': taskJobActivityEngineerTimers, - 'taskJobHistories': taskJobHistories?.map((e) => e.toJson()).toList(), - 'installationBuilding': installationBuilding?.toJson(), - 'installationFloor': installationFloor, - 'installationDepartment': installationDepartment, - 'serialNo': serialNo, - 'installationDate': installationDate, - 'completedAction': completedAction, - 'impactStatus': impactStatus, - 'isUserAcknowledge': isUserAcknowledge, - 'typeOfAlert': typeOfAlert, - 'riskLevel': riskLevel, - 'resource': resource, - 'actionNeeded': actionNeeded, - 'alertNo': alertNo, - 'estimationDeliveryDate': estimationDeliveryDate, - 'reasonOfFSCA': reasonOfFSCA, - 'correctiveActionDescription': correctiveActionDescription, - 'evaluatorUser': evaluatorUser?.toJson(), - }; + data['id'] = id; + data['statusValue'] = statusValue; + data['taskJobNo'] = taskJobNo; + + if (userCreated != null) { + data['userCreated'] = userCreated!.toJson(); + } + + if (taskJobContactPersons != null) { + data['taskJobContactPersons'] = taskJobContactPersons!.map((e) => e.toJson()).toList(); + } + + if (taskType != null) { + data['taskType'] = taskType!.toJson(); + } + + if (taskJobStatus != null) { + data['taskJobStatus'] = taskJobStatus!.toJson(); + } + + data['asset'] = asset?.toJson(); + data['site'] = site; + data['building'] = building; + data['floor'] = floor; + data['department'] = department; + data['room'] = room; + data['callComment'] = callComment; + + if (assignedEngineer != null) { + data['assignedEngineer'] = assignedEngineer!.toJson(); + } + if (taskJobAssistantEmployees != null) { + data['taskJobAssistantEmployees'] = taskJobAssistantEmployees!.map((v) => v.toJson()).toList(); + } + if (taskJobHistories != null) { + data['taskJobHistories'] = taskJobHistories!.map((e) => e.toJson()).toList(); + } + if (installationBuilding != null) { + data['installationBuilding'] = installationBuilding!.toJson(); + } + data['installationFloor'] = installationFloor; + data['installationDepartment'] = installationDepartment; + data['serialNo'] = serialNo; + data['installationDate'] = installationDate; + data['completedAction'] = completedAction; + data['impactStatus'] = impactStatus; + data['isUserAcknowledge'] = isUserAcknowledge; + data['typeOfAlert'] = typeOfAlert; + data['riskLevel'] = riskLevel; + data['resource'] = resource; + data['actionNeeded'] = actionNeeded; + data['alertNo'] = alertNo; + data['estimationDeliveryDate'] = estimationDeliveryDate; + data['reasonOfFSCA'] = reasonOfFSCA; + data['correctiveActionDescription'] = correctiveActionDescription; + + if (evaluatorUser != null) { + data['evaluatorUser'] = evaluatorUser!.toJson(); + } + if (taskJobActivityEngineerTimers != null) { + data['taskJobActivityEngineerTimers'] = taskJobActivityEngineerTimers!.map((e) => e.toJson()).toList(); + } + if (taskJobAttachments != null) { + data['taskJobAttachments'] = taskJobAttachments?.map((e) => e.toJson()).toList(); + } + + return data; + } + + Map toEngineerUpdateJson() { + final Map data = {}; + data['id'] = id; + data['statusValue'] = statusValue; + if (assistantEmployees != null && assistantEmployees!.isNotEmpty) { + data['taskJobAssistantEmployees'] = [modelAssistantEmployees?.toJson()]; + } else { + data['taskJobAssistantEmployees'] = []; + } + if (taskJobActivityEngineerTimers != null) { + data['taskJobActivityEngineerTimers'] = taskJobActivityEngineerTimers!.map((v) => v.toJson()).toList(); + } + if (taskJobAttachments != null) { + data['attachments'] = taskJobAttachments!.map((v) => v.toJson()).toList(); + } + data['installationBuildingId'] = building?.id; + data['installationFloorId'] = floor?.id; + data['installationDepartmentId'] = department?.id; + data['serialNo'] = serialNo; + data['installationDate'] = installationDate; + data['completedActionId'] = actionNeeded?.id; + data['impactStatusId'] = impactStatus?.id; + data['isUserAcknowledge'] = isUserAcknowledge; + return data; + } +} + +class TaskJobActivityEngineerTimer { + int? id; + String? startDate; + String? endDate; + double? totalWorkingHour; + String? comment; + + TaskJobActivityEngineerTimer({ + this.id, + this.startDate, + this.endDate, + this.totalWorkingHour, + this.comment, + }); + + factory TaskJobActivityEngineerTimer.fromJson(Map json) { + return TaskJobActivityEngineerTimer( + id: json['id'] as int?, + startDate: json['startDate'], + endDate: json['endDate'], + totalWorkingHour: (json['totalWorkingHour'] as num?)?.toDouble(), + comment: json['comment'] as String?, + ); + } + + Map toJson() { + return { + 'id': id, + 'startDate': startDate, + 'endDate': endDate, + 'totalWorkingHour': totalWorkingHour, + 'comment': comment, + }; + } } class User { @@ -372,6 +481,44 @@ class TaskJobAttachment { }; } +class TaskJobAssistantEmployees { + DateTime? startDate; + int? id; + String? assistantEnginerId; + DateTime? endDate; + double? workingHours; + String? comment; + AssignedEmployee? user; + AssistantEngineer? assistantEngineer; + + TaskJobAssistantEmployees({this.startDate, this.endDate, this.workingHours, this.comment, this.assistantEnginerId, this.user, this.id, this.assistantEngineer}); + + TaskJobAssistantEmployees.fromJson(Map json) { + id = json['id']; + startDate = json['startDate'] != null ? DateTime.parse(json['startDate']) : null; + endDate = json['endDate'] != null ? DateTime.parse(json['endDate']) : null; + if (startDate != null && endDate != null) { + workingHours = endDate!.difference(startDate!).inMinutes / 60.0; + } else { + workingHours = null; + } + comment = json['comment']; + user = json['assistantEnginer'] != null ? AssignedEmployee.fromJson(json['assistantEnginer']) : null; + assistantEngineer = json['assistantEnginer'] != null ? AssistantEngineer.fromJson(json['assistantEnginer']) : null; + } + + Map toJson() { + final Map data = {}; + data['id'] = id ?? 0; + data['startDate'] = startDate?.toIso8601String(); + data['endDate'] = endDate?.toIso8601String(); + data['workingHours'] = workingHours; + data['comment'] = comment; + data['assistantEnginerId'] = user?.userId; + return data; + } +} + class TaskJobHistory { final int? id; final RelatedTo? action; @@ -561,3 +708,82 @@ class TaskEvaluatorUser extends Base { return data; } } + +class AssistantEngineer { + String? userId; + String? userName; + String? email; + String? employeeId; + int? languageId; + + AssistantEngineer({this.userId, this.userName, this.email, this.employeeId, this.languageId}); + + factory AssistantEngineer.fromJson(Map json) { + return AssistantEngineer(userId: json['userId'], userName: json['userName'], email: json['email'], employeeId: json['employeeId'], languageId: json['languageId']); + } + + Map toJson() { + final Map data = {}; + data['userId'] = userId; + data['userName'] = userName; + data['email'] = email; + data['employeeId'] = employeeId; + data['languageId'] = languageId; + return data; + } +} + +class TaskAsset { + int? id; + String? assetNumber; + String? serialNo; + String? assetName; + String? model; + String? manufacturer; + String? siteName; + int? siteId; + String? buildingName; + String? floorName; + String? departmentName; + String? roomName; + String? supplierName; + String? assetTypeName; + + TaskAsset(); + + TaskAsset.fromJson(Map json) { + id = json['id']; + assetNumber = json['assetNumber']; + serialNo = json['serialNo']; + assetName = json['assetName']; + model = json['model']; + manufacturer = json['manufacturer']; + siteName = json['siteName']; + siteId = json['siteId']; + buildingName = json['buildingName']; + floorName = json['floorName']; + departmentName = json['departmentName']; + roomName = json['roomName']; + supplierName = json['supplierName']; + assetTypeName = json['assetTypeName']; + } + + Map toJson() { + return { + 'id': id, + 'assetNumber': assetNumber, + 'serialNo': serialNo, + 'assetName': assetName, + 'model': model, + 'manufacturer': manufacturer, + 'siteName': siteName, + 'siteId': siteId, + 'buildingName': buildingName, + 'floorName': floorName, + 'departmentName': departmentName, + 'roomName': roomName, + 'supplierName': supplierName, + 'assetTypeName': assetTypeName, + }; + } +} diff --git a/lib/models/new_models/work_order_detail_model.dart b/lib/models/new_models/work_order_detail_model.dart index 8117d902..c02bc0a0 100644 --- a/lib/models/new_models/work_order_detail_model.dart +++ b/lib/models/new_models/work_order_detail_model.dart @@ -98,7 +98,7 @@ class WorkOrderData { Lookup? manufacturer; Lookup? model; Lookup? assetNdModel; - Site? site; + WorkOrderSite? site; Lookup? building; Lookup? floor; AssetGroup? department; @@ -145,7 +145,7 @@ class WorkOrderData { manufacturer: json["manufacturer"] == null ? null : Lookup.fromJson(json["manufacturer"]), model: json["model"] == null ? null : Lookup.fromJson(json["model"]), assetNdModel: json["assetNDModel"] == null ? null : Lookup.fromJson(json["assetNDModel"]), - site: json["site"] == null ? null : Site.fromJson(json["site"]), + site: json["site"] == null ? null : WorkOrderSite.fromJson(json["site"]), building: json["building"] == null ? null : Lookup.fromJson(json["building"]), floor: json["floor"] == null ? null : Lookup.fromJson(json["floor"]), department: json["department"] == null ? null : AssetGroup.fromJson(json["department"]), @@ -466,8 +466,8 @@ class PartCatalogItem { } } -class Site { - Site({ +class WorkOrderSite { + WorkOrderSite({ required this.id, required this.siteName, }); @@ -475,8 +475,8 @@ class Site { int? id; String? siteName; - factory Site.fromJson(Map json) { - return Site( + factory WorkOrderSite.fromJson(Map json) { + return WorkOrderSite( id: json["id"], siteName: json["siteName"], ); diff --git a/lib/models/timer_model.dart b/lib/models/timer_model.dart index 93957ba0..4bc29963 100644 --- a/lib/models/timer_model.dart +++ b/lib/models/timer_model.dart @@ -2,8 +2,9 @@ class TimerModel { DateTime? startAt; DateTime? endAt; int? durationInSecond; + String? comments; //requested to add comments for each timer for task module.. // bool stopped; - TimerModel({this.startAt, this.endAt, this.durationInSecond}); + TimerModel({this.startAt, this.endAt, this.durationInSecond,this.comments}); } diff --git a/lib/new_views/common_widgets/single_item_drop_down_menu.dart b/lib/new_views/common_widgets/single_item_drop_down_menu.dart index 6a5b808b..ade6aaf7 100644 --- a/lib/new_views/common_widgets/single_item_drop_down_menu.dart +++ b/lib/new_views/common_widgets/single_item_drop_down_menu.dart @@ -167,7 +167,7 @@ class _SingleItemDropDownMenuState( // Specify return type @@ -194,7 +194,7 @@ class _SingleItemDropDownMenuState { } } } +class TaskJobImpactStatusProvider extends LoadingListNotifier { + @override + Future getDate() async { + if (loading == true) return -2; + loading = true; + notifyListeners(); + try { + Response response = await ApiManager.instance.get(URLs.taskJobImpactStatus); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + // client's request was successfully received + List categoriesListJson = json.decode(response.body)["data"]; + items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList(); + } + loading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + loading = false; + stateCode = -1; + notifyListeners(); + return -1; + } + } +} diff --git a/lib/providers/task_request_provider/task_request_provider.dart b/lib/providers/task_request_provider/task_request_provider.dart index 2936ac35..bc5fe032 100644 --- a/lib/providers/task_request_provider/task_request_provider.dart +++ b/lib/providers/task_request_provider/task_request_provider.dart @@ -1,13 +1,19 @@ import 'dart:convert'; +import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:http/http.dart'; import 'package:test_sa/controllers/api_routes/api_manager.dart'; import 'package:test_sa/controllers/api_routes/urls.dart'; +import 'package:test_sa/controllers/providers/api/hospitals_provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/models/lookup.dart'; +import 'package:test_sa/models/new_models/building.dart'; +import 'package:test_sa/models/new_models/department.dart'; +import 'package:test_sa/models/new_models/floor.dart'; +import 'package:test_sa/models/new_models/site.dart'; import 'package:test_sa/models/new_models/task_request/task_request_model.dart'; import 'package:test_sa/models/new_models/task_request/task_type_model.dart'; import 'package:test_sa/models/service_request/pending_service_request_model.dart'; @@ -25,19 +31,17 @@ import '../../models/user.dart'; import '../../new_views/common_widgets/app_lazy_loading.dart'; class TaskRequestProvider extends ChangeNotifier { - // number of items call in each request final pageItemNumber = 10; - - - // state code of current request to defied error message - // like 400 customer request failed - // 500 service not available int? stateCode; - - bool isLoading = false; + bool isSiteLoading = false; + TaskData? taskRequestModel; + List? siteList; - + void updateTaskModel(TaskData? value) { + taskRequestModel = value; + notifyListeners(); + } Future addTask({ required BuildContext context, @@ -62,4 +66,72 @@ class TaskRequestProvider extends ChangeNotifier { Navigator.pop(context); } } + + //getTaskById...... + Future getTaskById({required int id, bool? showLoading = true}) async { + try { + isLoading = showLoading ?? true; + notifyListeners(); + final response = await ApiManager.instance.get(URLs.getTaskByIdUrl + "?taskJobId=$id"); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + taskRequestModel = TaskData.fromJson(json.decode(response.body)['data']); + notifyListeners(); + } + isLoading = false; + notifyListeners(); + } catch (e) { + log("getTask [error] : $e"); + isLoading = false; + notifyListeners(); + return null; + } + } + + Future getSiteData({required int? siteId}) async { + isSiteLoading = true; + notifyListeners(); + try { + Response response = await ApiManager.instance.get(URLs.getSiteAutoCompleteWithoutConditionSites + "?id=$siteId"); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + List? siteData = json.decode(response.body)["data"]; + if (siteData != null && siteData.isNotEmpty) { + taskRequestModel?.site = Site.fromJson(siteData.first); + } + taskRequestModel?.building = taskRequestModel?.site?.buildings?.firstWhere((element) => element.value == taskRequestModel?.installationBuilding?.value, orElse: () => Building()); + taskRequestModel?.floor = taskRequestModel?.building?.floors?.firstWhere((element) => element.value == taskRequestModel?.installationFloor?.value, orElse: () => Floor()); + taskRequestModel?.department = taskRequestModel?.floor?.departments?.firstWhere((element) => element.name == taskRequestModel?.installationDepartment?.name, orElse: () => Department()); + } + isSiteLoading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + isSiteLoading = false; + stateCode = -1; + notifyListeners(); + return -1; + } + } + + Future updateTaskByEngineer() async { + notifyListeners(); + try { + final response = await ApiManager.instance.post(URLs.updateTaskByEngineerUrl, body: taskRequestModel!.toEngineerUpdateJson()); + stateCode = response.statusCode; + notifyListeners(); + return response.statusCode; + } catch (error) { + return -1; + } + } + + void updateTaskTimer({TimerModel? timer}) { + taskRequestModel?.taskTimerModel = timer; + if (timer?.startAt != null && timer?.endAt != null) { + taskRequestModel?.timerModelList = taskRequestModel?.timerModelList ?? []; + taskRequestModel?.timerModelList!.add(timer!); + } + notifyListeners(); + } } diff --git a/lib/views/pages/user/gas_refill/request_gas_refill.dart b/lib/views/pages/user/gas_refill/request_gas_refill.dart index 462e30c7..8ea8b9d7 100644 --- a/lib/views/pages/user/gas_refill/request_gas_refill.dart +++ b/lib/views/pages/user/gas_refill/request_gas_refill.dart @@ -192,6 +192,7 @@ class _RequestGasRefillState extends State { clientName = _userProvider.user?.clientName; } + HospitalsProvider().getHospitalsListByVal(searchVal: clientName??'').then((value) { _gasRefillProvider!.hospital = value.firstWhere((element) => element.name == clientName, orElse: null); _gasRefillProvider!.building = _gasRefillProvider!.hospital?.buildings?.firstWhere((element) => element.name == widget.gasRefillModel?.building?.name, orElse: null); diff --git a/lib/views/pages/user/tasks_request/create_task_view.dart b/lib/views/pages/user/tasks_request/create_task_view.dart index b01f0af8..3afa0227 100644 --- a/lib/views/pages/user/tasks_request/create_task_view.dart +++ b/lib/views/pages/user/tasks_request/create_task_view.dart @@ -1,8 +1,8 @@ +import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:test_sa/controllers/providers/api/service_requests_provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; @@ -24,8 +24,6 @@ import 'package:test_sa/providers/loading_list_notifier.dart'; import 'package:test_sa/providers/task_request_provider/task_job_provider.dart'; import 'package:test_sa/providers/task_request_provider/task_request_provider.dart'; import 'package:test_sa/service_request_latest/views/components/action_button/footer_action_button.dart'; -import 'package:test_sa/views/pages/user/requests/pending_requests_screen.dart'; -import 'package:test_sa/views/widgets/bottom_sheets/pending_request_bottom_sheet.dart'; import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; import 'package:test_sa/views/widgets/equipment/asset_picker.dart'; import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; @@ -44,18 +42,16 @@ class CreateTaskView extends StatefulWidget { } class _CreateTaskViewState extends State with TickerProviderStateMixin { - final List _deviceImages = []; + final List _deviceImages = []; final GlobalKey _formKey = GlobalKey(); final GlobalKey _scaffoldKey = GlobalKey(); - - //TODO need to replace with model attribute - Asset? _device; + List _deviceList = []; + Asset? device; PendingAssetServiceRequest? pendingAssetServiceRequest; TaskType? selectedType; AddTaskModel? _addTaskModel = AddTaskModel(id: 0); TextEditingController commentController = TextEditingController(); - @override Widget build(BuildContext context) { return Scaffold( @@ -88,11 +84,13 @@ class _CreateTaskViewState extends State with TickerProviderStat } }, ), - 12.height, + 8.height, taskTypeWidget(selectedType?.relatedTo?.value), + 8.height, if (selectedType != null && selectedType!.isRecallAndAlert!) ...[ recallAndAlert(), ], + 8.height, AppTextFormField( initialValue: _addTaskModel?.callComment, controller: commentController, @@ -121,7 +119,7 @@ class _CreateTaskViewState extends State with TickerProviderStat child: AppFilledButton( buttonColor: AppColor.primary10, label: context.translation.submitRequest, - onPressed: checkPendingRequest ? null : _submit, + onPressed: _submit, // buttonColor: AppColor.primary10, ), ), @@ -135,7 +133,7 @@ class _CreateTaskViewState extends State with TickerProviderStat Widget taskTypeWidget(int? type) { switch (type) { case 1: - return relatedToAsset(); + return scanAssetButton(); case 2: return relatedToLocation(); default: @@ -285,22 +283,24 @@ class _CreateTaskViewState extends State with TickerProviderStat }, ), 8.height, - SingleItemDropDownMenu( - context: context, - height: 56.toScreenHeight, - // title: context.translation.typeofAlert, - title: 'Task Job Action', - showShadow: false, - backgroundColor: AppColor.neutral100, - showAsBottomSheet: true, - initialValue: _addTaskModel?.actionNeeded, - onSelect: (status) { - if (status != null) { - _addTaskModel?.actionNeeded = status; - setState(() {}); - } - }, - ), + if (_addTaskModel?.typeOfAlert?.value != 1) ...[ + SingleItemDropDownMenu( + context: context, + height: 56.toScreenHeight, + // title: context.translation.typeofAlert, + title: 'Task Job Action', + showShadow: false, + backgroundColor: AppColor.neutral100, + showAsBottomSheet: true, + initialValue: _addTaskModel?.actionNeeded, + onSelect: (status) { + if (status != null) { + _addTaskModel?.actionNeeded = status; + setState(() {}); + } + }, + ), + ], 8.height, SingleItemDropDownMenu( context: context, @@ -319,34 +319,36 @@ class _CreateTaskViewState extends State with TickerProviderStat }, ), 8.height, - ADatePicker( - label: "Estimated delivery date", - // label: context.translation.returnToService, - hideShadow: true, - backgroundColor: AppColor.neutral100, - date: DateTime.tryParse(_addTaskModel?.estimationDeliveryDate ?? ""), - formatDateWithTime: true, - onDatePicker: (selectedDate) { - showTimePicker( - context: context, - initialTime: TimeOfDay.now(), - ).then((selectedTime) { - // Handle the selected date and time here. - if (selectedTime != null) { - DateTime? selectedDateTime = DateTime( - selectedDate.year, - selectedDate.month, - selectedDate.day, - selectedTime.hour, - selectedTime.minute, - ); - setState(() { - _addTaskModel?.estimationDeliveryDate = selectedDateTime.toIso8601String(); - }); - } - }); - }, - ), + if (_addTaskModel?.typeOfAlert?.value != 1) ...[ + ADatePicker( + label: "Estimated delivery date", + // label: context.translation.returnToService, + hideShadow: true, + backgroundColor: AppColor.neutral100, + date: DateTime.tryParse(_addTaskModel?.estimationDeliveryDate ?? ""), + formatDateWithTime: true, + onDatePicker: (selectedDate) { + showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + ).then((selectedTime) { + // Handle the selected date and time here. + if (selectedTime != null) { + DateTime? selectedDateTime = DateTime( + selectedDate.year, + selectedDate.month, + selectedDate.day, + selectedTime.hour, + selectedTime.minute, + ); + setState(() { + _addTaskModel?.estimationDeliveryDate = selectedDateTime.toIso8601String(); + }); + } + }); + }, + ), + ], 8.height, AppTextFormField( // labelText: context.translation.travelingHours, @@ -391,61 +393,35 @@ class _CreateTaskViewState extends State with TickerProviderStat ); } - Widget relatedToAsset() { - return Column( - children: [ - scanAssetButton(), - if (pendingAssetServiceRequest != null && pendingAssetServiceRequest!.details!.isNotEmpty) ...[ - 8.height, - Row( - children: [ - const Icon(Icons.warning, color: Color(0xffEE404C), size: 14), - 8.width, - Text( - "This asset already have ${pendingAssetServiceRequest!.details!.length} request pending", - style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xff7D859A), decoration: TextDecoration.underline), - ).expanded, - ], - ).onPress(() { - showPendingRequests(); - }), - ], - 16.height, - ], - ); - } - Widget scanAssetButton() { - return AssetPicker( - deviceList: _deviceList, - showLoading: checkPendingRequest, - borderColor: AppColor.black20, - buttonColor: AppColor.white936, - multiSelection: true, - onAssetRemove: (itemId) { - _deviceList.removeWhere((asset) => asset.id?.toInt() == itemId); - setState(() {}); - }, - onMultiAssetPick: (assetList) { - _deviceList.addAll(assetList); - final seenIds = {}; - _deviceList = _deviceList.where((item) => seenIds.add(item.id)).toList(); - setState(() {}); - }, - ); - } - - bool checkPendingRequest = false; - - Future checkAssetForPendingServiceRequest(int assetId) async { - checkPendingRequest = true; - setState(() {}); - - pendingAssetServiceRequest = await Provider.of(context, listen: false).checkAssetPendingRequest(assetId); - await Future.delayed(const Duration(milliseconds: 250)); + return selectedType?.linkWithMultiAssets == true + ? AssetPicker( + deviceList: _deviceList, + showLoading: false, + borderColor: AppColor.black20, + buttonColor: AppColor.white936, + multiSelection: true, + onAssetRemove: (itemId) { + _deviceList.removeWhere((asset) => asset.id?.toInt() == itemId); + setState(() {}); + }, + onMultiAssetPick: (assetList) { + _deviceList.addAll(assetList); + final seenIds = {}; + _deviceList = _deviceList.where((item) => seenIds.add(item.id)).toList(); - checkPendingRequest = false; - setState(() {}); + setState(() {}); + }, + ) + : AssetPicker( + device: device, + showLoading: false, + borderColor: AppColor.black20, + onPick: (asset) async { + device = asset; + setState(() {}); + }, + ); } Future _submit() async { @@ -453,10 +429,20 @@ class _CreateTaskViewState extends State with TickerProviderStat _formKey.currentState!.save(); _addTaskModel?.attachments = []; + _addTaskModel?.assetIds = []; + if (selectedType?.linkWithMultiAssets == true) { + for (var element in _deviceList) { + _addTaskModel!.assetIds?.add(int.parse(element.id.toString())); + } + } else { + if (device != null) { + _addTaskModel!.assetIds?.add(int.parse(device!.id.toString())); + } + } for (var item in _deviceImages) { _addTaskModel?.attachments?.add(TaskJobAttachment(id: 0, name: "${item.path.split("/").last}|${base64Encode(item.readAsBytesSync())}")); } - TaskRequestProvider taskRequestProvider = Provider.of(context,listen: false); + TaskRequestProvider taskRequestProvider = Provider.of(context, listen: false); taskRequestProvider.addTask(context: context, task: _addTaskModel!); } } diff --git a/lib/views/pages/user/tasks_request/task_request_detail_view.dart b/lib/views/pages/user/tasks_request/task_request_detail_view.dart new file mode 100644 index 00000000..6445b67b --- /dev/null +++ b/lib/views/pages/user/tasks_request/task_request_detail_view.dart @@ -0,0 +1,210 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/string_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/all_requests_and_count_model.dart'; +import 'package:test_sa/models/new_models/task_request/task_request_model.dart'; +import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; +import 'package:test_sa/providers/task_request_provider/task_request_provider.dart'; +import 'package:test_sa/views/pages/user/tasks_request/task_request_form_view.dart'; +import 'package:test_sa/views/widgets/loaders/app_loading.dart'; + +import '../../../../controllers/providers/api/user_provider.dart'; +import '../../../../models/enums/user_types.dart'; +import '../../../../new_views/app_style/app_color.dart'; +import '../../../../new_views/common_widgets/app_filled_button.dart'; +import '../../../widgets/requests/request_status.dart'; + +class TaskRequestDetailsView extends StatefulWidget { + static const String id = "/task-request-detail"; + final int taskId; + final RequestsDetails? requestDetails; + + const TaskRequestDetailsView({Key? key, required this.taskId, this.requestDetails}) : super(key: key); + + @override + _TaskRequestDetailsViewState createState() { + return _TaskRequestDetailsViewState(); + } +} + +class _TaskRequestDetailsViewState extends State { + UserProvider? userProvider; + + TaskRequestProvider? taskProvider; + + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) { + getTaskData(); + }); + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + Future getTaskData() async { + taskProvider = Provider.of(context, listen: false); + await taskProvider?.getTaskById(id: widget.taskId); + } + + @override + Widget build(BuildContext context) { + userProvider ??= Provider.of(context, listen: false); + return Scaffold( + appBar: DefaultAppBar(title: context.translation.taskRequest), + body: SafeArea( + child: Consumer(builder: (context, taskProvider, child) { + TaskData? taskModel = taskProvider.taskRequestModel; + return taskProvider.isLoading + ? const ALoading() + : Column(children: [ + SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + StatusLabel( + label: widget.requestDetails?.priority!, + textColor: AppColor.getRequestStatusTextColorByName(context, widget.requestDetails?.priority!), + backgroundColor: AppColor.getRequestStatusColorByName(context, widget.requestDetails?.priority!), + ), + 8.width, + StatusLabel( + label: widget.requestDetails?.status!, + textColor: AppColor.getRequestStatusTextColorByName(context, widget.requestDetails?.status!), + backgroundColor: AppColor.getRequestStatusColorByName(context, widget.requestDetails?.status!), + ), + 1.width.expanded, + Text( + widget.requestDetails?.date?.toServiceRequestCardFormat ?? "-", + textAlign: TextAlign.end, + style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50), + ), + ], + ), + 8.height, + '${context.translation.taskType}: ${widget.requestDetails?.nameOfType ?? "-"}'.bodyText(context), + '${context.translation.requestNo}: ${widget.requestDetails?.requestNo ?? "-"}'.bodyText(context), + if (taskProvider.taskRequestModel?.taskType?.isInstallation == true) ...[installationWidget(taskModel: taskProvider.taskRequestModel!)], + if (taskProvider.taskRequestModel?.taskType?.isRecallAndAlert == true) ...[ + recallAlertTypeWidget(taskModel: taskProvider.taskRequestModel!), + ], + if (taskProvider.taskRequestModel?.taskType?.relatedTo?.value == 2) ...[ + linkWithLocationWidget(taskModel: taskProvider.taskRequestModel!), + ], + if (taskProvider.taskRequestModel?.taskType?.relatedTo?.value == 1) ...[ + linkWithAssetWidget(taskModel: taskProvider.taskRequestModel!), + ], + ], + ).toShadowContainer(context).paddingAll(16), + ).expanded, + if (userProvider!.user!.type == UsersTypes.engineer && (taskModel?.taskJobStatus?.value != 4 && taskModel?.taskJobStatus?.value != 3)) + AppFilledButton( + onPressed: () async { + if (taskProvider.taskRequestModel?.taskType?.isInstallation == true) { + taskProvider.getSiteData(siteId: taskProvider.taskRequestModel?.asset?.siteId); + } + Navigator.of(context).push(MaterialPageRoute(builder: (_) => TaskRequestForm(taskId: widget.taskId))); + }, + label: context.translation.updateRequest, + ).paddingAll(16) + ]); + }), + ), + ); + } + + Widget installationWidget({required TaskData taskModel}) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + assetDetails(), + '${context.translation.installationSite}: ${taskModel.asset?.siteName?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}'.bodyText(context), + '${context.translation.installationBuilding}: ${taskModel.installationBuilding?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? '-'}'.bodyText(context), + '${context.translation.installationFloor}: ${taskModel.installationFloor?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? '-'}'.bodyText(context), + '${context.translation.installationDepartment}: ${taskModel.installationDepartment?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? '-'}'.bodyText(context), + '${context.translation.installationDate}: ${taskModel.installationDate?.toAssetDetailsFormat ?? "-"}'.bodyText(context), + '${context.translation.serialNo}: ${taskModel.serialNo ?? '-'}'.bodyText(context), + const Divider().defaultStyle(context), + ], + ); + } + + Widget linkWithAssetWidget({required TaskData taskModel}) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + assetDetails(), + ], + ); + } + + Widget linkWithLocationWidget({required TaskData taskModel}) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + '${context.translation.site}: ${widget.requestDetails?.site?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}'.bodyText(context), + ], + ); + } + + Widget assetDetails() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + '${context.translation.assetName}: ${widget.requestDetails?.assetName?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}'.bodyText(context), + '${context.translation.assetNo}: ${widget.requestDetails?.assetNo ?? "-"}'.bodyText(context), + '${context.translation.assetSN}: ${widget.requestDetails?.assetSN ?? "-"}'.bodyText(context), + const Divider().defaultStyle(context), + ], + ); + } + + Widget recallAlertTypeWidget({required TaskData taskModel}) { + if (taskModel.typeOfAlert != null) { + switch (taskModel.typeOfAlert!.value) { + case 1: + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [assetDetails(), '${context.translation.acknowledge}: ${taskModel.isUserAcknowledge ?? "-"}'.bodyText(context)], + ); + case 2: + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + assetDetails(), + '${context.translation.completedActions}: ${taskModel.actionNeeded?.name ?? "-"}'.bodyText(context), + if (taskModel.actionNeeded?.value == 1) ...[ + '${context.translation.impactStatus}: ${taskModel.impactStatus?.name ?? "-"}'.bodyText(context), + ], + ], + ); + + case 3: + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + assetDetails(), + '${context.translation.acknowledge}: ${taskModel.isUserAcknowledge ?? "-"}'.bodyText(context), + '${context.translation.completedActions}: ${taskModel.actionNeeded?.name ?? "-"}'.bodyText(context), + if (taskModel.actionNeeded?.value == 1) ...[ + '${context.translation.impactStatus}: ${taskModel.impactStatus?.name ?? "-"}'.bodyText(context), + ], + ], + ); + default: + return const SizedBox(); + } + } + return const SizedBox(); + } +} diff --git a/lib/views/pages/user/tasks_request/task_request_form_view.dart b/lib/views/pages/user/tasks_request/task_request_form_view.dart new file mode 100644 index 00000000..b22a2e94 --- /dev/null +++ b/lib/views/pages/user/tasks_request/task_request_form_view.dart @@ -0,0 +1,717 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/all_requests_provider.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/string_extensions.dart'; +import 'package:test_sa/models/new_models/building.dart'; +import 'package:test_sa/models/new_models/department.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/lookup.dart'; +import 'package:test_sa/models/new_models/floor.dart'; +import 'package:test_sa/models/new_models/task_request/task_request_model.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; +import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart'; +import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; +import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; +import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; +import 'package:test_sa/providers/gas_request_providers/site_provider.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; +import 'package:test_sa/providers/task_request_provider/task_job_provider.dart'; +import 'package:test_sa/providers/task_request_provider/task_request_provider.dart'; +import 'package:test_sa/service_request_latest/utilities/service_request_utils.dart'; +import 'package:test_sa/service_request_latest/views/components/action_button/footer_action_button.dart'; +import 'package:test_sa/views/app_style/sizing.dart'; +import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; +import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; +import 'package:test_sa/views/widgets/loaders/app_loading.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; +import 'package:test_sa/views/widgets/status/report/service_report_assistant_employee_menu.dart'; +import 'package:test_sa/views/widgets/timer/app_timer.dart'; +import '../../../../models/new_models/site.dart'; +import '../../../../models/new_models/work_order_detail_model.dart'; + +class TaskRequestForm extends StatefulWidget { + final int taskId; + + const TaskRequestForm({Key? key, required this.taskId}) : super(key: key); + + @override + State createState() => _TaskRequestFormState(); +} + +class _TaskRequestFormState extends State { + TaskRequestProvider? _taskProvider; + final TextEditingController _requestedQuantityController = TextEditingController(); + final GlobalKey _formKey = GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); + List _files = []; + bool installationType = true; + + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) { + assignData(); + }); + + super.initState(); + } + + Future assignData() async { + _taskProvider = Provider.of(context, listen: false); + TaskData? taskModel = _taskProvider!.taskRequestModel; + _taskProvider?.updateTaskModel(taskModel); + if (taskModel != null) { + _files.addAll(taskModel.taskJobAttachments!.map((e) => File(e.name ?? '')).toList()); + // if (taskModel.taskType?.isInstallation == true) { + // await _taskProvider!.getSiteData(siteId: taskModel.asset?.siteId); + // } + } + } + + @override + void dispose() { + _requestedQuantityController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Consumer(builder: (context, taskProvider, child) { + return Scaffold( + appBar: DefaultAppBar(title: context.translation.taskRequest), + key: _scaffoldKey, + body: taskProvider.isLoading + ? const ALoading() + : taskProvider.taskRequestModel != null + ? SafeArea( + child: Form( + key: _formKey, + child: Stack( + children: [ + SingleChildScrollView( + padding: EdgeInsets.all(12 * AppStyle.getScaleFactor(context)), + child: Column( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _timerWidget(context, true, taskProvider), + 8.height, + if (taskProvider.taskRequestModel?.taskType?.isInstallation == true) ...[installationWidget(taskModel: taskProvider.taskRequestModel!)], + if (taskProvider.taskRequestModel?.taskType?.isRecallAndAlert == true) ...[ + recallAlertTypeWidget(taskModel: taskProvider.taskRequestModel!), + ], + // if (previousComments.isNotEmpty) ...[ + // 'Previous Comments'.bodyText2(context).custom(color: AppColor.neutral50), + // 8.height, + buildPreviousComments(taskProvider: taskProvider), + // 8.height, + // ], + AppTextFormField( + initialValue: "", + labelText: context.translation.technicalComment, + textInputType: TextInputType.multiline, + backgroundColor: AppColor.neutral90, + showShadow: false, + alignLabelWithHint: true, + onChange: (value) { + taskProvider.taskRequestModel?.taskTimerModel?.comments = value; + }, + onSaved: (value) {}, + ), + 20.height, + MultiFilesPicker( + label: context.translation.attachFiles, + files: _files, + buttonColor: AppColor.black10, + onlyImages: false, + buttonIcon: 'image-plus'.toSvgAsset(color: AppColor.neutral120), + ), + ], + ).toShadowContainer(context), + 16.height, + const AssistantEmployeeCard().toShadowContainer(context, paddingObject: const EdgeInsets.symmetric(horizontal: 16)), + ], + ), + ).paddingOnly(bottom: 85), + FooterActionButton.footerContainer( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + AppFilledButton( + label: context.translation.save, + buttonColor: AppColor.white60, + textColor: AppColor.black10, + onPressed: () => _updateTask(context: context, status: 0), + ).expanded, + 12.width, + AppFilledButton( + label: context.translation.complete, + buttonColor: AppColor.primary10, + onPressed: () => _updateTask(context: context, status: 1), + ).expanded, + ], + ), + ), + ], + ), + ), + ) + : NoDataFound(message: context.translation.noDataFound).center, + ); + }); + } + + void _updateTask({required BuildContext context, required int status}) async { + TaskRequestProvider taskRequestProvider = Provider.of(context, listen: false); + TaskData? taskModel = taskRequestProvider.taskRequestModel; + taskModel?.statusValue = status; + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + if (validate(model: taskModel)) { + List attachement = []; + for (var item in _files) { + attachement.add(TaskJobAttachment(id: 0, name: ServiceRequestUtils.isLocalUrl(item.path) ? "${item.path.split("/").last}|${base64Encode(item.readAsBytesSync())}" : item.path)); + } + taskModel?.taskJobAttachments = attachement; + taskModel?.timerModelList?.forEach((timer) { + int durationInSecond = timer.endAt!.difference(timer.startAt!).inSeconds; + taskModel.taskJobActivityEngineerTimers?.add( + TaskJobActivityEngineerTimer( + id: 0, + startDate: timer.startAt!.toIso8601String(), + // Handle potential null + endDate: timer.endAt?.toIso8601String(), + // Handle potential null + totalWorkingHour: ((durationInSecond) / 60 / 60), + comment: timer.comments, + ), + ); + }); + await taskRequestProvider.updateTaskByEngineer().whenComplete(() async { + if (taskRequestProvider.stateCode == 200) { + if (status == 1) { + AllRequestsProvider allRequestsProvider = Provider.of(context, listen: false); + allRequestsProvider.reset(); + await allRequestsProvider.getAllRequests(context, typeTransaction: 6); + Navigator.pop(context); + Navigator.pop(context); + Navigator.pop(context); + } else { + await taskRequestProvider.getTaskById(id: widget.taskId, showLoading: false); + Navigator.pop(context); + Navigator.pop(context); + } + } else { + Navigator.pop(context); + } + }); + } + } + + Widget installationWidget({required TaskData taskModel}) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ADatePicker( + label: context.translation.installationDate, + hideShadow: true, + backgroundColor: AppColor.neutral90, + date: DateTime.tryParse(taskModel.installationDate ?? ""), + formatDateWithTime: false, + onDatePicker: (selectedDate) { + showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + ).then((selectedTime) { + // Handle the selected date and time here. + if (selectedTime != null) { + DateTime selectedDateTime = DateTime( + selectedDate.year, + selectedDate.month, + selectedDate.day, + selectedTime.hour, + selectedTime.minute, + ); + taskModel.installationDate = selectedDateTime.toIso8601String(); + setState(() {}); + } + }); + }, + ), + 8.height, + AppTextFormField( + labelText: context.translation.serialNo, + backgroundColor: AppColor.neutral90, + showShadow: false, + labelStyle: AppTextStyles.textFieldLabelStyle, + initialValue: taskModel.serialNo ?? '', + textInputType: TextInputType.text, + onChange: (value) { + taskModel.serialNo = value; + }, + ), + 8.height, + SingleItemDropDownMenu( + context: context, + title: 'Installation Site', + initialValue: taskModel.site, + loading: _taskProvider?.isSiteLoading, + showAsBottomSheet: true, + backgroundColor: AppColor.neutral100, + showShadow: false, + enabled: false, + onSelect: (value) { + if (value == null) { + return; + } + taskModel.site = value; + taskModel.building = null; + taskModel.floor = null; + taskModel.department = null; + setState(() {}); + }, + ), + 8.height, + SingleItemDropDownMenu( + context: context, + title: 'Installation Building', + backgroundColor: AppColor.neutral100, + showAsBottomSheet: true, + loading: _taskProvider?.isSiteLoading, + showShadow: false, + initialValue: taskModel.building, + enabled: taskModel.site?.buildings?.isNotEmpty ?? false, + staticData: taskModel.site?.buildings ?? [], + onSelect: (value) { + if (value == null) { + return; + } + taskModel.building = value; + taskModel.floor = null; + taskModel.department = null; + setState(() {}); + }, + ), + 8.height, + SingleItemDropDownMenu( + context: context, + showAsBottomSheet: true, + backgroundColor: AppColor.neutral100, + loading: _taskProvider?.isSiteLoading, + showShadow: false, + title: 'Installation Floor', + initialValue: taskModel.floor, + enabled: taskModel.building?.floors?.isNotEmpty ?? false, + staticData: taskModel.building?.floors ?? [], + onSelect: (value) { + if (value == null) { + return; + } + taskModel.floor = value; + taskModel.department = null; + setState(() {}); + }, + ), + 8.height, + SingleItemDropDownMenu( + context: context, + title: 'Installation Department', + backgroundColor: AppColor.neutral100, + loading: _taskProvider?.isSiteLoading, + showAsBottomSheet: true, + showShadow: false, + initialValue: taskModel.department, + enabled: taskModel.floor?.departments?.isNotEmpty ?? false, + staticData: taskModel.floor?.departments ?? [], + onSelect: (value) { + if (value == null) { + return; + } + taskModel.department = value; + taskModel.room = null; + setState(() {}); + }, + ), + ], + ); + } + + Widget recallAlertTypeWidget({required TaskData taskModel}) { + Widget userAcknowledgeWidget() { + return Row( + children: [ + InkWell( + child: taskModel.isUserAcknowledge == true + ? const Icon( + Icons.check_box, + color: AppColor.primary10, + ) + : const Icon( + Icons.check_box_outline_blank, + color: AppColor.neutral120, + ), + onTap: () { + setState(() { + taskModel.isUserAcknowledge = !taskModel.isUserAcknowledge!; + }); + }, + ), + 6.width, + Flexible( + child: context.translation.acknowledge.bodyText(context).custom(color: context.isDark ? AppColor.primary50 : AppColor.neutral120), + ), + ], + ); + } + + if (taskModel.typeOfAlert != null) { + switch (taskModel.typeOfAlert!.value) { + case 1: + return userAcknowledgeWidget(); + case 2: + return Column( + children: [ + SingleItemDropDownMenu( + context: context, + height: 56.toScreenHeight, + title: context.translation.completedActions, + showShadow: false, + backgroundColor: AppColor.neutral90, + enabled: false, + showAsBottomSheet: true, + initialValue: taskModel.actionNeeded, + onSelect: (status) { + //read only ... + }, + ), + if (taskModel.actionNeeded?.value == 1) ...[ + 8.height, + SingleItemDropDownMenu( + context: context, + height: 56.toScreenHeight, + title: context.translation.impactStatus, + showShadow: false, + backgroundColor: AppColor.neutral90, + showAsBottomSheet: true, + initialValue: taskModel.impactStatus, + onSelect: (status) { + if (status != null) { + taskModel.impactStatus = status; + setState(() {}); + } + }, + ) + ], + ], + ); + + case 3: + return Column( + children: [ + userAcknowledgeWidget(), + 8.height, + SingleItemDropDownMenu( + context: context, + height: 56.toScreenHeight, + title: context.translation.completedActions, + showShadow: false, + backgroundColor: AppColor.neutral90, + enabled: false, + showAsBottomSheet: true, + initialValue: taskModel.actionNeeded, + onSelect: (status) { + //this is read only ... + }, + ), + 8.height, + if (taskModel.actionNeeded?.value == 1) ...[ + SingleItemDropDownMenu( + context: context, + height: 56.toScreenHeight, + title: context.translation.impactStatus, + showShadow: false, + backgroundColor: AppColor.neutral90, + showAsBottomSheet: true, + initialValue: taskModel.impactStatus, + onSelect: (status) { + if (status != null) { + taskModel.impactStatus = status; + setState(() {}); + } + }, + ) + ], + ], + ); + default: + return const SizedBox(); + } + } + return const SizedBox(); + } + + Widget _timerWidget(BuildContext context, bool isTimerEnable, TaskRequestProvider taskProvider) { + double totalWorkingHours = + taskProvider.taskRequestModel?.taskJobActivityEngineerTimers?.fold(0.0, (sum, item) => (sum ?? 0) + DateTime.parse(item.endDate!).difference(DateTime.parse(item.startDate!)).inSeconds) ?? 0; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AppTimer( + label: context.translation.timer, + timer: taskProvider.taskRequestModel?.taskTimerModel, + width: double.infinity, + enabled: isTimerEnable, + decoration: BoxDecoration( + color: AppColor.neutral90, + borderRadius: BorderRadius.circular(10), + ), + timerProgress: (isRunning) {}, + onChange: (timer) async { + taskProvider.updateTaskTimer(timer: timer); + return true; + }, + ), + if (totalWorkingHours > 0.0) ...[ + 8.height, + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + 'Total Working Time:'.bodyText2(context).custom(color: AppColor.neutral50), + 8.width, + Text( + ServiceRequestUtils.formatTimerDuration(totalWorkingHours.round()), + style: AppTextStyles.bodyText.copyWith(color: AppColor.neutral50, fontWeight: FontWeight.w600), + ), + ], + ), + ], + 8.height, + ], + ); + } + + Widget buildPreviousComments({required TaskRequestProvider taskProvider}) { + String previousComments = taskProvider.taskRequestModel?.taskJobActivityEngineerTimers?.map((e) => e.comment?.trim()).where((comment) => comment != null && comment!.isNotEmpty).join('\n') ?? ''; + if (previousComments.isNotEmpty) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 8.height, + 'Previous Comments'.bodyText2(context).custom(color: AppColor.neutral50), + 8.height, + Container( + constraints: BoxConstraints( + maxHeight: 200.toScreenHeight, + ), + child: SingleChildScrollView( + child: previousComments.bodyText2(context).custom(color: AppColor.neutral50), + ), + ).toShadowContainer(showShadow: false, context, backgroundColor: AppColor.neutral90, paddingObject: const EdgeInsets.only(left: 10, right: 20, bottom: 20, top: 10)), + 8.height, + ], + ); + } + return const SizedBox(); + } +} + +bool validate({TaskData? model}) { + if (model?.taskTimerModel?.startAt == null) { + Fluttertoast.showToast(msg: "Working Hours Required"); + return false; + } + if (model?.taskTimerModel?.endAt == null) { + Fluttertoast.showToast(msg: "Please Stop The Timer"); + return false; + } + return true; +} + +class AssistantEmployeeCard extends StatefulWidget { + const AssistantEmployeeCard({super.key}); + + @override + State createState() => _AssistantEmployeeCardState(); +} + +class _AssistantEmployeeCardState extends State { + bool status = false; + final TextEditingController _workingHoursController = TextEditingController(text: ''); + bool isExpanded = false; + TaskData? taskModel; + + @override + void initState() { + TaskRequestProvider taskRequestProvider = Provider.of(context, listen: false); + taskModel = taskRequestProvider.taskRequestModel; + + super.initState(); + } + + @override + void dispose() { + // TODO: implement dispose + + _workingHoursController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Consumer(builder: (context, taskRequestProvider, child) { + return Column( + children: [ + SizedBox( + height: 56.toScreenHeight, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + context.translation.assistantEmployee.heading6(context).custom(color: AppColor.black10), + Icon(isExpanded ? Icons.keyboard_arrow_up_rounded : Icons.keyboard_arrow_down_rounded), + ], + ), + ).onPress(() { + setState(() { + isExpanded = !isExpanded; + }); + }), + isExpanded + ? Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ServiceReportAssistantEmployeeMenu( + title: context.translation.select, + backgroundColor: AppColor.neutral100, + initialValue: (taskModel?.assistantEmployees?.isNotEmpty ?? false) ? taskModel?.assistantEmployees?.first : null, + onSelect: (employee) { + if (employee == null) { + taskModel?.assistantEmployees = []; + } else { + taskModel?.assistantEmployees = [employee.copyWith(id: 0)]; + taskModel?.modelAssistantEmployees?.user = AssignedEmployee(userId: employee.user?.id, userName: employee.user?.name); + } + }, + ), + 8.height, + Row( + mainAxisSize: MainAxisSize.min, + children: [ + ADatePicker( + label: context.translation.startTime, + hideShadow: true, + backgroundColor: AppColor.neutral100, + date: taskModel?.modelAssistantEmployees?.startDate, + formatDateWithTime: true, + onDatePicker: (selectedDate) { + showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + ).then((selectedTime) { + // Handle the selected date and time here. + if (selectedTime != null) { + DateTime selectedDateTime = DateTime( + selectedDate.year, + selectedDate.month, + selectedDate.day, + selectedTime.hour, + selectedTime.minute, + ); + taskModel?.modelAssistantEmployees?.startDate = selectedDateTime; + taskRequestProvider.updateTaskModel(taskModel); + ServiceRequestUtils.calculateAndAssignWorkingHours( + startTime: taskModel?.modelAssistantEmployees?.startDate, + endTime: taskModel?.modelAssistantEmployees?.startDate, + workingHoursController: _workingHoursController, + updateModel: (hours) { + taskModel!.modelAssistantEmployees!.workingHours = hours; + }); + } + }); + }, + ).expanded, + 8.width, + ADatePicker( + label: context.translation.endTime, + hideShadow: true, + backgroundColor: AppColor.neutral100, + date: taskModel?.modelAssistantEmployees?.endDate, + formatDateWithTime: true, + onDatePicker: (selectedDate) { + showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + ).then((selectedTime) { + // Handle the selected date and time here. + if (selectedTime != null) { + DateTime selectedDateTime = DateTime( + selectedDate.year, + selectedDate.month, + selectedDate.day, + selectedTime.hour, + selectedTime.minute, + ); + + if (taskModel?.modelAssistantEmployees?.startDate != null && selectedDateTime.isBefore(taskModel!.modelAssistantEmployees!.startDate!)) { + "End Date time must be greater then start date".showToast; + return; + } + taskModel?.modelAssistantEmployees?.endDate = selectedDateTime; + taskRequestProvider.updateTaskModel(taskModel); + ServiceRequestUtils.calculateAndAssignWorkingHours( + startTime: taskModel?.modelAssistantEmployees?.startDate, + endTime: taskModel?.modelAssistantEmployees?.endDate, + workingHoursController: _workingHoursController, + updateModel: (hours) { + taskModel!.modelAssistantEmployees!.workingHours = hours; + }); + } + }); + }, + ).expanded, + ], + ), + 8.height, + AppTextFormField( + labelText: context.translation.workingHours, + backgroundColor: AppColor.neutral80, + controller: _workingHoursController, + suffixIcon: "clock".toSvgAsset(width: 20, color: context.isDark ? AppColor.neutral10 : null).paddingOnly(end: 16), + initialValue: taskModel?.modelAssistantEmployees?.workingHours != null ? taskModel!.modelAssistantEmployees!.workingHours.toString() : '', + textAlign: TextAlign.center, + labelStyle: AppTextStyles.textFieldLabelStyle, + enable: false, + showShadow: false, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, + AppTextFormField( + initialValue: taskModel?.modelAssistantEmployees?.comment, + labelText: context.translation.comment, + backgroundColor: AppColor.neutral100, + showShadow: false, + labelStyle: AppTextStyles.textFieldLabelStyle, + alignLabelWithHint: true, + textInputType: TextInputType.multiline, + onChange: (value) { + taskModel?.modelAssistantEmployees?.comment = value; + }, + onSaved: (value) { + taskModel?.modelAssistantEmployees?.comment = value; + }, + ), + 16.height, + ], + ) + : const SizedBox(), + ], + ); + }); + } +} diff --git a/lib/views/pages/user/tasks_request/task_request_item_view.dart b/lib/views/pages/user/tasks_request/task_request_item_view.dart index eca68f6a..fe0d7f7f 100644 --- a/lib/views/pages/user/tasks_request/task_request_item_view.dart +++ b/lib/views/pages/user/tasks_request/task_request_item_view.dart @@ -10,7 +10,8 @@ import 'package:test_sa/models/new_models/dashboard_detail.dart'; import 'package:test_sa/models/plan_preventive_visit/plan_preventive_visit_model.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/views/pages/user/ppm/ppm_work_order/recurrent_wo/recurrent_work_order_view.dart'; -import 'package:test_sa/views/pages/user/tasks_request/task_request_view.dart'; +import 'package:test_sa/views/pages/user/tasks_request/task_request_detail_view.dart'; +import 'package:test_sa/views/pages/user/tasks_request/task_request_form_view.dart'; import 'package:test_sa/views/widgets/requests/request_status.dart'; class TaskRequestItemView extends StatelessWidget { @@ -22,54 +23,65 @@ class TaskRequestItemView extends StatelessWidget { @override Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + StatusLabel( + label: requestDetails!.priority!, + textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails?.priority!), + backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails?.priority!), + ), + 8.width, + StatusLabel( + label: requestDetails!.status!, + textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails?.status!), + backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails?.status!), + ), + 1.width.expanded, + Text( + requestDetails!.date?.toServiceRequestCardFormat ?? "", + textAlign: TextAlign.end, + style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50), + ), + ], + ), + 8.height, + (requestDetails?.nameOfType ?? context.translation.taskRequest).heading5(context), + 8.height, + infoWidget(label: context.translation.requestNo, value: requestDetails!.requestNo, context: context), + infoWidget(label: context.translation.assetName, value: requestDetails!.assetName, context: context), + infoWidget(label: context.translation.assetNo, value: requestDetails!.assetNo, context: context), + infoWidget(label: context.translation.assetSN, value: requestDetails!.assetSN, context: context), + infoWidget(label: context.translation.site, value: requestDetails!.site, context: context), + 12.height, + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + context.translation.viewDetails, + style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)), + ), + 4.width, + Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14) + ], + ), + ], + ).toShadowContainer(context, withShadow: showShadow).onPress(() async { + Navigator.of(context).push(MaterialPageRoute( + builder: (_) => TaskRequestDetailsView( + taskId: requestDetails!.id!, + requestDetails: requestDetails, + ))); + }); + } - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - StatusLabel( - label: requestDetails!.priority!, - textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails?.priority!), - backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails?.priority!), - ), - 8.width, - StatusLabel( - label: requestDetails!.status!, - textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails?.status!), - backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails?.status!), - ), - 1.width.expanded, - Text( - requestDetails!.date?.toServiceRequestCardFormat ?? "", - textAlign: TextAlign.end, - style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50), - ), - ], - ), - 8.height, - (requestDetails?.nameOfType ?? context.translation.taskRequest).heading5(context), - 8.height, - '${context.translation.requestNo}: ${requestDetails!.requestNo}'.bodyText(context), - '${context.translation.assetName}: ${requestDetails!.assetName}'.bodyText(context), - '${context.translation.assetNo}: ${requestDetails!.assetNo}'.bodyText(context), - '${context.translation.assetSN}: ${requestDetails!.assetSN}'.bodyText(context), - 16.height, - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - context.translation.viewDetails, - style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)), - ), - 4.width, - Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14) - ], - ), - ], - ).toShadowContainer(context, withShadow: showShadow).onPress(() async { - Navigator.of(context).push(MaterialPageRoute(builder: (_) => TaskRequestForm(model: AssetTransfer()))); - }); + Widget infoWidget({required String label, String? value, required BuildContext context}) { + if (value != null && value.isNotEmpty) { + return '$label: $value'.bodyText(context); + } + return const SizedBox(); } } diff --git a/lib/views/pages/user/tasks_request/task_request_view.dart b/lib/views/pages/user/tasks_request/task_request_view.dart deleted file mode 100644 index 385d78ae..00000000 --- a/lib/views/pages/user/tasks_request/task_request_view.dart +++ /dev/null @@ -1,502 +0,0 @@ -import 'dart:io'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:test_sa/controllers/providers/api/user_provider.dart'; -import 'package:test_sa/extensions/context_extension.dart'; -import 'package:test_sa/extensions/int_extensions.dart'; -import 'package:test_sa/models/new_models/department.dart'; -import 'package:test_sa/extensions/text_extensions.dart'; -import 'package:test_sa/extensions/widget_extensions.dart'; -import 'package:test_sa/models/device/asset_transfer.dart'; -import 'package:test_sa/models/lookup.dart'; -import 'package:test_sa/models/new_models/work_order_detail_model.dart'; -import 'package:test_sa/models/timer_model.dart'; -import 'package:test_sa/new_views/app_style/app_color.dart'; -import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; -import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; -import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; -import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; -import 'package:test_sa/providers/loading_list_notifier.dart'; -import 'package:test_sa/providers/service_request_providers/last_situation_provider.dart'; -import 'package:test_sa/service_request_latest/service_request_detail_provider.dart'; -import 'package:test_sa/service_request_latest/utilities/service_request_utils.dart'; -import 'package:test_sa/service_request_latest/views/components/action_button/footer_action_button.dart'; -import 'package:test_sa/views/app_style/sizing.dart'; -import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; -import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; -import 'package:test_sa/views/widgets/loaders/loading_manager.dart'; -import 'package:test_sa/views/widgets/status/report/service_report_assistant_employee_menu.dart'; -import 'package:test_sa/views/widgets/timer/app_timer.dart'; - -class TaskRequestForm extends StatefulWidget { - final AssetTransfer model; - - const TaskRequestForm({Key? key, required this.model}) : super(key: key); - - @override - State createState() => _TaskRequestFormState(); -} - -class _TaskRequestFormState extends State { - final bool _isLoading = false; - final TextEditingController _requestedQuantityController = TextEditingController(); - final AssetTransfer _formModel = AssetTransfer(); - final GlobalKey _formKey = GlobalKey(); - final GlobalKey _scaffoldKey = GlobalKey(); - List _files = []; - bool installationTye = false; - - List completedActions = [ - Lookup(value: 0, name: 'Physical Check'), - Lookup(value: 1, name: 'Software Update'), - Lookup(value: 0, name: 'Hardware Update'), - ]; - List impactStatus = [ - Lookup(value: 0, name: 'Impacted'), - Lookup(value: 1, name: 'Not Impacted'), - ]; - Lookup selectedValue = Lookup(value: 0, name: 'Impacted'); - - @override - void initState() { - _formModel.fromDetails(widget.model); - _files = widget.model.receiverAttachments?.map((e) => File(e.attachmentName!)).toList() ?? []; - super.initState(); - } - - @override - void dispose() { - _requestedQuantityController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: DefaultAppBar(title: context.translation.taskRequest), - key: _scaffoldKey, - body: SafeArea( - child: LoadingManager( - isLoading: _isLoading, - isFailedLoading: false, - stateCode: 200, - onRefresh: () async {}, - child: Form( - key: _formKey, - child: Column( - children: [ - SingleChildScrollView( - padding: EdgeInsets.all(12 * AppStyle.getScaleFactor(context)), - child: Column( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - _timerWidget(context, 0, true), - 12.height, - installationTye - ? Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ADatePicker( - label: context.translation.installationDate, - hideShadow: true, - backgroundColor: AppColor.neutral90, - date: DateTime.now(), - formatDateWithTime: false, - onDatePicker: (selectedDate) { - // Handle the selected date and time here. - DateTime selectedDateTime = DateTime( - selectedDate.year, - selectedDate.month, - selectedDate.day, - ); - }, - ), - 12.height, - //TODO replace with provided lookup.. - SingleItemDropDownMenu( - context: context, - height: 56.toScreenHeight, - title: context.translation.serialNo, - showShadow: false, - backgroundColor: AppColor.neutral90, - showAsBottomSheet: true, - initialValue: null, - onSelect: (status) { - if (status != null) { - setState(() {}); - } - }, - ), - 12.height, - //TODO replace with provided lookup.. - SingleItemDropDownMenu( - context: context, - title: context.translation.department, - initialValue: Department(), - enabled: true, - backgroundColor: AppColor.neutral90, - showShadow: false, - staticData: [], - showAsBottomSheet: true, - onSelect: (value) {}, - ), - ], - ) - : Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - context.translation.completedActions.bodyText(context).custom(color: AppColor.white936), - completedActionWidget(), - 16.height, - context.translation.impactStatus.bodyText(context).custom(color: AppColor.white936), - impactStatusWidget(), - ]), - 12.height, - AppTextFormField( - initialValue: _formModel.receiverComment ?? "", - labelText: context.translation.technicalComment, - textInputType: TextInputType.multiline, - backgroundColor: AppColor.neutral90, - showShadow: false, - alignLabelWithHint: true, - onSaved: (value) { - _formModel.receiverComment = value; - }, - ), - 20.height, - MultiFilesPicker( - label: context.translation.attachFiles, - files: _files, - buttonColor: AppColor.black10, - onlyImages: false, - buttonIcon: 'image-plus'.toSvgAsset(color: AppColor.neutral120), - ), - ], - ).toShadowContainer(context), - 16.height, - const AssistantEmployeeCard().toShadowContainer(context, paddingObject: const EdgeInsets.symmetric(horizontal: 16)), - ], - ), - ).expanded, - FooterActionButton.footerContainer( - child: AppFilledButton( - buttonColor: AppColor.green70, - label: context.translation.markAsCompleted, - onPressed: () {}, - // buttonColor: AppColor.primary10, - ), - ), - ], - ), - ), - ), - ), - ); - } - - void updateTimer({TimerModel? timer}) { - _formModel.tbsTimer = timer; - // if (timer?.startAt != null && timer?.endAt != null) { - // _formModel.timerModelList = _formModel.timerModelList ?? []; - // _formModel.timerModelList!.add(timer!); - // } - // notifyListeners(); - } - - Widget completedActionWidget() { - return Column( - children: completedActions.asMap().entries.map((entry) { - int index = entry.key; - Lookup action = entry.value; - return Row( - children: [ - Checkbox( - value: action.value == 1, - activeColor: AppColor.blueStatus(context), - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - onChanged: (value) { - setState(() { - completedActions[index] = Lookup(value: value! ? 1 : 0, name: action.name); - }); - }, - ), - action.name! - .bodyText(context) - .custom( - color: context.isDark ? AppColor.primary50 : AppColor.neutral50, - ) - .expanded, - ], - ); - }).toList(), - ); - } - - Widget impactStatusWidget() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, // Aligns to the start - children: impactStatus.map((item) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Radio( - value: item, - groupValue: selectedValue, - activeColor: AppColor.blueStatus(context), - visualDensity: VisualDensity.compact, - // Removes extra spacing - onChanged: (value) { - selectedValue = value!; - setState(() {}); - }, - ), - Text( - item.name!, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: context.isDark ? AppColor.primary50 : AppColor.neutral50, - ), - ), - 16.width, // Adds spacing between items - ], - ); - }).toList(), - ); - } - - Widget _timerWidget(BuildContext context, double totalWorkingHours, bool isTimerEnable) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - AppTimer( - label: context.translation.workingHours, - timer: _formModel.tbsTimer, - width: double.infinity, - enabled: isTimerEnable, - decoration: BoxDecoration( - color: AppColor.neutral90, - borderRadius: BorderRadius.circular(10), - ), - timerProgress: (isRunning) {}, - onChange: (timer) async { - updateTimer(timer: timer); - return true; - }, - ), - if (totalWorkingHours > 0.0) ...[ - 12.height, - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - 'Total Working Time:'.bodyText2(context).custom(color: AppColor.neutral90), - 8.width, - Text( - ServiceRequestUtils.formatTimerDuration(totalWorkingHours.round()), - style: AppTextStyles.bodyText.copyWith(color: AppColor.neutral50, fontWeight: FontWeight.w600), - ), - ], - ), - ], - ], - ); - } -} - -class AssistantEmployeeCard extends StatefulWidget { - const AssistantEmployeeCard({super.key}); - - @override - State createState() => _AssistantEmployeeCardState(); -} - -class _AssistantEmployeeCardState extends State { - bool status = false; - final TextEditingController _workingHoursController = TextEditingController(text: ''); - bool isCurrentUserIsAssistantEmp = false; - bool isExpanded = false; - - @override - void initState() { - // TODO: implement initState - WidgetsBinding.instance.addPostFrameCallback((_) { - getInitialData(); - }); - super.initState(); - } - - Future getInitialData() async { - final user = Provider.of(context, listen: false).user!; - ServiceRequestDetailProvider requestDetailProvider = Provider.of(context, listen: false); - isCurrentUserIsAssistantEmp = (user.userID != requestDetailProvider.currentWorkOrder?.data?.assignedEmployee?.userId); - - // if (isCurrentUserIsAssistantEmp) { - // // _subWorkOrders.assistantEmployees = [widget.workOrder.assistantEmployees?.first?.copyWith(id: 0)]; - // } - } - - @override - void dispose() { - // TODO: implement dispose - _workingHoursController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Consumer(builder: (context, requestDetailProvider, child) { - return Column( - children: [ - SizedBox( - height: 56.toScreenHeight, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - context.translation.assistantEmployee.heading6(context).custom(color: AppColor.black10), - Icon(isExpanded ? Icons.keyboard_arrow_up_rounded : Icons.keyboard_arrow_down_rounded), - ], - ), - ).onPress(() { - setState(() { - isExpanded = !isExpanded; - }); - }), - isExpanded - ? Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ServiceReportAssistantEmployeeMenu( - title: context.translation.select, - backgroundColor: AppColor.neutral100, - - initialValue: null, - assetId: 23, - //TODO add check... - // enable: !isCurrentUserIsAssistantEmp, - onSelect: (employee) { - if (employee == null) { - requestDetailProvider.activityMaintenanceHelperModel?.assistantEmployees = []; - } else { - requestDetailProvider.activityMaintenanceHelperModel?.assistantEmployees = [employee.copyWith(id: 0)]; - requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees?.user = AssignedEmployee(userId: employee.user?.id, userName: employee.user?.name); - } - }, - ), - 12.height, - Row( - mainAxisSize: MainAxisSize.min, - children: [ - ADatePicker( - label: context.translation.startTime, - hideShadow: true, - backgroundColor: AppColor.neutral100, - date: requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees?.startDate, - formatDateWithTime: true, - onDatePicker: (selectedDate) { - showTimePicker( - context: context, - initialTime: TimeOfDay.now(), - ).then((selectedTime) { - // Handle the selected date and time here. - if (selectedTime != null) { - DateTime selectedDateTime = DateTime( - selectedDate.year, - selectedDate.month, - selectedDate.day, - selectedTime.hour, - selectedTime.minute, - ); - requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees?.startDate = selectedDateTime; - requestDetailProvider.updateActivityMaintenanceHelperModel(requestDetailProvider.activityMaintenanceHelperModel); - ServiceRequestUtils.calculateAndAssignWorkingHours( - startTime: requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees?.startDate, - endTime: requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees?.startDate, - workingHoursController: _workingHoursController, - updateModel: (hours) { - requestDetailProvider.activityMaintenanceHelperModel!.modelAssistantEmployees!.workingHours = hours; - }); - } - }); - }, - ).expanded, - 8.width, - ADatePicker( - label: context.translation.endTime, - hideShadow: true, - backgroundColor: AppColor.neutral100, - date: requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees?.endDate, - formatDateWithTime: true, - onDatePicker: (selectedDate) { - showTimePicker( - context: context, - initialTime: TimeOfDay.now(), - ).then((selectedTime) { - // Handle the selected date and time here. - if (selectedTime != null) { - DateTime selectedDateTime = DateTime( - selectedDate.year, - selectedDate.month, - selectedDate.day, - selectedTime.hour, - selectedTime.minute, - ); - //TODO need to replace with model attributes.. - // if (requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees?.startDate != null && - // selectedDateTime.isBefore(requestDetailProvider.activityMaintenanceHelperModel!.modelAssistantEmployees!.startDate!)) { - // "End Date time must be greater then start date".showToast; - // return; - // } - // requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees?.endDate = selectedDateTime; - // requestDetailProvider.updateActivityMaintenanceHelperModel(requestDetailProvider.activityMaintenanceHelperModel); - // ServiceRequestUtils.calculateAndAssignWorkingHours( - // startTime: requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees?.startDate, - // endTime: requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees?.endDate, - // workingHoursController: _workingHoursController, - // updateModel: (hours) { - // requestDetailProvider.activityMaintenanceHelperModel!.modelAssistantEmployees!.workingHours = hours; - // }); - } - }); - }, - ).expanded, - ], - ), - 12.height, - AppTextFormField( - labelText: context.translation.workingHours, - backgroundColor: AppColor.neutral80, - controller: _workingHoursController, - suffixIcon: "clock".toSvgAsset(width: 20, color: context.isDark ? AppColor.neutral10 : null).paddingOnly(end: 16), - initialValue: requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees?.workingHours != null - ? requestDetailProvider.activityMaintenanceHelperModel!.modelAssistantEmployees!.workingHours.toString() - : '', - textAlign: TextAlign.center, - labelStyle: AppTextStyles.textFieldLabelStyle, - enable: false, - showShadow: false, - style: Theme.of(context).textTheme.titleMedium, - ), - 12.height, - AppTextFormField( - initialValue: requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees?.technicalComment, - labelText: context.translation.technicalComment, - backgroundColor: AppColor.neutral100, - showShadow: false, - labelStyle: AppTextStyles.textFieldLabelStyle, - alignLabelWithHint: true, - textInputType: TextInputType.multiline, - onChange: (value) { - requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees?.technicalComment = value; - }, - onSaved: (value) { - requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees?.technicalComment = value; - }, - ), - 16.height, - ], - ) - : const SizedBox(), - ], - ); - }); - } -} diff --git a/lib/views/widgets/status/report/service_report_assistant_employee_menu.dart b/lib/views/widgets/status/report/service_report_assistant_employee_menu.dart index 533faace..0dab25cc 100644 --- a/lib/views/widgets/status/report/service_report_assistant_employee_menu.dart +++ b/lib/views/widgets/status/report/service_report_assistant_employee_menu.dart @@ -11,10 +11,10 @@ class ServiceReportAssistantEmployeeMenu extends StatelessWidget { final AssistantEmployees? initialValue; // Now nullable Color? backgroundColor; final String title; - final num assetId; + final num ?assetId; final bool enable; - ServiceReportAssistantEmployeeMenu({Key? key, required this.onSelect, required this.title, required this.initialValue, required this.assetId, this.backgroundColor, this.enable = true}) + ServiceReportAssistantEmployeeMenu({Key? key, required this.onSelect, required this.title, required this.initialValue, this.assetId, this.backgroundColor, this.enable = true}) : super(key: key); @override @@ -25,7 +25,7 @@ class ServiceReportAssistantEmployeeMenu extends StatelessWidget { isFailedLoading: menuProvider.assistantEmployees == null, stateCode: menuProvider.stateCode, onRefresh: () async { - await menuProvider.getAssistantEmployees(assetId); + await menuProvider.getAssistantEmployees(assetId:assetId); }, child: AssistantEmployeeMenu( initialStatus: initialValue,