task request module flow completed and tested with backend

design_3.0_task_module_new
WaseemAbbasi22 6 months ago
parent 042760e1c9
commit 9edaca4326

@ -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";

@ -50,11 +50,17 @@ class ServiceReportAssistantsEmployeeProvider extends ChangeNotifier {
/// lib\controllers\http_status_manger\http_status_manger.dart
///
Future<int> getAssistantEmployees(num assetId) async {
Future<int> 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;

@ -555,6 +555,11 @@
"createTaskRequest": "إنشاء طلب مهمة",
"taskRequest": "طلب مهمة",
"completedActions": "الإجراءات المكتملة",
"impactStatus": "حالة التأثير"
"impactStatus": "حالة التأثير",
"installationSite": "موقع التركيب",
"installationBuilding": "مبنى التركيب",
"installationFloor": "طابق التركيب",
"installationDepartment": "قسم التركيب",
"acknowledge": "إقرار"
}

@ -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"
}

@ -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()),

@ -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<String, dynamic> 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<TaskJobContactPerson>? 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<TaskJobAttachment>? taskJobAttachments;
final User? assignedEngineer;
final List<dynamic>? taskJobAssistantEmployees;
final List<dynamic>? taskJobActivityEngineerTimers;
final List<TaskJobHistory>? 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<TaskJobContactPerson>? taskJobContactPersons;
TaskTypeModel? taskType;
TaskJobStatus? taskJobStatus;
TaskAsset? asset;
Site? site;
Building? building;
Floor? floor;
Department? department;
Rooms? room;
String? callComment;
List<TaskJobAttachment>? taskJobAttachments;
User? assignedEngineer;
List<TaskJobAssistantEmployees>? taskJobAssistantEmployees;
TaskJobAssistantEmployees? modelAssistantEmployees;
List<AssistantEmployees>? assistantEmployees;
List<TaskJobHistory>? 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<TaskJobActivityEngineerTimer>? taskJobActivityEngineerTimers = [];
TimerModel? taskTimerModel = TimerModel();
double? totalWorkingHours = 0.0;
List<TimerModel>? 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<String, dynamic> 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<String, dynamic> json) {
id = json['id'];
taskJobNo = json['taskJobNo'];
userCreated = json['userCreated'] != null ? User.fromJson(json['userCreated']) : null;
if (json['taskJobContactPersons'] != null) {
taskJobContactPersons = <TaskJobContactPerson>[];
json['taskJobContactPersons'].forEach((v) {
taskJobContactPersons!.add(TaskJobContactPerson.fromJson(v));
});
}
if (json['attachments'] != null) {
taskJobAttachments = <TaskJobAttachment>[];
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 = <TaskJobAttachment>[];
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 = <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 = <TaskJobActivityEngineerTimer>[];
json['taskJobActivityEngineerTimers'].forEach((v) {
taskJobActivityEngineerTimers!.add(TaskJobActivityEngineerTimer.fromJson(v));
});
}
if (json['taskJobHistories'] != null) {
taskJobHistories = <TaskJobHistory>[];
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<String, dynamic> 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<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
Map<String, dynamic> 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<String, dynamic> toEngineerUpdateJson() {
final Map<String, dynamic> data = <String, dynamic>{};
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<String, dynamic> 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<String, dynamic> 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<String, dynamic> 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<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
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<String, dynamic> json) {
return AssistantEngineer(userId: json['userId'], userName: json['userName'], email: json['email'], employeeId: json['employeeId'], languageId: json['languageId']);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
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<String, dynamic> 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<String, dynamic> 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,
};
}
}

@ -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<String, dynamic> json) {
return Site(
factory WorkOrderSite.fromJson(Map<String, dynamic> json) {
return WorkOrderSite(
id: json["id"],
siteName: json["siteName"],
);

@ -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});
}

@ -167,7 +167,7 @@ class _SingleItemDropDownMenuState<T extends Base, X extends LoadingListNotifier
? () {
openDialog();
}
: (widget.showAsBottomSheet
:widget.enabled? (widget.showAsBottomSheet
? () async {
final selectedT = await showModalBottomSheet<T?>(
// Specify return type
@ -194,7 +194,7 @@ class _SingleItemDropDownMenuState<T extends Base, X extends LoadingListNotifier
widget.onSelect!(selectedT); // Non-null assertion after null check
}
}
: null)),
: null):null),
],
),
],

@ -163,6 +163,31 @@ class TaskJobActionNeededProvider extends LoadingListNotifier<Lookup> {
}
}
}
class TaskJobImpactStatusProvider extends LoadingListNotifier<Lookup> {
@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;
}
}
}

@ -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<Site>? siteList;
void updateTaskModel(TaskData? value) {
taskRequestModel = value;
notifyListeners();
}
Future<void> 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<int> 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();
}
}

@ -192,6 +192,7 @@ class _RequestGasRefillState extends State<RequestGasRefill> {
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);

