Merge branch 'refs/heads/design_3.0_TM_Module' into design_3.0_task_module_new_merge

# Conflicts:
#	lib/modules/cm_module/service_request_detail_provider.dart
#	lib/new_views/pages/land_page/land_page.dart
#	lib/views/pages/device_transfer/update_device_transfer.dart
design_3.0_TM_Module_snagsFix
Sikander Saleem 3 months ago
commit ca1ff5892e

@ -19,6 +19,7 @@ class ApiManager {
Map<String, String> get _headers => {
'Content-Type': 'application/json',
'X-Timezone-Offset': DateTime.now().timeZoneOffset.toString().split(".").first,
if (user != null) 'Authorization': 'Bearer ${user!.token}',
if (assetGroup != null) 'AssetGroup': assetGroup!.id.toString(),
};
@ -156,7 +157,6 @@ class ApiManager {
headers.addAll(_headers);
Uri url0 = Uri.parse(url);
print(url0);
// print(headers);
// log(json.encode(body));
var request = http.Request('PUT', url0);

@ -1,7 +1,7 @@
class URLs {
URLs._();
static const String appReleaseBuildNumber = "19";
static const String appReleaseBuildNumber = "21";
// static const host1 = "https://atomsm.hmg.com"; // production url
// static const host1 = "https://atomsmdev.hmg.com"; // local DEV url
@ -219,6 +219,8 @@ class URLs {
static get getServiceReportReasonsNew => "$_baseUrl/Lookups/GetLookupReasonNew?lookupEnum=505";
static get getWoFrames => "$_baseUrl/Lookups/GetLookup?lookupEnum=1254";
static get getServiceReportRetirementType => "$_baseUrl/Lookups/GetLookup?lookupEnum=415";
static get getVisitReminderTimeValue => "$_baseUrl/Lookups/GetLookup?lookupEnum=1211";

@ -204,7 +204,6 @@ class FirebaseNotificationManger {
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('notification i got is ${message.toMap()}');
if (Platform.isAndroid) {
if (message.data["notificationType"] != 'NurseConfirmArrive') {
NotificationManger.showNotification(

@ -31,7 +31,6 @@ class NotificationManger {
} else if (Platform.isAndroid) {
final AndroidFlutterLocalNotificationsPlugin? androidImplementation = localNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>();
final bool granted = await androidImplementation?.requestNotificationsPermission() ?? false;
print('permission value is $granted');
if (!granted) {
if (kDebugMode) {
print("-------------------- Permission Granted ------------------------");
@ -43,7 +42,8 @@ class NotificationManger {
await localNotificationsPlugin.initialize(initializationSettings, onDidReceiveNotificationResponse: onNotificationPressed);
} // push new notification
static const AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails(
static const AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails(
'com.hmg.atoms',
'ATOMS',
channelDescription: 'Push notification service for ATOMS',
@ -76,7 +76,6 @@ class NotificationManger {
// groupKey: 'com.hmg.atoms',
// );
// const DarwinNotificationDetails iosNotificationDetails = DarwinNotificationDetails(
// categoryIdentifier: "atoms",
// );
@ -86,16 +85,9 @@ class NotificationManger {
await localNotificationsPlugin.show(hashcode, title, subtext, platformChannel, payload: payload);
}
static Future scheduleNotification(
{int?id,
String? title,
String? body,
String? payLoad,
required DateTime scheduledNotificationDateTime}) async {
print('time i got is ${scheduledNotificationDateTime}');
static Future scheduleNotification({int? id, String? title, String? body, String? payLoad, required DateTime scheduledNotificationDateTime}) async {
return localNotificationsPlugin.zonedSchedule(
id??0,
id ?? 0,
title,
body,
// tz.TZDateTime.now(tz.local).add(const Duration(seconds: 5)),
@ -103,13 +95,12 @@ class NotificationManger {
scheduledNotificationDateTime,
tz.local,
),
const NotificationDetails(android: androidNotificationDetails, iOS: iosNotificationDetails, macOS: iosNotificationDetails),
const NotificationDetails(android: androidNotificationDetails, iOS: iosNotificationDetails, macOS: iosNotificationDetails),
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime);
uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime);
}
static Future<void> cancelNotificationById(int notificationId) async {
await localNotificationsPlugin.cancel(notificationId);
print("Notification with ID $notificationId has been canceled.");
}
}

@ -143,7 +143,7 @@ class AllRequestsProvider extends ChangeNotifier {
}
final type = typeTransaction == null
? search?.typeTransaction == null || (search?.typeTransaction?.isEmpty ?? false)
? [1, 2, 3, 4, 5,6] //added 6 to get task request ...
? [1, 2, 3, 4, 5, 6] //added 6 to get task request ...
: search!.typeTransaction
: [typeTransaction];
List<int> status = (search?.statuses == null || (search?.statuses?.isEmpty ?? false)) ? (((search?.isArchived ?? false) ? [3] : [1, 2, 4])) : search!.statuses!;
@ -222,7 +222,7 @@ class AllRequestsProvider extends ChangeNotifier {
}
}
Future<int> updateRecurrentWo({required int status}) async {
Future<bool> updateRecurrentWo({required int status}) async {
isLoading = true;
Response response;
try {
@ -230,12 +230,15 @@ class AllRequestsProvider extends ChangeNotifier {
stateCode = response.statusCode;
isLoading = false;
notifyListeners();
return response.statusCode;
if (stateCode == 200) {
return true;
}
return false;
} catch (error) {
isLoading = false;
stateCode = -1;
notifyListeners();
return -1;
return false;
}
}

@ -47,15 +47,12 @@ class GasRefillProvider extends ChangeNotifier {
// failed _loading = false
bool isLoading = false;
Future<GasRefillModel?> getGasRefillObjectById(num id) async {
try {
Response response = await ApiManager.instance.get(URLs.getGasRefillById + "?gasRefillId=$id");
if (response.statusCode >= 200 && response.statusCode < 300) {
return GasRefillModel.fromJson(json.decode(response.body)["data"]);
return GasRefillModel.fromJson(json.decode(response.body)["data"]);
} else {
return null;
}
@ -64,7 +61,6 @@ class GasRefillProvider extends ChangeNotifier {
}
}
/// return -2 if request in progress
/// return -1 if error happen when sending request
/// return state code if request complete may be 200, 404 or 403
@ -139,6 +135,7 @@ class GasRefillProvider extends ChangeNotifier {
print(error);
}
}
Future<void> updateGasRefillRequestByNurse({
required BuildContext context,
required GasRefillModel model,
@ -167,7 +164,7 @@ class GasRefillProvider extends ChangeNotifier {
}
}
Future<int> updateGasRefill({required int status, required GasRefillModel model}) async {
Future<bool> updateGasRefill({required int status, required GasRefillModel model}) async {
isLoading = true;
Response response;
try {
@ -175,12 +172,15 @@ class GasRefillProvider extends ChangeNotifier {
stateCode = response.statusCode;
isLoading = false;
notifyListeners();
return response.statusCode;
if (stateCode == 200) {
return true;
}
return false;
} catch (error) {
isLoading = false;
stateCode = -1;
notifyListeners();
return -1;
return false;
}
}

@ -52,7 +52,6 @@ class NotificationsProvider extends ChangeNotifier {
response = await ApiManager.instance.post(URLs.getSystemNotifications, body: body);
stateCode = response.statusCode;
print('notifaction response is ${response.body}');
if (response.statusCode >= 200 && response.statusCode < 300) {
// client's request was successfully received
List requestsListJson = json.decode(response.body)["data"];

@ -182,21 +182,23 @@ class PpmProvider extends ChangeNotifier {
}
}
Future<int> updateVisitByEngineer({required int status}) async {
Future<bool> updateVisitByEngineer({required int status}) async {
isLoading = true;
Response response;
try {
response = await ApiManager.instance.post(URLs.updateVisitByEngineer, body: planPreventiveVisit!.toJson(status: status));
print('response i got is ${response.body}');
stateCode = response.statusCode;
isLoading = false;
notifyListeners();
return response.statusCode;
if (stateCode == 200) {
return true;
}
return false;
} catch (error) {
isLoading = false;
stateCode = -1;
notifyListeners();
return -1;
return false;
}
}

@ -7,7 +7,6 @@ import 'package:test_sa/controllers/api_routes/api_manager.dart';
import 'package:test_sa/controllers/api_routes/urls.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/service_request/pending_service_request_model.dart';
import 'package:test_sa/models/service_request/service_report.dart';
import 'package:test_sa/models/service_request/service_request.dart';
@ -268,7 +267,7 @@ class ServiceRequestsProvider extends ChangeNotifier {
Future<PendingAssetServiceRequest?> checkAssetPendingRequest(int assetId) async {
Response response;
try {
response = await ApiManager.instance.post(URLs.CheckIfAssetHasAnotherServiceRequest + "?assetId=$assetId",body: {});
response = await ApiManager.instance.post(URLs.CheckIfAssetHasAnotherServiceRequest + "?assetId=$assetId", body: {});
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {}
return PendingAssetServiceRequest.fromJson(json.decode(response.body)["data"]);
@ -423,7 +422,6 @@ class ServiceRequestsProvider extends ChangeNotifier {
Future<CallRequest?> getCallRequestForWorkOrder({required String callId}) async {
Response response;
print('call id i got is ${callId}');
try {
response = await ApiManager.instance.get(URLs.getCallRequestForWorkOrder + "?callId=$callId");
stateCode = response.statusCode;

@ -22,10 +22,7 @@ extension BuildContextExtension on BuildContext {
void showConfirmDialog(String message, {String? title, VoidCallback? onTap}) => showDialog(
context: this,
builder: (BuildContext cxt) => ConfirmDialog(
message: message,
onTap: onTap,
),
builder: (BuildContext cxt) => ConfirmDialog(message: message, onTap: onTap, title: title),
);
Future showBottomSheet(Widget childWidget, {bool? isDismissible, String? title}) => showModalBottomSheet(

@ -49,7 +49,19 @@ class Utils {
return null;
}
}
static String getOrdinal(int number) {
if (number >= 11 && number <= 13) return "${number}th";
switch (number % 10) {
case 1:
return "${number}st";
case 2:
return "${number}nd";
case 3:
return "${number}rd";
default:
return "${number}th";
}
}
static int stringToHex(String colorCode) {
try {
return int.parse(colorCode.replaceAll("#", "0xff"));

@ -71,6 +71,7 @@ import 'package:test_sa/providers/work_order/retirement_type_provider.dart';
import 'package:test_sa/providers/work_order/service_type_provider.dart';
import 'package:test_sa/providers/work_order/supplier_engineer_provider.dart';
import 'package:test_sa/providers/work_order/vendor_provider.dart';
import 'package:test_sa/providers/work_order/wo_frame_provider.dart';
import 'package:test_sa/views/pages/device_transfer/create__device_transfer_request.dart';
import 'package:test_sa/views/pages/device_transfer/device_filter_screen.dart';
import 'package:test_sa/views/pages/device_transfer/device_search_screen.dart';
@ -239,6 +240,7 @@ class MyApp extends StatelessWidget {
/// Loan availability not required
ChangeNotifierProvider(create: (_) => LoanAvailabilityProvider()),
ChangeNotifierProvider(create: (_) => ReasonProvider()),
ChangeNotifierProvider(create: (_) => WoFrameProvider()),
ChangeNotifierProvider(create: (_) => RejectReasonProvider()),
ChangeNotifierProvider(create: (_) => LastSituationProvider()),
ChangeNotifierProvider(create: (_) => FaultDescriptionProvider()),

@ -24,7 +24,7 @@ class AssetByIdModel {
int? testsDay;
num? purchasingPrice;
String? nbv;
String? currency;
Lookup? currency;
String? poDate;
String? poNo;
String? invoiceNumber;
@ -153,7 +153,7 @@ class AssetByIdModel {
testsDay = json['testsDay'];
purchasingPrice = json['purchasingPrice'];
nbv = json['nbv'];
currency = json['currency'];
currency = json['currency'] != null ? Lookup.fromJson(json['currency']) : null;
poDate = json['poDate'];
poNo = json['poNo'];
invoiceNumber = json['invoiceNumber'];
@ -247,7 +247,9 @@ class AssetByIdModel {
data['testsDay'] = testsDay;
data['purchasingPrice'] = purchasingPrice;
data['nbv'] = nbv;
data['currency'] = currency;
if (currency != null) {
data['currency'] = currency?.toJson();
}
data['poDate'] = poDate;
data['poNo'] = poNo;
data['invoiceNumber'] = invoiceNumber;

@ -1,6 +1,7 @@
import 'package:test_sa/models/device/asset.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/work_order_detail_model.dart';
import 'package:test_sa/models/ppm/ppm.dart';
import 'package:test_sa/models/timer_model.dart';
@ -89,8 +90,10 @@ class DeviceTransfer {
this.timerModelList,
this.assistantEmployees,
this.modelAssistantEmployees,
this.assistantEmployList,
this.assetTransferAssistantEmployeesReceiver,
this.assetTransferAssistantEmployeesSender,
this.statusValue,
});
@ -305,10 +308,11 @@ class DeviceTransfer {
List<VisitTimers>? assetTransferEngineerTimers;
List<TimerModel>? timerModelList = [];
List<AssistantEmployees>? assistantEmployees;
List<AssetTransferAssistantEmployees>? assetTransferAssistantEmployeesSender;
List<AssetTransferAssistantEmployees>? assetTransferAssistantEmployeesReceiver;
List<AssetTransferAssistantEmployees>? assetTransferAssistantEmployeesSender=[];
List<AssetTransferAssistantEmployees>? assetTransferAssistantEmployeesReceiver=[];
List<AssetTransferContactPerson>? assetTransferContactPersons;
AssetTransferAssistantEmployees? modelAssistantEmployees;
List<AssetTransferAssistantEmployees>? assistantEmployList=[];
TimerModel? tbsTimer = TimerModel();
TimerModel? deviceTimePicker;
@ -384,6 +388,7 @@ class DeviceTransfer {
String? destDepartmentName,
List<VisitTimers>? senderVisitTimers,
List<VisitTimers>? receiverVisitTimers,
List<AssetTransferAssistantEmployees>? assistantEmployList,
TimerModel? tbsTimer,
TimerModel? deviceTimePicker}) =>
DeviceTransfer(
@ -457,6 +462,7 @@ class DeviceTransfer {
senderVisitTimers: senderVisitTimers ?? this.senderVisitTimers,
receiverVisitTimers: receiverVisitTimers ?? this.receiverVisitTimers,
tbsTimer: tbsTimer ?? this.tbsTimer,
assistantEmployList: assistantEmployList??this.assistantEmployList,
deviceTimePicker: deviceTimePicker ?? this.deviceTimePicker,
manufacturerName: manufacturerName ?? this.manufacturerName);
@ -573,13 +579,18 @@ class DeviceTransfer {
if (assetTransferEngineerTimers != null) {
map['assetTransferEngineerTimers'] = assetTransferEngineerTimers!.map((v) => v.toJson()).toList();
}
if (modelAssistantEmployees != null) {
if (modelAssistantEmployees!.employeeId != null) {
map['assetTransferAssistantEmployees'] = [modelAssistantEmployees!.toJson()];
} else {
map['assetTransferAssistantEmployees'] = [];
}
if (assistantEmployList != null && assistantEmployList!.isNotEmpty) {
map['assetTransferAssistantEmployees'] = assistantEmployList;
} else {
map['assetTransferAssistantEmployees'] = [];
}
// if (modelAssistantEmployees != null) {
// if (modelAssistantEmployees!.employeeId != null) {
// map['assetTransferAssistantEmployees'] = [modelAssistantEmployees!.toJson()];
// } else {
// map['assetTransferAssistantEmployees'] = [];
// }
// }
return map;
}

@ -35,6 +35,7 @@ class ActivityMaintenanceHelperModel {
List<ActivityMaintenanceTimers>? activityMaintenanceTimers = [];
TimerModel? activityMaintenanceTimerModel = TimerModel();
TimerModel? activityTimePicker;
List<AssistantEmployeesModel>? assistantEmployList=[];
List<TimerModel>? timerModelList = [];
ActivityMaintenanceHelperModel(
@ -63,6 +64,7 @@ class ActivityMaintenanceHelperModel {
this.assignedEmployee,
this.activityMaintenanceTimers,
this.timerModelList,
this.assistantEmployList,
this.modelAssistantEmployees});
Map<String, dynamic> toJson() {
@ -85,11 +87,17 @@ class ActivityMaintenanceHelperModel {
data['supplierWorkingHour'] = supplierWorkingHour;
//TODO fix this properly...
data['activityMaintenanceTimers'] = activityMaintenanceTimers;
if (assistantEmployees != null && assistantEmployees!.isNotEmpty) {
data['assistantEmployees'] = [modelAssistantEmployees?.toJson()];
if (assistantEmployList != null && assistantEmployList!.isNotEmpty) {
data['assistantEmployees'] = assistantEmployList;
} else {
data['assistantEmployees'] = [];
}
//TODO need to remove this .. its not required now
// if (assistantEmployees != null && assistantEmployees!.isNotEmpty) {
// data['assistantEmployees'] = [modelAssistantEmployees?.toJson()];
// } else {
// data['assistantEmployees'] = [];
// }
return data;
}
}

@ -82,16 +82,19 @@ class EngineerUpdateWorkOrderHelperModel {
num? loanAssetId;
WorkOrderAsset? loanAsset;
Lookup? failureReason;
Lookup? cmFrameId;
FaultDescription? faultDescription;
String? solution;
String? callResponse;
String? descriptionOfFinding;
String? actionTaken;
String? edd;
EngineerUpdateWorkOrderHelperModel({
this.workOrderId,
this.equipmentStatus,
this.failureReason,
this.cmFrameId,
this.faultDescription,
this.loanAvailability,
this.loanAssetId,
@ -99,6 +102,7 @@ class EngineerUpdateWorkOrderHelperModel {
this.serviceType,
this.solution,
this.returnToService,
this.edd,
this.callResponse,
this.descriptionOfFinding,
this.actionTaken,
@ -109,9 +113,11 @@ class EngineerUpdateWorkOrderHelperModel {
data['workOrderId'] = workOrderId;
data['equipmentStatusId'] = equipmentStatus?.id;
data['returnToService'] = returnToService;
data['edd'] = edd;
data['loanAvailabilityId'] = loanAvailability?.id;
data['loanAssetId'] = loanAssetId;
data['failureReasonId'] = failureReason?.id;
data['cmFrameId'] = cmFrameId?.id;
data['faultDescriptionId'] = faultDescription?.id;
data['callResponse'] = callResponse;
data['descriptionOfFinding'] = descriptionOfFinding;

@ -152,7 +152,6 @@ class GasRefillModel {
floor = json['floor'] != null ? Floor.fromJson(json['floor']) : null;
department = json['department'] != null ? Department.fromJson(json['department']) : null;
mapSite = json['site'] != null ? MappedSite.fromJson(json['site']) : null;
print('site i got is ::${mapSite?.toJson()}');
mappedBuilding = mapSite?.buildings?.firstWhere((element) => element.identifier == building?.identifier, orElse: () => MappedBuilding());
mappedFloor = mappedBuilding?.floors?.firstWhere((element) => element.identifier == floor?.identifier, orElse: () => MappedFloor());
mappedDepartment = mappedFloor?.departments?.firstWhere((element) => element.identifier == department?.identifier, orElse: () => MappedDepartment());

@ -3,6 +3,7 @@ import 'package:test_sa/models/fault_description.dart';
import 'package:test_sa/models/helper_data_models/spare_part/activity_spare_part_model.dart';
import 'package:test_sa/models/helper_data_models/workorder/work_order_helper_models.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/models/new_models/assistant_employee.dart';
class WorkOrderDetail {
WorkOrderDetail({
@ -94,6 +95,7 @@ class WorkOrderData {
this.closedDate,
this.since,
this.cmFrame,
this.edd,
this.rejectComment,
this.callResponse,
this.descriptionOfFinding,
@ -148,6 +150,7 @@ class WorkOrderData {
Lookup? failureReasone;
FaultDescription? faultDescription;
Lookup? solution;
String? edd;
Lookup? cmFrame;
//cmComments
@ -222,6 +225,7 @@ class WorkOrderData {
problemDescription: json["problemDescription"] == null ? null : Lookup.fromJson(json["problemDescription"]),
comments: json["comments"],
voiceNote: json["voiceNote"],
edd: json["edd"],
workOrderAttachments: json["workOrderAttachments"] == null ? [] : List.from(json['workOrderAttachments']).map((e) => WorkOrderAttachments.fromJson(e)).toList(),
returnToService: json["returnToService"],
serviceType: json["serviceType"] == null ? null : Lookup.fromJson(json["serviceType"]),
@ -268,10 +272,12 @@ class WorkOrderData {
"typeofRequest": typeofRequest?.toJson(),
"loanAvailablity": loanAvailablity?.toJson(),
"assetLoan": assetLoan?.toJson(),
"cmFrame": cmFrame?.toJson(),
"safety": safety?.toJson(),
"problemDescription": problemDescription?.toJson(),
"comments": comments,
"voiceNote": voiceNote,
"edd": edd,
"workOrderAttachments": workOrderAttachments.map((e) => e.toJson()).toList(),
"returnToService": returnToService,
"serviceType": serviceType?.toJson(),
@ -686,7 +692,7 @@ class ActivityMaintenance {
String? supplierEndTime;
double? supplierWorkingHours;
String? activityType;
List<ActivityMaintenanceAssistantEmployees>? assistantEmployees;
List<AssistantEmployeesModel>? assistantEmployees;
List<ActivityMaintenanceTimers>? activityMaintenanceTimers;
ActivityMaintenance(
@ -729,9 +735,9 @@ class ActivityMaintenance {
supplierEndTime = json['supplierEndTime'];
supplierWorkingHours = json['supplierWorkingHours'];
if (json['assistantEmployees'] != null) {
assistantEmployees = <ActivityMaintenanceAssistantEmployees>[];
assistantEmployees = <AssistantEmployeesModel>[];
json['assistantEmployees'].forEach((v) {
assistantEmployees!.add(ActivityMaintenanceAssistantEmployees.fromJson(v));
assistantEmployees!.add(AssistantEmployeesModel.fromJson(v));
});
}
if (json['activityMaintenanceTimers'] != null) {
@ -822,15 +828,29 @@ class ActivityMaintenanceAssistantEmployees {
double? workingHours;
String? technicalComment;
AssignedEmployee? user;
AssistantEmployees ?employee;
ActivityMaintenanceAssistantEmployees({this.startDate, this.endDate, this.workingHours, this.technicalComment, this.user});
ActivityMaintenanceAssistantEmployees({this.startDate, this.endDate, this.workingHours, this.technicalComment, this.user,this.employee});
ActivityMaintenanceAssistantEmployees.fromJson(Map<String, dynamic> json) {
Map<String,dynamic> assistEmpData={};
startDate = json['startDate'] != null ? DateTime.parse(json['startDate']) : null;
endDate = json['endDate'] != null ? DateTime.parse(json['endDate']) : null;
workingHours = json['workingHours'];
technicalComment = json['technicalComment'];
user = json['user'] != null ? AssignedEmployee.fromJson(json['user']) : null;
if(json['user']!=null) {
assistEmpData = {
'id': null,
'user': {
'id': user?.userId,
'name': user?.userName,
},
};
}
employee = AssistantEmployees.fromJson(assistEmpData);
}
Map<String, dynamic> toJson() {
@ -846,6 +866,38 @@ class ActivityMaintenanceAssistantEmployees {
}
}
// class ActivityMaintenanceAssistantEmployees {
// DateTime? startDate;
// DateTime? endDate;
// double? workingHours;
// String? technicalComment;
// AssignedEmployee? user;
//
//
// ActivityMaintenanceAssistantEmployees({this.startDate, this.endDate, this.workingHours, this.technicalComment, this.user});
//
// ActivityMaintenanceAssistantEmployees.fromJson(Map<String, dynamic> json) {
// startDate = json['startDate'] != null ? DateTime.parse(json['startDate']) : null;
// endDate = json['endDate'] != null ? DateTime.parse(json['endDate']) : null;
// workingHours = json['workingHours'];
// technicalComment = json['technicalComment'];
// user = json['user'] != null ? AssignedEmployee.fromJson(json['user']) : null;
// }
//
// Map<String, dynamic> toJson() {
// final Map<String, dynamic> data = <String, dynamic>{};
// data['startDate'] = startDate?.toIso8601String();
// data['endDate'] = endDate?.toIso8601String();
// data['workingHours'] = workingHours;
// data['technicalComment'] = technicalComment;
// if (user != null) {
// data['userId'] = user?.userId;
// }
// return data;
// }
// }
class ActivityMaintenanceTimers {
int? id;
String? startTime;
@ -870,3 +922,48 @@ class ActivityMaintenanceTimers {
return data;
}
}
class AssistantEmployeesModel {
DateTime? startDate;
DateTime? endDate;
double? workingHours;
String? technicalComment;
AssignedEmployee? user;
AssistantEmployees ?employee;
AssistantEmployeesModel({this.startDate, this.endDate, this.workingHours, this.technicalComment, this.user,this.employee});
AssistantEmployeesModel.fromJson(Map<String, dynamic> json) {
Map<String,dynamic> assistEmpData={};
startDate = json['startDate'] != null ? DateTime.parse(json['startDate']) : null;
endDate = json['endDate'] != null ? DateTime.parse(json['endDate']) : null;
workingHours = json['workingHours'];
technicalComment = json['technicalComment'];
user = json['user'] != null ? AssignedEmployee.fromJson(json['user']) : null;
if(json['user']!=null) {
assistEmpData = {
'id': null,
'user': {
'id': user?.userId,
'name': user?.userName,
},
};
}
employee = AssistantEmployees.fromJson(assistEmpData);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['startDate'] = startDate?.toIso8601String();
data['endDate'] = endDate?.toIso8601String();
data['workingHours'] = workingHours;
data['technicalComment'] = technicalComment;
if (user != null) {
data['userId'] = user?.userId;
}
return data;
}
}

@ -102,7 +102,6 @@ class ServiceRequest {
List list = parsedJson["attachmentsCallRequest"];
images = list.map((e) => URLs.getFileUrl(e["name"]!) as String).toList();
}
// print(parsedJson["requestedDate"]??"");
return ServiceRequest(
id: parsedJson["id"].toString(),
@ -180,7 +179,6 @@ class ServiceRequest {
}
return true;
}
}
class CallCreatedBy {

@ -611,40 +611,47 @@ class ServiceRequestDetailProvider extends ChangeNotifier {
}
//engineerUpdateWorkOrder......
Future<void> engineerUpdateWorkOrder() async {
Future<bool> engineerUpdateWorkOrder() async {
isLoading = true;
Response response;
try {
isLoading = true;
notifyListeners();
final response = await ApiManager.instance.post(URLs.engineerUpdateWorkOrderUrl, body: engineerUpdateWorkOrderHelperModel!.toJson());
response = await ApiManager.instance.post(URLs.engineerUpdateWorkOrderUrl, body: engineerUpdateWorkOrderHelperModel!.toJson());
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
currentWorkOrder = await WorkOrderDetail.fromJson(json.decode(response.body));
}
isLoading = false;
notifyListeners();
} catch (e) {
log("engineer update workorder [error] : $e");
if (stateCode == 200) {
currentWorkOrder = await WorkOrderDetail.fromJson(json.decode(response.body));
notifyListeners();
return true;
}
return false;
} catch (error) {
isLoading = false;
stateCode = -1;
notifyListeners();
return false;
}
}
//engineerUpdateWorkOrder......
Future<void> engineerUpdateCost() async {
Future<bool> engineerUpdateCost() async {
isLoading = true;
Response response;
try {
isLoading = true;
notifyListeners();
final response = await ApiManager.instance.post(URLs.engineerUpdateCost, body: workOrderCostModel!.toJson());
response = await ApiManager.instance.post(URLs.engineerUpdateCost, body: workOrderCostModel!.toJson());
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
currentWorkOrder = await WorkOrderDetail.fromJson(json.decode(response.body));
}
isLoading = false;
notifyListeners();
} catch (e) {
log("engineer update workorder [error] : $e");
if (stateCode == 200) {
currentWorkOrder = await WorkOrderDetail.fromJson(json.decode(response.body));
notifyListeners();
return true;
}
return false;
} catch (error) {
isLoading = false;
stateCode = -1;
notifyListeners();
return false;
}
}
@ -737,36 +744,43 @@ class ServiceRequestDetailProvider extends ChangeNotifier {
}
}
Future<int> updateActivitySparePart() async {
Future<bool> updateActivitySparePart() async {
isLoading = true;
Response response;
try {
final response = await ApiManager.instance.post(URLs.updateActivitySparePartUrl, body: sparePartHelperModel!.toJson());
response = await ApiManager.instance.post(URLs.updateActivitySparePartUrl, body: sparePartHelperModel!.toJson());
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
// request.engineerName = employee.name;
isLoading = false;
notifyListeners();
if (stateCode == 200) {
return true;
}
return response.statusCode;
return false;
} catch (error) {
return -1;
isLoading = false;
stateCode = -1;
notifyListeners();
return false;
}
}
Future<int> updateActivityMaintenance() async {
Future<bool> updateActivityMaintenance() async {
isLoading = true;
notifyListeners();
Response response;
try {
final response = await ApiManager.instance.put(URLs.updateActivityMaintenanceUrl, body: activityMaintenanceHelperModel!.toJson());
response = await ApiManager.instance.put(URLs.updateActivityMaintenanceUrl, body: activityMaintenanceHelperModel!.toJson());
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
// request.engineerName = employee.name;
}
isLoading = false;
notifyListeners();
return response.statusCode;
if (stateCode == 200) {
return true;
}
return false;
} catch (error) {
isLoading = false;
stateCode = -1;
notifyListeners();
return -1;
return false;
}
}
@ -798,7 +812,6 @@ class ServiceRequestDetailProvider extends ChangeNotifier {
body: {},
);
print('response i got is ${response.body}');
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
@ -861,41 +874,49 @@ class ServiceRequestDetailProvider extends ChangeNotifier {
}
}
Future<int> createActivitySparePart() async {
Future<bool> createActivitySparePart() async {
isLoading = true;
Response response;
try {
final response = await ApiManager.instance.post(URLs.createActivitySparePartUrl, body: sparePartHelperModel!.toJson());
response = await ApiManager.instance.post(
URLs.createActivitySparePartUrl,
body: sparePartHelperModel!.toJson(),
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
// currentWorkOrder = await WorkOrderDetail.fromJson(json.decode(response.body));
// // updateCurrentWorkOrder(currentWorkOrder);
// notifyListeners();
isLoading = false;
notifyListeners();
if (stateCode == 200) {
return true;
}
return response.statusCode;
return false;
} catch (error) {
return -1;
isLoading = false;
stateCode = -1;
notifyListeners();
return false;
}
}
Future<int> createActivityMaintenanceRequest() async {
Future<bool> createActivityMaintenanceRequest() async {
isLoading = true;
notifyListeners();
Response response;
try {
final response = await ApiManager.instance.post(
response = await ApiManager.instance.post(
URLs.createActivityMaintenanceUrl,
body: activityMaintenanceHelperModel!.toJson(),
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
// currentWorkOrder = await WorkOrderDetail.fromJson(json.decode(response.body));
// updateCurrentWorkOrder(currentWorkOrder);
}
isLoading = false;
notifyListeners();
return response.statusCode;
if (stateCode == 200) {
return true;
}
return false;
} catch (error) {
isLoading = false;
stateCode = -1;
notifyListeners();
return -1;
return false;
}
}

@ -40,7 +40,10 @@ class FooterActionButton {
required UserProvider userProvider,
bool isEmpIsAssigned = false}) {
ServiceRequestDetailProvider requestDetailProvider = Provider.of<ServiceRequestDetailProvider>(context, listen: false);
bool showMarkAsFixedButton = activities.isEmpty ? false : activities.any((object) => object.activityStatus!.value == 14 || object.activityStatus!.value == 19);
bool showMarkAsFixedButton = activities.isEmpty
? false
: activities.any((object) =>
object.activityStatus!.value == 14 || object.activityStatus!.value == 19 || object.activityStatus!.value == 11); // value 14,19,20 for fixed,out of scope and duplicate activity status
if (userProvider.user?.type == UsersTypes.engineer) {
if (workOrderNextStepStatus == WorkOrderNextStepEnum.assignToMe && isEmpIsAssigned) return const SizedBox();
switch (workOrderNextStepStatus) {
@ -130,7 +133,7 @@ class FooterActionButton {
maxWidth: true,
buttonColor: AppColor.green70,
onPressed: () async {
requestDetailProvider.engineerAcceptWorkOrder(id: requestDetailProvider.currentWorkOrder!.data!.requestId.toString()).whenComplete(() {});
requestDetailProvider.engineerAcceptWorkOrder(id: requestDetailProvider.currentWorkOrder!.data!.requestId.toString());
},
).expanded,
],

@ -311,19 +311,19 @@ class _ActivitiesListViewState extends State<ActivitiesListView> {
}
void editMaintenanceRequest({required BuildContext context, required ServiceRequestDetailProvider requestDetailProvider, required Activities activity}) async {
Map<String, dynamic> assistEmpData = {};
// Map<String, dynamic> assistEmpData = {};
try {
if (activity.activityMaintenance?.assistantEmployees != null && activity.activityMaintenance!.assistantEmployees!.isNotEmpty) {
assistEmpData = {
// 'id': activity.activityMaintenance?.assistantEmployees?[0].user?.userId != null ? num.tryParse(activity.activityMaintenance!.assistantEmployees![0].user!.userId!) : null,
'id': null,
'user': {
'id': activity.activityMaintenance?.assistantEmployees?[0].user?.userId,
// 'id': activity.activityMaintenance?.assistantEmployees?[0].user?.userId != null ? num.tryParse(activity.activityMaintenance!.assistantEmployees![0].user!.userId!) : null,
'name': activity.activityMaintenance?.assistantEmployees?[0].user?.userName,
},
};
}
// if (activity.activityMaintenance?.assistantEmployees != null && activity.activityMaintenance!.assistantEmployees!.isNotEmpty) {
// assistEmpData = {
// // 'id': activity.activityMaintenance?.assistantEmployees?[0].user?.userId != null ? num.tryParse(activity.activityMaintenance!.assistantEmployees![0].user!.userId!) : null,
// 'id': null,
// 'user': {
// 'id': activity.activityMaintenance?.assistantEmployees?[0].user?.userId,
// // 'id': activity.activityMaintenance?.assistantEmployees?[0].user?.userId != null ? num.tryParse(activity.activityMaintenance!.assistantEmployees![0].user!.userId!) : null,
// 'name': activity.activityMaintenance?.assistantEmployees?[0].user?.userName,
// },
// };
// }
requestDetailProvider.activityMaintenanceHelperModel = ActivityMaintenanceHelperModel(
id: activity.id,
@ -337,10 +337,11 @@ class _ActivitiesListViewState extends State<ActivitiesListView> {
// lastSituation: activity.activityMaintenance?.lastSituation,
assignedEmployee: activity.activityMaintenance?.assignedEmployee != null ? WorkOrderAssignedEmployee.fromJson(activity.activityMaintenance!.assignedEmployee!.toJson()) : null,
technicalComment: activity.activityMaintenance?.technicalComment,
assistantEmployees:
activity.activityMaintenance?.assistantEmployees != null && activity.activityMaintenance!.assistantEmployees!.isNotEmpty ? [AssistantEmployees.fromJson(assistEmpData)] : [],
modelAssistantEmployees:
activity.activityMaintenance?.assistantEmployees != null && activity.activityMaintenance!.assistantEmployees!.isNotEmpty ? activity.activityMaintenance!.assistantEmployees![0] : null,
assistantEmployList:activity.activityMaintenance!.assistantEmployees??[] ,
// assistantEmployees:
// activity.activityMaintenance?.assistantEmployees != null && activity.activityMaintenance!.assistantEmployees!.isNotEmpty ? [AssistantEmployees.fromJson(assistEmpData)] : [],
// modelAssistantEmployees:
// activity.activityMaintenance?.assistantEmployees != null && activity.activityMaintenance!.assistantEmployees!.isNotEmpty ? activity.activityMaintenance!.assistantEmployees![0] : null,
supplierStartTime: activity.activityMaintenance?.supplierStartTime != null ? DateTime.parse(activity.activityMaintenance!.supplierStartTime!) : null,
supplierEndTime: activity.activityMaintenance?.supplierEndTime != null ? DateTime.parse(activity.activityMaintenance!.supplierEndTime!) : null,
supplierWorkingHour: activity.activityMaintenance?.supplierWorkingHours,

@ -37,7 +37,17 @@ class AssetDetailCard extends StatelessWidget {
textColor: AppColor.white10,
backgroundColor: AppColor.getEquipmentStatusColor(context, requestDetailProvider.engineerUpdateWorkOrderHelperModel!.equipmentStatus!.id ?? 0),
),
5.width,
if(requestDetailProvider.engineerUpdateWorkOrderHelperModel?.cmFrameId!=null)...[
6.width,
StatusLabel(
label: requestDetailProvider.engineerUpdateWorkOrderHelperModel?.cmFrameId?.name,
id: requestDetailProvider.engineerUpdateWorkOrderHelperModel?.cmFrameId?.id ?? 0,
radius: 4,
textColor: AppColor.getActivityTypeTextColor(requestDetailProvider.engineerUpdateWorkOrderHelperModel!.cmFrameId?.name ?? ''),
backgroundColor: AppColor.getActivityTypeBgColor(requestDetailProvider.engineerUpdateWorkOrderHelperModel!.cmFrameId?.name ?? ''),
),
],
6.width,
StatusLabel(
label: requestDetailProvider.engineerUpdateWorkOrderHelperModel?.serviceType?.name,
id: requestDetailProvider.engineerUpdateWorkOrderHelperModel?.serviceType?.id ?? 0,
@ -64,6 +74,10 @@ class AssetDetailCard extends StatelessWidget {
'${context.translation.returnToService}: ${requestDetailProvider.engineerUpdateWorkOrderHelperModel?.returnToService?.toAssetDetailsFormat ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'EDD: ${requestDetailProvider.engineerUpdateWorkOrderHelperModel?.edd?.toAssetDetailsFormat ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.loanAvailability}: ${requestDetailProvider.engineerUpdateWorkOrderHelperModel?.loanAvailability?.name ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),

@ -355,8 +355,10 @@ class _ServiceRequestDetailViewState extends State<ServiceRequestDetailView> {
],
] else ...[
8.height,
const Divider().defaultStyle(context),
FilesList(images: _attachments.map((toElement) => URLs.getFileUrl(toElement.name!)!).toList()),
if (_attachments.isNotEmpty) ...[
const Divider().defaultStyle(context),
FilesList(images: _attachments.map((toElement) => URLs.getFileUrl(toElement.name!)!).toList()),
],
//handle nurse case..
],
@ -580,6 +582,8 @@ class _ServiceRequestDetailViewState extends State<ServiceRequestDetailView> {
serviceType: currentWorkOrderData.serviceType,
descriptionOfFinding: currentWorkOrderData.descriptionOfFinding,
actionTaken: currentWorkOrderData.actionTaken,
edd: currentWorkOrderData.edd,
cmFrameId: currentWorkOrderData.cmFrame,
);
return const AssetDetailCard();
} else {

@ -24,6 +24,7 @@ import 'package:test_sa/providers/service_request_providers/equipment_status_pro
import 'package:test_sa/providers/service_request_providers/loan_availability_provider.dart';
import 'package:test_sa/providers/work_order/fault_description_provider.dart';
import 'package:test_sa/providers/work_order/reason_provider.dart';
import 'package:test_sa/providers/work_order/wo_frame_provider.dart';
import 'package:test_sa/views/widgets/date_and_time/date_picker.dart';
import 'package:test_sa/views/widgets/equipment/pick_asset.dart';
@ -77,6 +78,8 @@ class _VerifyAssetDetailsState extends State<VerifyAssetDetails> with TickerProv
callResponse: currentWorkOrderData.callResponse,
descriptionOfFinding: currentWorkOrderData.descriptionOfFinding,
actionTaken: currentWorkOrderData.actionTaken,
edd: currentWorkOrderData.edd,
cmFrameId: currentWorkOrderData.cmFrame,
);
if (currentWorkOrderData.assetLoan != null) {
loanAvailabilityAsset = Asset(
@ -101,6 +104,7 @@ class _VerifyAssetDetailsState extends State<VerifyAssetDetails> with TickerProv
_loanAvailabilityProvider!.reset();
_equipmentStatusProvider = Provider.of<EquipmentStatusProvider>(context, listen: false);
_equipmentStatusProvider!.reset();
Provider.of<WoFrameProvider>(context, listen: false).reset();
WidgetsBinding.instance.addPostFrameCallback((_) {
_equipmentStatusProvider!.getData();
});
@ -129,7 +133,7 @@ class _VerifyAssetDetailsState extends State<VerifyAssetDetails> with TickerProv
hideShadow: true,
backgroundColor: AppColor.neutral100,
// initialDate: DateTime.tryParse(requestDetailProvider.engineerUpdateWorkOrderHelperModel?.returnToService ?? ""),
from:requestDetailProvider.currentWorkOrder?.data?.requestedDate,
from: requestDetailProvider.currentWorkOrder?.data?.requestedDate,
date: DateTime.tryParse(requestDetailProvider.engineerUpdateWorkOrderHelperModel?.returnToService ?? ""),
formatDateWithTime: true,
onDatePicker: (selectedDate) {
@ -158,37 +162,21 @@ class _VerifyAssetDetailsState extends State<VerifyAssetDetails> with TickerProv
});
},
),
12.height,
SingleItemDropDownMenu<Lookup, LoanAvailabilityProvider>(
SingleItemDropDownMenu<Lookup, WoFrameProvider>(
context: context,
title: context.translation.loanAvailability,
title: "WO Frame",
backgroundColor: AppColor.neutral100,
height: 56.toScreenHeight,
showShadow: false,
initialValue: requestDetailProvider.engineerUpdateWorkOrderHelperModel?.loanAvailability,
onSelect: (status) {
if (status != null) {
requestDetailProvider.engineerUpdateWorkOrderHelperModel?.loanAvailability = status;
if (status.value != 1) {
loanAvailabilityAsset = null;
requestDetailProvider.engineerUpdateWorkOrderHelperModel?.loanAssetId = null;
}
setState(() {});
initialValue: requestDetailProvider.engineerUpdateWorkOrderHelperModel?.cmFrameId,
onSelect: (value) {
if (value != null) {
requestDetailProvider.engineerUpdateWorkOrderHelperModel?.cmFrameId = value;
}
},
),
if (requestDetailProvider.engineerUpdateWorkOrderHelperModel?.loanAvailability?.value == 1) 8.height,
if (requestDetailProvider.engineerUpdateWorkOrderHelperModel?.loanAvailability?.value == 1)
PickAsset(
device: loanAvailabilityAsset, // ?? _serviceReport.device,
cardColor: AppColor.neutral100,
onPickAsset: (asset) {
requestDetailProvider.engineerUpdateWorkOrderHelperModel?.loanAssetId = asset.id;
setState(() {
loanAvailabilityAsset = asset;
});
},
),
12.height,
SingleItemDropDownMenu<Lookup, ReasonProvider>(
context: context,
@ -227,7 +215,73 @@ class _VerifyAssetDetailsState extends State<VerifyAssetDetails> with TickerProv
? requestDetailProvider.engineerUpdateWorkOrderHelperModel!.solution!.bodyText2(context).custom(color: AppColor.neutral120, align: TextAlign.justify)
: const SizedBox(),
],
8.height,
12.height,
SingleItemDropDownMenu<Lookup, LoanAvailabilityProvider>(
context: context,
title: context.translation.loanAvailability,
backgroundColor: AppColor.neutral100,
height: 56.toScreenHeight,
showShadow: false,
initialValue: requestDetailProvider.engineerUpdateWorkOrderHelperModel?.loanAvailability,
onSelect: (status) {
if (status != null) {
requestDetailProvider.engineerUpdateWorkOrderHelperModel?.loanAvailability = status;
if (status.value != 1) {
loanAvailabilityAsset = null;
requestDetailProvider.engineerUpdateWorkOrderHelperModel?.loanAssetId = null;
}
setState(() {});
}
},
),
if (requestDetailProvider.engineerUpdateWorkOrderHelperModel?.loanAvailability?.value == 1) 8.height,
if (requestDetailProvider.engineerUpdateWorkOrderHelperModel?.loanAvailability?.value == 1)
PickAsset(
device: loanAvailabilityAsset, // ?? _serviceReport.device,
cardColor: AppColor.neutral100,
onPickAsset: (asset) {
requestDetailProvider.engineerUpdateWorkOrderHelperModel?.loanAssetId = asset.id;
setState(() {
loanAvailabilityAsset = asset;
});
},
),
12.height,
ADatePicker(
label: "EDD",
hideShadow: true,
backgroundColor: AppColor.neutral100,
// initialDate: DateTime.tryParse(requestDetailProvider.engineerUpdateWorkOrderHelperModel?.edd ?? ""),
from: requestDetailProvider.currentWorkOrder?.data?.requestedDate,
date: DateTime.tryParse(requestDetailProvider.engineerUpdateWorkOrderHelperModel?.edd ?? ""),
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 (requestDetailProvider.engineerUpdateWorkOrderHelperModel?.edd != null &&
// selectedDateTime.isBefore(DateTime.parse(requestDetailProvider.engineerUpdateWorkOrderHelperModel!.edd!))) {
// "Return To Service Date time must be greater then previous date".showToast;
// return;
// }
setState(() {
requestDetailProvider.engineerUpdateWorkOrderHelperModel?.edd = selectedDateTime.toIso8601String();
});
}
});
},
),
12.height,
AppTextFormField(
labelText: context.translation.callResponse,
backgroundColor: AppColor.neutral100,
@ -241,7 +295,7 @@ class _VerifyAssetDetailsState extends State<VerifyAssetDetails> with TickerProv
style: Theme.of(context).textTheme.titleMedium,
),
if (Provider.of<SettingProvider>(context, listen: false).isUserFMS) ...[
8.height,
12.height,
AppTextFormField(
labelText: "Description of Finding",
backgroundColor: AppColor.neutral100,
@ -254,7 +308,7 @@ class _VerifyAssetDetailsState extends State<VerifyAssetDetails> with TickerProv
},
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
12.height,
AppTextFormField(
labelText: "Action Taken",
backgroundColor: AppColor.neutral100,
@ -283,9 +337,13 @@ class _VerifyAssetDetailsState extends State<VerifyAssetDetails> with TickerProv
onPressed: () async {
if (validateForm(requestDetailProvider: requestDetailProvider)) {
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await requestDetailProvider.engineerUpdateWorkOrder();
Navigator.pop(context);
Navigator.pop(context);
await requestDetailProvider.engineerUpdateWorkOrder().then((success){
Navigator.pop(context);
if(success){
Navigator.pop(context);
}
});
}
},
),

@ -89,7 +89,7 @@ class _CostDetailFormScreenState extends State<CostDetailFormScreen> with Ticker
initialValue: requestDetailProvider.workOrderCostModel?.labourCost?.toString(),
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
textInputType:const TextInputType.numberWithOptions(decimal: true),
textInputType: const TextInputType.numberWithOptions(decimal: true),
showShadow: false,
onChange: (value) {
requestDetailProvider.workOrderCostModel?.labourCost = num.parse(value);
@ -131,7 +131,7 @@ class _CostDetailFormScreenState extends State<CostDetailFormScreen> with Ticker
initialValue: requestDetailProvider.workOrderCostModel?.prNo,
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
textInputType:const TextInputType.numberWithOptions(decimal: true),
textInputType: const TextInputType.numberWithOptions(decimal: true),
showShadow: false,
onChange: (value) {
requestDetailProvider.workOrderCostModel?.prNo = value;
@ -145,7 +145,7 @@ class _CostDetailFormScreenState extends State<CostDetailFormScreen> with Ticker
initialValue: requestDetailProvider.workOrderCostModel?.poNo,
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
textInputType:const TextInputType.numberWithOptions(decimal: true),
textInputType: const TextInputType.numberWithOptions(decimal: true),
showShadow: false,
onChange: (value) {
requestDetailProvider.workOrderCostModel?.poNo = value;
@ -164,9 +164,12 @@ class _CostDetailFormScreenState extends State<CostDetailFormScreen> with Ticker
onPressed: () async {
if (validateForm(requestDetailProvider: requestDetailProvider)) {
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await requestDetailProvider.engineerUpdateCost();
Navigator.pop(context);
Navigator.pop(context);
await requestDetailProvider.engineerUpdateCost().then((success) {
Navigator.pop(context);
if (success) {
Navigator.pop(context);
}
});
}
},
),

@ -69,7 +69,7 @@ class _AssistantEmployeeCardState extends State<AssistantEmployeeCard> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
context.translation.assistantEmployee.bodyText(context).custom(color: AppColor.black20),
Icon(isExpanded ? Icons.arrow_drop_up_outlined : Icons.arrow_drop_down),
Icon(isExpanded ? Icons.keyboard_arrow_up_rounded : Icons.keyboard_arrow_down_rounded),
],
),
).onPress(() {

@ -0,0 +1,291 @@
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/new_models/work_order_detail_model.dart';
import 'package:test_sa/modules/cm_module/service_request_detail_provider.dart';
import 'package:test_sa/modules/cm_module/utilities/service_request_utils.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/views/widgets/date_and_time/date_picker.dart';
import 'package:test_sa/views/widgets/status/report/service_report_assistant_employee_menu.dart';
class AssistantEmployeeList extends StatefulWidget {
final List<AssistantEmployeesModel>? assistantEmployeeList;
final ValueChanged<List<AssistantEmployeesModel>>? onListChanged;
final double? cardPadding;
final dynamic assetId;
const AssistantEmployeeList({
super.key,
this.assistantEmployeeList,
this.onListChanged,
required this.assetId,
this.cardPadding,
});
@override
State<AssistantEmployeeList> createState() => _AssistantEmployeeListState();
}
class _AssistantEmployeeListState extends State<AssistantEmployeeList> {
late List<AssistantEmployeesModel> _list;
late List<TextEditingController> _controllers;
@override
void initState() {
super.initState();
_list = List<AssistantEmployeesModel>.from(widget.assistantEmployeeList ?? []);
_controllers = _list.map((e) => TextEditingController(text: e.workingHours?.toString() ?? '')).toList();
}
void _addNewEntry() {
setState(() {
_list.add(AssistantEmployeesModel());
_controllers.add(TextEditingController());
});
widget.onListChanged?.call(_list);
}
void _removeEntry(int index) {
setState(() {
_list.removeAt(index);
_controllers.removeAt(index);
});
widget.onListChanged?.call(_list);
}
void _updateModel(int index, void Function(AssistantEmployeesModel model) updateList) {
setState(() {
updateList(_list[index]);
});
}
@override
Widget build(BuildContext context) {
final isReadOnly = Provider.of<ServiceRequestDetailProvider>(context, listen: false).isReadOnlyRequest;
return ListView.builder(
itemCount: _list.length + 1,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.all(widget.cardPadding ?? 16),
itemBuilder: (context, index) {
if (index == _list.length) {
return Visibility(
visible: !isReadOnly,
child: AppFilledButton(
label: "Add Assistant Employee".addTranslation,
maxWidth: true,
textColor: AppColor.black10,
buttonColor: context.isDark ? AppColor.neutral60 : AppColor.white10,
icon: Icon(Icons.add_circle, color: AppColor.blueStatus(context)),
showIcon: true,
onPressed: _addNewEntry,
),
);
}
return EmployeeCard(
model: _list[index],
assetId: widget.assetId,
index: index,
isReadOnly: isReadOnly,
onUpdate: (updateList) => _updateModel(index, updateList),
onRemove: () => _removeEntry(index),
workingHoursController: _controllers[index],
);
},
);
}
}
class EmployeeCard extends StatelessWidget {
final AssistantEmployeesModel model;
final int index;
final bool isReadOnly;
final dynamic assetId;
final void Function(void Function(AssistantEmployeesModel model)) onUpdate;
final VoidCallback onRemove;
final TextEditingController workingHoursController;
const EmployeeCard({
super.key,
required this.model,
required this.assetId,
required this.index,
required this.isReadOnly,
required this.onUpdate,
required this.onRemove,
required this.workingHoursController,
});
@override
Widget build(BuildContext context) {
final requestedDate = Provider.of<ServiceRequestDetailProvider>(context, listen: false).currentWorkOrder?.data?.requestedDate;
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
context.translation.assistantEmployee.bodyText(context).custom(color: AppColor.black20),
if (!isReadOnly)
Container(
height: 32,
width: 32,
padding: const EdgeInsets.all(6),
child: "trash".toSvgAsset(height: 20, width: 20),
).onPress(onRemove),
],
),
8.height,
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ServiceReportAssistantEmployeeMenu(
title: context.translation.select,
backgroundColor: AppColor.neutral100,
assetId: assetId,
initialValue: model.employee,
onSelect: (employee) {
if (employee != null) {
onUpdate((model) {
model.employee = employee.copyWith(id: 0);
model.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: model.startDate,
formatDateWithTime: true,
from: requestedDate,
onDatePicker: (selectedDate) {
showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
).then((selectedTime) {
if (selectedTime != null) {
final selectedDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
selectedTime.hour,
selectedTime.minute,
);
if (requestedDate != null && selectedDateTime.isBefore(requestedDate)) {
"Start time is before the request time.".showToast;
return;
}
if (selectedDateTime.isAfter(DateTime.now())) {
"Start time is after the current time".showToast;
return;
}
onUpdate((model) {
model.startDate = selectedDateTime;
ServiceRequestUtils.calculateAndAssignWorkingHours(
startTime: model.startDate,
endTime: model.endDate,
workingHoursController: workingHoursController,
updateModel: (hours) => model.workingHours = hours,
);
});
}
});
},
).expanded,
8.width,
ADatePicker(
label: context.translation.endTime,
hideShadow: true,
backgroundColor: AppColor.neutral100,
date: model.endDate,
formatDateWithTime: true,
from: requestedDate,
onDatePicker: (selectedDate) {
showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
).then((selectedTime) {
if (selectedTime != null) {
final endDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
selectedTime.hour,
selectedTime.minute,
);
if (!endDateTime.isBefore(DateTime.now())) {
"Please select a time before the current time.".showToast;
return;
}
if (model.startDate == null || !endDateTime.isAfter(model.startDate!)) {
"End date must be after start date".showToast;
return;
}
onUpdate((model) {
model.endDate = endDateTime;
ServiceRequestUtils.calculateAndAssignWorkingHours(
startTime: model.startDate,
endTime: model.endDate,
workingHoursController: workingHoursController,
updateModel: (hours) => model.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),
textAlign: TextAlign.center,
enable: false,
showShadow: false,
labelStyle: AppTextStyles.textFieldLabelStyle,
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
initialValue: model.technicalComment,
labelText: context.translation.technicalComment,
backgroundColor: AppColor.neutral100,
showShadow: false,
labelStyle: AppTextStyles.textFieldLabelStyle,
alignLabelWithHint: true,
textInputType: TextInputType.multiline,
onChange: (value) => onUpdate((model) => model.technicalComment = value),
// onSaved: (value) => onUpdate((model) => model.technicalComment = value),
),
8.height,
],
)
],
).toShadowContainer(context, paddingObject: const EdgeInsets.symmetric(horizontal: 16, vertical: 12)).paddingOnly(bottom: 12);
}
}

@ -8,14 +8,13 @@ import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/modules/cm_module/service_request_detail_provider.dart';
import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart';
import 'package:test_sa/modules/cm_module/views/forms/maintenance_request/components/assistant_employee_list.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart';
import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart';
import 'package:test_sa/providers/service_request_providers/last_situation_provider.dart';
import 'package:test_sa/views/widgets/timer/app_timer.dart';
import 'assistant_employee_card.dart';
class InternalMaintenanceRequest extends StatefulWidget {
static const String id = "/add-internal-activity";
@ -119,9 +118,7 @@ class _InternalMaintenanceRequestState extends State<InternalMaintenanceRequest>
onPick: (time) {
requestDetailProvider.activityMaintenanceHelperModel?.activityTimePicker = time;
},
timerProgress: (isRunning) {
print("timerProgress:$isRunning");
},
timerProgress: (isRunning) {},
onChange: (timer) async {
requestDetailProvider.activityMaintenanceHelperModel?.activityMaintenanceTimerModel = timer;
if (timer.startAt != null && timer.endAt != null) {
@ -298,7 +295,16 @@ class _InternalMaintenanceRequestState extends State<InternalMaintenanceRequest>
],
),
).toShadowContainer(context).paddingOnly(start: 13, end: 14, top: 12),
const AssistantEmployeeCard().toShadowContainer(context, paddingObject: const EdgeInsets.symmetric(horizontal: 16)).paddingOnly(start: 13, end: 14, top: 12),
AssistantEmployeeList(
assetId: requestDetailProvider.currentWorkOrder?.data?.asset?.id,
assistantEmployeeList: requestDetailProvider.activityMaintenanceHelperModel?.assistantEmployList,
onListChanged: (updatedList) {
setState(() {
requestDetailProvider.activityMaintenanceHelperModel?.assistantEmployList = updatedList;
});
},
),
// const AssistantEmployeeCard().toShadowContainer(context, paddingObject: const EdgeInsets.symmetric(horizontal: 16)).paddingOnly(start: 13, end: 14, top: 12),
100.height,
],
),

@ -7,6 +7,7 @@ import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/helper/utils.dart';
import 'package:test_sa/models/helper_data_models/maintenance_request/activity_maintenance_model.dart';
import 'package:test_sa/models/new_models/work_order_detail_model.dart';
import 'package:test_sa/modules/cm_module/service_request_detail_provider.dart';
@ -40,8 +41,9 @@ class _MaintenanceRequestFormState extends State<MaintenanceRequestForm> with Si
Future<void> getInitialData() async {
Provider.of<ServiceReportRepairLocationProvider>(context, listen: false).getTypes();
ServiceRequestDetailProvider requestDetailProvider = Provider.of<ServiceRequestDetailProvider>(context, listen: false);
requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees =
requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees ?? ActivityMaintenanceAssistantEmployees();
// requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees =
// requestDetailProvider.activityMaintenanceHelperModel?.modelAssistantEmployees ?? ActivityMaintenanceAssistantEmployees();
requestDetailProvider.activityMaintenanceHelperModel?.assistantEmployList = requestDetailProvider.activityMaintenanceHelperModel?.assistantEmployList ?? [];
}
@override
@ -147,19 +149,24 @@ class _MaintenanceRequestFormState extends State<MaintenanceRequestForm> with Si
),
);
});
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
int status = -1;
if (requestDetailProvider.activityMaintenanceHelperModel?.id == 0) {
status = await requestDetailProvider.createActivityMaintenanceRequest();
} else {
status = await requestDetailProvider.updateActivityMaintenance();
}
if (status == 200) {
requestDetailProvider.getWorkOrderById(id: requestDetailProvider.currentWorkOrder!.data!.requestId!);
Navigator.pop(context);
Navigator.pop(context);
await requestDetailProvider.createActivityMaintenanceRequest().then((success) {
Navigator.pop(context);
if (success) {
requestDetailProvider.getWorkOrderById(id: requestDetailProvider.currentWorkOrder!.data!.requestId!);
Navigator.pop(context);
}
});
} else {
Navigator.pop(context);
await requestDetailProvider.updateActivityMaintenance().then((success) {
Navigator.pop(context);
if (success) {
requestDetailProvider.getWorkOrderById(id: requestDetailProvider.currentWorkOrder!.data!.requestId!);
Navigator.pop(context);
}
});
}
}
}
@ -197,6 +204,36 @@ class _MaintenanceRequestFormState extends State<MaintenanceRequestForm> with Si
return false;
}
}
// assistant employee validation
if (model.assistantEmployList?.isNotEmpty ?? false) {
for (int i = 0; i < model.assistantEmployList!.length; i++) {
final employee = model.assistantEmployList![i];
final position = Utils.getOrdinal(i + 1);
if (employee.user == null) {
Fluttertoast.showToast(
msg: "Please select the $position assistant employee",
);
return false;
}
if (employee.user?.userId != null) {
if (employee.startDate == null) {
Fluttertoast.showToast(
msg: "Please select start time for assistant employee ${employee.user?.userName}",
);
return false;
}
if (employee.endDate == null) {
Fluttertoast.showToast(
msg: "Please select end time for assistant employee ${employee.user?.userName}",
);
return false;
}
}
}
}
//
// if (model.activityMaintenanceTimerModel?.startAt == null) {

@ -8,6 +8,7 @@ import 'package:test_sa/controllers/providers/api/parts_provider.dart';
import 'package:test_sa/controllers/validator/validator.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/helper_data_models/spare_part/activity_spare_part_model.dart';
@ -309,33 +310,8 @@ class _SparePartRequestState extends State<SparePartRequest> with TickerProvider
child: AppFilledButton(
label: _requestDetailProvider?.sparePartHelperModel?.id == 0 ? context.translation.addSparePartActivity : context.translation.updateSparePartActivity,
buttonColor: AppColor.green70,
onPressed: () async {
requestDetailProvider.sparePartHelperModel?.sparePartAttachments?.clear();
for (var pickerObject in _files) {
String fileData =
_isLocalUrl(pickerObject.file.path) ? ("${pickerObject.file.path.split("/").last}|${base64Encode(File(pickerObject.file.path).readAsBytesSync())}") : pickerObject.file.path;
requestDetailProvider.sparePartHelperModel?.sparePartAttachments?.add(SparePartAttachments(id: pickerObject.id, name: fileData));
}
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
int status = -1;
if (_requestDetailProvider?.sparePartHelperModel?.id == 0) {
status = await requestDetailProvider.createActivitySparePart();
} else {
status = await requestDetailProvider.updateActivitySparePart();
}
if (status == 200) {
await requestDetailProvider.getWorkOrderById(id: requestDetailProvider.currentWorkOrder!.data!.requestId!);
//this is for hide the dialoge...
Navigator.pop(context);
Navigator.pop(context);
//show this only for add form..
if (_requestDetailProvider?.sparePartHelperModel?.id == 0) {
ServiceRequestBottomSheet.addAnotherSpareRequestBottomSheet(context: context);
SizedBox().flushBar(context: context, title: context.translation.sparePartActivitySuccess, message: '');
}
} else {
Navigator.pop(context);
}
onPressed: () {
_onSubmit(requestDetailProvider: requestDetailProvider);
},
)),
],
@ -344,6 +320,53 @@ class _SparePartRequestState extends State<SparePartRequest> with TickerProvider
}),
);
}
_onSubmit({required ServiceRequestDetailProvider requestDetailProvider}) async {
if (requestDetailProvider.sparePartHelperModel?.sparePart?.id == null) {
"Please select spare part".showToast;
return;
} else if (requestDetailProvider.sparePartHelperModel?.quantity == null) {
"Please enter quantity".showToast;
return;
}
requestDetailProvider.sparePartHelperModel?.sparePartAttachments?.clear();
for (var pickerObject in _files) {
String fileData = _isLocalUrl(pickerObject.file.path) ? "${pickerObject.file.path.split("/").last}|${base64Encode(File(pickerObject.file.path).readAsBytesSync())}" : pickerObject.file.path;
requestDetailProvider.sparePartHelperModel?.sparePartAttachments?.add(
SparePartAttachments(id: pickerObject.id, name: fileData),
);
}
// Show loading dialog
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const AppLazyLoading(),
);
bool success = false;
if (_requestDetailProvider?.sparePartHelperModel?.id == 0) {
success = await requestDetailProvider.createActivitySparePart();
} else {
success = await requestDetailProvider.updateActivitySparePart();
}
if (mounted) Navigator.pop(context);
if (success) {
requestDetailProvider.getWorkOrderById(
id: requestDetailProvider.currentWorkOrder!.data!.requestId!,
);
if (mounted) Navigator.pop(context);
if (_requestDetailProvider?.sparePartHelperModel?.id == 0) {
ServiceRequestBottomSheet.addAnotherSpareRequestBottomSheet(context: context);
const SizedBox().flushBar(
context: context,
title: context.translation.sparePartActivitySuccess,
message: '',
);
}
}
}
}
class PartDetailBottomSheetSheet extends StatelessWidget {

@ -7,6 +7,7 @@ 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/plan_preventive_visit/plan_preventive_visit_model.dart';
import 'package:test_sa/models/service_request/supp_engineer_work_orders.dart';
import 'package:test_sa/models/service_request/supplier_details.dart';
import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
@ -15,6 +16,7 @@ import 'package:test_sa/new_views/common_widgets/app_text_form_field.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/work_order/vendor_provider.dart';
import 'package:test_sa/views/pages/user/requests/add_supplier_engineer_bottom_sheet.dart';
import 'package:test_sa/views/widgets/date_and_time/date_picker.dart';
class PpmExternalDetailsForm extends StatefulWidget {
@ -160,20 +162,53 @@ class _ExternalDetailItemState extends State<ExternalDetailItem> {
},
),
8.height,
SingleItemDropDownMenu<SuppPersons, NullableLoadingProvider>(
context: context,
title: context.translation.supplierEngineer,
enabled: widget.model.supplier != null,
backgroundColor: AppColor.neutral100,
initialValue: widget.model.suppPerson,
staticData: widget.model.supplier?.suppPersons,
showAsBottomSheet: true,
showShadow: false,
onSelect: (suppPerson) {
if (suppPerson != null) {
widget.model.suppPerson = suppPerson;
}
},
Row(
children: [
SingleItemDropDownMenu<SuppPersons, NullableLoadingProvider>(
context: context,
title: context.translation.supplierEngineer,
enabled: widget.model.supplier != null,
backgroundColor: AppColor.neutral100,
initialValue: widget.model.suppPerson,
staticData: widget.model.supplier?.suppPersons,
showAsBottomSheet: true,
showShadow: false,
onSelect: (suppPerson) {
if (suppPerson != null) {
widget.model.suppPerson = suppPerson;
}
},
).expanded,
8.width,
Container(
height: 56.toScreenHeight,
width: 60.toScreenWidth,
decoration: BoxDecoration(
color: AppColor.neutral100,
borderRadius: BorderRadius.circular(10),
//boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)],
),
child: Icon(Icons.add, color: context.isDark ? null : AppColor.neutral60),
).onPress(() async {
if(widget.model.supplier==null) {
"Please select supplier".showToast;
return;
}
SuppEngineerWorkOrders? suppEngineer = (await showModalBottomSheet(
context: context,
useSafeArea: true,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => AddSupplierEngineerBottomSheet(widget.model.supplier!.id!.toInt()),
)) as SuppEngineerWorkOrders?;
if (suppEngineer == null) return;
widget.model.supplier?.suppPersons ??= [];
widget.model.supplier?.suppPersons!.add(SuppPersons.fromJson(suppEngineer.toJson()));
// requestDetailProvider.activityMaintenanceHelperModel?.supplier?.suppPersons?.add(SuppPersons.fromJson(suppEngineer.toJson()));
widget.model.suppPerson = SuppPersons.fromJson(suppEngineer.toJson());
setState(() {});
}),
],
),
8.height,
Row(
@ -193,7 +228,8 @@ class _ExternalDetailItemState extends State<ExternalDetailItem> {
).then((selectedTime) {
if (selectedTime != null) {
DateTime selectedDateTime = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute);
if (DateTime.tryParse(_ppmProvider?.planPreventiveVisit?.createdDate ?? '') != null && selectedDateTime.isBefore(DateTime.tryParse(_ppmProvider?.planPreventiveVisit?.createdDate ?? '')!)) {
if (DateTime.tryParse(_ppmProvider?.planPreventiveVisit?.createdDate ?? '') != null &&
selectedDateTime.isBefore(DateTime.tryParse(_ppmProvider?.planPreventiveVisit?.createdDate ?? '')!)) {
"Start time is before the request time.".showToast;
selectedTime = null;
return;

@ -76,16 +76,15 @@ class _UpdatePpmState extends State<UpdatePpm> with TickerProviderStateMixin {
);
});
await ppmProvider.updateVisitByEngineer(status: status).whenComplete(() {
if (status == 1) {
await ppmProvider.updateVisitByEngineer(status: status).then((success) {
Navigator.pop(context);
if (success) {
AllRequestsProvider allRequestsProvider = Provider.of<AllRequestsProvider>(context, listen: false);
allRequestsProvider.reset();
allRequestsProvider.getAllRequests(context, typeTransaction: 4);
ppmProvider.ppmPlanAttachments = [];
Navigator.pop(context);
}
// allRequestsProvider.recurrentWoData?.recurrentWoTimerModel=null;
Navigator.pop(context);
Navigator.pop(context);
});
}
}

@ -145,15 +145,17 @@ void _updateTask({required BuildContext context, required int status}) async {
.updateRecurrentWo(
status: status,
)
.whenComplete(() {
if (status == 1) {
// when click complete then this request remove from the list and status changes to closed..
allRequestsProvider.reset();
allRequestsProvider.getAllRequests(context, typeTransaction: 5);
}
allRequestsProvider.recurrentWoData?.recurrentWoTimerModel = null;
Navigator.pop(context);
.then((success) {
Navigator.pop(context);
if (success) {
if (status == 1) {
// when click complete then this request remove from the list and status changes to closed..
allRequestsProvider.reset();
allRequestsProvider.getAllRequests(context, typeTransaction: 5);
}
allRequestsProvider.recurrentWoData?.recurrentWoTimerModel = null;
Navigator.pop(context);
}
});
}
}

@ -223,6 +223,23 @@ class _UpdateTaskRequestState extends State<UpdateTaskRequest> {
);
});
// TODO need to test this when task is enabled ...
// await taskRequestProvider.updateTaskByEngineer().then((success) {
// Navigator.pop(context);
// if (success) {
// if (status == 1) {
// AllRequestsProvider allRequestsProvider = Provider.of<AllRequestsProvider>(context, listen: false);
// allRequestsProvider.reset();
// allRequestsProvider.getAllRequests(context, typeTransaction: 6);
// } else {
// taskRequestProvider.getTaskById(id: widget.taskId, showLoading: false);
//
// }
// Navigator.pop(context);
// Navigator.pop(context);
// }
// });
await taskRequestProvider.updateTaskByEngineer().whenComplete(() async {
if (taskRequestProvider.stateCode == 200) {
if (status == 1) {

@ -77,9 +77,8 @@ class _SingleItemDropDownMenuState<T extends Base, X extends LoadingListNotifier
@override
void didUpdateWidget(covariant SingleItemDropDownMenu<T, X> oldWidget) {
if (widget.initialValue != null) {
// print("$provider:start3:${DateTime.now()}");
final result = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.where((element) => element.identifier == widget.initialValue?.identifier);
// print("$provider:start4:${DateTime.now()}");
if (result?.isNotEmpty ?? false) {
_selectedItem = result?.first as T?;
} else {

@ -1,6 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:pinput/pinput.dart';
import 'package:provider/provider.dart';
@ -10,14 +9,14 @@ import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/new_models/general_response_model.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.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/forget_password_module/reset_password_view.dart';
class ForgetPasswordVerifyOtpView extends StatefulWidget {
Map<String,dynamic> data={};
Map<String, dynamic> data = {};
ForgetPasswordVerifyOtpView({Key ?key,required this.data}) : super(key: key);
ForgetPasswordVerifyOtpView({Key? key, required this.data}) : super(key: key);
@override
State<ForgetPasswordVerifyOtpView> createState() => _ForgetPasswordVerifyOtpViewState();
@ -25,7 +24,7 @@ class ForgetPasswordVerifyOtpView extends StatefulWidget {
class _ForgetPasswordVerifyOtpViewState extends State<ForgetPasswordVerifyOtpView> {
String otp = '';
Timer? _timer;
Timer? _timer;
int _remainingSeconds = 180; // 3 minutes in seconds
@override
@ -111,17 +110,16 @@ class _ForgetPasswordVerifyOtpViewState extends State<ForgetPasswordVerifyOtpVie
InkWell(
onTap: _remainingSeconds == 0
? () async {
UserProvider _userProvider = Provider.of<UserProvider>(context, listen: false);
String employeeId = widget.data['employeeId'];
GeneralResponseModel response = await _userProvider.sendForgetPasswordOtp(
context: context,
employeeId: employeeId,
);
print('Response of send OTP: ${response.toJson()}');
UserProvider _userProvider = Provider.of<UserProvider>(context, listen: false);
String employeeId = widget.data['employeeId'];
GeneralResponseModel response = await _userProvider.sendForgetPasswordOtp(
context: context,
employeeId: employeeId,
);
// Restart the timer
_startTimer();
}
// Restart the timer
_startTimer();
}
: null,
child: Text(
'Resend',
@ -150,15 +148,16 @@ class _ForgetPasswordVerifyOtpViewState extends State<ForgetPasswordVerifyOtpVie
).paddingOnly(start: 20, end: 20, bottom: 16),
);
}
void verifyOtp() async{
void verifyOtp() async {
if (otp.isNotEmpty) {
UserProvider _userProvider = Provider.of<UserProvider>(context, listen: false);
GeneralResponseModel generalResponseModel = await _userProvider.forgetPasswordValidateOtp(
context: context,
employeeId:widget.data['employeeId'],
employeeId: widget.data['employeeId'],
otp: otp,
);
if (generalResponseModel.isSuccess==true) {
if (generalResponseModel.isSuccess == true) {
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => ResetPasswordView()));
}
}

@ -1,217 +1,217 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/notification/firebase_notification_manger.dart';
import 'package:test_sa/controllers/notification/notification_manger.dart';
import 'package:test_sa/controllers/providers/api/all_requests_provider.dart';
import 'package:test_sa/controllers/providers/api/notifications_provider.dart';
import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/controllers/providers/settings/setting_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';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/user.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/dashboard_latest/widgets/progress_fragment.dart';
import 'package:test_sa/dashboard_latest/widgets/recent_activites_fragment.dart';
import 'package:test_sa/dashboard_latest/widgets/requests_fragment.dart';
import 'package:test_sa/views/pages/user/notifications/notifications_page.dart';
class DashboardPage extends StatefulWidget {
final VoidCallback onDrawerPress;
const DashboardPage({Key? key,required this.onDrawerPress}) : super(key: key);
@override
State<DashboardPage> createState() => _DashboardPageState();
}
class _DashboardPageState extends State<DashboardPage> {
int _currentPage = 0;
@override
void initState() {
super.initState();
getAllRequests();
}
void getAllRequests() {
WidgetsBinding.instance.addPostFrameCallback((_) {
Provider.of<AllRequestsProvider>(context, listen: false).getRequests();
Provider.of<NotificationsProvider>(context, listen: false).getSystemNotifications(user: Provider.of<UserProvider>(context, listen: false).user!, resetProvider: true);
});
}
@override
void dispose() {
super.dispose();
}
bool isFCM = true;
@override
Widget build(BuildContext context) {
if (isFCM) {
FirebaseNotificationManger.initialized(context);
NotificationManger.initialisation((notificationDetails) {
FirebaseNotificationManger.handleMessage(context, json.decode(notificationDetails.payload!));
}, (id, title, body, payload) async {});
isFCM = false;
}
final User user = Provider.of<UserProvider>(context, listen: false).user!;
final setting = Provider.of<SettingProvider>(context, listen: false);
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
titleSpacing: 0,
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Consumer<UserProvider>(builder: (context, snapshot, _) {
return CircleAvatar(
radius: 24,
backgroundColor: context.isDark ? AppColor.neutral50 : AppColor.neutral40,
child: Padding(
padding: const EdgeInsets.all(1), // Border radius
child: ClipOval(
child: snapshot.profileImage != null
? Image.file(snapshot.profileImage!)
: (snapshot.user?.profilePhotoName?.isNotEmpty ?? false)
? Image.network(snapshot.user!.profilePhotoName!)
: const Icon(Icons.person, size: 24, color: Colors.white),
),
),
);
}).onPress(widget.onDrawerPress),
const Spacer(),
Container(
padding: const EdgeInsets.fromLTRB(12, 6, 6, 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: AppColor.background(context),
boxShadow: const [
BoxShadow(
color: Color(0x07000000),
blurRadius: 14,
offset: Offset(0, 0),
spreadRadius: 0,
)
],
),
child: DropdownButton<AssetGroup>(
value: setting.assetGroup,
//iconSize: 24,
isDense: true,
icon: const Icon(Icons.keyboard_arrow_down),
elevation: 8,
// dropdownColor: Colors.amber,
borderRadius: BorderRadius.circular(8),
style: TextStyle(color: Theme.of(context).primaryColor),
underline: const SizedBox.shrink(),
onChanged: (newValue) {
if (setting.assetGroup != newValue) {
Provider.of<SettingProvider>(context, listen: false).setAssetGroup(newValue);
setState(() {});
getAllRequests();
}
},
items: user.assetGroups!.map<DropdownMenuItem<AssetGroup>>((value) {
return DropdownMenuItem<AssetGroup>(
value: value,
child: Text(
value.name ?? "",
style: Theme.of(context).textTheme.bodyLarge,
),
);
}).toList(),
),
),
16.width,
Stack(
alignment: Alignment.topRight,
children: [
Icon(Icons.notifications, color: context.isDark ? AppColor.neutral30 : AppColor.neutral20, size: 30).paddingOnly(top: 6, end: 0),
// todo @sikander will add count for unread notifications
// Positioned(
// top: 0,
// right: 0,
// child: Container(
// padding: const EdgeInsets.all(4),
// decoration: const ShapeDecoration(
// color: Color(0xFFD02127),
// shape: CircleBorder(),
// ),
// child: Text("", style: AppTextStyles.bodyText),
// ),
// )
],
).onPress(() {
Navigator.of(context).pushNamed(NotificationsPage.id);
}),
],
).paddingOnly(start: 16, end: 16),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
context.translation.welcome,
style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20),
),
Text(
user.username ?? "",
style: AppTextStyles.heading2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50, fontWeight: FontWeight.w600),
),
24.height,
Row(
children: [
indicatorView(0),
3.width,
indicatorView(1),
3.width,
indicatorView(2),
10.width,
"0${_currentPage + 1}/03".tinyFont(context).custom(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral60),
],
),
],
).paddingOnly(start: 16, end: 16, top: 8, bottom: 8),
PageView(
onPageChanged: (index) => setState(() => _currentPage = index),
children: [
const RequestsFragment(),
ProgressFragment(),
const RecentActivitiesFragment(),
],
).expanded,
],
),
);
}
Widget indicatorView(int index) {
bool isActive = _currentPage == index;
return AnimatedContainer(
duration: const Duration(milliseconds: 250),
width: (isActive ? 30 : 12).toScreenWidth,
height: 9.toScreenHeight,
decoration: BoxDecoration(
color: isActive
? AppColor.greenStatus(context)
: context.isDark
? AppColor.neutral20
: AppColor.neutral40,
borderRadius: BorderRadius.circular(8)),
);
}
}
// import 'dart:convert';
//
// import 'package:flutter/material.dart';
// import 'package:provider/provider.dart';
// import 'package:test_sa/controllers/notification/firebase_notification_manger.dart';
// import 'package:test_sa/controllers/notification/notification_manger.dart';
// import 'package:test_sa/controllers/providers/api/all_requests_provider.dart';
// import 'package:test_sa/controllers/providers/api/notifications_provider.dart';
// import 'package:test_sa/controllers/providers/api/user_provider.dart';
// import 'package:test_sa/controllers/providers/settings/setting_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';
// import 'package:test_sa/extensions/widget_extensions.dart';
// import 'package:test_sa/models/user.dart';
// import 'package:test_sa/new_views/app_style/app_color.dart';
// import 'package:test_sa/dashboard_latest/widgets/progress_fragment.dart';
// import 'package:test_sa/dashboard_latest/widgets/recent_activites_fragment.dart';
// import 'package:test_sa/dashboard_latest/widgets/requests_fragment.dart';
// import 'package:test_sa/views/pages/user/notifications/notifications_page.dart';
//
// class DashboardPage extends StatefulWidget {
// final VoidCallback onDrawerPress; //todo @delete
//
// const DashboardPage({Key? key,required this.onDrawerPress}) : super(key: key);
//
// @override
// State<DashboardPage> createState() => _DashboardPageState();
// }
//
// class _DashboardPageState extends State<DashboardPage> {
// int _currentPage = 0;
//
// @override
// void initState() {
// super.initState();
// getAllRequests();
// }
//
// void getAllRequests() {
// WidgetsBinding.instance.addPostFrameCallback((_) {
// Provider.of<AllRequestsProvider>(context, listen: false).getRequests();
// Provider.of<NotificationsProvider>(context, listen: false).getSystemNotifications(user: Provider.of<UserProvider>(context, listen: false).user!, resetProvider: true);
// });
// }
//
// @override
// void dispose() {
// super.dispose();
// }
//
// bool isFCM = true;
//
// @override
// Widget build(BuildContext context) {
// if (isFCM) {
// FirebaseNotificationManger.initialized(context);
// NotificationManger.initialisation((notificationDetails) {
// FirebaseNotificationManger.handleMessage(context, json.decode(notificationDetails.payload!));
// }, (id, title, body, payload) async {});
//
// isFCM = false;
// }
// final User user = Provider.of<UserProvider>(context, listen: false).user!;
// final setting = Provider.of<SettingProvider>(context, listen: false);
// return Scaffold(
// appBar: AppBar(
// automaticallyImplyLeading: false,
// backgroundColor: Theme.of(context).scaffoldBackgroundColor,
// titleSpacing: 0,
// title: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// Consumer<UserProvider>(builder: (context, snapshot, _) {
// return CircleAvatar(
// radius: 24,
// backgroundColor: context.isDark ? AppColor.neutral50 : AppColor.neutral40,
// child: Padding(
// padding: const EdgeInsets.all(1), // Border radius
// child: ClipOval(
// child: snapshot.profileImage != null
// ? Image.file(snapshot.profileImage!)
// : (snapshot.user?.profilePhotoName?.isNotEmpty ?? false)
// ? Image.network(snapshot.user!.profilePhotoName!)
// : const Icon(Icons.person, size: 24, color: Colors.white),
// ),
// ),
// );
// }).onPress(widget.onDrawerPress),
// const Spacer(),
// Container(
// padding: const EdgeInsets.fromLTRB(12, 6, 6, 6),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(8),
// color: AppColor.background(context),
// boxShadow: const [
// BoxShadow(
// color: Color(0x07000000),
// blurRadius: 14,
// offset: Offset(0, 0),
// spreadRadius: 0,
// )
// ],
// ),
// child: DropdownButton<AssetGroup>(
// value: setting.assetGroup,
// //iconSize: 24,
// isDense: true,
// icon: const Icon(Icons.keyboard_arrow_down),
// elevation: 8,
// // dropdownColor: Colors.amber,
// borderRadius: BorderRadius.circular(8),
// style: TextStyle(color: Theme.of(context).primaryColor),
// underline: const SizedBox.shrink(),
// onChanged: (newValue) {
// if (setting.assetGroup != newValue) {
// Provider.of<SettingProvider>(context, listen: false).setAssetGroup(newValue);
// setState(() {});
// getAllRequests();
// }
// },
// items: user.assetGroups!.map<DropdownMenuItem<AssetGroup>>((value) {
// return DropdownMenuItem<AssetGroup>(
// value: value,
// child: Text(
// value.name ?? "",
// style: Theme.of(context).textTheme.bodyLarge,
// ),
// );
// }).toList(),
// ),
// ),
// 16.width,
// Stack(
// alignment: Alignment.topRight,
// children: [
// Icon(Icons.notifications, color: context.isDark ? AppColor.neutral30 : AppColor.neutral20, size: 30).paddingOnly(top: 6, end: 0),
// // todo @sikander will add count for unread notifications
// // Positioned(
// // top: 0,
// // right: 0,
// // child: Container(
// // padding: const EdgeInsets.all(4),
// // decoration: const ShapeDecoration(
// // color: Color(0xFFD02127),
// // shape: CircleBorder(),
// // ),
// // child: Text("", style: AppTextStyles.bodyText),
// // ),
// // )
// ],
// ).onPress(() {
// Navigator.of(context).pushNamed(NotificationsPage.id);
// }),
// ],
// ).paddingOnly(start: 16, end: 16),
// ),
// body: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisSize: MainAxisSize.min,
// children: [
// Text(
// context.translation.welcome,
// style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20),
// ),
// Text(
// user.username ?? "",
// style: AppTextStyles.heading2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50, fontWeight: FontWeight.w600),
// ),
// 24.height,
// Row(
// children: [
// indicatorView(0),
// 3.width,
// indicatorView(1),
// 3.width,
// indicatorView(2),
// 10.width,
// "0${_currentPage + 1}/03".tinyFont(context).custom(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral60),
// ],
// ),
// ],
// ).paddingOnly(start: 16, end: 16, top: 8, bottom: 8),
// PageView(
// onPageChanged: (index) => setState(() => _currentPage = index),
// children: [
// const RequestsFragment(),
// ProgressFragment(),
// const RecentActivitiesFragment(),
// ],
// ).expanded,
// ],
// ),
// );
// }
//
// Widget indicatorView(int index) {
// bool isActive = _currentPage == index;
//
// return AnimatedContainer(
// duration: const Duration(milliseconds: 250),
// width: (isActive ? 30 : 12).toScreenWidth,
// height: 9.toScreenHeight,
// decoration: BoxDecoration(
// color: isActive
// ? AppColor.greenStatus(context)
// : context.isDark
// ? AppColor.neutral20
// : AppColor.neutral40,
// borderRadius: BorderRadius.circular(8)),
// );
// }
// }

@ -106,9 +106,9 @@ class _LandPageState extends State<LandPage> {
if (_userProvider!.user != null && _userProvider!.user!.employeeIsHMG == false) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_userProvider!.getSwipeLastTransaction(userId: _userProvider!.user!.userID!);
Provider.of<VendorProvider>(context, listen: false).getDate();
});
}
Provider.of<VendorProvider>(context, listen: false).getData();
_pages = <Widget>[
DashboardView(onDrawerPress: (() {
_scaffoldKey.currentState!.isDrawerOpen ? _scaffoldKey.currentState!.closeDrawer() : _scaffoldKey.currentState!.openDrawer();

@ -15,6 +15,8 @@ import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/new_views/pages/land_page/land_page.dart';
import 'package:test_sa/new_views/pages/login_page.dart';
import 'package:test_sa/new_views/pages/unsafe_device_view.dart';
import 'package:test_sa/new_views/swipe_module/dialoge/local_auth_failed_dialog.dart';
import 'package:test_sa/new_views/swipe_module/dialoge/single_btn_dialog.dart';
import 'package:test_sa/new_views/swipe_module/swipe_view.dart';
import 'package:test_sa/views/update_available_screen.dart';
@ -84,24 +86,54 @@ class _SplashPageState extends State<SplashPage> {
loading = false;
});
if (isValid && _settingProvider.isLocalAuthEnable) {
bool isSuccess = await checkDualAuthentication();
if (isSuccess) {
_userProvider.setUser(_settingProvider.user!);
if (_userProvider.user!.onlySwipe!) {
Navigator.of(context).pushNamedAndRemoveUntil(SwipeView.routeName, (routes) => true);
} else {
Navigator.of(context).pushNamedAndRemoveUntil(LandPage.routeName, (routes) => true);
}
} else {
Navigator.of(context).pushNamedAndRemoveUntil(LoginPage.routeName, (routes) => true);
}
handleLocalAuth();
} else {
Navigator.of(context).pushNamedAndRemoveUntil(LoginPage.routeName, (routes) => true);
showDialog(
context: context,
builder: (BuildContext cxt) => SingleBtnDialog(
title: "Session Expired",
message: "Login session is expired, Please login.",
okTitle: "Login",
onTap: () {
Navigator.pop(context);
Navigator.of(context).pushNamedAndRemoveUntil(LoginPage.routeName, (routes) => true);
}),
);
}
}
}
void handleLocalAuth() async {
bool isSuccess = false;
try {
isSuccess = await checkDualAuthentication();
} catch (ex) {
// ios exception when scan failed.
}
if (isSuccess) {
_userProvider.setUser(_settingProvider.user!);
if (_userProvider.user!.onlySwipe!) {
Navigator.of(context).pushNamedAndRemoveUntil(SwipeView.routeName, (routes) => true);
} else {
Navigator.of(context).pushNamedAndRemoveUntil(LandPage.routeName, (routes) => true);
}
} else {
showDialog(
context: context,
builder: (BuildContext cxt) => LocalAuthFailedDialog(
onRetry: () {
handleLocalAuth();
},
onLogin: () {
Navigator.of(context).pushNamedAndRemoveUntil(LoginPage.routeName, (routes) => true);
},
),
);
}
}
Future<bool> checkDualAuthentication() async {
return await _settingProvider.auth.authenticate(
localizedReason: Platform.isAndroid ? "Scan your fingerprint to authenticate" : "Scan with face id to authenticate",
@ -139,7 +171,7 @@ class _SplashPageState extends State<SplashPage> {
animation: "splash",
callback: (animation) async {
bool isSafe = await checkDeviceSafety();
print('is safe is ${isSafe}');
if (!isSafe) {
Navigator.pushNamedAndRemoveUntil(context, UnsafeDeviceScreen.routeName, (_) => false);
} else {

@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
class LocalAuthFailedDialog extends StatelessWidget {
final String? title;
final String? message;
final String? okTitle;
final String? retryBtnText;
final String? loginBtnText;
final VoidCallback onRetry;
final VoidCallback onLogin;
const LocalAuthFailedDialog(
{Key? key, this.title, this.message = "Authentication Failed, Please try again or login", this.okTitle, required this.onRetry, required this.onLogin, this.retryBtnText, this.loginBtnText})
: super(key: key);
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(),
insetPadding: const EdgeInsets.only(left: 21, right: 21),
child: Padding(
padding: const EdgeInsets.only(left: 20, right: 20, top: 18, bottom: 28),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Text(
"Confirm",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: Colors.black87, height: 35 / 24, letterSpacing: -0.96),
).paddingOnly(top: 16, bottom: 8),
message != null ? message!.heading5(context).custom(color: AppColor.neutral50) : const SizedBox(),
28.height,
Column(
children: [
AppFilledButton(
label: retryBtnText ?? "Retry",
fontSize: 18,
onPressed: () {
Navigator.of(context).pop();
onRetry();
},
buttonColor: AppColor.primary90,
),
16.height,
AppFilledButton(
label: loginBtnText ?? "Login",
buttonColor: Colors.white54,
textColor: AppColor.black10,
height: 42,
showBorder: true,
onPressed: () {
Navigator.of(context).pop();
onLogin();
},
),
],
),
],
),
),
);
}
}

@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
class SingleBtnDialog extends StatelessWidget {
final String? title;
final String? message;
final String? okTitle;
final VoidCallback? onTap;
const SingleBtnDialog({Key? key, this.title, this.message, this.okTitle, this.onTap}) : super(key: key);
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(),
insetPadding: const EdgeInsets.only(left: 21, right: 21),
child: Padding(
padding: const EdgeInsets.only(left: 20, right: 20, top: 18, bottom: 28),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
title ?? "Confirm",
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: Colors.black87, height: 35 / 24, letterSpacing: -0.96),
).paddingOnly(top: 16),
message != null ? message!.heading5(context).custom(color: AppColor.neutral70) : const SizedBox(),
28.height,
AppFilledButton(
label: okTitle ?? "OK",
height: 46,
onPressed: onTap ?? () => Navigator.pop(context),
textColor: Colors.white,
//color: Ap.green,
),
],
),
),
);
}
}

@ -116,15 +116,23 @@ class TaskRequestProvider extends ChangeNotifier {
}
}
Future<int> updateTaskByEngineer() async {
notifyListeners();
Future<bool> updateTaskByEngineer() async {
isLoading = true;
Response response;
try {
final response = await ApiManager.instance.post(URLs.updateTaskByEngineerUrl, body: taskRequestModel!.toEngineerUpdateJson());
response = await ApiManager.instance.post(URLs.updateTaskByEngineerUrl, body: taskRequestModel!.toEngineerUpdateJson());
stateCode = response.statusCode;
isLoading = false;
notifyListeners();
return response.statusCode;
if (stateCode == 200) {
return true;
}
return false;
} catch (error) {
return -1;
isLoading = false;
stateCode = -1;
notifyListeners();
return false;
}
}

@ -8,7 +8,6 @@ import '../../controllers/api_routes/urls.dart';
import '../../models/lookup.dart';
class ReasonProvider extends LoadingListNotifier<Lookup> {
String? serviceRequestId;
@override
@ -17,7 +16,7 @@ class ReasonProvider extends LoadingListNotifier<Lookup> {
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.getServiceReportReasonsNew+"&serviceRequestId=$serviceRequestId");
Response response = await ApiManager.instance.get(URLs.getServiceReportReasonsNew + "&serviceRequestId=$serviceRequestId");
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
List categoriesListJson = json.decode(response.body)["data"];

@ -0,0 +1,33 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
import '../../controllers/api_routes/api_manager.dart';
import '../../controllers/api_routes/urls.dart';
import '../../models/lookup.dart';
class WoFrameProvider extends LoadingListNotifier<Lookup> {
@override
Future getDate() async {
if (loading == true) return -2;
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.getWoFrames);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
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;
}
}
}

@ -10,6 +10,7 @@ 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/widget_extensions.dart';
import 'package:test_sa/helper/utils.dart';
import 'package:test_sa/models/device/asset_transfer_attachment.dart';
import 'package:test_sa/models/device/device_transfer.dart';
import 'package:test_sa/models/new_models/assigned_employee.dart';
@ -18,6 +19,7 @@ import 'package:test_sa/models/timer_model.dart';
import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/modules/cm_module/views/components/bottom_sheets/service_request_bottomsheet.dart';
import 'package:test_sa/modules/cm_module/views/forms/maintenance_request/components/assistant_employee_list.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
import 'package:test_sa/views/app_style/sizing.dart';
import 'package:test_sa/views/widgets/date_and_time/date_picker.dart';
@ -164,15 +166,34 @@ class _UpdateDeviceTransferState extends State<UpdateDeviceTransfer> {
return false;
}
}
// assistant employee validation
if (_formModel.assistantEmployList?.isNotEmpty ?? false) {
for (int i = 0; i < _formModel.assistantEmployList!.length; i++) {
final employee = _formModel.assistantEmployList![i];
final position = Utils.getOrdinal(i + 1);
if (employee.employeeId == null) {
Fluttertoast.showToast(
msg: "Please select the $position assistant employee",
);
return false;
}
if (_formModel.assistantEmployees != null) {
if (_formModel.modelAssistantEmployees?.startDate == null) {
Fluttertoast.showToast(msg: "Please Select Assistant Employee Start Time");
return false;
}
if (_formModel.modelAssistantEmployees?.endDate == null) {
Fluttertoast.showToast(msg: "Please Select Assistant Employee End Time");
return false;
if (employee.employeeId != null) {
if (employee.startDate == null) {
Fluttertoast.showToast(
msg: "Please select start time for assistant employee ${employee.employeeName}",
);
return false;
}
if (employee.endDate == null) {
Fluttertoast.showToast(
msg: "Please select end time for assistant employee ${employee.employeeName}",
);
return false;
}
}
}
}
return true;
@ -210,88 +231,96 @@ class _UpdateDeviceTransferState extends State<UpdateDeviceTransfer> {
),
key: _scaffoldKey,
body: 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, totalWorkingHours, isTimerEnable),
8.height,
AppTextFormField(
initialValue: widget.isSender ? _formModel.senderComment ?? "" : _formModel.receiverComment ?? "",
labelText: context.translation.technicalComment,
labelStyle: AppTextStyles.tinyFont.copyWith(color: AppColor.neutral20),
textInputType: TextInputType.multiline,
backgroundColor: AppColor.neutral100,
showShadow: false,
alignLabelWithHint: true,
onSaved: (value) {
widget.isSender ? _formModel.senderComment = value : _formModel.receiverComment = value;
},
),
8.height,
MultiFilesPicker(
label: context.translation.attachFiles,
files: _files,
buttonColor: AppColor.black10,
onlyImages: false,
buttonIcon: 'image-plus'.toSvgAsset(color: AppColor.neutral120),
),
8.height,
],
).toShadowContainer(context),
16.height,
AssistantEmployeeCard(
isSender: widget.isSender,
formModel: _formModel,
).toShadowContainer(context, paddingObject: const EdgeInsets.symmetric(horizontal: 16)),
],
),
).expanded,
FooterActionButton.footerContainer(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
AppFilledButton(
label: context.translation.save,
buttonColor: AppColor.white60,
textColor: AppColor.black10,
onPressed: () => _update(status: 0),
).expanded,
12.width,
if (!widget.isSender && _formModel.senderMachineStatusValue == 3) ...[
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, totalWorkingHours, isTimerEnable),
8.height,
AppTextFormField(
initialValue: widget.isSender ? _formModel.senderComment ?? "" : _formModel.receiverComment ?? "",
labelText: context.translation.technicalComment,
labelStyle: AppTextStyles.tinyFont.copyWith(color: AppColor.neutral20),
textInputType: TextInputType.multiline,
backgroundColor: AppColor.neutral100,
showShadow: false,
alignLabelWithHint: true,
onSaved: (value) {
widget.isSender ? _formModel.senderComment = value : _formModel.receiverComment = value;
},
),
8.height,
MultiFilesPicker(
label: context.translation.attachFiles,
files: _files,
buttonColor: AppColor.black10,
onlyImages: false,
buttonIcon: 'image-plus'.toSvgAsset(color: AppColor.neutral120),
),
8.height,
],
).toShadowContainer(context),
16.height,
DeviceTransferAssistantEmployeeList(
assetId: _formModel.assetId,
createdDate: _formModel.createdDate??'',
assistantEmployeeList: widget.isSender?_formModel.assetTransferAssistantEmployeesSender:_formModel.assetTransferAssistantEmployeesReceiver,
cardPadding: 0,
onListChanged: (updatedList) {
setState(() {
_formModel.assistantEmployList = updatedList;
});
},
),
],
),
).expanded,
FooterActionButton.footerContainer(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
AppFilledButton(
label: context.translation.save,
buttonColor: AppColor.white60,
textColor: AppColor.black10,
onPressed: () => _update(status: 0),
).expanded,
12.width,
if (!widget.isSender && _formModel.senderMachineStatusValue == 3) ...[
AppFilledButton(
label: context.translation.complete,
buttonColor: AppColor.primary10,
onPressed: () {
_update(status: 1);
}).expanded,
] else if (widget.isSender) ...[
AppFilledButton(
label: context.translation.complete,
buttonColor: AppColor.primary10,
onPressed: () {
_update(status: 1);
}).expanded,
] else if (widget.isSender) ...[
AppFilledButton(
label: context.translation.complete,
buttonColor: AppColor.primary10,
onPressed: () {
_update(status: 1);
},
).expanded,
},
).expanded,
],
],
],
),
),
),
],
],
),
),
),
),
).handlePopScope(
cxt: context,
onSave: () {
@ -351,224 +380,513 @@ class _UpdateDeviceTransferState extends State<UpdateDeviceTransfer> {
}
}
class AssistantEmployeeCard extends StatefulWidget {
bool? isSender = false;
DeviceTransfer? formModel;
AssistantEmployeeCard({super.key, this.isSender, this.formModel});
class DeviceTransferAssistantEmployeeList extends StatefulWidget {
final List<AssetTransferAssistantEmployees>? assistantEmployeeList;
final ValueChanged<List<AssetTransferAssistantEmployees>>? onListChanged;
final double ?cardPadding;
final dynamic assetId;
final String createdDate;
const DeviceTransferAssistantEmployeeList({
super.key,
this.assistantEmployeeList,
this.onListChanged,
required this.assetId,
this.cardPadding,
required this.createdDate,
});
@override
State<AssistantEmployeeCard> createState() => _AssistantEmployeeCardState();
State<DeviceTransferAssistantEmployeeList> createState() => _DeviceTransferAssistantEmployeeListState();
}
class _AssistantEmployeeCardState extends State<AssistantEmployeeCard> {
final TextEditingController _workingHoursController = TextEditingController(text: '');
bool isCurrentUserIsAssistantEmp = false;
bool isExpanded = false;
List<AssetTransferAssistantEmployees> employeeList = [];
AssistantEmployees selectedEmployee = AssistantEmployees();
class _DeviceTransferAssistantEmployeeListState extends State<DeviceTransferAssistantEmployeeList> {
late List<AssetTransferAssistantEmployees> _list;
late List<TextEditingController> _controllers;
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
getInitialData();
});
super.initState();
_list = List<AssetTransferAssistantEmployees>.from(widget.assistantEmployeeList ?? []);
_controllers = _list.map((e) => TextEditingController(text: e.workingHours?.toString() ?? '')).toList();
}
Future<void> getInitialData() async {
if (widget.isSender!) {
employeeList = widget.formModel!.assetTransferAssistantEmployeesSender ?? [];
} else {
employeeList = widget.formModel!.assetTransferAssistantEmployeesReceiver ?? [];
}
void _addNewEntry() {
setState(() {
_list.add(AssetTransferAssistantEmployees());
_controllers.add(TextEditingController());
});
widget.onListChanged?.call(_list);
}
widget.formModel?.modelAssistantEmployees = employeeList.isEmpty ? AssetTransferAssistantEmployees() : employeeList[0];
void _removeEntry(int index) {
setState(() {
_list.removeAt(index);
_controllers.removeAt(index);
});
widget.onListChanged?.call(_list);
}
AssignedEmployee? assignedUser = AssignedEmployee();
if (employeeList.isNotEmpty) {
assignedUser = AssignedEmployee(
id: employeeList[0].employeeId,
name: employeeList[0].employeeName,
);
}
selectedEmployee = AssistantEmployees(userId: assignedUser.id, user: assignedUser);
void _updateModel(int index, void Function(AssetTransferAssistantEmployees model) updateList, {bool updateController = false}) {
setState(() {
updateList(_list[index]);
});
}
@override
void dispose() {
_workingHoursController.dispose();
super.dispose();
Widget build(BuildContext context) {
return ListView.builder(
itemCount: _list.length + 1,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.all(widget.cardPadding??16),
itemBuilder: (context, index) {
if (index == _list.length) {
return AppFilledButton(
label: "Add Assistant Employee".addTranslation,
maxWidth: true,
textColor: AppColor.black10,
buttonColor: context.isDark ? AppColor.neutral60 : AppColor.white10,
icon: Icon(Icons.add_circle, color: AppColor.blueStatus(context)),
showIcon: true,
onPressed: _addNewEntry,
);
}
AssistantEmployees selectedEmployee = AssistantEmployees();
AssignedEmployee? assignedUser = AssignedEmployee();
assignedUser = AssignedEmployee(
id: _list[index].employeeId,
name: _list[index].employeeName,
);
selectedEmployee = AssistantEmployees( userId: assignedUser.id, user: assignedUser);
return EmployeeCard(
model: _list[index],
assetId: widget.assetId,
index: index,
createdDate: widget.createdDate,
selectedEmployee: selectedEmployee,
onUpdate: (updateList) => _updateModel(index, updateList),
onRemove: () => _removeEntry(index),
workingHoursController: _controllers[index],
);
},
);
}
}
class EmployeeCard extends StatelessWidget {
final AssetTransferAssistantEmployees model;
final int index;
final dynamic assetId ;
final String ? createdDate;
AssistantEmployees? selectedEmployee;
final void Function(void Function(AssetTransferAssistantEmployees model)) onUpdate;
final VoidCallback onRemove;
final TextEditingController workingHoursController;
EmployeeCard({
super.key,
required this.model,
required this.assetId,
this.selectedEmployee,
required this.index,
required this.onUpdate,
this.createdDate,
required this.onRemove,
required this.workingHoursController,
});
// AssistantEmployees selectedEmployee = AssistantEmployees();
@override
Widget build(BuildContext context) {
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,
assetId: widget.formModel?.assetId ?? 0,
initialValue: selectedEmployee,
onSelect: (employee) {
if (employee == null) {
widget.formModel?.assistantEmployees = [];
} else {
selectedEmployee = employee;
widget.formModel?.assistantEmployees = [employee.copyWith(id: 0)];
widget.formModel?.modelAssistantEmployees?.employeeId = employee.user?.id;
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
context.translation.assistantEmployee.bodyText(context).custom(color: AppColor.black20),
Container(
height: 32,
width: 32,
padding: const EdgeInsets.all(6),
child: "trash".toSvgAsset(height: 20, width: 20),
).onPress(onRemove),
],
),
8.height,
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ServiceReportAssistantEmployeeMenu(
title: context.translation.select,
backgroundColor: AppColor.neutral100,
assetId:assetId,
initialValue: selectedEmployee,
onSelect: (employee) {
if (employee != null) {
onUpdate((model) {
selectedEmployee = employee.copyWith(id: 0);
model.employeeId = employee.user?.id;
model.employeeName = employee.user?.name;
selectedEmployee?.user = AssignedEmployee(
id: employee.user?.id,
name: employee.user?.name,
);
});
}
},
),
8.height,
Row(
mainAxisSize: MainAxisSize.min,
children: [
ADatePicker(
label: context.translation.startTime,
hideShadow: true,
backgroundColor: AppColor.neutral100,
date: model.startDate,
formatDateWithTime: true,
from: DateTime.tryParse(createdDate ?? ''),
onDatePicker: (selectedDate) {
showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
).then((selectedTime) {
if (selectedTime != null) {
final selectedDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
selectedTime.hour,
selectedTime.minute,
);
if (createdDate != null && selectedDateTime.isBefore(DateTime.tryParse(createdDate ?? '')!)) {
"Start time is before the request time.".showToast;
return;
}
if (selectedDateTime.isAfter(DateTime.now())) {
"Start time is after the current time".showToast;
return;
}
onUpdate((model) {
model.startDate = selectedDateTime;
ServiceRequestUtils.calculateAndAssignWorkingHours(
startTime: model.startDate,
endTime: model.endDate,
workingHoursController: workingHoursController,
updateModel: (hours) => model.workingHours = hours,
);
});
}
},
),
8.height,
Row(
mainAxisSize: MainAxisSize.min,
children: [
ADatePicker(
label: context.translation.startTime,
hideShadow: true,
backgroundColor: AppColor.neutral100,
date: widget.formModel?.modelAssistantEmployees?.startDate,
from: DateTime.tryParse(widget.formModel?.createdDate ?? ''),
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 (DateTime.tryParse(widget.formModel?.createdDate ?? '') != null && selectedDateTime.isBefore(DateTime.tryParse(widget.formModel?.createdDate ?? '')!)) {
"Start time is before the request time.".showToast;
selectedTime = null;
return;
}
if (selectedDateTime.isAfter(DateTime.now())) {
"Start time is after than current time".showToast;
selectedTime = null;
return;
}
widget.formModel?.modelAssistantEmployees?.startDate = selectedDateTime;
ServiceRequestUtils.calculateAndAssignWorkingHours(
startTime: widget.formModel?.modelAssistantEmployees?.startDate,
endTime: widget.formModel?.modelAssistantEmployees?.endDate,
workingHoursController: _workingHoursController,
updateModel: (hours) {
widget.formModel?.modelAssistantEmployees?.workingHours = hours;
});
setState(() {});
}
});
},
).expanded,
8.width,
ADatePicker(
label: context.translation.endTime,
hideShadow: true,
backgroundColor: AppColor.neutral100,
date: widget.formModel?.modelAssistantEmployees?.endDate,
formatDateWithTime: true,
onDatePicker: (selectedDate) {
showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
).then((selectedTime) {
if (selectedTime != null) {
DateTime selectedDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
selectedTime.hour,
selectedTime.minute,
);
if (widget.formModel?.modelAssistantEmployees?.startDate != null && selectedDateTime.isBefore(widget.formModel!.modelAssistantEmployees!.startDate!)) {
"End Date time must be greater then start date".showToast;
return;
}
selectedDate = selectedDate.add(Duration(hours: selectedTime.hour, minutes: selectedTime.minute));
bool isBeforeCurrentTime = selectedDate.isBefore(DateTime.now());
bool isAfterStartTime = selectedDate.isAfter(widget.formModel!.modelAssistantEmployees!.startDate!);
if (!isBeforeCurrentTime) {
"Please select a time before the current time.".showToast;
return;
}
if (!isAfterStartTime) {
"End Date time must be greater then start date".showToast;
return;
}
widget.formModel?.modelAssistantEmployees?.endDate = selectedDateTime;
ServiceRequestUtils.calculateAndAssignWorkingHours(
startTime: widget.formModel?.modelAssistantEmployees?.startDate,
endTime: widget.formModel?.modelAssistantEmployees?.endDate,
workingHoursController: _workingHoursController,
updateModel: (hours) {
widget.formModel?.modelAssistantEmployees?.workingHours = hours;
});
setState(() {});
}
});
},
).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: widget.formModel?.modelAssistantEmployees?.workingHours != null ? widget.formModel?.modelAssistantEmployees?.workingHours.toString() : '',
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
enable: false,
showShadow: false,
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
initialValue: widget.formModel?.modelAssistantEmployees?.techComment,
labelText: context.translation.technicalComment,
backgroundColor: AppColor.neutral100,
showShadow: false,
labelStyle: AppTextStyles.textFieldLabelStyle,
alignLabelWithHint: true,
textInputType: TextInputType.multiline,
onChange: (value) {
widget.formModel?.modelAssistantEmployees?.techComment = value;
},
onSaved: (value) {
widget.formModel?.modelAssistantEmployees?.techComment = value;
},
),
16.height,
],
)
: const SizedBox(),
});
},
).expanded,
8.width,
ADatePicker(
label: context.translation.endTime,
hideShadow: true,
backgroundColor: AppColor.neutral100,
date: model.endDate,
formatDateWithTime: true,
from: DateTime.tryParse(createdDate ?? ''),
onDatePicker: (selectedDate) {
showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
).then((selectedTime) {
if (selectedTime != null) {
final endDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
selectedTime.hour,
selectedTime.minute,
);
if (!endDateTime.isBefore(DateTime.now())) {
"Please select a time before the current time.".showToast;
return;
}
if (model.startDate == null || !endDateTime.isAfter(model.startDate!)) {
"End date must be after start date".showToast;
return;
}
onUpdate((model) {
model.endDate = endDateTime;
ServiceRequestUtils.calculateAndAssignWorkingHours(
startTime: model.startDate,
endTime: model.endDate,
workingHoursController: workingHoursController,
updateModel: (hours) => model.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),
textAlign: TextAlign.center,
enable: false,
showShadow: false,
labelStyle: AppTextStyles.textFieldLabelStyle,
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
initialValue: model.techComment,
labelText: context.translation.technicalComment,
backgroundColor: AppColor.neutral100,
showShadow: false,
labelStyle: AppTextStyles.textFieldLabelStyle,
alignLabelWithHint: true,
textInputType: TextInputType.multiline,
onChange: (value) => onUpdate((model) => model.techComment = value),
// onSaved: (value) => onUpdate((model) => model.technicalComment = value),
),
8.height,
],
)
],
);
).toShadowContainer(context, paddingObject: const EdgeInsets.symmetric(horizontal: 16, vertical: 12))
.paddingOnly(bottom: 12);
}
}
// class AssistantEmployeeCard extends StatefulWidget {
// bool? isSender = false;
// DeviceTransfer? formModel;
//
// AssistantEmployeeCard({super.key, this.isSender, this.formModel});
//
// @override
// State<AssistantEmployeeCard> createState() => _AssistantEmployeeCardState();
// }
//
// class _AssistantEmployeeCardState extends State<AssistantEmployeeCard> {
// final TextEditingController _workingHoursController = TextEditingController(text: '');
// bool isCurrentUserIsAssistantEmp = false;
// bool isExpanded = false;
// List<AssetTransferAssistantEmployees> employeeList = [];
// AssistantEmployees selectedEmployee = AssistantEmployees();
// @override
// void initState() {
// WidgetsBinding.instance.addPostFrameCallback((_) {
// getInitialData();
// });
// super.initState();
// }
//
// Future<void> getInitialData() async {
// if (widget.isSender!) {
// employeeList = widget.formModel!.assetTransferAssistantEmployeesSender ?? [];
// } else {
// employeeList = widget.formModel!.assetTransferAssistantEmployeesReceiver ?? [];
// }
//
// widget.formModel?.modelAssistantEmployees = employeeList.isEmpty ? AssetTransferAssistantEmployees() : employeeList[0];
//
// AssignedEmployee? assignedUser = AssignedEmployee();
// if (employeeList.isNotEmpty) {
// assignedUser = AssignedEmployee(
// id: employeeList[0].employeeId,
// name: employeeList[0].employeeName,
// );
// }
// selectedEmployee = AssistantEmployees(userId: assignedUser.id, user: assignedUser);
// }
//
// @override
// void dispose() {
// _workingHoursController.dispose();
// super.dispose();
// }
//
// @override
// Widget build(BuildContext context) {
// 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,
// assetId: widget.formModel?.assetId ?? 0,
// initialValue: selectedEmployee,
// onSelect: (employee) {
// if (employee == null) {
// widget.formModel?.assistantEmployees = [];
// } else {
// selectedEmployee = employee;
// widget.formModel?.assistantEmployees = [employee.copyWith(id: 0)];
// widget.formModel?.modelAssistantEmployees?.employeeId = employee.user?.id;
// }
// },
// ),
// 8.height,
// Row(
// mainAxisSize: MainAxisSize.min,
// children: [
// ADatePicker(
// label: context.translation.startTime,
// hideShadow: true,
// backgroundColor: AppColor.neutral100,
// date: widget.formModel?.modelAssistantEmployees?.startDate,
// from: DateTime.tryParse(widget.formModel?.createdDate ?? ''),
// 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 (DateTime.tryParse(widget.formModel?.createdDate ?? '') != null && selectedDateTime.isBefore(DateTime.tryParse(widget.formModel?.createdDate ?? '')!)) {
// "Start time is before the request time.".showToast;
// selectedTime = null;
// return;
// }
// if (selectedDateTime.isAfter(DateTime.now())) {
// "Start time is after than current time".showToast;
// selectedTime = null;
// return;
// }
// widget.formModel?.modelAssistantEmployees?.startDate = selectedDateTime;
// ServiceRequestUtils.calculateAndAssignWorkingHours(
// startTime: widget.formModel?.modelAssistantEmployees?.startDate,
// endTime: widget.formModel?.modelAssistantEmployees?.endDate,
// workingHoursController: _workingHoursController,
// updateModel: (hours) {
// widget.formModel?.modelAssistantEmployees?.workingHours = hours;
// });
// setState(() {});
// }
// });
// },
// ).expanded,
// 8.width,
// ADatePicker(
// label: context.translation.endTime,
// hideShadow: true,
// backgroundColor: AppColor.neutral100,
// date: widget.formModel?.modelAssistantEmployees?.endDate,
// formatDateWithTime: true,
// onDatePicker: (selectedDate) {
// showTimePicker(
// context: context,
// initialTime: TimeOfDay.now(),
// ).then((selectedTime) {
// if (selectedTime != null) {
// DateTime selectedDateTime = DateTime(
// selectedDate.year,
// selectedDate.month,
// selectedDate.day,
// selectedTime.hour,
// selectedTime.minute,
// );
// if (widget.formModel?.modelAssistantEmployees?.startDate != null && selectedDateTime.isBefore(widget.formModel!.modelAssistantEmployees!.startDate!)) {
// "End Date time must be greater then start date".showToast;
// return;
// }
//
// selectedDate = selectedDate.add(Duration(hours: selectedTime.hour, minutes: selectedTime.minute));
// bool isBeforeCurrentTime = selectedDate.isBefore(DateTime.now());
// bool isAfterStartTime = selectedDate.isAfter(widget.formModel!.modelAssistantEmployees!.startDate!);
// if (!isBeforeCurrentTime) {
// "Please select a time before the current time.".showToast;
// return;
// }
// if (!isAfterStartTime) {
// "End Date time must be greater then start date".showToast;
// return;
// }
//
//
//
//
// widget.formModel?.modelAssistantEmployees?.endDate = selectedDateTime;
// ServiceRequestUtils.calculateAndAssignWorkingHours(
// startTime: widget.formModel?.modelAssistantEmployees?.startDate,
// endTime: widget.formModel?.modelAssistantEmployees?.endDate,
// workingHoursController: _workingHoursController,
// updateModel: (hours) {
// widget.formModel?.modelAssistantEmployees?.workingHours = hours;
// });
// setState(() {});
// }
// });
// },
// ).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: widget.formModel?.modelAssistantEmployees?.workingHours != null ? widget.formModel?.modelAssistantEmployees?.workingHours.toString() : '',
// textAlign: TextAlign.center,
// labelStyle: AppTextStyles.textFieldLabelStyle,
// enable: false,
// showShadow: false,
// style: Theme.of(context).textTheme.titleMedium,
// ),
// 8.height,
// AppTextFormField(
// initialValue: widget.formModel?.modelAssistantEmployees?.techComment,
// labelText: context.translation.technicalComment,
// backgroundColor: AppColor.neutral100,
// showShadow: false,
// labelStyle: AppTextStyles.textFieldLabelStyle,
// alignLabelWithHint: true,
// textInputType: TextInputType.multiline,
// onChange: (value) {
// widget.formModel?.modelAssistantEmployees?.techComment = value;
// },
// onSaved: (value) {
// widget.formModel?.modelAssistantEmployees?.techComment = value;
// },
// ),
// 16.height,
// ],
// )
// : const SizedBox(),
// ],
// );
// }
// }

@ -156,15 +156,18 @@ class _UpdateGasRefillRequestState extends State<UpdateGasRefillRequest> {
_formModel.gasRefillAttachments?.add(GasRefillAttachments(
id: 0, gasRefillId: _formModel.id ?? 0, attachmentName: ServiceRequestUtils.isLocalUrl(item.path) ? "${item.path.split("/").last}|${base64Encode(item.readAsBytesSync())}" : item.path));
}
await _gasRefillProvider?.updateGasRefill(status: status, model: _formModel).whenComplete(() {
if (status == 1) {
AllRequestsProvider allRequestsProvider = Provider.of<AllRequestsProvider>(context, listen: false);
// when click complete then this request remove from the list and status changes to closed..
_gasRefillProvider?.reset();
allRequestsProvider.getAllRequests(context, typeTransaction: 2);
}
Navigator.pop(context);
await _gasRefillProvider?.updateGasRefill(status: status, model: _formModel).then((success) {
Navigator.pop(context);
if (success) {
if (status == 1) {
AllRequestsProvider allRequestsProvider = Provider.of<AllRequestsProvider>(context, listen: false);
// when click complete then this request remove from the list and status changes to closed..
_gasRefillProvider?.reset();
allRequestsProvider.getAllRequests(context, typeTransaction: 2);
}
Navigator.pop(context);
}
});
}

@ -127,8 +127,7 @@ class _AddSupplierEngineerBottomSheetState extends State<AddSupplierEngineerBott
}
engineer.personRoleId = suppEngRole?.id;
// print(engineer.toJson());
// return;
SuppEngineerWorkOrders? suppEngineer = await snapshot.addSupplierEngineer(engineer);
if (suppEngineer == null) {
Fluttertoast.showToast(msg: context.translation.failedToCompleteRequest);

@ -378,11 +378,6 @@ class CreateServiceRequestPageState extends State<CreateServiceRequestPage> {
_serviceRequest.requestedThrough = Provider.of<RequestedThroughProvider>(context, listen: false).items.firstWhere((element) => element.value == 3, orElse: null);
_serviceRequest.type = Provider.of<TypeOfRequestProvider>(context, listen: false).items.firstWhere((element) => element.value == 1, orElse: null);
// print("_serviceRequest?.requestedThrough:${_serviceRequest?.requestedThrough.toJson()}");
// print("_serviceRequest?.type:${_serviceRequest?.type.toJson()}");
// print("_serviceRequest?.priority:${_serviceRequest?.priority.toJson()}");
// return;
if (_formKey.currentState!.validate() && await _serviceRequest.validateNewRequest(context)) {
_formKey.currentState!.save();

@ -60,7 +60,8 @@ class FilesList extends StatelessWidget {
body: SafeArea(
child: Stack(
children: [
InteractiveViewer(child: Image(image: getImageObject(itemIndex))).center,
// InteractiveViewer(child: Image(image: getImageObject(itemIndex))).center,
InteractiveViewer(child: getImageWidget(itemIndex)).center,
const ABackButton(),
],
),
@ -138,6 +139,13 @@ class FilesList extends StatelessWidget {
// });
}
Widget getImageWidget(int itemIndex) {
if (_isLocalUrl(images[itemIndex])) {
return Image.file(File(images[itemIndex]));
}
return ImageLoader(url: images[itemIndex]);
}
ImageProvider getImageObject(int itemIndex) {
if (_isLocalUrl(images[itemIndex])) {
return FileImage(File(images[itemIndex]));

@ -32,7 +32,6 @@ class _AutoCompletePartsFieldState extends State<AutoCompletePartsField> {
@override
void initState() {
print('initial value i got is ${widget.initialValue}');
_controller = TextEditingController(text: widget.initialValue);
super.initState();
}
@ -75,7 +74,7 @@ class _AutoCompletePartsFieldState extends State<AutoCompletePartsField> {
displayStringForOption: (SparePartsWorkOrders option) => widget.byName ? option.sparePart?.partName ?? "" : option.sparePart?.partNo ?? "",
fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) {
return TextField(
controller: _controller,
controller: _controller,
focusNode: fieldFocusNode,
style: AppTextStyles.bodyText.copyWith(color: AppColor.black10),
textAlign: TextAlign.start,
@ -96,8 +95,8 @@ class _AutoCompletePartsFieldState extends State<AutoCompletePartsField> {
labelStyle: AppTextStyles.tinyFont.copyWith(color: AppColor.neutral120),
),
textInputAction: TextInputAction.search,
onChanged: (text){
fieldTextEditingController.text =text;
onChanged: (text) {
fieldTextEditingController.text = text;
},
onSubmitted: (String value) {
onFieldSubmitted();

@ -158,7 +158,6 @@ class _ASoundPlayerState extends State<ASoundPlayer> {
if (_audio != widget.audio) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
_audio = widget.audio;
print('audio position i got is $_audioPosition');
if (_isLocalFile) {
await _audioPlayer.setSourceDeviceFile(_audio);
} else {
@ -199,7 +198,8 @@ class _ASoundPlayerState extends State<ASoundPlayer> {
).paddingOnly(start: 8, end: 8).expanded,
_failedToLoad
? Text("Failed to load", style: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red60))
: Text("${format(_audioPosition??Duration.zero)}/${format(_audioTime??Duration.zero)}", style: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)),
: Text("${format(_audioPosition ?? Duration.zero)}/${format(_audioTime ?? Duration.zero)}",
style: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)),
],
),
],

@ -1,12 +1,12 @@
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.dart';
import'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/models/lookup.dart';
import '../../../new_views/app_style/app_color.dart';
class SingleStatusMenu extends StatefulWidget {
final List<Lookup>? statuses;// Nullable list
final List<Lookup>? statuses; // Nullable list
final Lookup? initialStatus; // Nullable
final Function(Lookup?)? onSelect; // Nullable function, accepts nullable Lookup
final bool enabled;
@ -35,9 +35,7 @@ class _SingleStatusMenuState extends State<SingleStatusMenu> {
@override
void didUpdateWidget(covariant SingleStatusMenu oldWidget) {
_selectedStatus = widget.statuses?.firstWhere(
(element) => element == widget.initialStatus,
orElse: null);
_selectedStatus = widget.statuses?.firstWhere((element) => element == widget.initialStatus, orElse: null);
if (widget.initialStatus != _selectedStatus) {
widget.onSelect?.call(_selectedStatus); // Use null-aware operator
@ -48,10 +46,7 @@ class _SingleStatusMenuState extends State<SingleStatusMenu> {
@override
void initState() {
print('status lenght is ${widget.statuses?.length}');
_selectedStatus = widget.statuses?.firstWhere(
(element) => element == widget.initialStatus,
orElse: () => Lookup());
_selectedStatus = widget.statuses?.firstWhere((element) => element == widget.initialStatus, orElse: () => Lookup());
if (widget.initialStatus != _selectedStatus) {
widget.onSelect?.call(_selectedStatus); // Use null-aware operator
@ -62,25 +57,22 @@ class _SingleStatusMenuState extends State<SingleStatusMenu> {
@override
Widget build(BuildContext context) {
return Container(height: 60.toScreenHeight,
return Container(
height: 60.toScreenHeight,
padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth),
decoration: BoxDecoration(
color: context.isDark && (!widget.enabled || widget.statuses!.isEmpty)
? AppColor.neutral50
: (!widget.enabled || widget.statuses!.isEmpty)
? AppColor.neutral40
: AppColor.background(context),
? AppColor.neutral40
: AppColor.background(context),
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)
],
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)],
),
child: Stack(
alignment: Alignment.center,
children: [
if (widget.enabled)
const PositionedDirectional(
end: 0, child: Icon(Icons.keyboard_arrow_down_rounded)),
if (widget.enabled) const PositionedDirectional(end: 0, child: Icon(Icons.keyboard_arrow_down_rounded)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
@ -88,9 +80,7 @@ class _SingleStatusMenuState extends State<SingleStatusMenu> {
if (widget.title != null)
Text(
widget.title!, // Use null assertion operator
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: context.isDark ? null : AppColor.neutral20,
fontWeight: FontWeight.w500),
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500),
),
DropdownButton<Lookup>(
value: _selectedStatus,
@ -107,22 +97,22 @@ class _SingleStatusMenuState extends State<SingleStatusMenu> {
underline: const SizedBox.shrink(),
onChanged: !widget.enabled
? null
: (Lookup? newValue) { // Nullable Lookup
setState(() {
_selectedStatus = newValue;
});
widget.onSelect?.call(newValue); // Use null-aware operator
},
items: widget.statuses
?.map<DropdownMenuItem<Lookup>>((value) {
: (Lookup? newValue) {
// Nullable Lookup
setState(() {
_selectedStatus = newValue;
});
widget.onSelect?.call(newValue); // Use null-aware operator
},
items: widget.statuses?.map<DropdownMenuItem<Lookup>>((value) {
return DropdownMenuItem<Lookup>(
value: value,
child: Text(value.name ?? "", // Use null-aware operator
child: Text(
value.name ?? "", // Use null-aware operator
style: Theme.of(context).textTheme.bodyLarge,
),
);
})
.toList(),
}).toList(),
),
],
),
@ -130,4 +120,4 @@ class _SingleStatusMenuState extends State<SingleStatusMenu> {
),
);
}
}
}

@ -0,0 +1,95 @@
import 'package:flutter/material.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/new_models/work_order_detail_model.dart';
import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
class TotalWorkingTimeDetailBottomSheet extends StatelessWidget {
final List<ActivityMaintenanceTimers> activityMaintenanceTimers;
const TotalWorkingTimeDetailBottomSheet({Key? key, this.activityMaintenanceTimers = const []}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height * 0.5,
padding: const EdgeInsets.all(16),
child: Column(
children: [
Container(
width: 40.toScreenWidth,
height: 5.toScreenHeight,
decoration: BoxDecoration(color: AppColor.neutral40, borderRadius: BorderRadius.circular(30)),
),
8.height,
Align(
alignment: AlignmentDirectional.centerStart,
child: Text(
"Total Working Time",
style: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w600, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
),
ListView.separated(
itemCount: activityMaintenanceTimers.length,
separatorBuilder: (cxt, index) => const Divider().defaultStyle(context),
padding: const EdgeInsets.only(top: 16, bottom: 16),
itemBuilder: (cxt, index) {
int totalWorkingHours = DateTime.parse(activityMaintenanceTimers[index].endTime!).difference(DateTime.parse(activityMaintenanceTimers[index].startTime!)).inSeconds;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
RichText(
text: TextSpan(
text: "From: ",
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500),
children: [TextSpan(text: activityMaintenanceTimers[index].startTime!.toServiceRequestDetailsFormat, style: Theme.of(context).textTheme.bodySmall)])),
4.height,
RichText(
text: TextSpan(
text: "To: ",
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500),
children: [TextSpan(text: activityMaintenanceTimers[index].endTime!.toServiceRequestDetailsFormat, style: Theme.of(context).textTheme.bodySmall)])),
4.height,
RichText(
text: TextSpan(
text: "Duration: ",
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500),
children: [TextSpan(text: " ${ServiceRequestUtils.formatTimerDuration(totalWorkingHours.round())}", style: Theme.of(context).textTheme.bodySmall)])),
],
);
}).expanded,
],
),
);
}
}
class TimerModel {
int? id;
String? startTime;
String? endTime;
dynamic workingHours;
TimerModel({this.id, this.startTime, this.endTime, this.workingHours});
TimerModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
startTime = json['startTime'];
endTime = json['endTime'];
workingHours = json['workingHours'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['startTime'] = startTime;
data['endTime'] = endTime;
data['workingHours'] = workingHours;
return data;
}
}

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.3.3+22
version: 1.3.5+24
environment:
sdk: ">=3.5.0 <4.0.0"

Loading…
Cancel
Save