@ -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<CreateTaskView> with TickerProviderStateMixin {
final List<File> _deviceImages = [];
final List<File> _deviceImages = [];
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
//TODO need to replace with model attribute
Asset? _device;
List<Asset> _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<CreateTaskView> 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<CreateTaskView> 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<CreateTaskView> 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<CreateTaskView> with TickerProviderStat
},
),
8.height,
SingleItemDropDownMenu<Lookup, TaskJobActionNeededProvider>(
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<Lookup, TaskJobActionNeededProvider>(
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<TaskEvaluatorUser, TaskEvaluatorUserProvider>(
context: context,
@ -319,34 +319,36 @@ class _CreateTaskViewState extends State<CreateTaskView> 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<CreateTaskView> 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 = <num?>{};
_deviceList = _deviceList.where((item) => seenIds.add(item.id)).toList();
setState(() {});
},
);
}
bool checkPendingRequest = false;
Future<void> checkAssetForPendingServiceRequest(int assetId) async {
checkPendingRequest = true;
setState(() {});
pendingAssetServiceRequest = await Provider.of<ServiceRequestsProvider>(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 = <num?>{};
_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<void> _submit() async {
@ -453,10 +429,20 @@ class _CreateTaskViewState extends State<CreateTaskView> 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<TaskRequestProvider>(context,listen: false);
TaskRequestProvider taskRequestProvider = Provider.of<TaskRequestProvider>(context, listen: false);
taskRequestProvider.addTask(context: context, task: _addTaskModel!);
}
}

@ -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<TaskRequestDetailsView> {
UserProvider? userProvider;
TaskRequestProvider? taskProvider;
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
getTaskData();
});
super.initState();
}
@override
void dispose() {
super.dispose();
}
Future<void> getTaskData() async {
taskProvider = Provider.of<TaskRequestProvider>(context, listen: false);
await taskProvider?.getTaskById(id: widget.taskId);
}
@override
Widget build(BuildContext context) {
userProvider ??= Provider.of<UserProvider>(context, listen: false);
return Scaffold(
appBar: DefaultAppBar(title: context.translation.taskRequest),
body: SafeArea(
child: Consumer<TaskRequestProvider>(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();
}
}

@ -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<TaskRequestForm> createState() => _TaskRequestFormState();
}
class _TaskRequestFormState extends State<TaskRequestForm> {
TaskRequestProvider? _taskProvider;
final TextEditingController _requestedQuantityController = TextEditingController();
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
List<File> _files = [];
bool installationType = true;
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
assignData();
});
super.initState();
}
Future<void> assignData() async {
_taskProvider = Provider.of<TaskRequestProvider>(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<TaskRequestProvider>(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<TaskRequestProvider>(context, listen: false);
TaskData? taskModel = taskRequestProvider.taskRequestModel;
taskModel?.statusValue = status;
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
if (validate(model: taskModel)) {
List<TaskJobAttachment> 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<AllRequestsProvider>(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<Site, SiteProvider>(
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<Building, NullableLoadingProvider>(
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<Floor, NullableLoadingProvider>(
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<Department, NullableLoadingProvider>(
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<Lookup, TaskJobActionNeededProvider>(
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<Lookup, TaskJobImpactStatusProvider>(
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<Lookup, TaskJobActionNeededProvider>(
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<Lookup, TaskJobImpactStatusProvider>(
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<AssistantEmployeeCard> createState() => _AssistantEmployeeCardState();
}
class _AssistantEmployeeCardState extends State<AssistantEmployeeCard> {
bool status = false;
final TextEditingController _workingHoursController = TextEditingController(text: '');
bool isExpanded = false;
TaskData? taskModel;
@override
void initState() {
TaskRequestProvider taskRequestProvider = Provider.of<TaskRequestProvider>(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<TaskRequestProvider>(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(),
],
);
});
}
}

@ -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();
}
}

@ -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<TaskRequestForm> createState() => _TaskRequestFormState();
}
class _TaskRequestFormState extends State<TaskRequestForm> {
final bool _isLoading = false;
final TextEditingController _requestedQuantityController = TextEditingController();
final AssetTransfer _formModel = AssetTransfer();
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
List<File> _files = [];
bool installationTye = false;
List<Lookup> completedActions = [
Lookup(value: 0, name: 'Physical Check'),
Lookup(value: 1, name: 'Software Update'),
Lookup(value: 0, name: 'Hardware Update'),
];
List<Lookup> 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<Lookup, LastSituationProvider>(
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<Department, NullableLoadingProvider>(
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<Lookup>(
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<AssistantEmployeeCard> createState() => _AssistantEmployeeCardState();
}
class _AssistantEmployeeCardState extends State<AssistantEmployeeCard> {
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<void> getInitialData() async {
final user = Provider.of<UserProvider>(context, listen: false).user!;
ServiceRequestDetailProvider requestDetailProvider = Provider.of<ServiceRequestDetailProvider>(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<ServiceRequestDetailProvider>(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(),
],
);
});
}
}

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

Loading…
Cancel
Save