Merge branch 'design_3.0_asset_delivery_module' into design_3.0_medical_gas

# Conflicts:
#	lib/controllers/api_routes/urls.dart
#	lib/main.dart
#	lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart
#	lib/new_views/pages/land_page/my_request/my_requests_page.dart
design_3.0_demo_module
Sikander Saleem 2 months ago
commit 178309eb1e

@ -469,8 +469,6 @@
"-framework",
"\"nanopb\"",
"-framework",
"\"qr_code_scanner\"",
"-framework",
"\"speech_to_text\"",
"-framework",
"\"sqflite\"",
@ -689,8 +687,6 @@
"-framework",
"\"nanopb\"",
"-framework",
"\"qr_code_scanner\"",
"-framework",
"\"speech_to_text\"",
"-framework",
"\"sqflite\"",
@ -795,8 +791,6 @@
"-framework",
"\"nanopb\"",
"-framework",
"\"qr_code_scanner\"",
"-framework",
"\"speech_to_text\"",
"-framework",
"\"sqflite\"",

@ -1,12 +1,11 @@
class URLs {
URLs._();
static const String appReleaseBuildNumber = "28";
static const String appReleaseBuildNumber = "32";
// static const host1 = "https://atomsm.hmg.com"; // production url
// static const host1 = "https://atomsmdev.hmg.com"; // local DEV url
static const host1 = "https://atomsmuat.hmg.com"; // local UAT url
// static const host1 = "http://10.201.111.125:9495"; // temporary Server UAT url
// static String _baseUrl = "$_host/mobile";
@ -20,6 +19,7 @@ class URLs {
static const String chatHubUrlApi = "$chatHubUrl/api"; // new V2 apis
static const String chatHubUrlChat = "$chatHubUrl/hubs/chat"; // new V2 apis
static const String resetMessageCount = "$chatHubUrlApi/userChatHistory/AckMessages"; // new V2 apis
static const String chatApiKey = "f53a98286f82798d588f67a7f0db19f7aebc839e"; // new V2 apis
static String _host = host1;
@ -67,8 +67,12 @@ class URLs {
//service request new flow urls.
static get nurseDashboardCountUrl => '$_baseUrl/ServiceRequest/GetDashboardNurseCount';
static get qaAuditorCount => '$_baseUrl/ServiceRequest/GetDashboardQAAuditorCount';
static get commissioningEngineerCount => '$_baseUrl/ServiceRequest/GetDashboardCommissioningEngineerCount';
static get nurseDashboardDetailsUrl => '$_baseUrl/ServiceRequest/GetDashboardNurseDetails';
static get qaAuditorDetails => '$_baseUrl/ServiceRequest/GetDashboardQAAuditorDetails';
static get commissioningEngineerDetails => '$_baseUrl/ServiceRequest/GetDashboardCommissioningEngineerDetails';
static get nurseRejectUrl => '$_baseUrl/ServiceRequest/NurseReject';
@ -410,8 +414,10 @@ class URLs {
static get getLineFromExternalPo => '$_baseUrl/AssetDeliveryExternalDeliveryInspection/GetLineFromExternalPo';
static get getCostCenterListToLineDeliveryInspection => '$_baseUrl/AssetDeliveryExternalDeliveryInspection/GetCostCenterToLineDeliveryInspection';
static get getCostCenterAndSerialNoToLineDeliveryInspection => '$_baseUrl/AssetDeliveryExternalDeliveryInspection/GetCostCenterAndSerialNoToLineDeliveryInspection';
static get getCostCenterToPo => '$_baseUrl/AssetDeliveryExternalDeliveryInspection/GetCostCenterToPo';
static get saveCostCenterToLineDeliveryInspection => '$_baseUrl/AssetDeliveryExternalDeliveryInspection/SaveCostCenterToLineDeliveryInspection';
static get saveCostCenterAndSerialNoToLineDeliveryInspection => '$_baseUrl/AssetDeliveryExternalDeliveryInspection/SaveCostCenterAndSerialNoToLineDeliveryInspection';
static get getDeliveryHistories => '$_baseUrl/AssetDeliveryExternalDelivery/GetDeliveryHistories';
//Technical Inscption...
static get getLineFromTechnicalInspection => '$_baseUrl/AssetDeliveryExternalTechnicalInspection/GetLineFromDeliveryInspection';
@ -422,7 +428,7 @@ class URLs {
static get getAssetFromInternalTechnicalInspection => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/GetAssetFromInternal";
static get getAssetDetailsByAssetId => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/GetAssetByIdFromInternal";
static get saveTechnicalInspectionAsset => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/SaveTechnicalInspectionItem";
static get getAttachments => "$_baseUrl/AssetDeliveryExternalDeliveryAttachment/GetAttachments";
static get getAttachments => "$_baseUrl/AssetDeliveryExternalDeliveryAttachment/GetAttachmentsBasedOnType";
static get saveAttachments => "$_baseUrl/AssetDeliveryExternalDeliveryAttachment/Save";
//End user Acceptance...
static get getEndUserAcceptanceCostCenter => "$_baseUrl/AssetDeliveryEndUserAcceptance/GetEndUserAcceptanceCostCenter";
@ -430,7 +436,9 @@ class URLs {
static get saveEndUserFormData => "$_baseUrl/AssetDeliveryEndUserAcceptance/SaveMaster";
static get saveEndUserAssetData => "$_baseUrl/AssetDeliveryEndUserAcceptance/SaveDetail";
static get endUserStatusLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=534";
static get endUserRejectionReasonLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=5007";
// static get endUserRejectionReasonLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=5007";
static get endUserRejectionReasonLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=415";
static get attachmentTypeLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=5009";
//Medical Gas Request..
// 4004

@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
@ -13,6 +14,7 @@ import 'package:test_sa/modules/cx_module/survey/survey_page.dart';
import 'package:test_sa/modules/pm_module/ppm_wo/ppm_details_page.dart';
import 'package:test_sa/modules/pm_module/recurrent_wo/recurrent_work_order_view.dart';
import 'package:test_sa/modules/tm_module/device_transfer/device_transfer_details.dart';
import 'package:test_sa/modules/tm_module/tasks/task_request_detail_view.dart';
import 'package:test_sa/modules/tm_module/gas_refill/gas_refill_details.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
@ -88,6 +90,44 @@ class FirebaseNotificationManger {
String? transactionType = messageData["transactionType"]?.toString();
if (transactionType == null) {
return;
} else if (transactionType == "17" && messageData["requestType"] == "chat") {
int moduleId = int.parse(messageData["moduleId"].toString());
int requestNumber = int.parse(messageData["requestNumber"].toString());
switch (moduleId) {
case 1: // cm
serviceClass = CMDetailPage(requestId: requestNumber, moduleId: moduleId);
break;
case 2: // gas refill
serviceClass = GasRefillDetailsPage(
priority: messageData["priority"],
date: messageData["createdOn"],
moduleId: moduleId,
model: GasRefillModel(id: requestNumber),
);
break;
case 3: //transfer
serviceClass = DeviceTransferDetails(model: DeviceTransfer(id: requestNumber), moduleId: moduleId);
break;
case 6: // task
serviceClass = TaskRequestDetailsView(
taskId: requestNumber,
moduleId: moduleId,
// requestDetails: RequestsDetails(nameOfType: requestData?.nameOfType, status: requestData?.statusName, priority: requestData?.priorityName, date: requestData?.transactionDate, ),
);
// ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1);
break;
default:
serviceClass = const Scaffold(body: Center(child: NoDataFound()));
}
Navigator.of(context).push(MaterialPageRoute(builder: (_) => serviceClass!));
return;
}
// PPM=1,
// ServiceRequestEngineer = 3,
// AssetTransfer=7,
@ -101,21 +141,22 @@ class FirebaseNotificationManger {
break;
//these three request are same corrective maintenance....
case "3":
serviceClass = CMDetailPage(requestId: int.parse(messageData["requestNumber"].toString()));
serviceClass = CMDetailPage(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1);
break;
case "8":
serviceClass = CMDetailPage(requestId: int.parse(messageData["requestNumber"].toString()));
serviceClass = CMDetailPage(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1);
break;
case "11":
serviceClass = CMDetailPage(requestId: int.parse(messageData["requestNumber"].toString()));
serviceClass = CMDetailPage(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1);
break;
case "7":
serviceClass = DeviceTransferDetails(model: DeviceTransfer(id: int.parse(messageData["requestNumber"].toString())));
serviceClass = DeviceTransferDetails(model: DeviceTransfer(id: int.parse(messageData["requestNumber"].toString())), moduleId: 3);
break;
case "9":
serviceClass = GasRefillDetailsPage(
priority: messageData["priority"],
date: messageData["createdOn"],
moduleId: 2,
model: GasRefillModel(id: int.parse(messageData["requestNumber"].toString())),
);
break;

@ -140,6 +140,7 @@ class AllRequestsProvider extends ChangeNotifier {
}
if (context.userProvider.isQualityUser) {
list = [10, 11];
if (context.settingProvider.isUserFlowMedical) list.insert(0, 7);
return list;
}
@ -165,7 +166,11 @@ class AllRequestsProvider extends ChangeNotifier {
if (context.userProvider.isNurse) {
list.add(9); //
}
list.add(12); // loan module
list.add(12);
if (context.userProvider.isCommissioningEngineer) {
list = [13];
}
// loan module
return list;
}

@ -47,6 +47,7 @@ class UserProvider extends ChangeNotifier {
bool get isAssessor => user!.type == UsersTypes.assessor || user!.type == UsersTypes.assessorTl;
bool get isQualityUser => user!.type == UsersTypes.qualityUser;
bool get isCommissioningEngineer => user!.type == UsersTypes.commissioningEngineer;
VerifyOtpModel _verifyOtpModel = VerifyOtpModel();
SwipeTransaction _swipeTransactionModel = SwipeTransaction();

@ -106,11 +106,22 @@ class DashBoardProvider extends ChangeNotifier {
isAllCountLoading = true;
notifyListeners();
String url = '';
if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) {
url = URLs.engineerDashboardCountUrl;
} else {
url = URLs.nurseDashboardCountUrl;
}
// if (usersType == UsersTypes.qualityUser) {
// url = URLs.qaAuditorCount;
// } else if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) {
// url = URLs.engineerDashboardCountUrl;
// } else {
// url = URLs.nurseDashboardCountUrl;
// }
final urlMap = {
UsersTypes.qualityUser: URLs.qaAuditorCount,
UsersTypes.commissioningEngineer: URLs.commissioningEngineerCount,
UsersTypes.engineer: URLs.engineerDashboardCountUrl,
UsersTypes.assessor: URLs.engineerDashboardCountUrl,
UsersTypes.assessorTl: URLs.engineerDashboardCountUrl,
};
url = urlMap[usersType] ?? URLs.nurseDashboardCountUrl;
Response response;
try {
response = await ApiManager.instance.post(url, body: {});
@ -233,6 +244,32 @@ class DashBoardProvider extends ChangeNotifier {
}
}
String getDashboardUrl({
required UsersTypes usersType,
int? status,
int? tabId,
}) {
switch (usersType) {
case UsersTypes.qualityUser:
return URLs.qaAuditorDetails;
case UsersTypes.commissioningEngineer:
return URLs.commissioningEngineerDetails;
case UsersTypes.engineer:
case UsersTypes.assessor:
case UsersTypes.assessorTl:
if (status == 0) {
return URLs.engineerDashboardUpcoming;
} else if (status == 1 && tabId == 1) {
return URLs.engineerDashboardNotAssignDetails;
} else {
// status == 1 && tabId == 2 OR any other case
return URLs.engineerDashboardDetailsUrl;
}
default:
return URLs.nurseDashboardDetailsUrl;
}
}
Future<int> getRequestDetail({bool showLoader = true, required UsersTypes usersType, int? status, bool isHighPriority = false, bool isOverdue = false, String? date, int? tabId}) async {
if (showLoader) {
isDetailLoading = showLoader;
@ -241,22 +278,26 @@ class DashBoardProvider extends ChangeNotifier {
Response response;
String url = '';
if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) {
//need to check pagination for not assigned task @waseem.
//these checks are to call different apis for dashboard for engineer...
if (status == 0) {
url = URLs.engineerDashboardUpcoming;
} else if (status == 1 && tabId == 1) {
url = URLs.engineerDashboardNotAssignDetails;
} else if (status == 1 && tabId == 2) {
url = URLs.engineerDashboardDetailsUrl;
} else {
url = URLs.engineerDashboardDetailsUrl;
}
} else {
url = URLs.nurseDashboardDetailsUrl;
}
// if (usersType == UsersTypes.qualityUser) {
// url = URLs.qaAuditorDetails;
// } else if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) {
// //need to check pagination for not assigned task @waseem.
//
// //these checks are to call different apis for dashboard for engineer...
// if (status == 0) {
// url = URLs.engineerDashboardUpcoming;
// } else if (status == 1 && tabId == 1) {
// url = URLs.engineerDashboardNotAssignDetails;
// } else if (status == 1 && tabId == 2) {
// url = URLs.engineerDashboardDetailsUrl;
// } else {
// url = URLs.engineerDashboardDetailsUrl;
// }
// } else {
// url = URLs.nurseDashboardDetailsUrl;
// }
url = getDashboardUrl(usersType: usersType, status: status, tabId: tabId);
try {
Map<String, dynamic> body = {"pageNumber": pageNum, "pageSize": pageItemNumber};
@ -372,6 +413,19 @@ class CategoryTabs {
return tabs;
}
if (userType == UsersTypes.qualityUser) {
tabs.add(CategoryTabs(label: 'Open', tag: 1, id: 2));
tabs.add(CategoryTabs(label: 'In Progress', tag: 2, id: 3));
tabs.add(CategoryTabs(label: 'Closed', tag: 3, id: 4));
return tabs;
}
if (userType == UsersTypes.commissioningEngineer) {
tabs.add(CategoryTabs(label: 'Open', tag: 1, id: 2));
tabs.add(CategoryTabs(label: 'In Progress', tag: 2, id: 3));
tabs.add(CategoryTabs(label: 'Closed', tag: 3, id: 4));
return tabs;
}
tabs.add(CategoryTabs(label: 'Open', tag: 1, id: 2));
tabs.add(CategoryTabs(label: 'In Progress', tag: 2, id: 3));
tabs.add(CategoryTabs(label: 'Pending Acknowledgement', tag: 3, id: 1));

@ -73,14 +73,14 @@ class _DashboardViewState extends State<DashboardView> {
// await requestDetailProvider.engineerRejectWorkOrder(id: '3', feedBack: 'Abcdef');
// await dashBoardProvider.getDashBoardCount(usersType: user.type!);
// await getAllRequests();
if (isFCM) {
FirebaseNotificationManger.initialized(context);
NotificationManger.initialisation((notificationDetails) {
FirebaseNotificationManger.handleMessage(context, json.decode(notificationDetails.payload!));
}, (id, title, body, payload) async {});
isFCM = false;
}
// if (isFCM) {
// FirebaseNotificationManger.initialized(context);
// NotificationManger.initialisation((notificationDetails) {
// FirebaseNotificationManger.handleMessage(context, json.decode(notificationDetails.payload!));
// }, (id, title, body, payload) async {});
//
// isFCM = false;
// }
});
}

@ -182,6 +182,13 @@ class ProgressFragment extends StatelessWidget {
ChartData('Open', snapshot.dashboardCount?.data?.countOpen?.toDouble() ?? 0.0, AppColor.blueStatus(context)),
ChartData('In Progress', snapshot.dashboardCount?.data?.countInprogress?.toDouble() ?? 0.0, AppColor.orange70),
];
} else if (_userProvider.isQualityUser) {
total = (snapshot.dashboardCount?.data?.countComplete ?? 0) + (snapshot.dashboardCount?.data?.countInprogress ?? 0) + (snapshot.dashboardCount?.data?.countOpen ?? 0);
chartData = [
ChartData('Closed', snapshot.dashboardCount?.data?.countComplete?.toDouble() ?? 0.0, AppColor.green70),
ChartData('Open', snapshot.dashboardCount?.data?.countOpen?.toDouble() ?? 0.0, AppColor.blueStatus(context)),
ChartData('In Progress', snapshot.dashboardCount?.data?.countInprogress?.toDouble() ?? 0.0, AppColor.orange70),
];
} else {
total = (snapshot.dashboardCount?.data?.countComplete ?? 0) + (snapshot.dashboardCount?.data?.countInprogress ?? 0) + (snapshot.dashboardCount?.data?.countOpen ?? 0);
chartData = [
@ -248,7 +255,7 @@ class ProgressFragment extends StatelessWidget {
showChartValuesOutside: false,
decimalPlaces: 1,
),
).toShimmer(isShow: snapshot.isAllCountLoading, radius: 250,context: context).paddingAll(0).toShadowContainer(context),
).toShimmer(isShow: snapshot.isAllCountLoading, radius: 250, context: context).paddingAll(0).toShadowContainer(context),
],
),
),

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/new_models/dashboard_detail.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_item_view.dart';
import 'package:test_sa/modules/asset_inventory_module/pages/inventory_session_item_view.dart';
import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_item_view.dart';
import 'package:test_sa/modules/tm_module/tasks/task_request_item_view.dart';
@ -62,6 +63,8 @@ class RequestCategoryList extends StatelessWidget {
return EquipmentInternalAuditItemView(requestData: request);
case 11:
return EquipmentInternalAuditItemView(requestData: request);
case 13:
return AssetDeliveryItemView(requestData: request);
default:
return Container(
height: 100,

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

@ -121,7 +121,37 @@ extension StringExtensionsRecurrentTaskInpesctionDataType on String {
}
}
}
extension IntExtensionsAssetDeliveryStage on int {
AssetDeliveryStageEnum toAssetDeliveryStageEnum() {
switch (this) {
case 2:
return AssetDeliveryStageEnum.deliveryInspection;
case 3:
return AssetDeliveryStageEnum.technicalInspection;
case 4:
return AssetDeliveryStageEnum.endUserAcceptance;
default:
return AssetDeliveryStageEnum.other;
}
}
}
enum DropDownMatchType {
identifier,
name,
}
enum CostCenterType {
costCenter,
serialNumber,
}
enum AssetDeliveryStageEnum {
deliveryInspection,
technicalInspection,
endUserAcceptance,
other,
}

@ -28,6 +28,7 @@ import 'package:test_sa/controllers/providers/api/status_drop_down/report/servic
import 'package:test_sa/controllers/providers/api/status_drop_down/report/service_report_repair_location_provider.dart';
import 'package:test_sa/controllers/providers/api/status_drop_down/report/service_types_provider.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/attachment_type_provider.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/cost_center_provider.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/end_user_rejection_reason_lookup_provider.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/end_user_status_lookup_provider.dart';
@ -140,6 +141,7 @@ import 'providers/service_request_providers/reject_reason_provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
// HttpOverrides.global = MyHttpOverrides(); // for later use.
_configureLocalTimeZone();
NotificationManger.initialisation((notificationDetails) {}, (id, title, body, payload) async {});
@ -331,6 +333,7 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => PowerSupplyTypeProvider()),
ChangeNotifierProvider(create: (_) => EndUserStatusLookupProvider()),
ChangeNotifierProvider(create: (_) => EndUserRejectionReasonLookupProvider()),
ChangeNotifierProvider(create: (_) => AttachmentTypeLookupProvider()),
ChangeNotifierProvider(create: (_) => MedicalGasOrderTypeProvider()),
ChangeNotifierProvider(create: (_) => MedicalGasItemTypeProvider()),
ChangeNotifierProvider(create: (_) => MedicalGasInspectionProvider()),

@ -38,6 +38,7 @@ class DeviceTransfer {
this.senderEngSignature,
this.senderAttachments,
this.receiverAssignedEmployeeId,
this.receiverAssignedEmployeeNumber,
this.receiverMachineStatusId,
this.senderMachineStatusValue,
this.receiverComment,
@ -74,6 +75,7 @@ class DeviceTransfer {
this.receiverMachineStatusValue,
this.receiverMachineStatusName,
this.senderAssignedEmployeeName,
this.senderAssignedEmployeeNumber,
this.senderBuildingName,
this.senderDepartmentName,
this.senderEngSignatureUrl,
@ -144,6 +146,7 @@ class DeviceTransfer {
});
}
receiverAssignedEmployeeId = json['receiverAssignedEmployeeId'];
receiverAssignedEmployeeNumber = json['receiverAssignedEmployeeNumber'];
receiverMachineStatusId = json['receiverMachineStatusId'];
receiverMachineStatusValue = json['receiverMachineStatusValue'];
receiverComment = json['receiverComment'];
@ -220,6 +223,7 @@ class DeviceTransfer {
receiverEngSignatureUrl = json['receiverEngSignatureUrl'];
receiverMachineStatusName = json['receiverMachineStatusName'];
senderAssignedEmployeeName = json['senderAssignedEmployeeName'];
senderAssignedEmployeeNumber = json['senderAssignedEmployeeNumber'];
senderBuildingName = json['senderBuildingName'];
senderDepartmentName = json['senderDepartmentName'];
senderEngSignatureUrl = json['senderEngSignatureUrl'];
@ -270,6 +274,7 @@ class DeviceTransfer {
List<AssetTransferAttachment>? attachments;
List<AssetTransferAttachment>? assetTransferAttachments;
String? receiverAssignedEmployeeId;
String? receiverAssignedEmployeeNumber;
num? receiverMachineStatusId;
int? receiverMachineStatusValue;
String? receiverComment;
@ -290,6 +295,7 @@ class DeviceTransfer {
String? senderFloorName;
String? senderDepartmentName;
String? senderAssignedEmployeeName;
String? senderAssignedEmployeeNumber;
String? senderMachineStatusName;
String? senderEngSignatureUrl;
String? receiverAssignedEmployeeName;
@ -345,6 +351,7 @@ class DeviceTransfer {
String? senderEngSignature,
List<AssetTransferAttachment>? senderAttachments,
String? receiverAssignedEmployeeId,
String? receiverAssignedEmployeeNumber,
num? receiverMachineStatusId,
String? receiverComment,
String? receiverStartDate,
@ -364,6 +371,7 @@ class DeviceTransfer {
String? senderFloorName,
String? senderDepartmentName,
String? senderAssignedEmployeeName,
String? senderAssignedEmployeeNumber,
String? senderMachineStatusName,
String? senderEngSignatureUrl,
String? receiverAssignedEmployeeName,
@ -420,6 +428,7 @@ class DeviceTransfer {
senderEngSignature: senderEngSignature ?? this.senderEngSignature,
senderAttachments: senderAttachments ?? this.senderAttachments,
receiverAssignedEmployeeId: receiverAssignedEmployeeId ?? this.receiverAssignedEmployeeId,
receiverAssignedEmployeeNumber: receiverAssignedEmployeeNumber ?? this.receiverAssignedEmployeeNumber,
receiverMachineStatusId: receiverMachineStatusId ?? this.receiverMachineStatusId,
receiverComment: receiverComment ?? this.receiverComment,
receiverStartDate: receiverStartDate ?? this.receiverStartDate,
@ -439,6 +448,7 @@ class DeviceTransfer {
senderFloorName: senderFloorName ?? this.senderFloorName,
senderDepartmentName: senderDepartmentName ?? this.senderDepartmentName,
senderAssignedEmployeeName: senderAssignedEmployeeName ?? this.senderAssignedEmployeeName,
senderAssignedEmployeeNumber: senderAssignedEmployeeNumber ?? this.senderAssignedEmployeeNumber,
senderMachineStatusName: senderMachineStatusName ?? this.senderMachineStatusName,
senderMachineStatusValue: senderMachineStatusValue ?? this.senderMachineStatusValue,
senderEngSignatureUrl: senderEngSignatureUrl ?? this.senderEngSignatureUrl,
@ -503,6 +513,7 @@ class DeviceTransfer {
map['senderAttachments'] = senderAttachments!.map((v) => v.toJson()).toList();
}
map['receiverAssignedEmployeeId'] = receiverAssignedEmployeeId;
map['receiverAssignedEmployeeNumber'] = receiverAssignedEmployeeNumber;
map['receiverMachineStatusId'] = receiverMachineStatusId;
map['receiverComment'] = receiverComment;
map['receiverStartDate'] = receiverStartDate;
@ -524,6 +535,7 @@ class DeviceTransfer {
map["senderFloorName"] = senderFloorName;
map["senderDepartmentName"] = senderDepartmentName;
map["senderAssignedEmployeeName"] = senderAssignedEmployeeName;
map["senderAssignedEmployeeNumber"] = senderAssignedEmployeeNumber;
map["senderMachineStatusName"] = senderMachineStatusName;
map["senderEngSignatureUrl"] = senderEngSignatureUrl;
map["receiverAssignedEmployeeName"] = receiverAssignedEmployeeName;
@ -631,6 +643,7 @@ class DeviceTransfer {
map['assetTransferReceiverTimers'] = receiverVisitTimers!.map((v) => v.toJson()).toList();
}
map['receiverAssignedEmployeeId'] = receiverAssignedEmployeeId;
map['receiverAssignedEmployeeNumber'] = receiverAssignedEmployeeNumber;
map['receiverMachineStatusId'] = receiverMachineStatusId;
map['receiverComment'] = receiverComment;
map['receiverStartDate'] = receiverStartDate;
@ -674,6 +687,7 @@ class DeviceTransfer {
senderEngSignature = assetTransfer.senderEngSignature;
senderAttachments = assetTransfer.senderAttachments;
receiverAssignedEmployeeId = assetTransfer.receiverAssignedEmployeeId;
receiverAssignedEmployeeNumber = assetTransfer.receiverAssignedEmployeeNumber;
receiverMachineStatusId = assetTransfer.receiverMachineStatusId;
receiverComment = assetTransfer.receiverComment;
receiverStartDate = assetTransfer.receiverStartDate;
@ -693,6 +707,7 @@ class DeviceTransfer {
senderFloorName = assetTransfer.senderFloorName;
senderDepartmentName = assetTransfer.senderDepartmentName;
senderAssignedEmployeeName = assetTransfer.senderAssignedEmployeeName;
senderAssignedEmployeeNumber = assetTransfer.senderAssignedEmployeeNumber;
senderMachineStatusName = assetTransfer.senderMachineStatusName;
senderEngSignatureUrl = assetTransfer.senderEngSignatureUrl;
receiverAssignedEmployeeName = assetTransfer.receiverAssignedEmployeeName;

@ -4,5 +4,6 @@ enum UsersTypes {
nurse, // 1
assessor,
assessorTl,
qualityUser
qualityUser,
commissioningEngineer
}

@ -7,9 +7,10 @@ class Lookup extends Base {
int? id; // Now nullable
int? value; // Now nullable
WorkOrderNextStepEnum? workOrderNextStepEnum; // Now nullable
AssetDeliveryStageEnum? assetDeliveryStageEnum; // Now nullable
String? name; // Now nullable
Lookup({this.id, this.value, this.name, this.workOrderNextStepEnum}) : super(identifier: id?.toString(), name: name);
Lookup({this.id, this.value, this.name, this.workOrderNextStepEnum, this.assetDeliveryStageEnum}) : super(identifier: id?.toString(), name: name);
@override
bool operator ==(Object other) => identical(this, other) || other is Lookup && ((value != null && value == other.value) || (id != null && id == other.id));
@ -38,10 +39,11 @@ class Lookup extends Base {
// }
factory Lookup.fromJson(Map<String, dynamic>? parsedJson) {
if(parsedJson==null) return Lookup();
if (parsedJson == null) return Lookup();
return Lookup(
name: parsedJson["name"],
workOrderNextStepEnum: parsedJson["value"] == null ? null : (parsedJson["value"] as int).toWorkOrderNextStepEnum(),
assetDeliveryStageEnum: parsedJson["value"] == null ? null : (parsedJson["value"] as int).toAssetDeliveryStageEnum(),
id: parsedJson["id"],
value: parsedJson["value"],
);

@ -523,3 +523,55 @@ class GasRefillContactPerson {
};
}
}
class AssignedEmployee {
String? id;
String? name;
String? email;
String? employeeId;
int? languageId;
String? extensionNo;
String? phoneNumber;
String? positionName;
String? position;
bool? isActive;
AssignedEmployee(
{this.id,
this.name,
this.email,
this.employeeId,
this.languageId,
this.extensionNo,
this.phoneNumber,
this.positionName,
this.position,
this.isActive});
AssignedEmployee.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
email = json['email'];
employeeId = json['employeeId'];
languageId = json['languageId'];
extensionNo = json['extensionNo'];
phoneNumber = json['phoneNumber'];
positionName = json['positionName'];
position = json['position'];
isActive = json['isActive'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
data['email'] = this.email;
data['employeeId'] = this.employeeId;
data['languageId'] = this.languageId;
data['extensionNo'] = this.extensionNo;
data['phoneNumber'] = this.phoneNumber;
data['positionName'] = this.positionName;
data['position'] = this.position;
data['isActive'] = this.isActive;
return data;
}
}

@ -12,6 +12,7 @@ class User {
List<String>? departmentName;
String? message;
String? username;
String? employeeId;
String? userID;
String? email;
String? password;
@ -53,6 +54,7 @@ class User {
this.departmentName,
this.message,
this.username,
this.employeeId,
this.userID,
this.email,
this.password,
@ -108,8 +110,13 @@ class User {
return UsersTypes.assessor;
case "R-19": // Head Nurse Role
return UsersTypes.assessorTl;
//TODO need to confirm why repeat.
case "R-3": // Quality User
return UsersTypes.qualityUser;
case "R-58": // Quality User
return UsersTypes.qualityUser;
case "R-201": // Quality User
return UsersTypes.commissioningEngineer;
default:
return null;
}
@ -146,6 +153,7 @@ class User {
map['message'] = message;
map['username'] = username;
map['userID'] = userID;
map['employeeId'] = employeeId;
map['email'] = email;
map['password'] = password;
map['token'] = token;
@ -207,6 +215,7 @@ class User {
message = json['message'];
username = json['username'];
userID = json['userID'];
employeeId = json['employeeId'];
email = json['email'];
password = json['password'];
token = json['token'];

@ -0,0 +1,73 @@
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/models/generic_attachment_model.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/attachment_view.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/attachment_type_provider.dart';
import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart';
import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
class HelperFunction {
static void attachmentTap({required BuildContext context, bool viewOnly = false, required AssetDeliveryProvider assetDeliveryProvider, required dynamic deliveryTableItemId}) async {
Lookup? result;
result = await showAttachmentTypeBottomSheet(context);
List<GenericAttachmentModel> list = [];
if (result != null) {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => const AppLazyLoading(),
);
list = await assetDeliveryProvider.getAttachments(tableItemId: deliveryTableItemId, attachmentType: result.value);
Navigator.pop(context);
// if (list.isNotEmpty) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AssetDeliveryAttachmentView(
tableItemId: deliveryTableItemId,
attachmentType: result!.value,
viewOnly: viewOnly,
attachmentList: list,
)));
// }
}
}
static Future<Lookup?> showAttachmentTypeBottomSheet(BuildContext context) {
return showModalBottomSheet<Lookup?>(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
builder: (_) {
return SafeArea(
top: false,
child: Padding(
padding: const EdgeInsets.only(
top: 16,
left: 16,
right: 16,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SingleItemDropDownMenu<Lookup, AttachmentTypeLookupProvider>(
context: context,
title: context.translation.reason,
onSelect: (value) {
Navigator.pop(context, value);
},
),
],
),
),
);
},
);
}
}

@ -241,7 +241,7 @@ class AssetDeliveryInternalDetail {
num? qtyCancelled;
num? unitPrice;
num? unitPriceWithTax;
String? itemCategory;
Lookup? itemCategory;
num? totalAssets;
num? childPerAsset;
@ -259,7 +259,9 @@ class AssetDeliveryInternalDetail {
qtyCancelled = json['qtyCancelled'];
unitPrice = json['unitPrice'];
unitPriceWithTax = json['unitPriceWithTax'];
itemCategory = json['itemCategory'];
itemCategory = json['itemCategory'] != null
? Lookup.fromJson(json['itemCategory'])
: null;
totalAssets = json['totalAssets'];
childPerAsset = json['childPerAsset'];
}
@ -303,6 +305,7 @@ class AssetDeliveryTableModel {
status = json['status'] != null
? Lookup.fromJson(json['status'])
: null;
}
Map<String, dynamic> toJson() {

@ -40,11 +40,6 @@ class CostCenterModel {
data['id'] = id ?? 0;
data['qty'] = quantity;
// data['costCenter'] = costCenter;
// data['acceptedBy'] = acceptedBy;
// data['acceptanceDate'] = acceptanceDate;
// data['acceptedByEmail'] = acceptedByEmail;
// data['acceptedBySignature'] = acceptedBySignature;
data['costCenter'] = costCenterName;
return data;
}

@ -0,0 +1,53 @@
import 'package:test_sa/modules/asset_delivery_module/models/cost_center_model.dart';
class CostCenterSerialNoFormModel {
List<CostCenterModel>? costCenters;
List<SerialNoModel>? serialNos;
CostCenterSerialNoFormModel({
this.costCenters,
this.serialNos,
});
// From JSON
CostCenterSerialNoFormModel.fromJson(Map<String, dynamic> json) {
if (json['costCenters'] != null) {
costCenters = List<CostCenterModel>.from((json['costCenters'] as List).map((e) => CostCenterModel.fromJson(e)));
}
if (json['serialNos'] != null) {
serialNos = List<SerialNoModel>.from((json['serialNos'] as List).map((e) => SerialNoModel.fromJson(e)));
}
}
// To JSON
Map<String, dynamic> toJson() {
return {
'costCenters': costCenters?.map((e) => e.toJson()).toList() ?? [],
'serialNos': serialNos?.map((e) => e.toJson()).toList() ?? [],
};
}
}
class SerialNoModel {
int? id;
String? serialNo;
SerialNoModel({
this.id,
this.serialNo,
});
SerialNoModel.fromJson(Map<String, dynamic> json) {
id = json['id'] ?? json['costCenterId'];
serialNo = json['serialNo'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['id'] = id ?? 0;
data['serialNo'] = serialNo;
return data;
}
}

@ -1,6 +1,11 @@
import 'package:test_sa/modules/asset_delivery_module/models/inspection_person_model.dart';
class DeliveryInspectionFormModel {
int? assetDeliveryExternalDeliveryId;
String? deliveryInspectionInspectedBy;
InspectionPersonModel? inspectedByModel;
InspectionPersonModel? approvedByModel;
InspectionPersonModel? thirdApprovalModel;
DateTime? deliveryInspectionInspectedDateFrom;
DateTime? deliveryInspectionInspectedDateTo;
num? totalTime;
@ -9,6 +14,9 @@ class DeliveryInspectionFormModel {
DeliveryInspectionFormModel({
this.assetDeliveryExternalDeliveryId,
this.deliveryInspectionInspectedBy,
this.inspectedByModel,
this.approvedByModel,
this.thirdApprovalModel,
this.deliveryInspectionInspectedDateFrom,
this.deliveryInspectionInspectedDateTo,
this.totalTime,
@ -18,20 +26,32 @@ class DeliveryInspectionFormModel {
DeliveryInspectionFormModel.fromJson(Map<String, dynamic> json) {
assetDeliveryExternalDeliveryId = json['assetDeliveryExternalDeliveryId'];
deliveryInspectionInspectedBy = json['deliveryInspectionInspectedBy'];
deliveryInspectionInspectedDateFrom = json['deliveryInspectionInspectedDateFrom']!=null?DateTime.tryParse(json['deliveryInspectionInspectedDateFrom']):null;
deliveryInspectionInspectedDateTo = json['deliveryInspectionInspectedDateTo']!=null?DateTime.tryParse(json['deliveryInspectionInspectedDateTo']):null;
inspectedByModel = InspectionPersonModel(name: json['deliveryInspectionInspectedBy'], email: json['deliveryInspectionInspectedByEmail'], signature: json['deliveryInspectionInspectedBySignature']);
approvedByModel = InspectionPersonModel(name: json['deliveryInspectionApprovedBy'], email: json['deliveryInspectionApprovedByEmail'], signature: json['deliveryInspectionApprovedBySignature']);
thirdApprovalModel =
InspectionPersonModel(name: json['deliveryInspectionThirdApproverBy'], email: json['deliveryInspectionThirdApproverByEmail'], signature: json['deliveryInspectionThirdApproverBySignature']);
deliveryInspectionInspectedDateFrom = json['deliveryInspectionInspectedDateFrom'] != null ? DateTime.tryParse(json['deliveryInspectionInspectedDateFrom']) : null;
deliveryInspectionInspectedDateTo = json['deliveryInspectionInspectedDateTo'] != null ? DateTime.tryParse(json['deliveryInspectionInspectedDateTo']) : null;
totalTime = json['totalTime'];
deliveryInspectionApprovedBy = json['deliveryInspectionApprovedBy'];
}
//TODO need to set payload..
Map<String, dynamic> toJson() {
return {
'assetDeliveryExternalDeliveryId': assetDeliveryExternalDeliveryId,
'deliveryInspectionInspectedBy': deliveryInspectionInspectedBy,
'deliveryInspectionInspectedDateFrom': deliveryInspectionInspectedDateFrom?.toIso8601String(),
'deliveryInspectionInspectedDateFrom': deliveryInspectionInspectedDateFrom?.toIso8601String(),
'deliveryInspectionInspectedDateTo': deliveryInspectionInspectedDateTo?.toIso8601String(),
'totalTime': totalTime,
'deliveryInspectionApprovedBy': deliveryInspectionApprovedBy,
'deliveryInspectionInspectedBy': inspectedByModel?.name,
"deliveryInspectionInspectedBySignature": inspectedByModel?.signature,
"deliveryInspectionInspectedByEmail": inspectedByModel?.email,
'deliveryInspectionApprovedBy': approvedByModel?.name,
"deliveryInspectionApprovedBySignature": approvedByModel?.signature,
"deliveryInspectionApprovedByEmail": approvedByModel?.email,
"deliveryInspectionThirdApproverBy": thirdApprovalModel?.name,
"deliveryInspectionThirdApproverBySignature": thirdApprovalModel?.signature,
"deliveryInspectionThirdApproverByEmail": thirdApprovalModel?.email,
};
}
}

@ -0,0 +1,15 @@
import 'package:flutter/services.dart';
class InspectionPersonModel {
String? name;
String? email;
String? signature;
Uint8List? localSignature;
InspectionPersonModel({
this.name,
this.email,
this.signature,
this.localSignature,
});
}

@ -136,7 +136,7 @@ class TechnicalInspectionAssetModel {
Map<String, dynamic> toChildJson() {
final Map<String, dynamic> data = {};
data['id'] = id;
data['id'] = id ?? 0;
data['assetDeliveryInternalDetailAssetChildId'] = assetDeliveryInternalDetailAssetChildId;
data['assetNumber'] = assetNumber;
data['assetOriginId'] = assetOrigin?.id;

@ -6,6 +6,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/modules/asset_delivery_module/models/asset_delivery_data_model.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_stage_tab_page.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_table_card_view.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/change_status_bottomsheet.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/delivery_inspection/delivery_inspection_form_view.dart';
@ -35,6 +36,7 @@ class AssetDeliveryPage extends StatefulWidget {
class _AssetDeliveryPageState extends State<AssetDeliveryPage> {
AssetDeliveryDataModel? dataModel;
AssetDeliveryProvider? assetDeliveryProvider;
bool loading = false;
@override
void initState() {
@ -46,12 +48,18 @@ class _AssetDeliveryPageState extends State<AssetDeliveryPage> {
}
Future<void> getDetailsById() async {
setState(() {
loading = true;
});
final futures = <Future>[
assetDeliveryProvider!.getAssetDeliveryDetailById(requestId: widget.requestId).then((value) => dataModel = value),
assetDeliveryProvider!.getAssetDeliveryTableListById(requestId: widget.requestId),
];
await Future.wait(futures);
setState(() {
loading = false;
});
}
@override
@ -68,66 +76,64 @@ class _AssetDeliveryPageState extends State<AssetDeliveryPage> {
body: Consumer<AssetDeliveryProvider>(
builder: (context, provider, child) {
final dataModel = provider.assetDeliveryDataModel;
if (provider.isLoading) {
if (loading) {
return const CircularProgressIndicator(color: AppColor.primary10).center;
}
if (dataModel == null) {
return const NoDataFound().center;
}
return ListView(
padding: const EdgeInsets.all(12),
children: [
requestDetailCard(context),
const Divider().defaultStyle(context),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Asset Delivery Table",
style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
Row(
children: [
Container(
height: 24,
width: 24,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: AppColor.primary10, // or AppColor.blueStatus(context)
),
alignment: Alignment.center,
child: const Icon(
Icons.add,
color: Colors.white,
size: 16,
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
requestDetailCard(context),
const Divider().defaultStyle(context),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Asset Delivery Table",
style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
Row(
children: [
Container(
height: 24,
width: 24,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: AppColor.primary10, // or AppColor.blueStatus(context)
),
alignment: Alignment.center,
child: const Icon(
Icons.add,
color: Colors.white,
size: 16,
),
),
),
4.width,
Text(
"Add more",
style: AppTextStyles.bodyText.copyWith(
color: context.isDark ? AppColor.neutral30 : AppColor.neutral50,
decoration: TextDecoration.underline,
4.width,
Text(
"Add more",
style: AppTextStyles.bodyText.copyWith(
color: context.isDark ? AppColor.neutral30 : AppColor.neutral50,
decoration: TextDecoration.underline,
),
),
),
],
).onPress(() async {
await assetDeliveryProvider?.addNewDelivery(requestId: widget.requestId).then((status) async {
if (status) {
await assetDeliveryProvider?.getAssetDeliveryTableListById(requestId: widget.requestId);
}
});
}),
],
),
8.height,
assetDeliveryTableList(context: context, assetDeliveryProvider: provider),
],
).toShadowContainer(
context,
borderRadius: 20,
padding: 0,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
],
).onPress(() async {
await assetDeliveryProvider?.addNewDelivery(requestId: widget.requestId).then((status) async {
if (status) {
await assetDeliveryProvider?.getAssetDeliveryTableListById(requestId: widget.requestId);
}
});
}),
],
),
8.height,
assetDeliveryTableList(context: context, assetDeliveryProvider: provider),
],
).toShadowContainer(context, borderRadius: 20),
);
},
),
@ -248,16 +254,17 @@ class _AssetDeliveryPageState extends State<AssetDeliveryPage> {
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
8.height,
Text(
'EDD (Estimated Delivery Date)'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
dataModel?.edd != null ? dataModel!.edd!.toAssetDetailsFormat : '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
8.height,
//Removed as request by Ahmed backend...
// Text(
// 'EDD (Estimated Delivery Date)'.addTranslation,
// style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
// ),
// 8.height,
// Text(
// dataModel?.edd != null ? dataModel!.edd!.toAssetDetailsFormat : '-',
// style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
// ),
// 8.height,
Text(
'Total'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
@ -323,10 +330,21 @@ class _AssetDeliveryPageState extends State<AssetDeliveryPage> {
});
});
} else {
// Navigator.of(context).push(MaterialPageRoute(
// builder: (_) =>
// DeliveryInspectionFormView(
// deliveryTableModel: model,
// requestModel: dataModel,
// ),
// ));
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => DeliveryInspectionFormView(
deliveryTableModel: model,
requestModel: dataModel,
builder: (_) => AssetDeliveryStageTabPage(
title: 'Delivery Inspection'.addTranslation,
detailWidget: DeliveryInspectionFormView(
deliveryTableModel: model,
requestModel: dataModel,
),
tableId: model.id,
),
));
}

@ -0,0 +1,90 @@
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/enum_extensions.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/modules/asset_delivery_module/pages/history_log_view.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
class AssetDeliveryStageTabPage extends StatefulWidget {
String title;
Widget detailWidget;
int? tableId;
AssetDeliveryStageTabPage({
Key? key,
required this.title,
required this.detailWidget,
this.tableId,
}) : super(key: key);
@override
_AssetDeliveryStageTabPageState createState() {
return _AssetDeliveryStageTabPageState();
}
}
class _AssetDeliveryStageTabPageState extends State<AssetDeliveryStageTabPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: DefaultAppBar(
title: widget.title,
),
body: DefaultTabController(
length: 2,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 16.toScreenWidth, right: 16.toScreenWidth, top: 12.toScreenHeight),
decoration: BoxDecoration(color: context.isDark ? AppColor.neutral50 : AppColor.white10, borderRadius: BorderRadius.circular(10)),
child: TabBar(
padding: EdgeInsets.symmetric(vertical: 4.toScreenHeight, horizontal: 4.toScreenWidth),
labelColor: context.isDark ? AppColor.neutral30 : AppColor.black20,
unselectedLabelColor: context.isDark ? AppColor.neutral30 : AppColor.black20,
unselectedLabelStyle: AppTextStyles.bodyText,
labelStyle: AppTextStyles.bodyText,
indicatorPadding: EdgeInsets.zero,
indicatorSize: TabBarIndicatorSize.tab,
dividerColor: Colors.transparent,
indicator: BoxDecoration(color: context.isDark ? AppColor.neutral60 : AppColor.neutral110, borderRadius: BorderRadius.circular(7)),
tabs: [
Tab(text: context.translation.details, height: 57.toScreenHeight),
Tab(text: context.translation.historyLogs, height: 57.toScreenHeight),
],
),
),
// 12.height,
TabBarView(
children: [
widget.detailWidget,
AssetDeliveryHistoryLogView(
tableItemId: widget.tableId,
),
],
).expanded,
],
),
));
}
String getPageTitle({AssetDeliveryStageEnum? deliveryStage}) {
if (deliveryStage == AssetDeliveryStageEnum.deliveryInspection) {
return 'Delivery Inspection'.addTranslation;
}
if (deliveryStage == AssetDeliveryStageEnum.technicalInspection) {
return 'Technical Inspection'.addTranslation;
}
if (deliveryStage == AssetDeliveryStageEnum.endUserAcceptance) {
return 'End-User Acceptance'.addTranslation;
} else {
return 'Details'.addTranslation;
}
}
}

@ -26,28 +26,33 @@ class AssetDeliveryTableCard extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
StatusLabel(
label: deliveryTableModel.status?.name,
textColor: AppColor.neutral50,
backgroundColor: AppColor.orange30,
),
Row(
children: [
'history_icon'.toSvgAsset(height: 14, width: 14),
8.width,
Text(
'View History'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: AppColor.primary10),
),
],
).onPress(() {
onViewHistoryTap();
}),
],
StatusLabel(
label: deliveryTableModel.status?.name,
textColor: AppColor.neutral50,
backgroundColor: AppColor.orange30,
),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// StatusLabel(
// label: deliveryTableModel.status?.name,
// textColor: AppColor.neutral50,
// backgroundColor: AppColor.orange30,
// ),
// Row(
// children: [
// 'history_icon'.toSvgAsset(height: 14, width: 14),
// 8.width,
// Text(
// 'View History'.addTranslation,
// style: AppTextStyles.bodyText.copyWith(color: AppColor.primary10),
// ),
// ],
// ).onPress(() {
// onViewHistoryTap();
// }),
// ],
// ),
8.height,
Text(
'Delivery Number: ${deliveryTableModel.number ?? '-'}',

@ -1,8 +1,10 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.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/extensions/text_extensions.dart';
@ -15,21 +17,22 @@ import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/views/widgets/images/files_list.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
class AssetDeliveryAttachmentView extends StatefulWidget {
///Need to check which parm need here.
final int? lineId;
final int? tableItemId;
final int? requestId;
final bool? isHmg;
final int? attachmentType;
bool viewOnly = false;
final List<GenericAttachmentModel> attachmentList;
const AssetDeliveryAttachmentView({
AssetDeliveryAttachmentView({
super.key,
this.lineId,
this.requestId,
required this.attachmentList,
this.viewOnly=false,
this.attachmentType,
this.tableItemId,
this.isHmg,
});
@override
@ -52,15 +55,17 @@ class _AssetDeliveryAttachmentViewState extends State<AssetDeliveryAttachmentVie
void getInitialData() async {
///get previoues Attachment from Apis.
isLoading = true;
// isLoading = true;
setState(() {});
_attachments = await assetDeliveryProvider.getAttachments(tableItemId: widget.tableItemId);
isLoading = false;
_attachments = widget.attachmentList;
// await assetDeliveryProvider.getAttachments(tableItemId: widget.tableItemId, attachmentType: widget.attachmentType);
// isLoading = false;
setState(() {});
}
@override
Widget build(BuildContext context) {
log('value ${widget.attachmentType}');
return Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: DefaultAppBar(
@ -69,58 +74,79 @@ class _AssetDeliveryAttachmentViewState extends State<AssetDeliveryAttachmentVie
Navigator.pop(context);
},
),
body: isLoading
? const CircularProgressIndicator(color: AppColor.primary10).center
body: widget.attachmentList.isEmpty
? const NoDataFound().center
: Column(
children: [
AttachmentPicker(
label: context.translation.attachments,
attachment: _attachments,
buttonColor: AppColor.primary10,
onlyImages: false,
// showAsGrid: true,
buttonIcon: 'quotation_icon'.toSvgAsset(color: AppColor.primary10),
onChange: (attachment) {},
)
.toShadowContainer(
context,
borderRadius: 20,
padding: 12,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
)
.expanded,
Flexible(
fit: FlexFit.loose, // expands only as much as needed
child: Column(
children: [
widget.attachmentType == 5 && !widget.viewOnly
? AttachmentPicker(
label: context.translation.attachments,
attachment: _attachments,
buttonColor: AppColor.primary10,
onlyImages: false,
showAsListView: true,
buttonIcon: 'quotation_icon'.toSvgAsset(color: AppColor.primary10),
onChange: (attachment) {},
).toShadowContainer(
context,
borderRadius: 20,
padding: 12,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
)
: FilesList(images: _attachments.map((e) => URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []).toShadowContainer(
context,
borderRadius: 20,
padding: 12,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
],
),
),
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
label: 'Save'.addTranslation,
maxWidth: true,
onPressed: () async {
List<GenericAttachmentModel> attachement = [];
for (var item in _attachments) {
String fileName =
CMRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? '';
attachement.add(GenericAttachmentModel(id: item.id ?? 0, name: fileName));
}
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
Map<String,dynamic> payLoad ={
'assetDeliveryExternalDeliveryId': widget.tableItemId,
'attachments':attachement,
};
await assetDeliveryProvider.saveAttachment(payload:payLoad).then((status) async {
Navigator.pop(context);
if (status) {
///Need to verify need to stay on this page and refresh or go back..
Navigator.pop(context);
// assetDeliveryProvider.getDeliveryLinesListById(
// itemId: widget.tableItemId,
// );
}
});
}),
buttonColor: AppColor.primary10,
label: 'Save'.addTranslation,
maxWidth: true,
onPressed: _onSavePressed,
),
),
],
),
);
}
Future<void> _onSavePressed() async {
final List<GenericAttachmentModel> attachmentPayload = _attachments.map((item) {
final String name = CMRequestUtils.isLocalUrl(item.name ?? '') ? '${item.name?.split("/").last}|${base64Encode(File(item.name!).readAsBytesSync())}' : item.name ?? '';
return GenericAttachmentModel(
id: item.id ?? 0,
name: name,
);
}).toList();
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => const AppLazyLoading(),
);
final payload = {
'assetDeliveryExternalDeliveryId': widget.tableItemId,
'attachments': attachmentPayload,
};
final status = await assetDeliveryProvider.saveAttachment(payload: payload);
Navigator.pop(context);
if (status) {
Navigator.pop(context);
}
}
}

@ -1,5 +1,3 @@
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
@ -21,7 +19,7 @@ class AssetDeliveryBottomSheet {
);
}
static Future changeStatusBottomSheet({required BuildContext context,required VoidCallback cancelStatusTap,required VoidCallback statusChangeTap}) {
static Future changeStatusBottomSheet({required BuildContext context, required VoidCallback cancelStatusTap, required VoidCallback statusChangeTap}) {
return buildBottomSheetParent(
context: context,
childWidget: Column(
@ -31,14 +29,17 @@ class AssetDeliveryBottomSheet {
8.height,
Align(
alignment: AlignmentDirectional.centerStart,
child: Text('Change Status'.addTranslation,style: TextStyle(
fontSize: 21.toScreenWidth,
fontWeight: FontWeight.w500,
fontStyle: FontStyle.normal,
decoration: TextDecoration.none,
),),
child: Text(
'Change Status'.addTranslation,
style: TextStyle(
fontSize: 21.toScreenWidth,
fontWeight: FontWeight.w500,
fontStyle: FontStyle.normal,
decoration: TextDecoration.none,
),
),
),
10.height,
16.height,
Row(
children: [
AppFilledButton(
@ -50,14 +51,16 @@ class AssetDeliveryBottomSheet {
showBorder: true,
onPressed: () async {
cancelStatusTap();
},
).expanded,
12.width,
AppFilledButton(buttonColor: AppColor.green70, label: 'Change Status'.addTranslation, maxWidth: true, onPressed: () {
statusChangeTap();
}).expanded,
AppFilledButton(
buttonColor: AppColor.green70,
label: 'Change Status'.addTranslation,
maxWidth: true,
onPressed: () {
statusChangeTap();
}).expanded,
],
),
],

@ -1,6 +1,3 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
@ -11,6 +8,7 @@ import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/modules/asset_delivery_module/models/cost_center_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/cost_center_serial_no_form_model.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/cost_center_provider.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
@ -48,8 +46,9 @@ class CostCenterFormView extends StatefulWidget {
}
class _CostCenterFormViewState extends State<CostCenterFormView> {
List<CostCenterModel> _list = [];
CostCenterSerialNoFormModel formModel = CostCenterSerialNoFormModel(costCenters: [], serialNos: []);
late AssetDeliveryProvider assetDeliveryProvider;
CostCenterType _selectedType = CostCenterType.costCenter;
@override
void initState() {
@ -61,15 +60,20 @@ class _CostCenterFormViewState extends State<CostCenterFormView> {
}
void getInitialData() async {
_list = await assetDeliveryProvider.getAssetDeliveryCostCenterListForLines(
formModel = await assetDeliveryProvider.getAssetDeliveryCostCenterListForLines(
lineId: widget.lineId,
tableItemId: widget.tableItemId,
);
if (_list.isEmpty) {
_list.add(CostCenterModel(
if (formModel.costCenters!.isEmpty) {
formModel.costCenters!.add(CostCenterModel(
id: 0,
));
}
// if (formModel.serialNos!.isEmpty) {
// formModel.serialNos!.add(SerialNoModel(
// id: 0,
// ));
// }
if (widget.isHmg == true) {
CostCenterProvider costCenterProvider = Provider.of<CostCenterProvider>(context, listen: false);
if (costCenterProvider.items.isEmpty) {
@ -79,21 +83,21 @@ class _CostCenterFormViewState extends State<CostCenterFormView> {
setState(() {});
}
void _addNewEntry() {
void addItem<T>(List<T> list, T Function() creator) {
setState(() {
_list.add(CostCenterModel(costCenter: Lookup()));
list.add(creator());
});
}
void _removeEntry(int index) {
void removeItem<T>(List<T> list, int index) {
setState(() {
_list.removeAt(index);
list.removeAt(index);
});
}
void _updateModel(int index, void Function(CostCenterModel model) updateList) {
void updateItem<T>(List<T> list, int index, void Function(T item) updater) {
setState(() {
updateList(_list[index]);
updater(list[index]);
});
}
@ -103,102 +107,207 @@ class _CostCenterFormViewState extends State<CostCenterFormView> {
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: DefaultAppBar(
title: 'Cost Center for Item'.addTranslation,
onBackPress: () {
Navigator.pop(context);
},
// onBackPress: () {
// Navigator.pop(context);
// },
),
body: Consumer<AssetDeliveryProvider>(builder: (context, provider, child) {
return Column(
children: [
itemDetails(context: context, itemNo: widget.itemNo, itemDescription: widget.itemDescription),
provider.isLoading
? const CircularProgressIndicator(color: AppColor.primary10).center
: ListView.builder(
itemCount: _list.length + 1,
shrinkWrap: true,
padding: EdgeInsets.all(widget.cardPadding ?? 16),
itemBuilder: (context, index) {
if (index == _list.length) {
return Visibility(
child: AppFilledButton(
label: "Add Cost Center".addTranslation,
maxWidth: true,
textColor: AppColor.textColor(context),
buttonColor: context.isDark ? AppColor.neutral60 : AppColor.white10,
icon: Icon(Icons.add_circle, color: AppColor.blueStatus(context)),
showIcon: true,
onPressed: _addNewEntry,
),
);
}
return CostCenterItemCard(
model: _list[index],
index: index,
isReadOnly: false,
isHmg: widget.isHmg,
requestId: widget.requestId,
onUpdate: (updateList) => _updateModel(index, updateList),
onRemove: () => _removeEntry(index),
);
},
).expanded,
8.height,
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
label: 'Save'.addTranslation,
maxWidth: true,
onPressed: () async {
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await assetDeliveryProvider.saveCostCenterToLineDeliveryInspection(tableItemId: widget.tableItemId, lineId: widget.lineId, list: _list).then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context);
assetDeliveryProvider.getDeliveryLinesListById(
itemId: widget.tableItemId,
);
}
});
}),
),
],
);
return provider.isLoading
? const CircularProgressIndicator(color: AppColor.primary10).center
: Column(
children: [
itemDetails(),
selectionButton(),
_selectedType == CostCenterType.costCenter ? _buildCostCenterList() : _buildSerialNumberList(),
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
label: 'Save'.addTranslation,
maxWidth: true,
onPressed: () async {
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await assetDeliveryProvider
.saveCostCenterToLineDeliveryInspection(tableItemId: widget.tableItemId, lineId: widget.lineId, list: formModel.costCenters, serialNoList: formModel.serialNos)
.then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context);
assetDeliveryProvider.getDeliveryLinesListById(
itemId: widget.tableItemId,
);
}
});
}),
),
],
);
}),
);
}
}
Widget itemDetails({required BuildContext context, String? itemNo, String? itemDescription}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Item No'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
itemNo ?? '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
8.height,
Text(
'Item Description'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
Widget _buildCostCenterList() {
return ListView.builder(
itemCount: formModel.costCenters!.length + 1,
shrinkWrap: true,
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16, top: 8),
itemBuilder: (context, index) {
if (index == formModel.costCenters!.length) {
return AppFilledButton(
label: "Add Cost Center".addTranslation,
maxWidth: true,
textColor: AppColor.textColor(context),
buttonColor: context.isDark ? AppColor.neutral60 : AppColor.white10,
icon: Icon(Icons.add_circle, color: AppColor.blueStatus(context)),
showIcon: true,
onPressed: () {
addItem(formModel.costCenters!, () => CostCenterModel(costCenter: Lookup()));
},
);
}
return CostCenterItemCard(
model: formModel.costCenters![index],
index: index,
isReadOnly: false,
isHmg: widget.isHmg,
requestId: widget.requestId,
onUpdate: (update) {
updateItem<CostCenterModel>(formModel.costCenters!, index, update);
},
onRemove: () {
removeItem<CostCenterModel>(formModel.costCenters!, index);
},
);
},
).expanded;
}
Widget _buildSerialNumberList() {
return ListView.builder(
itemCount: formModel.serialNos!.length + 1,
shrinkWrap: true,
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16, top: 8),
itemBuilder: (context, index) {
if (index == formModel.serialNos!.length) {
return AppFilledButton(
label: "Add Serial Number".addTranslation,
maxWidth: true,
textColor: AppColor.textColor(context),
buttonColor: context.isDark ? AppColor.neutral60 : AppColor.white10,
icon: Icon(Icons.add_circle, color: AppColor.blueStatus(context)),
showIcon: true,
onPressed: () {
addItem(formModel.serialNos!, () => SerialNoModel());
},
);
}
return SerialNumberItemCard(
model: formModel.serialNos![index],
onUpdate: (update) {
updateItem<SerialNoModel>(formModel.serialNos!, index, update);
},
onRemove: () {
removeItem<SerialNoModel>(formModel.serialNos!, index);
},
);
},
).expanded;
}
Widget selectionButton() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
Expanded(
child: _radioItem(
value: CostCenterType.costCenter,
groupValue: _selectedType,
title: 'Cost Center',
onTap: () {
setState(() {
_selectedType = CostCenterType.costCenter;
});
},
),
),
Expanded(
child: _radioItem(
value: CostCenterType.serialNumber,
groupValue: _selectedType,
title: 'Serial Number',
onTap: () {
setState(() {
_selectedType = CostCenterType.serialNumber;
});
},
),
),
],
),
8.height,
Text(
itemDescription ?? '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
);
}
Widget _radioItem({
required CostCenterType value,
required CostCenterType groupValue,
required String title,
required VoidCallback onTap,
}) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: Row(
children: [
Radio<CostCenterType>(
value: value,
groupValue: groupValue,
onChanged: (_) => onTap(),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity.compact,
),
4.width,
title.addTranslation.bodyText(context),
],
),
],
).toShadowContainer(
context,
borderRadius: 20,
padding: 12,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
);
);
}
Widget itemDetails() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Item No'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
widget.itemNo ?? '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
8.height,
Text(
'Item Description'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
widget.itemDescription ?? '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
],
).toShadowContainer(
context,
borderRadius: 20,
padding: 12,
showShadow: false,
margin: const EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8),
);
}
}
class CostCenterItemCard extends StatelessWidget {
@ -293,6 +402,56 @@ class CostCenterItemCard extends StatelessWidget {
],
)
],
).toShadowContainer(context, paddingObject: const EdgeInsets.symmetric(horizontal: 16, vertical: 12)).paddingOnly(bottom: 12);
).toShadowContainer(context, paddingObject: const EdgeInsets.symmetric(horizontal: 12, vertical: 12)).paddingOnly(bottom: 12);
}
}
class SerialNumberItemCard extends StatelessWidget {
final SerialNoModel model;
final VoidCallback onRemove;
final void Function(void Function(SerialNoModel model)) onUpdate;
const SerialNumberItemCard({
super.key,
required this.model,
required this.onRemove,
required this.onUpdate,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
'Serial Number'.addTranslation.bodyText(context),
Container(
height: 28,
width: 28,
padding: const EdgeInsets.all(6),
child: "trash".toSvgAsset(height: 20, width: 20),
).onPress(onRemove),
],
),
8.height,
AppTextFormField(
labelText: 'Serial Number'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
initialValue: model.serialNo ?? '',
textAlign: TextAlign.center,
showShadow: false,
onChange: (value) {
model.serialNo = value;
},
style: Theme.of(context).textTheme.titleMedium,
),
],
)
.toShadowContainer(
context,
paddingObject: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
)
.paddingOnly(bottom: 12);
}
}

@ -1,27 +1,33 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/enum_extensions.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/lookup.dart';
import 'package:test_sa/modules/asset_delivery_module/helper_function.dart';
import 'package:test_sa/modules/asset_delivery_module/models/asset_delivery_data_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/delivery_inspection_form_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/delivery_line_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/inspection_person_model.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_line_card_view.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/attachment_view.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_stage_tab_page.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/delivery_inspection/cost_center_form_view.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/delivery_inspection/oracle_user_autocomplete_field.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/delivery_inspection/inpection_bottomsheet.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_lines_list_view.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/attachment_type_provider.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart';
import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart';
import 'package:test_sa/views/widgets/date_and_time/date_picker.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
import '../../../../new_views/common_widgets/app_filled_button.dart';
@ -85,12 +91,12 @@ class _DeliveryInspectionFormViewState extends State<DeliveryInspectionFormView>
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: DefaultAppBar(
title: 'Delivery Inspection'.addTranslation,
onBackPress: () {
Navigator.pop(context);
},
),
// appBar: DefaultAppBar(
// title: 'Delivery Inspection'.addTranslation,
// onBackPress: () {
// Navigator.pop(context);
// },
// ),
body: isLoading
? const CircularProgressIndicator(color: AppColor.primary10).center
: Form(
@ -110,9 +116,16 @@ class _DeliveryInspectionFormViewState extends State<DeliveryInspectionFormView>
),
8.height,
Text(
model.totalTime != null ? '${model.totalTime.toString()} hours' : '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
formatInspectionDuration(model.totalTime),
style: AppTextStyles.bodyText2.copyWith(
color: context.isDark ? AppColor.neutral30 : AppColor.neutral120,
),
),
// Text(
// model.totalTime != null ? '${model.totalTime.toString()} hours' : '-',
// style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
// ),
8.height,
const Divider().defaultStyle(context),
Text(
@ -124,32 +137,50 @@ class _DeliveryInspectionFormViewState extends State<DeliveryInspectionFormView>
],
).toShadowContainer(context, borderRadius: 20, padding: 12))
.expanded,
if (!viewOnly) ...[
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(buttonColor: AppColor.primary10, label: 'Attachments'.addTranslation, maxWidth: true, onPressed: _attachmentTap),
),
8.height,
],
FooterActionButton.footerContainer(
context: context,
child: !viewOnly
? Row(
? Column(
children: [
AppFilledButton(
label: 'Cancel'.addTranslation,
maxWidth: true,
buttonColor: AppColor.background(context),
// textColor: context.isDark ? AppColor.neutral30 : Colors.white,
textColor: AppColor.red30,
showBorder: true,
onPressed: _cancelTap,
).expanded,
12.width,
AppFilledButton(buttonColor: AppColor.green70, label: 'Change Status'.addTranslation, maxWidth: true, onPressed: _changeStatusTap).expanded,
buttonColor: AppColor.primary10,
label: 'Attachments'.addTranslation,
maxWidth: true,
onPressed: () {
HelperFunction.attachmentTap(context: context, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id);
}),
16.height,
Row(
children: [
AppFilledButton(
label: 'Cancel'.addTranslation,
maxWidth: true,
buttonColor: AppColor.background(context),
// textColor: context.isDark ? AppColor.neutral30 : Colors.white,
textColor: AppColor.red30,
showBorder: true,
onPressed: _cancelTap,
).expanded,
12.width,
AppFilledButton(buttonColor: AppColor.green70, label: 'Change Status'.addTranslation, maxWidth: true, onPressed: _changeStatusTap).expanded,
],
),
],
)
: AppFilledButton(buttonColor: AppColor.primary10, label: 'Next'.addTranslation, maxWidth: true, onPressed: _nextTap),
: Row(
children: [
AppFilledButton(
buttonColor: AppColor.white60,
label: 'Attachments'.addTranslation,
maxWidth: true,
textColor: AppColor.black10,
onPressed: () {
HelperFunction.attachmentTap(context: context, viewOnly: true, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id);
}).expanded,
8.width,
AppFilledButton(buttonColor: AppColor.primary10, label: 'Next'.addTranslation, maxWidth: true, onPressed: _nextTap).expanded,
],
),
),
],
),
@ -195,69 +226,132 @@ class _DeliveryInspectionFormViewState extends State<DeliveryInspectionFormView>
}
void calculateTotalTime() {
if (model.deliveryInspectionInspectedDateFrom == null || model.deliveryInspectionInspectedDateTo == null) {
final from = model.deliveryInspectionInspectedDateFrom;
final to = model.deliveryInspectionInspectedDateTo;
if (from == null || to == null) {
model.totalTime = null;
return;
}
final fromDate = model.deliveryInspectionInspectedDateFrom!;
final toDate = model.deliveryInspectionInspectedDateTo!;
final hours = toDate.difference(fromDate).inSeconds / 3600.0;
final diffInSeconds = to.difference(from).inSeconds;
if (diffInSeconds < 0) {
model.totalTime = null;
return;
}
final hours = diffInSeconds / 3600;
model.totalTime = num.parse(hours.toStringAsFixed(3));
}
Widget inspectionDetailsForm() {
return Column(
children: [
if (widget.requestModel?.isHMG == true) ...[
OracleUserAutoCompleteField(
clearAfterPick: false,
initialValue: model.deliveryInspectionInspectedBy ?? '',
label: 'Inspected by'.addTranslation,
onPick: (value) {
model.deliveryInspectionInspectedBy = value.fullName;
setState(() {});
},
onChanged: (value) {},
),
8.height,
OracleUserAutoCompleteField(
clearAfterPick: false,
initialValue: model.deliveryInspectionApprovedBy ?? '',
label: 'Approved by'.addTranslation,
onChanged: (value) {},
onPick: (value) {
model.deliveryInspectionApprovedBy = value.fullName;
setState(() {});
},
),
] //No HMG user free text insted of dropdown ...
else ...[
AppTextFormField(
labelText: 'Inspected by'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
initialValue: model.deliveryInspectionInspectedBy ?? '',
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
onChange: (value) {
model.deliveryInspectionInspectedBy = value;
},
style: Theme.of(context).textTheme.titleMedium,
),
_inspectionTile(
title: 'Inspected by'.addTranslation,
model: model.inspectedByModel,
isHmg: widget.requestModel?.isHMG == true,
onTap: () async {
final result = await showInspectionBottomSheet(
context,
title: 'Inspected by'.addTranslation,
isHmg: widget.requestModel?.isHMG == true,
initialModel: model.inspectedByModel ?? InspectionPersonModel(),
);
if (result != null) {
setState(() => model.inspectedByModel = result);
}
},
),
8.height,
_inspectionTile(
title: 'Approved by'.addTranslation,
model: model.approvedByModel,
isHmg: widget.requestModel?.isHMG == true,
onTap: () async {
final result = await showInspectionBottomSheet(
context,
title: 'Approved by'.addTranslation,
isHmg: widget.requestModel?.isHMG == true,
initialModel: model.approvedByModel ?? InspectionPersonModel(),
);
if (result != null) {
setState(() => model.approvedByModel = result);
}
},
),
if (widget.requestModel?.paymentTerm?.value == 2) ...[
8.height,
AppTextFormField(
labelText: 'Approved by'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
initialValue: model.deliveryInspectionApprovedBy ?? '',
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
onChange: (value) {
model.deliveryInspectionApprovedBy = value;
_inspectionTile(
title: 'Third Approved by'.addTranslation,
model: model.thirdApprovalModel,
isHmg: widget.requestModel?.isHMG == true,
onTap: () async {
final result = await showInspectionBottomSheet(
context,
title: 'Third Approved by'.addTranslation,
isHmg: widget.requestModel?.isHMG == true,
initialModel: model.thirdApprovalModel ?? InspectionPersonModel(),
);
if (result != null) {
setState(() => model.thirdApprovalModel = result);
}
},
style: Theme.of(context).textTheme.titleMedium,
),
],
// if (widget.requestModel?.isHMG == true) ...[
// OracleUserAutoCompleteField(
// clearAfterPick: false,
// initialValue: model.deliveryInspectionInspectedBy ?? '',
// label: 'Inspected by'.addTranslation,
// onPick: (value) {
// model.deliveryInspectionInspectedBy = value.fullName;
// setState(() {});
// },
// onChanged: (value) {},
// ),
// 8.height,
// OracleUserAutoCompleteField(
// clearAfterPick: false,
// initialValue: model.deliveryInspectionApprovedBy ?? '',
// label: 'Approved by'.addTranslation,
// onChanged: (value) {},
// onPick: (value) {
// model.deliveryInspectionApprovedBy = value.fullName;
// setState(() {});
// },
// ),
// ] //No HMG user free text insted of dropdown ...
// else ...[
// AppTextFormField(
// labelText: 'Inspected by'.addTranslation,
// backgroundColor: AppColor.fieldBgColor(context),
// initialValue: model.deliveryInspectionInspectedBy ?? '',
// textAlign: TextAlign.center,
// labelStyle: AppTextStyles.textFieldLabelStyle,
// showShadow: false,
// onChange: (value) {
// model.deliveryInspectionInspectedBy = value;
// },
// style: Theme.of(context).textTheme.titleMedium,
// ),
// 8.height,
// AppTextFormField(
// labelText: 'Approved by'.addTranslation,
// backgroundColor: AppColor.fieldBgColor(context),
// initialValue: model.deliveryInspectionApprovedBy ?? '',
// textAlign: TextAlign.center,
// labelStyle: AppTextStyles.textFieldLabelStyle,
// showShadow: false,
// onChange: (value) {
// model.deliveryInspectionApprovedBy = value;
// },
// style: Theme.of(context).textTheme.titleMedium,
// ),
// ],
8.height,
ADatePicker(
label: 'Inspected Date/Time From'.addTranslation,
@ -267,52 +361,31 @@ class _DeliveryInspectionFormViewState extends State<DeliveryInspectionFormView>
date: model.deliveryInspectionInspectedDateFrom,
from: DateTime(DateTime.now().year, 1, 1),
formatDateWithTime: true,
onDatePicker: (selectedDate) {
showTimePicker(
onDatePicker: (selectedDate) async {
final selectedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
builder: (BuildContext context, Widget? child) {
final ThemeData currentTheme = Theme.of(context);
return Theme(
data: currentTheme.copyWith(
timePickerTheme: TimePickerThemeData(
dialHandColor: AppColor.primary10,
dialBackgroundColor: Colors.grey.withOpacity(0.1),
hourMinuteColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.grey.withOpacity(0.1)),
dayPeriodColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.transparent),
dayPeriodTextColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? Colors.white : AppColor.primary10),
dayPeriodBorderSide: BorderSide(color: Colors.grey.withOpacity(0.2)),
entryModeIconColor: AppColor.primary10,
),
textButtonTheme: TextButtonThemeData(style: TextButton.styleFrom(foregroundColor: AppColor.primary10)),
),
child: child!,
);
},
).then((selectedTime) {
if (selectedTime != null) {
model.deliveryInspectionInspectedDateFrom = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute);
// calculateTotalTime();
///need to check if need validation or not.
// if (widget.pickerFromDate != null && _pickerStartAt!.isBefore(widget.pickerFromDate!)) {
// "Start time is before the request time.".showToast;
// _pickerStartAt = null;
// selectedTime = null;
// return;
// }
if (model.deliveryInspectionInspectedDateFrom!.isAfter(DateTime.now())) {
"Start time is after than current time".showToast;
model.deliveryInspectionInspectedDateFrom = null;
selectedTime = null;
return;
}
setState(() {});
}
});
builder: _timePickerThemeBuilder,
);
if (selectedTime == null) return;
final fromDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
selectedTime.hour,
selectedTime.minute,
);
if (!_validateInspectionDates(from: fromDateTime, to: model.deliveryInspectionInspectedDateTo)) {
return; // invalid, do not assign
}
model.deliveryInspectionInspectedDateFrom = fromDateTime;
setState(() {});
},
),
8.height,
ADatePicker(
label: 'Inspected Date/Time To'.addTranslation,
@ -322,55 +395,120 @@ class _DeliveryInspectionFormViewState extends State<DeliveryInspectionFormView>
date: model.deliveryInspectionInspectedDateTo,
from: model.deliveryInspectionInspectedDateFrom,
formatDateWithTime: true,
onDatePicker: (selectedDate) {
showTimePicker(
onDatePicker: (selectedDate) async {
if (model.deliveryInspectionInspectedDateFrom == null) {
"Please select time to first".showToast;
return;
}
final selectedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
builder: (BuildContext context, Widget? child) {
final ThemeData currentTheme = Theme.of(context);
return Theme(
data: currentTheme.copyWith(
timePickerTheme: TimePickerThemeData(
dialHandColor: AppColor.primary10,
dialBackgroundColor: Colors.grey.withOpacity(0.1),
hourMinuteColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.grey.withOpacity(0.1)),
dayPeriodColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.transparent),
dayPeriodTextColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? Colors.white : AppColor.primary10),
dayPeriodBorderSide: BorderSide(color: Colors.grey.withOpacity(0.2)),
entryModeIconColor: AppColor.primary10,
),
textButtonTheme: TextButtonThemeData(style: TextButton.styleFrom(foregroundColor: AppColor.primary10)),
),
child: child!,
);
},
).then((selectedTime) {
if (selectedTime != null) {
model.deliveryInspectionInspectedDateTo = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute);
calculateTotalTime();
///Need to check need validation or not..
// if (widget.pickerFromDate != null && _pickerStartAt!.isBefore(widget.pickerFromDate!)) {
// "Start time is before the request time.".showToast;
// _pickerStartAt = null;
// selectedTime = null;
// return;
// }
if (model.deliveryInspectionInspectedDateTo!.isAfter(DateTime.now())) {
"End time is after than current time".showToast;
model.deliveryInspectionInspectedDateTo = null;
selectedTime = null;
return;
}
setState(() {});
}
});
builder: _timePickerThemeBuilder,
);
if (selectedTime == null) return;
final toDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
selectedTime.hour,
selectedTime.minute,
);
if (!_validateInspectionDates(from: model.deliveryInspectionInspectedDateFrom, to: toDateTime)) {
return; // invalid, do not assign
}
model.deliveryInspectionInspectedDateTo = toDateTime;
setState(() {});
},
),
],
);
}
bool _validateInspectionDates({DateTime? from, DateTime? to}) {
if (from == null && to == null) return true;
if (from != null) {
if (from.isAfter(DateTime.now())) {
"Time From cannot be in the future".showToast;
return false;
}
if (to != null && from.isAfter(to)) {
"Time To cannot be after Time From".showToast;
return false;
}
}
if (to != null) {
if (to.isAfter(DateTime.now())) {
"Time To cannot be in the future".showToast;
return false;
}
if (from != null && to.isBefore(from)) {
"Timer To cannot be before from time".showToast;
return false;
}
}
if (from != null && to != null) {
final diffInSeconds = to.difference(from).inSeconds;
if (diffInSeconds < 0) {
model.totalTime = null;
return false;
}
final hours = diffInSeconds / 3600.0;
model.totalTime = num.parse(hours.toStringAsFixed(3));
} else {
model.totalTime = null;
}
return true;
}
String formatInspectionDuration(num? hours) {
if (hours == null) return '-';
if (hours < 1) {
final minutes = (hours * 60).round();
return '$minutes minutes';
}
return '${hours.toStringAsFixed(2)} hours';
}
Widget _timePickerThemeBuilder(BuildContext context, Widget? child) {
final ThemeData currentTheme = Theme.of(context);
return Theme(
data: currentTheme.copyWith(
timePickerTheme: TimePickerThemeData(
dialHandColor: AppColor.primary10,
dialBackgroundColor: Colors.grey.withOpacity(0.1),
hourMinuteColor: MaterialStateColor.resolveWith(
(states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.grey.withOpacity(0.1),
),
dayPeriodColor: MaterialStateColor.resolveWith(
(states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.transparent,
),
dayPeriodTextColor: MaterialStateColor.resolveWith(
(states) => states.contains(MaterialState.selected) ? Colors.white : AppColor.primary10,
),
dayPeriodBorderSide: BorderSide(
color: Colors.grey.withOpacity(0.2),
),
entryModeIconColor: AppColor.primary10,
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: AppColor.primary10,
),
),
),
child: child!,
);
}
Widget inspectionDetails() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -418,13 +556,69 @@ class _DeliveryInspectionFormViewState extends State<DeliveryInspectionFormView>
);
}
void _attachmentTap() async {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AssetDeliveryAttachmentView(
tableItemId: widget.deliveryTableModel.id,
)));
Widget _inspectionTile({
required String title,
InspectionPersonModel? model,
required bool isHmg,
required VoidCallback onTap,
}) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: AppColor.fieldBgColor(context),
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: AppTextStyles.textFieldLabelStyle),
6.height,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
model?.name?.isNotEmpty == true ? model!.name! : 'Tap to add'.addTranslation,
style: AppTextStyles.bodyText,
),
///Need to display other info as well
// Text(
// model?.email?.isNotEmpty == true
// ? model!.email!
// : '',
// style: AppTextStyles.bodyText,
// ),
],
),
],
),
],
),
),
);
}
Future<Lookup?> showAttachmentTypePicker() {
return showDialog<Lookup>(
context: context,
builder: (_) {
return AlertDialog(
content: SingleItemDropDownMenu<Lookup, AttachmentTypeLookupProvider>(
context: context,
title: context.translation.reason,
showAsFullScreenDialog: false,
onSelect: (value) {
Navigator.pop(context, value);
},
),
);
},
);
}
void _cancelTap() async {
@ -440,21 +634,44 @@ class _DeliveryInspectionFormViewState extends State<DeliveryInspectionFormView>
void _nextTap() {
if (widget.deliveryTableModel.isContainsTechnicalInspection == true) {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => TechnicalInspectionLinesListView(
// deliveryTableModel: widget.deliveryTableModel,
// requestModel: widget.requestModel,
// ))
// );
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TechnicalInspectionLinesListView(
deliveryTableModel: widget.deliveryTableModel,
requestModel: widget.requestModel,
builder: (context) => AssetDeliveryStageTabPage(
title: 'Technical Inspection'.addTranslation,
detailWidget: TechnicalInspectionLinesListView(
deliveryTableModel: widget.deliveryTableModel,
requestModel: widget.requestModel,
),
tableId: widget.deliveryTableModel.id,
)));
} else {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EndUserCostCenterLIstView(
deliveryTableModel: widget.deliveryTableModel,
requestModel: widget.requestModel,
builder: (context) => AssetDeliveryStageTabPage(
title: 'End-User Acceptance'.addTranslation,
detailWidget: EndUserCostCenterLIstView(
deliveryTableModel: widget.deliveryTableModel,
requestModel: widget.requestModel,
),
tableId: widget.deliveryTableModel.id,
)));
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => EndUserCostCenterLIstView(
// deliveryTableModel: widget.deliveryTableModel,
// requestModel: widget.requestModel,
// )));
}
}
@ -470,4 +687,40 @@ class _DeliveryInspectionFormViewState extends State<DeliveryInspectionFormView>
}
});
}
Future<InspectionPersonModel?> showInspectionBottomSheet(
BuildContext context, {
required String title,
required bool isHmg,
required InspectionPersonModel initialModel,
}) {
return showModalBottomSheet<InspectionPersonModel>(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
builder: (_) {
if (initialModel.localSignature == null && initialModel.signature != null) {
initialModel.localSignature = signatureFromString(initialModel.signature);
}
return InspectionPersonBottomSheet(
title: title,
isHmg: isHmg,
model: InspectionPersonModel(
name: initialModel.name,
email: initialModel.email,
signature: initialModel.signature,
localSignature: initialModel.localSignature,
),
);
},
);
}
Uint8List? signatureFromString(String? value) {
if (value == null || !value.contains('|')) return null;
final base64Part = value.split('|').last;
return base64Decode(base64Part);
}
}

@ -0,0 +1,152 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter/services.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/modules/asset_delivery_module/models/inspection_person_model.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/delivery_inspection/oracle_user_autocomplete_field.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/e_signature/e_signature.dart';
class InspectionPersonBottomSheet extends StatefulWidget {
final String title;
bool isHmg;
final InspectionPersonModel model;
InspectionPersonBottomSheet({
super.key,
required this.title,
required this.isHmg,
required this.model,
});
@override
State<InspectionPersonBottomSheet> createState() => _InspectionPersonBottomSheetState();
}
class _InspectionPersonBottomSheetState extends State<InspectionPersonBottomSheet> {
late InspectionPersonModel _model;
late TextEditingController nameController;
late TextEditingController emailController;
@override
void initState() {
super.initState();
_model = InspectionPersonModel(name: widget.model.name, email: widget.model.email, signature: widget.model.signature, localSignature: widget.model.localSignature);
nameController = TextEditingController(text: _model.name ?? '');
emailController = TextEditingController(text: _model.email ?? '');
}
@override
void dispose() {
nameController.dispose();
emailController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SafeArea(
top: false,
child: Padding(
padding: EdgeInsets.only(
top: 16,
left: 16,
right: 16,
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(widget.title, style: Theme.of(context).textTheme.titleLarge),
16.height,
if (widget.isHmg)
OracleUserAutoCompleteField(
clearAfterPick: false,
initialValue: _model.name ?? '',
label: 'Name'.addTranslation,
onPick: (value) {
_model.name = value.fullName;
nameController.text = value.fullName ?? '';
emailController.text = value.employeeEmail ?? '';
setState(() {});
},
)
else
AppTextFormField(
labelText: 'Name'.addTranslation,
controller: nameController,
backgroundColor: AppColor.fieldBgColor(context),
showShadow: false,
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
style: Theme.of(context).textTheme.titleMedium,
onChange: (v) {
_model.name = v;
setState(() {});
},
),
12.height,
AppTextFormField(
labelText: 'Email'.addTranslation,
controller: emailController,
textInputType: TextInputType.emailAddress,
backgroundColor: AppColor.fieldBgColor(context),
showShadow: false,
enable: widget.isHmg?false:true,
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
style: Theme.of(context).textTheme.titleMedium,
onChange: (v) {
setState(() {
_model.email = v;
});
},
),
12.height,
ESignature(
title: '',
oldSignature: _model.localSignature == null ? _model.signature : null,
newSignature: _model.localSignature,
backgroundColor: AppColor.neutral100,
showShadow: false,
onChange: (signature) {
if (signature.isEmpty) return;
setState(() {
_model.localSignature = signature;
_model.signature = "${DateTime.now().toIso8601String()}.png|${base64Encode(signature)}";
});
},
onSaved: (signature) {
if (signature == null || signature.isEmpty) return;
setState(() {
_model.localSignature = signature;
_model.signature = "${DateTime.now().toIso8601String()}.png|${base64Encode(signature)}";
});
},
),
20.height,
AppFilledButton(
label: context.translation.save,
maxWidth: true,
buttonColor: AppColor.primary10,
onPressed: () async {
_model.name = nameController.text.trim();
_model.email = emailController.text.trim();
Navigator.pop(context, _model);
},
),
],
),
),
);
}
}

@ -26,25 +26,26 @@ class OracleUserAutoCompleteField extends StatefulWidget {
class _AutoCompletePartsFieldState extends State<OracleUserAutoCompleteField> {
AssetDeliveryProvider? assetDeliveryProvider;
late TextEditingController _controller;
// late TextEditingController _controller;
@override
void initState() {
_controller = TextEditingController(text: widget.initialValue);
// _controller = TextEditingController(text: widget.initialValue);
super.initState();
}
@override
void didUpdateWidget(covariant OracleUserAutoCompleteField oldWidget) {
if (widget.initialValue != oldWidget.initialValue) {
_controller = TextEditingController(text: widget.initialValue);
}
// if (widget.initialValue != oldWidget.initialValue) {
// _controller = TextEditingController(text: widget.initialValue);
// }
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
_controller.dispose();
// _controller.dispose();
super.dispose();
}
@ -59,16 +60,18 @@ class _AutoCompletePartsFieldState extends State<OracleUserAutoCompleteField> {
// boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)],
),
child: Autocomplete<OracleUserModel>(
key: ValueKey(widget.initialValue),
optionsBuilder: (TextEditingValue textEditingValue) async {
if (textEditingValue.text.isEmpty) {
return const Iterable<OracleUserModel>.empty();
}
return await assetDeliveryProvider!.getOracleUserAutoCompleteDetails(query: textEditingValue.text);
},
initialValue: TextEditingValue(text: widget.initialValue),
displayStringForOption: (OracleUserModel option) => option.fullName ?? "",
fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) {
return TextField(
controller: _controller,
controller: fieldTextEditingController,
focusNode: fieldFocusNode,
style: AppTextStyles.bodyText.copyWith(color: AppColor.black10),
textAlign: TextAlign.start,
@ -101,11 +104,11 @@ class _AutoCompletePartsFieldState extends State<OracleUserAutoCompleteField> {
);
},
onSelected: (OracleUserModel selection) {
if (widget.clearAfterPick) {
_controller.clear();
} else {
_controller.text = selection.fullName ?? '';
}
// if (widget.clearAfterPick) {
// fieldTextEditingController.clear();
// } else {
// _controller.text = selection.fullName ?? '';
// }
widget.onPick(selection);
},
),

@ -90,10 +90,12 @@ class EndUserAssetDetailCard extends StatelessWidget {
'Replaced Asset No: ${assetModel.replacedAssetNo ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Rejection Reason: ${assetModel.rejectionReason?.name ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
//TODO Need to confirm with @Ahmed need to hide this or not
if (assetModel.status?.value == 2)
Text(
'Rejection Reason: ${assetModel.rejectionReason?.name ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Description: ${assetModel.description ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),

@ -5,6 +5,7 @@ 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/modules/asset_delivery_module/helper_function.dart';
import 'package:test_sa/modules/asset_delivery_module/models/asset_delivery_data_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/cost_center_model.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/attachment_view.dart';
@ -71,10 +72,10 @@ class _EndUserCostCenterLIstViewState extends State<EndUserCostCenterLIstView> {
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: DefaultAppBar(
title: 'End-User Acceptance'.addTranslation,
titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
// appBar: DefaultAppBar(
// title: 'End-User Acceptance'.addTranslation,
// titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
// ),
body: Form(
key: _formKey,
child: isLoading
@ -83,31 +84,41 @@ class _EndUserCostCenterLIstViewState extends State<EndUserCostCenterLIstView> {
children: [
//Refactor this
SingleChildScrollView(padding: const EdgeInsets.all(16), child: linesList(context).toShadowContainer(context, borderRadius: 20, padding: 12)).expanded,
if (!viewOnly) ...[
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(buttonColor: AppColor.primary10, label: 'Attachments'.addTranslation, maxWidth: true, onPressed: _attachmentTap),
),
8.height,
],
!viewOnly
? FooterActionButton.footerContainer(
context: context,
child: Row(
child: Column(
children: [
AppFilledButton(
label: 'Cancel'.addTranslation,
maxWidth: true,
buttonColor: AppColor.background(context),
textColor: AppColor.red30,
showBorder: true,
onPressed: _cancelTap,
).expanded,
12.width,
AppFilledButton(buttonColor: AppColor.green70, label: 'Change Status'.addTranslation, maxWidth: true, onPressed: _changeStatusTap).expanded,
AppFilledButton(buttonColor: AppColor.primary10, label: 'Attachments'.addTranslation, maxWidth: true, onPressed: _attachmentTap),
16.height,
Row(
children: [
AppFilledButton(
label: 'Cancel'.addTranslation,
maxWidth: true,
buttonColor: AppColor.background(context),
textColor: AppColor.red30,
showBorder: true,
onPressed: _cancelTap,
).expanded,
12.width,
AppFilledButton(buttonColor: AppColor.green70, label: 'Change Status'.addTranslation, maxWidth: true, onPressed: _changeStatusTap).expanded,
],
),
],
))
: const SizedBox(),
: FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.white60,
label: 'Attachments'.addTranslation,
maxWidth: true,
textColor: AppColor.black10,
onPressed: () {
HelperFunction.attachmentTap(context: context, viewOnly: true, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id);
}),
),
],
),
),
@ -156,12 +167,7 @@ class _EndUserCostCenterLIstViewState extends State<EndUserCostCenterLIstView> {
}
void _attachmentTap() async {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AssetDeliveryAttachmentView(
tableItemId: widget.deliveryTableModel.id,
)));
HelperFunction.attachmentTap(context: context, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id);
}
void _cancelTap() async {

@ -1,6 +1,229 @@
// import 'dart:developer';
//
// import 'package:flutter/foundation.dart';
// 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/lookup.dart';
// import 'package:test_sa/modules/asset_delivery_module/models/end_user_asset_details_model.dart';
// import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart';
// import 'package:test_sa/modules/asset_delivery_module/provider/end_user_rejection_reason_lookup_provider.dart';
// import 'package:test_sa/modules/asset_delivery_module/provider/end_user_status_lookup_provider.dart';
// import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
// import 'package:test_sa/new_views/app_style/app_color.dart';
// import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart';
// import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart';
// import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart';
// import '../../../../new_views/common_widgets/app_filled_button.dart';
// import '../../../../new_views/common_widgets/default_app_bar.dart';
//
// class UpdateEndUserAssetDetailsView extends StatefulWidget {
// EndUserAssetDetailsModel? assetDetailsModel;
// String? costCenterName;
// int? tableId;
//
// UpdateEndUserAssetDetailsView({Key? key, this.assetDetailsModel, this.costCenterName, this.tableId}) : super(key: key);
//
// @override
// State<UpdateEndUserAssetDetailsView> createState() => _UpdateEndUserAssetDetailsViewState();
// }
//
// class _UpdateEndUserAssetDetailsViewState extends State<UpdateEndUserAssetDetailsView> {
// final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
// final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
// bool viewOnly = false;
// late AssetDeliveryProvider assetDeliveryProvider;
// EndUserAssetDetailsModel model = EndUserAssetDetailsModel();
// late TextEditingController quantityController;
// late TextEditingController descriptionController;
//
// @override
// void initState() {
// quantityController = TextEditingController();
// descriptionController = TextEditingController();
// WidgetsBinding.instance.addPostFrameCallback((_) {
// getInitialData();
// });
// super.initState();
// }
//
// @override
// void dispose() {
// super.dispose();
// }
//
// void getInitialData() async {
// assetDeliveryProvider = Provider.of<AssetDeliveryProvider>(context, listen: false);
// if (widget.assetDetailsModel != null) {
// model = EndUserAssetDetailsModel.fromJson(widget.assetDetailsModel!.toJson());
// model.costCenterName = widget.costCenterName;
// model.assetDeliveryExternalDeliveryId = widget.tableId;
// model.status = widget.assetDetailsModel?.status;
// model.rejectionReason = widget.assetDetailsModel?.rejectionReason;
// }
// quantityController.text = model.rejectedQty != null ? model.rejectedQty.toString() : '';
// descriptionController.text = model.description ?? '';
// model.type = 'Accessory';
// setState(() {});
// }
//
// @override
// Widget build(BuildContext context) {
// return Scaffold(
// key: _scaffoldKey,
// appBar: DefaultAppBar(
// title: 'Item Details'.addTranslation,
// titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
// ),
// body: Form(
// key: _formKey,
// child: Column(
// children: [
// SingleChildScrollView(
// padding: const EdgeInsets.all(16),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// SingleItemDropDownMenu<Lookup, EndUserStatusLookupProvider>(
// context: context,
// height: 56.toScreenHeight,
// title: 'Status'.addTranslation,
// titleTextColor: AppColor.neutral120,
// showShadow: false,
// validator: (value) {
// if (value == null) return "Please select status";
// return null;
// },
// backgroundColor: AppColor.fieldBgColor(context),
// showAsBottomSheet: true,
// initialValue: model.status,
// onSelect: (status) {
// if (status != null) {
// model.status = status;
// setState(() {});
// }
// },
// ),
// 8.height,
// if (model.type == 'Accessory') ...[
// AppTextFormField(
// labelText: 'Rejected Quantity'.addTranslation,
// controller: quantityController,
// backgroundColor: AppColor.fieldBgColor(context),
// textAlign: TextAlign.center,
// labelStyle: AppTextStyles.textFieldLabelStyle,
// textInputType: TextInputType.number,
// showShadow: false,
// validator: (value) {
// if ((value ?? "").isEmpty) return "Mandatory";
// return null;
// },
// onChange: (value) {
// if (value.isNotEmpty) {
// model.rejectedQty = model.rejectedQty = num.tryParse(value);
// setState(() {});
// }
// },
// style: Theme.of(context).textTheme.titleMedium,
// ),
// 8.height,
// if (model.status?.value == 1 && (model.rejectedQty != null && quantityController.text.isNotEmpty && model.rejectedQty! > 0))
// SingleItemDropDownMenu<Lookup, EndUserRejectionReasonLookupProvider>(
// context: context,
// height: 56.toScreenHeight,
// title: 'Rejection Reason '.addTranslation,
// validator: (value) {
// if (value == null) return "Please select rejection reason";
// return null;
// },
// titleTextColor: AppColor.neutral120,
// showShadow: false,
// backgroundColor: AppColor.fieldBgColor(context),
// showAsBottomSheet: true,
// initialValue: model.rejectionReason,
// onSelect: (reason) {
// if (reason != null) {
// model.rejectionReason = reason;
// setState(() {});
// }
// },
// ),
// 8.height,
// ],
// if (model.status != null && model.status!.value == 2) ...[
// SingleItemDropDownMenu<Lookup, EndUserRejectionReasonLookupProvider>(
// context: context,
// height: 56.toScreenHeight,
// title: 'Rejection Reason '.addTranslation,
// validator: (value) {
// if (value == null) return "Please select rejection reason";
// return null;
// },
// titleTextColor: AppColor.neutral120,
// showShadow: false,
// backgroundColor: AppColor.fieldBgColor(context),
// showAsBottomSheet: true,
// initialValue: model.rejectionReason,
// onSelect: (reason) {
// if (reason != null) {
// model.rejectionReason = reason;
// setState(() {});
// }
// },
// ),
// 8.height,
// ],
// AppTextFormField(
// controller: descriptionController,
// labelText: 'Description'.addTranslation,
// backgroundColor: AppColor.fieldBgColor(context),
// showShadow: false,
// labelStyle: AppTextStyles.textFieldLabelStyle,
// alignLabelWithHint: true,
// textInputType: TextInputType.multiline,
// onChange: (value) {
// model.description = value;
// },
// onSaved: (value) {
// model.description = value;
// },
// ),
// 8.height,
// ],
// ).toShadowContainer(context, borderRadius: 20, padding: 12))
// .expanded,
// FooterActionButton.footerContainer(context: context, child: AppFilledButton(buttonColor: AppColor.primary10, label: 'Save'.addTranslation, maxWidth: true, onPressed: _saveTap)),
// ],
// ),
// ),
// );
// }
//
// ///Need to check which details show here...
//
// void _saveTap() async {
// if (!_formKey.currentState!.validate()) {
// return;
// }
// _formKey.currentState!.save();
// log('payload ${model.toJson()}');
// return;
// showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
// await assetDeliveryProvider.saveEndUserAssetDataData(model: model).then((status) async {
// Navigator.pop(context);
// if (status) {
// Navigator.pop(context, true);
// }
// });
// }
// }
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
@ -22,11 +245,16 @@ import '../../../../new_views/common_widgets/app_filled_button.dart';
import '../../../../new_views/common_widgets/default_app_bar.dart';
class UpdateEndUserAssetDetailsView extends StatefulWidget {
EndUserAssetDetailsModel? assetDetailsModel;
String? costCenterName;
int? tableId;
final EndUserAssetDetailsModel? assetDetailsModel;
final String? costCenterName;
final int? tableId;
UpdateEndUserAssetDetailsView({Key? key, this.assetDetailsModel, this.costCenterName, this.tableId}) : super(key: key);
const UpdateEndUserAssetDetailsView({
Key? key,
this.assetDetailsModel,
this.costCenterName,
this.tableId,
}) : super(key: key);
@override
State<UpdateEndUserAssetDetailsView> createState() => _UpdateEndUserAssetDetailsViewState();
@ -34,147 +262,259 @@ class UpdateEndUserAssetDetailsView extends StatefulWidget {
class _UpdateEndUserAssetDetailsViewState extends State<UpdateEndUserAssetDetailsView> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
bool viewOnly = false;
late AssetDeliveryProvider assetDeliveryProvider;
EndUserAssetDetailsModel model = EndUserAssetDetailsModel();
late TextEditingController quantityController;
late TextEditingController descriptionController;
@override
void initState() {
super.initState();
quantityController = TextEditingController();
descriptionController = TextEditingController();
WidgetsBinding.instance.addPostFrameCallback((_) {
getInitialData();
_loadInitialData();
});
super.initState();
}
@override
void dispose() {
super.dispose();
}
void getInitialData() async {
void _loadInitialData() {
assetDeliveryProvider = Provider.of<AssetDeliveryProvider>(context, listen: false);
if (widget.assetDetailsModel != null) {
model = EndUserAssetDetailsModel.fromJson(widget.assetDetailsModel!.toJson());
model = EndUserAssetDetailsModel.fromJson(
widget.assetDetailsModel!.toJson(),
);
model.costCenterName = widget.costCenterName;
model.assetDeliveryExternalDeliveryId = widget.tableId;
model.status = widget.assetDetailsModel?.status;
model.rejectionReason = widget.assetDetailsModel?.rejectionReason;
model.receivedQty = widget.assetDetailsModel?.receivedQty;
}
quantityController.text = model.rejectedQty != null ? model.rejectedQty.toString() : '';
descriptionController.text = model.description ?? '';
setState(() {});
}
bool get _isAccessory => model.type == 'Accessory';
bool get _isAccepted => model.status?.value == 1;
bool get _isRejected => model.status?.value == 2;
bool get _showRejectedQty {
if (_isAccessory) return true;
return false;
}
bool get _showReceivedQty {
if (_isAccessory && model.receivedQty != null) return true;
return false;
}
bool get _showRejectionReason {
if (_isAccessory) {
if (_isRejected) return true;
if (_isAccepted && (model.rejectedQty ?? 0) > 0) return true;
return false;
}
if (_isRejected) return true;
return false;
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: DefaultAppBar(
title: 'Item Details'.addTranslation,
titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
titleStyle: AppTextStyles.heading3.copyWith(
fontWeight: FontWeight.w500,
color: context.isDark ? AppColor.neutral30 : AppColor.neutral50,
),
),
body: Form(
key: _formKey,
child: Column(
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleItemDropDownMenu<Lookup, EndUserStatusLookupProvider>(
context: context,
height: 56.toScreenHeight,
title: 'Status'.addTranslation,
titleTextColor: AppColor.neutral120,
showShadow: false,
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
initialValue: model.status,
onSelect: (status) {
if (status != null) {
model.status = status;
setState(() {});
}
},
),
8.height,
SingleItemDropDownMenu<Lookup, EndUserRejectionReasonLookupProvider>(
context: context,
height: 56.toScreenHeight,
title: 'Rejection Reason '.addTranslation,
titleTextColor: AppColor.neutral120,
showShadow: false,
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
initialValue: model.rejectionReason,
onSelect: (reason) {
if (reason != null) {
model.rejectionReason = reason;
setState(() {});
}
},
),
8.height,
if (model.type == 'Accessory') ...[
AppTextFormField(
labelText: 'Rejected Quantity'.addTranslation,
controller: quantityController,
backgroundColor: AppColor.fieldBgColor(context),
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
textInputType: TextInputType.number,
showShadow: false,
onChange: (value) {
if (value.isNotEmpty) {
model.rejectedQty = model.rejectedQty = num.tryParse(value);
}
},
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
],
AppTextFormField(
controller: descriptionController,
labelText: 'Description'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
showShadow: false,
labelStyle: AppTextStyles.textFieldLabelStyle,
alignLabelWithHint: true,
textInputType: TextInputType.multiline,
onChange: (value) {
model.description = value;
},
onSaved: (value) {
model.description = value;
},
),
8.height,
],
).toShadowContainer(context, borderRadius: 20, padding: 12))
.expanded,
FooterActionButton.footerContainer(context: context, child: AppFilledButton(buttonColor: AppColor.primary10, label: 'Save'.addTranslation, maxWidth: true, onPressed: _saveTap)),
padding: const EdgeInsets.all(16),
child: _formBody(context),
).expanded,
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
label: 'Save'.addTranslation,
maxWidth: true,
onPressed: _saveTap,
),
),
],
),
),
);
}
///Need to check which details show here...
Widget _formBody(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_statusDropdown(),
8.height,
if (_showReceivedQty) ...[
_receivedQtyField(),
8.height,
],
if (_showRejectedQty) ...[
_rejectedQtyField(),
8.height,
],
if (_showRejectionReason) ...[
_rejectionReasonDropdown(),
8.height,
],
_descriptionField(),
8.height,
],
).toShadowContainer(context, borderRadius: 20, padding: 12);
}
Widget _statusDropdown() {
return SingleItemDropDownMenu<Lookup, EndUserStatusLookupProvider>(
context: context,
height: 56.toScreenHeight,
title: 'Status'.addTranslation,
titleTextColor: AppColor.neutral120,
showShadow: false,
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
initialValue: model.status,
validator: (value) {
if (value == null) return 'Please select status';
return null;
},
onSelect: (status) {
if (status != null) {
model.status = status;
model.rejectionReason = null;
setState(() {});
}
},
);
}
Widget _rejectedQtyField() {
return AppTextFormField(
labelText: 'Rejected Quantity'.addTranslation,
controller: quantityController,
backgroundColor: AppColor.fieldBgColor(context),
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
textInputType: TextInputType.number,
showShadow: false,
style: Theme.of(context).textTheme.titleMedium,
validator: (value) {
if (!_showRejectedQty) return null;
if ((value ?? '').isEmpty) return 'Mandatory';
final qty = num.tryParse(value!);
if (qty == null || qty <= 0) return 'Enter valid quantity';
return null;
},
onChange: (value) {
model.rejectedQty = value.isNotEmpty ? num.tryParse(value) : null;
setState(() {});
},
);
}
Widget _receivedQtyField() {
return Container(
padding: const EdgeInsets.all(12),
width: double.infinity,
decoration: BoxDecoration(
color: AppColor.neutral40,
// color: AppColor.fieldBgColor(context),
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Received Qty', style: AppTextStyles.textFieldLabelStyle),
4.height,
Text(model.receivedQty.toString(), style: AppTextStyles.textFieldLabelStyle),
],
),
);
AppTextFormField(
labelText: 'Received Quantity'.addTranslation,
initialValue: model.receivedQty.toString(),
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
enable: false,
style: Theme.of(context).textTheme.titleMedium,
);
}
Widget _rejectionReasonDropdown() {
return SingleItemDropDownMenu<Lookup, EndUserRejectionReasonLookupProvider>(
context: context,
height: 56.toScreenHeight,
title: 'Rejection Reason'.addTranslation,
titleTextColor: AppColor.neutral120,
showShadow: false,
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
initialValue: model.rejectionReason,
validator: (value) {
if (!_showRejectionReason) return null;
if (value == null) return 'Please select rejection reason';
return null;
},
onSelect: (reason) {
if (reason != null) {
model.rejectionReason = reason;
setState(() {});
}
},
);
}
Widget _descriptionField() {
return AppTextFormField(
controller: descriptionController,
labelText: 'Description'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
showShadow: false,
labelStyle: AppTextStyles.textFieldLabelStyle,
alignLabelWithHint: true,
textInputType: TextInputType.multiline,
onChange: (value) => model.description = value,
onSaved: (value) => model.description = value,
);
}
void _saveTap() async {
if (!_formKey.currentState!.validate()) return;
_formKey.currentState!.save();
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await assetDeliveryProvider.saveEndUserAssetDataData(model: model).then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context, true);
}
});
log('payload ${model.toJson()}');
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => const AppLazyLoading(),
);
final status = await assetDeliveryProvider.saveEndUserAssetDataData(model: model);
Navigator.pop(context);
if (status) {
Navigator.pop(context, true);
}
}
}

@ -235,16 +235,6 @@ class _UpdateEndUserCostCenterDetailsViewState extends State<UpdateEndUserCostCe
onChanged: (value) {},
),
8.height,
AppTextFormField(
labelText: 'Accepted By Email'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
controller: acceptedByEmailController,
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
enable: false,
style: Theme.of(context).textTheme.titleMedium,
),
] else ...[
AppTextFormField(
labelText: 'Accepted By'.addTranslation,
@ -253,25 +243,34 @@ class _UpdateEndUserCostCenterDetailsViewState extends State<UpdateEndUserCostCe
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
onChange: (value) {
model.acceptedBy = value;
},
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
labelText: 'Accepted By Email'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
controller: acceptedByEmailController,
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
onChange: (value) {
model.acceptedByEmail = value;
},
style: Theme.of(context).textTheme.titleMedium,
),
],
AppTextFormField(
labelText: 'Accepted By Email'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
controller: acceptedByEmailController,
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
onChange: (value) {
model.acceptedByEmail = value;
},
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
enable: widget.isHmg == true ? false : true,
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
ESignature(
title: 'End User Signature',
@ -280,11 +279,6 @@ class _UpdateEndUserCostCenterDetailsViewState extends State<UpdateEndUserCostCe
backgroundColor: AppColor.neutral100,
showShadow: false,
onChange: (signature) {
log('onChange called.');
updateSignature(signature: signature);
},
onSaved: (signature) {
log('onSaved called.');
updateSignature(signature: signature);
},
)
@ -294,7 +288,10 @@ class _UpdateEndUserCostCenterDetailsViewState extends State<UpdateEndUserCostCe
void updateSignature({Uint8List? signature}) {
if (signature == null || signature.isEmpty) {
model.acceptedBySignature = '';
setState(() {
newSignature = null;
model.acceptedBySignature = null;
});
return;
}
setState(() {
@ -304,6 +301,23 @@ class _UpdateEndUserCostCenterDetailsViewState extends State<UpdateEndUserCostCe
}
void _saveTap() async {
if (!_formKey.currentState!.validate()) {
return;
}
if (model.acceptanceDate == null) {
"Select Accepted date".showToast;
return;
}
if (widget.isHmg == true) {
if ((model.acceptedBy ?? '').isEmpty || (model.acceptedByEmail ?? '').isEmpty) {
'Accepted By and Email are required'.addTranslation.showToast;
return;
}
}
if (model.acceptedBySignature == null || model.acceptedBySignature!.isEmpty) {
'Signature is required'.addTranslation.showToast;
return;
}
_formKey.currentState!.save();
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
model.assetDeliveryExternalDetailId = widget.tableItemId;

@ -41,12 +41,12 @@ class _AssetDeliveryHistoryLogViewState extends State<AssetDeliveryHistoryLogVie
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: DefaultAppBar(
title: 'History Log'.addTranslation,
onBackPress: () {
Navigator.pop(context);
},
),
// appBar: DefaultAppBar(
// title: 'History Log'.addTranslation,
// onBackPress: () {
// Navigator.pop(context);
// },
// ),
body: Consumer<AssetDeliveryProvider>(builder: (context, provider, _) {
return provider.isLoading
? const CircularProgressIndicator(color: AppColor.primary10).center

@ -7,40 +7,33 @@ 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/modules/asset_delivery_module/models/technical_inspection_asset_model.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/asset_delivery_utils.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_utils.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_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/common_widgets/default_app_bar.dart';
class InspectionChecklistBottomSheet extends StatefulWidget {
class InspectionCheckListView extends StatefulWidget {
final List<InspectionChecklistItem>? initialValues;
const InspectionChecklistBottomSheet({super.key, this.initialValues});
const InspectionCheckListView({super.key, this.initialValues});
@override
State<InspectionChecklistBottomSheet> createState() =>
_InspectionChecklistBottomSheetState();
State<InspectionCheckListView> createState() => _InspectionCheckListViewState();
}
class _InspectionChecklistBottomSheetState
extends State<InspectionChecklistBottomSheet> {
class _InspectionCheckListViewState extends State<InspectionCheckListView> {
late List<InspectionChecklistItem> checklist;
@override
void initState() {
super.initState();
checklist = AssetDeliveryUtils.inspectionChecklist.map((e) => InspectionChecklistItem(title: e.title)).toList();
/// Create local copy (no shared mutation)
checklist = AssetDeliveryUtils.inspectionChecklist
.map((e) => InspectionChecklistItem(title: e.title))
.toList();
/// Apply initial values only if provided
if (widget.initialValues != null && widget.initialValues!.isNotEmpty) {
for (final item in checklist) {
final matched = widget.initialValues!.firstWhere(
(e) => e.title == item.title,
(e) => e.title == item.title,
orElse: () => InspectionChecklistItem(),
);
item.status = matched.status; // null-safe
@ -54,70 +47,36 @@ class _InspectionChecklistBottomSheetState
});
}
List<InspectionChecklistItem> get selectedValues =>
checklist.where((e) => e.status != null).toList();
List<InspectionChecklistItem> get selectedValues => checklist.where((e) => e.status != null).toList();
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColor.white10,
appBar: DefaultAppBar(
title: 'Asset Check List'.addTranslation,
showBackButton: false,
titleStyle: AppTextStyles.heading3.copyWith(
fontWeight: FontWeight.w500,
color: context.isDark
? AppColor.neutral30
: AppColor.neutral50,
),
),
body: Column(
children: [
12.height,
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: checklist.length,
padding: const EdgeInsets.symmetric(horizontal: 12),
itemBuilder: (context, index) {
final item = checklist[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.title ?? '',
style: AppTextStyles.heading6.copyWith(
color: context.isDark
? AppColor.neutral30
: AppColor.black10,
),
),
Row(
children: [
Expanded(
child: RadioListTile<String>(
title: const Text('Pass'),
value: 'PASS',
groupValue: item.status,
onChanged: (v) =>
updateStatus(index, v!),
),
),
Expanded(
child: RadioListTile<String>(
title: const Text('Fail'),
value: 'FAIL',
groupValue: item.status,
onChanged: (v) =>
updateStatus(index, v!),
),
),
],
),
],
);
},
).expanded,
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: List.generate(
checklist.length,
(index) {
final model = checklist[index];
return inspectionStatusRadioWidget(
model: model,
index: index,
context: context,
);
},
),
).toShadowContainer(
context,
borderRadius: 20,
margin: const EdgeInsets.only(top: 12, left: 12, right: 12),
),
const Spacer(),
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
@ -133,5 +92,124 @@ class _InspectionChecklistBottomSheetState
),
);
}
}
Widget inspectionStatusRadioWidget({required int index, required InspectionChecklistItem model, required BuildContext context}) {
bool status = (model.status == null || model.status == null) ? true : (model.status != null ? (model.status == 'true' ? true : false) : false);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
model.title ?? '',
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? Colors.white : AppColor.white936),
),
14.height,
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
//
// (status ? 'Pass' : 'Fail').bodyText2(context).custom(color: AppColor.neutral120, fontWeight: FontWeight.w500),
// ],
// ).expanded,
GestureDetector(
onTap: () {
setState(() {
status = !status;
model.status = status.toString();
});
},
child: Container(
width: 99.toScreenWidth,
height: 40.toScreenHeight,
padding: EdgeInsetsDirectional.all(4.toScreenHeight),
decoration: BoxDecoration(
color: AppColor.fieldBgColor(context),
borderRadius: BorderRadius.circular(5),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
buildToggleOption(
label: "PASS",
isActive: status,
activeColor: AppColor.green20,
inactiveColor: Colors.transparent,
textColor: status
? AppColor.green50
: context.isDark
? Colors.white
: AppColor.black20,
),
buildToggleOption(
label: "FAIL",
isActive: !status,
activeColor: AppColor.red20,
inactiveColor: Colors.transparent,
textColor: status
? context.isDark
? Colors.white
: AppColor.black20
: AppColor.red30,
),
],
),
),
),
],
).paddingOnly(top: 4, bottom: 4);
}
Widget buildToggleOption({
required String label,
required bool isActive,
required Color activeColor,
required Color inactiveColor,
required Color textColor,
}) {
return Container(
width: 44.toScreenWidth,
height: 30.toScreenHeight,
alignment: Alignment.center,
decoration: BoxDecoration(
color: isActive ? activeColor : inactiveColor,
borderRadius: BorderRadius.circular(3),
),
child: label.bodyText2(context).custom(color: textColor),
);
}
//older widget ...
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// item.title ?? '',
// style: AppTextStyles.heading6.copyWith(
// color: context.isDark ? AppColor.neutral30 : AppColor.black10,
// ),
// ),
// Row(
// children: [
// Expanded(
// child: RadioListTile<String>(
// title: const Text('Pass'),
// value: 'PASS',
// groupValue: item.status,
// onChanged: (v) => updateStatus(index, v!),
// ),
// ),
// Expanded(
// child: RadioListTile<String>(
// title: const Text('Fail'),
// value: 'FAIL',
// groupValue: item.status,
// onChanged: (v) => updateStatus(index, v!),
// ),
// ),
// ],
// ),
// ],
// );
}

@ -75,6 +75,14 @@ class TechnicalInspectionAssetCard extends StatelessWidget {
'Asset Number: ${assetModel.assetNumber ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Model: ${assetModel.modelDefinition?.model ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Manufacturer: ${assetModel.modelDefinition?.manufacturer ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Serial Number: ${assetModel.serialNo ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),

@ -8,9 +8,11 @@ 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/lookup.dart';
import 'package:test_sa/modules/asset_delivery_module/helper_function.dart';
import 'package:test_sa/modules/asset_delivery_module/models/asset_delivery_data_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/delivery_line_model.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_line_card_view.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_stage_tab_page.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/attachment_view.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/end_user_acceptance/end_user_cost_center_list_view.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/update_technical_inspection_lines_view.dart';
@ -78,95 +80,106 @@ class _TechnicalInspectionLinesListViewState extends State<TechnicalInspectionLi
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: DefaultAppBar(
title: 'Technical Inspection'.addTranslation,
titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
// appBar: DefaultAppBar(
// title: 'Technical Inspection'.addTranslation,
// titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
// ),
body: Form(
key: _formKey,
child: Column(
children: [
Expanded(
child: linesList(context).toShadowContainer(context, borderRadius: 20, padding: 12),
),
if (!viewOnly) ...[
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(buttonColor: AppColor.primary10, label: 'Attachments'.addTranslation, maxWidth: true, onPressed: _attachmentTap),
),
8.height,
],
FooterActionButton.footerContainer(
context: context,
child: !viewOnly
? Row(
children: [
AppFilledButton(
label: 'Cancel'.addTranslation,
maxWidth: true,
buttonColor: AppColor.background(context),
// textColor: context.isDark ? AppColor.neutral30 : Colors.white,
textColor: AppColor.red30,
showBorder: true,
onPressed: _cancelTap,
).expanded,
12.width,
AppFilledButton(buttonColor: AppColor.green70, label: 'Change Status'.addTranslation, maxWidth: true, onPressed: _changeStatusTap).expanded,
],
)
: AppFilledButton(buttonColor: AppColor.primary10, label: 'Next'.addTranslation, maxWidth: true, onPressed: _nextTap),
),
],
),
child: (isLoading)
? const CircularProgressIndicator(color: AppColor.primary10).center
: (technicalInspectionLinesList.isEmpty)
? const NoDataFound().center
: Column(
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
children: [
ListView.separated(
padding: const EdgeInsets.all(0),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: technicalInspectionLinesList.length + 1,
// +1 for the header
separatorBuilder: (_, __) => 8.height,
itemBuilder: (context, index) {
if (index == 0) {
return Text(
"Lines",
style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
);
}
final DeliveryLineModel model = technicalInspectionLinesList[index - 1];
return AssetDeliveryLineCard(
deliveryLineModel: model,
isViewOnly: viewOnly,
isTechnical: true,
editPress: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UpdateTechnicalInspectionLinesView(
requestModel: widget.requestModel,
deliveryTableModel: widget.deliveryTableModel,
deliveryLineModel: model,
),
),
);
},
);
},
).toShadowContainer(context, borderRadius: 20, padding: 12),
],
),
).expanded,
FooterActionButton.footerContainer(
context: context,
child: !viewOnly
? Column(
children: [
AppFilledButton(buttonColor: AppColor.primary10, label: 'Attachments'.addTranslation, maxWidth: true, onPressed: _attachmentTap),
16.height,
Row(
children: [
AppFilledButton(
label: 'Cancel'.addTranslation,
maxWidth: true,
buttonColor: AppColor.background(context),
// textColor: context.isDark ? AppColor.neutral30 : Colors.white,
textColor: AppColor.red30,
showBorder: true,
onPressed: _cancelTap,
).expanded,
12.width,
AppFilledButton(buttonColor: AppColor.green70, label: 'Change Status'.addTranslation, maxWidth: true, onPressed: _changeStatusTap).expanded,
],
),
],
)
: Row(
children: [
AppFilledButton(
buttonColor: AppColor.white60,
label: 'Attachments'.addTranslation,
maxWidth: true,
textColor: AppColor.black10,
onPressed: () {
HelperFunction.attachmentTap(
context: context, viewOnly: true, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id);
}).expanded,
8.width,
AppFilledButton(buttonColor: AppColor.primary10, label: 'Next'.addTranslation, maxWidth: true, onPressed: _nextTap).expanded,
],
),
),
],
),
),
);
}
Widget linesList(BuildContext context) {
if (isLoading) {
return SizedBox.expand(
child: const CircularProgressIndicator(color: AppColor.primary10).center,
);
}
if (technicalInspectionLinesList.isEmpty) {
return const NoDataFound().center;
}
return ListView.separated(
padding: const EdgeInsets.all(16),
itemCount: technicalInspectionLinesList.length + 1, // +1 for the header
separatorBuilder: (_, __) => 8.height,
itemBuilder: (context, index) {
if (index == 0) {
return Text(
"Lines",
style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
);
}
final DeliveryLineModel model = technicalInspectionLinesList[index - 1];
return AssetDeliveryLineCard(
deliveryLineModel: model,
isViewOnly: viewOnly,
isTechnical: true,
editPress: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UpdateTechnicalInspectionLinesView(
requestModel: widget.requestModel,
deliveryTableModel: widget.deliveryTableModel,
deliveryLineModel: model,
),
),
);
},
);
},
).toShadowContainer(context, borderRadius: 20, padding: 12);
}
//May be need to show data.
Widget inspectionDetails() {
return Column(
@ -227,12 +240,13 @@ class _TechnicalInspectionLinesListViewState extends State<TechnicalInspectionLi
}
void _attachmentTap() async {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AssetDeliveryAttachmentView(
tableItemId: widget.deliveryTableModel.id,
)));
HelperFunction.attachmentTap(context: context, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id);
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => AssetDeliveryAttachmentView(
// tableItemId: widget.deliveryTableModel.id,
// )));
}
void _cancelTap() async {
@ -247,12 +261,24 @@ class _TechnicalInspectionLinesListViewState extends State<TechnicalInspectionLi
}
void _nextTap() async {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => EndUserCostCenterLIstView(
// requestModel: widget.requestModel,
// deliveryTableModel: widget.deliveryTableModel,
// )));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EndUserCostCenterLIstView(
requestModel: widget.requestModel,
deliveryTableModel: widget.deliveryTableModel,
builder: (context) => AssetDeliveryStageTabPage(
title: 'End-User Acceptance'.addTranslation,
detailWidget: EndUserCostCenterLIstView(
deliveryTableModel: widget.deliveryTableModel,
requestModel: widget.requestModel,
),
tableId: widget.deliveryTableModel.id,
)));
}
}

@ -10,7 +10,7 @@ import 'package:test_sa/models/new_models/building.dart';
import 'package:test_sa/models/new_models/floor.dart';
import 'package:test_sa/models/new_models/site.dart';
import 'package:test_sa/modules/asset_delivery_module/models/technical_inspection_asset_model.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/asset_delivery_utils.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_utils.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/search_asset_view.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/status_checklist_view.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart';
@ -25,8 +25,6 @@ import '../../../../models/new_models/department.dart';
import '../../../../new_views/common_widgets/app_filled_button.dart';
import '../../../../new_views/common_widgets/default_app_bar.dart';
//TODO need to refactor this screen when done ...
class UpdateChildAssetView extends StatefulWidget {
final TechnicalInspectionAssetModel assetModel;
final int? lineId;
@ -183,17 +181,53 @@ class _UpdateChildAssetViewState extends State<UpdateChildAssetView> {
await provider.getSiteData(siteId: assetModel.site?.id, model: provider.childFormModel);
}
provider.setParentModel(assetModel);
log('child list length ${assetModel.childAssetList?.length}');
populateTextFields(model: provider.childFormModel);
}
}
}),
8.height,
AppTextFormField(
labelText: 'Asset Name'.addTranslation,
// backgroundColor: AppColor.fieldBgColor(context),
initialValue: model.modelDefinition?.assetName,
textAlign: TextAlign.center,
enable: false,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
labelText: 'Model'.addTranslation,
// backgroundColor: AppColor.fieldBgColor(context),
initialValue: model.modelDefinition?.model,
textAlign: TextAlign.center,
enable: false,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
labelText: 'Manufacturer'.addTranslation,
// backgroundColor: AppColor.fieldBgColor(context),
initialValue: model.modelDefinition?.manufacturer ?? '',
textAlign: TextAlign.center,
enable: false,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
labelText: 'Serial Number'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
controller: serialNoCtrl,
initialValue: model.serialNo ?? '',
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
textInputType: TextInputType.number,
@ -346,7 +380,13 @@ class _UpdateChildAssetViewState extends State<UpdateChildAssetView> {
///Need to check which details show here...
void _saveTap() async {
if (!_formKey.currentState!.validate()) return;
if (assetDeliveryProvider.childFormModel.installationDate == null) {
"Installation Date is Required".showToast;
return;
}
_formKey.currentState!.save();
//Need to check if not auto update.
// assetDeliveryProvider.parentFormModel.childAssetList?[widget.index]= assetDeliveryProvider.childFormModel;
Navigator.pop(context);
@ -358,7 +398,7 @@ class _UpdateChildAssetViewState extends State<UpdateChildAssetView> {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => InspectionChecklistBottomSheet(
builder: (context) => InspectionCheckListView(
initialValues: checkList,
)));
if (result != null) {
@ -423,6 +463,10 @@ class _UpdateChildAssetViewState extends State<UpdateChildAssetView> {
}
String _statusFromBool(bool? value) {
return value == true ? 'PASS' :value==false? 'FAIL':'';
return value == true
? 'PASS'
: value == false
? 'FAIL'
: '';
}
}

@ -10,7 +10,7 @@ import 'package:test_sa/models/new_models/building.dart';
import 'package:test_sa/models/new_models/floor.dart';
import 'package:test_sa/models/new_models/site.dart';
import 'package:test_sa/modules/asset_delivery_module/models/technical_inspection_asset_model.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/asset_delivery_utils.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_utils.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/search_asset_view.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/status_checklist_view.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/technical_inpection_asset_card_view.dart';
@ -28,8 +28,6 @@ import '../../../../models/new_models/department.dart';
import '../../../../new_views/common_widgets/app_filled_button.dart';
import '../../../../new_views/common_widgets/default_app_bar.dart';
//TODO need to refactor this screen also add loaders when done ...
class UpdateParentAssetView extends StatefulWidget {
final TechnicalInspectionAssetModel assetModel;
final int? lineId;
@ -216,20 +214,64 @@ class _UpdateParentAssetViewState extends State<UpdateParentAssetView> {
if (assetModel.site != null) {
await provider.getSiteData(siteId: assetModel.site?.id, model: provider.parentFormModel);
}
assetModel.assetDeliveryInternalDetailAssetId = assetModel.id;
assetModel.id = 0;
assetModel.assetDeliveryInternalDetailAssetId = assetModel.id;
assetModel.id = 0;
if (assetModel.childAssetList != null && assetModel.childAssetList!.isNotEmpty) {
for (var asset in assetModel.childAssetList!) {
asset.assetDeliveryInternalDetailAssetChildId = asset.id;
asset.id = 0;
}
}
provider.setParentModel(assetModel);
log('child list length ${assetModel.childAssetList?.length}');
populateTextFields(model: provider.parentFormModel);
}
}
}),
8.height,
AppTextFormField(
labelText: 'Asset Name'.addTranslation,
// backgroundColor: AppColor.fieldBgColor(context),
initialValue: model.modelDefinition?.assetName,
textAlign: TextAlign.center,
enable: false,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
labelText: 'Model'.addTranslation,
// backgroundColor: AppColor.fieldBgColor(context),
initialValue: model.modelDefinition?.model,
textAlign: TextAlign.center,
enable: false,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
labelText: 'Manufacturer'.addTranslation,
// backgroundColor: AppColor.fieldBgColor(context),
initialValue: model.modelDefinition?.manufacturer ?? '',
textAlign: TextAlign.center,
enable: false,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
labelText: 'Serial Number'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
controller: serialNoCtrl,
initialValue: model.serialNo ?? '',
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
textInputType: TextInputType.number,
@ -384,12 +426,16 @@ class _UpdateParentAssetViewState extends State<UpdateParentAssetView> {
///Need to check which details show here...
void _saveTap() async {
if (!_formKey.currentState!.validate()) return;
if (assetDeliveryProvider.parentFormModel.installationDate == null) {
"Installation Date is Required".showToast;
return;
}
_formKey.currentState!.save();
assetDeliveryProvider.parentFormModel.lineId = widget.lineId;
assetDeliveryProvider.parentFormModel.tableItemId = widget.tableItemId;
assetDeliveryProvider.parentFormModel.costCenterId = widget.costCenterItemId;
log('model data ${assetDeliveryProvider.parentFormModel.toJson()}');
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await assetDeliveryProvider.saveTechnicalInspectionAssetData(model: assetDeliveryProvider.parentFormModel).then((status) async {
Navigator.pop(context);
@ -404,7 +450,7 @@ class _UpdateParentAssetViewState extends State<UpdateParentAssetView> {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => InspectionChecklistBottomSheet(
builder: (context) => InspectionCheckListView(
initialValues: checkList,
)));
@ -413,18 +459,6 @@ class _UpdateParentAssetViewState extends State<UpdateParentAssetView> {
setState(() {});
}
buildInspectionPayload(checkList);
if (checkList.isNotEmpty) {
final isPass = checkList.every((e) => e.status == 'PASS');
log('Final Status: ${isPass ? 'PASS' : 'FAIL'}');
}
// call this as bottomsheet.
// final result = await showModalBottomSheet<List<Map<String, dynamic>>>(
// context: context,
// isScrollControlled: true,
// backgroundColor: Colors.transparent,
// builder: (_) => const InspectionChecklistBottomSheet(),
// );
}
bool _isPass(List<InspectionChecklistItem> list, String title) {
@ -479,446 +513,9 @@ class _UpdateParentAssetViewState extends State<UpdateParentAssetView> {
break;
}
}
checkList.forEach((item) {
log('items ${item.toJson()}');
});
}
String _statusFromBool(bool? value) {
return value == true ? 'PASS' : 'FAIL';
}
}
// import 'dart:developer';
// 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/building.dart';
// import 'package:test_sa/models/new_models/floor.dart';
// import 'package:test_sa/models/new_models/site.dart';
// import 'package:test_sa/modules/asset_delivery_module/models/technical_inspection_asset_model.dart';
// import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/search_asset_view.dart';
// import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/status_checklist_view.dart';
// import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/technical_inpection_asset_card_view.dart';
// import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/update_child_asset_view.dart';
// import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart';
// import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.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/gas_request_providers/site_provider.dart';
// import 'package:test_sa/providers/loading_list_notifier.dart';
// import 'package:test_sa/views/widgets/date_and_time/date_picker.dart';
// import '../../../../models/new_models/department.dart';
// import '../../../../new_views/common_widgets/app_filled_button.dart';
// import '../../../../new_views/common_widgets/default_app_bar.dart';
//
// //TODO need to refactor this screen when done ...
//
// class UpdateParentAssetView extends StatefulWidget {
// final TechnicalInspectionParentAssetModel assetModel;
// final int? lineId;
//
// UpdateParentAssetView({
// Key? key,
// required this.assetModel,
// this.lineId,
// }) : super(key: key);
//
// @override
// State<UpdateParentAssetView> createState() => _UpdateParentAssetViewState();
// }
//
// class _UpdateParentAssetViewState extends State<UpdateParentAssetView> {
// final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
// final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
// List<InspectionChecklistItem> checkList = [];
// late SiteProvider siteProvider;
// late AssetDeliveryProvider assetDeliveryProvider;
//
// @override
// void initState() {
// siteProvider = Provider.of<SiteProvider>(context, listen: false);
// assetDeliveryProvider = Provider.of<AssetDeliveryProvider>(context, listen: false);
// // WidgetsBinding.instance.addPostFrameCallback((_) {
// populateForm();
// // });
// super.initState();
// }
//
// void populateForm() async {
// // _model = widget.assetModel;
// assetDeliveryProvider.setModel(widget.assetModel);
// applyInspectionFlagsToChecklist(model: widget.assetModel,checklist: checkList);
// if (widget.assetModel.site != null) {
// await assetDeliveryProvider.getSiteData(siteId: widget.assetModel.site?.id, model: _model);
// }
// setState(() {});
// }
//
// @override
// void dispose() {
// super.dispose();
// }
//
// @override
// Widget build(BuildContext context) {
// return Scaffold(
// key: _scaffoldKey,
// appBar: DefaultAppBar(
// title: 'Parent Details'.addTranslation,
// titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
// ),
// body: Form(
// key: _formKey,
// child: Column(
// children: [
// SingleChildScrollView(
// padding: const EdgeInsets.all(16),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// inspectionDetailsForm(lineId: widget.lineId),
// 8.height,
// const Divider().defaultStyle(context),
// 8.height,
// childList(context),
// ],
// ).toShadowContainer(context, borderRadius: 20, padding: 12))
// .expanded,
// FooterActionButton.footerContainer(context: context, child: AppFilledButton(buttonColor: AppColor.primary10, label: 'Save'.addTranslation, maxWidth: true, onPressed: _saveTap)),
// ],
// ),
// ),
// );
// }
//
// Widget childList(BuildContext context) {
// final assetDeliveryTableList = ['abc', 'def', 'ghi', 'fhg', 'hjhh'];
// return Column(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Row(
// children: [
// Text(
// "Test List",
// style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
// ),
// 8.width,
// 'web_link_icon'.toSvgAsset().onPress(() {
// openInspectionChecklist(context);
// }),
// ],
// ),
// 8.height,
// ListView.separated(
// itemCount: assetDeliveryTableList.length,
// separatorBuilder: (_, __) => 8.height,
// padding: EdgeInsets.zero,
// shrinkWrap: true,
// physics: const NeverScrollableScrollPhysics(),
// itemBuilder: (listContext, itemIndex) {
// return TechnicalInspectionAssetCard(
// assetModel: TechnicalInspectionParentAssetModel(),
// ischild: true,
// editPress: () {
// Navigator.push(context, MaterialPageRoute(builder: (context) => UpdateChildAssetView()));
// },
// isViewOnly: false,
// );
// },
// ),
// ],
// );
// }
//
// Widget inspectionDetailsForm({int? lineId}) {
// return Column(
// children: [
// //Replace with simple container...
// AppTextFormField(
// labelText: 'Asset Number'.addTranslation,
// backgroundColor: AppColor.fieldBgColor(context),
// initialValue: _model.assetNumber,
// textAlign: TextAlign.center,
// labelStyle: AppTextStyles.textFieldLabelStyle,
// textInputType: TextInputType.number,
// showShadow: false,
// enable: false,
// style: Theme.of(context).textTheme.titleMedium,
// ).onPress(() async {
// var data = await Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => TechnicalInspectionSearchAssetView(
// lineId: lineId,
// )));
// if (data != null) {
// TechnicalInspectionParentAssetModel? assetModel = await assetDeliveryProvider.getAssetDetails(assetId: data?.id);
//
// if (assetModel != null) {
// _model =TechnicalInspectionParentAssetModel();
// setState(() {
//
// });
// setState(() {
// _model = assetModel;
// });
// if (assetModel.site != null) {
// await assetDeliveryProvider.getSiteData(siteId: assetModel.site?.id, model: _model);
// }
// setState(() {});
// }
// }
// }),
// 8.height,
// AppTextFormField(
// labelText: 'Serial Number'.addTranslation,
// backgroundColor: AppColor.fieldBgColor(context),
// initialValue: _model.serialNo ?? '',
// textAlign: TextAlign.center,
// labelStyle: AppTextStyles.textFieldLabelStyle,
// textInputType: TextInputType.number,
// showShadow: false,
// onSaved: (value) {
// _model.serialNo = value;
// },
// style: Theme.of(context).textTheme.titleMedium,
// ),
// 8.height,
// AppTextFormField(
// labelText: 'System ID'.addTranslation,
// backgroundColor: AppColor.fieldBgColor(context),
// initialValue: _model.systemId ?? '',
// textAlign: TextAlign.center,
// labelStyle: AppTextStyles.textFieldLabelStyle,
// showShadow: false,
// onSaved: (value) {
// _model.systemId = value;
// },
// style: Theme.of(context).textTheme.titleMedium,
// ),
// 8.height,
// SingleItemDropDownMenu<Site, SiteProvider>(
// context: context,
// title: 'Site',
// initialValue: _model.site,
// loading: assetDeliveryProvider.isSiteLoading,
// showAsBottomSheet: true,
// backgroundColor: AppColor.fieldBgColor(context),
// showShadow: false,
// // enabled: false,
// onSelect: (value) {
// if (value == null) {
// return;
// }
// _model.site = value;
// _model.building = null;
// _model.floor = null;
// _model.department = null;
// setState(() {});
// },
// ),
// 8.height,
// SingleItemDropDownMenu<Building, NullableLoadingProvider>(
// context: context,
// title: 'Building',
// backgroundColor: AppColor.fieldBgColor(context),
// showAsBottomSheet: true,
// loading: assetDeliveryProvider.isSiteLoading,
// showShadow: false,
// initialValue: _model.building,
// enabled: _model.site?.buildings?.isNotEmpty ?? false,
// staticData: _model.site?.buildings ?? [],
// onSelect: (value) {
// if (value == null) {
// return;
// }
// _model.building = value;
// _model.floor = null;
// _model.department = null;
// setState(() {});
// },
// ),
// 8.height,
// SingleItemDropDownMenu<Floor, NullableLoadingProvider>(
// context: context,
// showAsBottomSheet: true,
// backgroundColor: AppColor.fieldBgColor(context),
// loading: assetDeliveryProvider.isSiteLoading,
// showShadow: false,
// title: 'Floor',
// initialValue: _model.floor,
// enabled: _model.building?.floors?.isNotEmpty ?? false,
// staticData: _model.building?.floors ?? [],
// onSelect: (value) {
// if (value == null) {
// return;
// }
// _model.floor = value;
// _model.department = null;
// setState(() {});
// },
// ),
// 8.height,
// SingleItemDropDownMenu<Department, NullableLoadingProvider>(
// context: context,
// title: 'Department',
// backgroundColor: AppColor.fieldBgColor(context),
// loading: assetDeliveryProvider.isSiteLoading,
// showAsBottomSheet: true,
// showShadow: false,
// initialValue: _model.department,
// enabled: _model.floor?.departments?.isNotEmpty ?? false,
// staticData: _model.floor?.departments ?? [],
// onSelect: (value) {
// if (value == null) {
// return;
// }
// _model.department = value;
// _model.room = null;
// setState(() {});
// },
// ),
//
// 8.height,
// ADatePicker(
// label: 'Installation Date'.addTranslation,
// hideShadow: true,
// height: 56.toScreenHeight,
// backgroundColor: AppColor.fieldBgColor(context),
// date: _model.installationDate,
// // from: widget.pickerFromDate,
// formatDateWithTime: true,
// onDatePicker: (selectedDate) {
// showTimePicker(
// context: context,
// initialTime: TimeOfDay.now(),
// builder: (BuildContext context, Widget? child) {
// final ThemeData currentTheme = Theme.of(context);
// return Theme(
// data: currentTheme.copyWith(
// timePickerTheme: TimePickerThemeData(
// dialHandColor: AppColor.primary10,
// dialBackgroundColor: Colors.grey.withOpacity(0.1),
// hourMinuteColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.grey.withOpacity(0.1)),
// dayPeriodColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.transparent),
// dayPeriodTextColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? Colors.white : AppColor.primary10),
// dayPeriodBorderSide: BorderSide(color: Colors.grey.withOpacity(0.2)),
// entryModeIconColor: AppColor.primary10,
// ),
// textButtonTheme: TextButtonThemeData(style: TextButton.styleFrom(foregroundColor: AppColor.primary10)),
// ),
// child: child!,
// );
// },
// ).then((selectedTime) {
// if (selectedTime != null) {
// _model.installationDate = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute);
// setState(() {});
// }
// });
// },
// ),
// 8.height,
// ],
// );
// }
//
// ///Need to check which details show here...
//
// void _saveTap() async {
// _formKey.currentState!.save();
// }
//
// Future<void> openInspectionChecklist(BuildContext context) async {
// final result = await Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => InspectionChecklistBottomSheet(
// initialValues: checkList,
// )));
//
// log('result i got is $result');
// if(result!=null){
// checkList = result;
// setState(() {});
// }
// if (checkList.isNotEmpty) {
// final isPass = checkList.every((e) => e.status == 'PASS');
// log('Final Status: ${isPass ? 'PASS' : 'FAIL'}');
// }
// final payload = buildInspectionPayload(checkList);
//
// log('API Payload: $payload');
// // call this as bottomsheet.
// // final result = await showModalBottomSheet<List<Map<String, dynamic>>>(
// // context: context,
// // isScrollControlled: true,
// // backgroundColor: Colors.transparent,
// // builder: (_) => const InspectionChecklistBottomSheet(),
// // );
// }
//
// bool _isPass(List<InspectionChecklistItem> list, String title) {
// return list
// .firstWhere(
// (e) => e.title == title,
// orElse: () => InspectionChecklistItem(status: 'FAIL'),
// )
// .status ==
// 'PASS';
// }
//
// Map<String, bool> buildInspectionPayload(List<InspectionChecklistItem> checklist) {
// return {
// 'flagphysicalInspection': _isPass(checklist, 'Physical Inspection As Per Manufacturer Instructions'),
// 'flagFunctionPerformance': _isPass(checklist, 'Functional Performance As Per Results Of Manufacturer Recommended Check List'),
// 'flagGroundingResistance': _isPass(checklist, 'Grounding Resistance: Ω'),
// 'flagChassiss': _isPass(checklist, 'Chassis Leakage Current: µA'),
// 'flagLeadsLeakage': _isPass(checklist, 'Leads Leakage Current: µA'),
// 'flagAlert': _isPass(checklist, 'This Device Is Not Subject To Any Alert Or Recall Until The Date Of Preparation Of This Inspection Form'),
// };
// }
//
// void applyInspectionFlagsToChecklist({
// required List<InspectionChecklistItem> checklist,
// required TechnicalInspectionParentAssetModel model,
// }) {
// for (final item in checklist) {
// switch (item.title) {
// case 'Physical Inspection As Per Manufacturer Instructions':
// item.status = _statusFromBool(model.flagphysicalInspection);
// break;
//
// case 'Functional Performance As Per Results Of Manufacturer Recommended Check List':
// item.status = _statusFromBool(model.flagFunctionPerformance);
// break;
//
// case 'Grounding Resistance: Ω':
// item.status = _statusFromBool(model.flagGroundingResistance);
// break;
//
// case 'Chassis Leakage Current: µA':
// item.status = _statusFromBool(model.flagChassiss);
// break;
//
// case 'Leads Leakage Current: µA':
// item.status = _statusFromBool(model.flagLeadsLeakage);
// break;
//
// case 'This Device Is Not Subject To Any Alert Or Recall Until The Date Of Preparation Of This Inspection Form':
// item.status = _statusFromBool(model.flagAlert);
// break;
// }
// }
// }
// String _statusFromBool(bool? value) {
// return value == true ? 'PASS' : 'FAIL';
// }
// }

@ -179,6 +179,10 @@ class _UpdateTechnicalInspectionLinesViewState extends State<UpdateTechnicalInsp
title: 'Power Supply Type'.addTranslation,
titleTextColor: AppColor.neutral120,
showShadow: false,
validator: (value) {
if (value == null) return 'Please select power supply type';
return null;
},
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
initialValue: model.powerSupplyType,
@ -198,6 +202,10 @@ class _UpdateTechnicalInspectionLinesViewState extends State<UpdateTechnicalInsp
labelStyle: AppTextStyles.textFieldLabelStyle,
textInputType: TextInputType.number,
showShadow: false,
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
onSaved: (value) {
model.powerReading = int.tryParse(value);
},
@ -212,6 +220,10 @@ class _UpdateTechnicalInspectionLinesViewState extends State<UpdateTechnicalInsp
labelStyle: AppTextStyles.textFieldLabelStyle,
textInputType: TextInputType.number,
showShadow: false,
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
onSaved: (value) {
model.maxCurrent = int.tryParse(value);
},
@ -224,6 +236,10 @@ class _UpdateTechnicalInspectionLinesViewState extends State<UpdateTechnicalInsp
textInputType: TextInputType.number,
initialValue: model.voltage != null ? model.voltage.toString() : '',
textAlign: TextAlign.center,
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
onSaved: (value) {
model.voltage = int.tryParse(value);
},
@ -238,6 +254,10 @@ class _UpdateTechnicalInspectionLinesViewState extends State<UpdateTechnicalInsp
initialValue: model.frequency != null ? model.frequency.toString() : '',
textInputType: TextInputType.number,
textAlign: TextAlign.center,
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
onSaved: (value) {
model.frequency = int.tryParse(value);
},
@ -250,6 +270,7 @@ class _UpdateTechnicalInspectionLinesViewState extends State<UpdateTechnicalInsp
}
void _saveTap() async {
if (!_formKey.currentState!.validate()) return;
_formKey.currentState!.save();
model.tableItemId = widget.deliveryTableModel.id;
model.lineId = widget.deliveryLineModel?.id;

@ -11,6 +11,7 @@ import 'package:test_sa/models/new_models/floor.dart';
import 'package:test_sa/models/new_models/room_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/asset_delivery_data_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/cost_center_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/cost_center_serial_no_form_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/delivery_inspection_form_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/delivery_line_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/end_user_asset_details_model.dart';
@ -171,33 +172,38 @@ class AssetDeliveryProvider extends ChangeNotifier {
}
}
Future<List<CostCenterModel>> getAssetDeliveryCostCenterListForLines({int? lineId, int? tableItemId}) async {
List<CostCenterModel> costCenterList = [];
Future<CostCenterSerialNoFormModel> getAssetDeliveryCostCenterListForLines({
int? lineId,
int? tableItemId,
}) async {
CostCenterSerialNoFormModel costCenterSerialNoFormModel = CostCenterSerialNoFormModel();
try {
isLoading = true;
notifyListeners();
final response = await ApiManager.instance.get(
"${URLs.getCostCenterListToLineDeliveryInspection}?AssetDeliveryExternalDetailId=$lineId&AssetDeliveryExternalDeliveryId=$tableItemId",
"${URLs.getCostCenterAndSerialNoToLineDeliveryInspection}?AssetDeliveryExternalDetailId=$lineId&AssetDeliveryExternalDeliveryId=$tableItemId",
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
final jsonData = json.decode(response.body)["data"];
if (jsonData is List) {
costCenterList = jsonData.map((e) => CostCenterModel.fromJson(e)).toList();
}
costCenterSerialNoFormModel = CostCenterSerialNoFormModel.fromJson(jsonData);
} else {
costCenterList = [];
costCenterSerialNoFormModel.costCenters = [];
costCenterSerialNoFormModel.serialNos = [];
}
isLoading = false;
notifyListeners();
return costCenterList;
} catch (e) {
log("get Delivery Lines List [error] : $e");
costCenterSerialNoFormModel.costCenters = [];
costCenterSerialNoFormModel.serialNos = [];
} finally {
isLoading = false;
costCenterList = [];
notifyListeners();
return costCenterList;
}
return costCenterSerialNoFormModel;
}
Future<bool> addNewDelivery({
@ -253,15 +259,17 @@ class AssetDeliveryProvider extends ChangeNotifier {
int? tableItemId,
int? lineId,
List<CostCenterModel>? list,
List<SerialNoModel>? serialNoList,
}) async {
isLoading = true;
Map<String, dynamic> payload = {
"assetDeliveryExternalDeliveryId": tableItemId,
"assetDeliveryExternalDetailId": lineId,
"costCenters": list?.map((x) => x.toJson()).toList(),
if (serialNoList != null && serialNoList.isNotEmpty) "serialNos": serialNoList.map((x) => x.toJson()).toList(),
};
try {
final response = await ApiManager.instance.post(URLs.saveCostCenterToLineDeliveryInspection, body: payload);
final response = await ApiManager.instance.post(URLs.saveCostCenterAndSerialNoToLineDeliveryInspection, body: payload);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
isLoading = false;
@ -421,10 +429,11 @@ class AssetDeliveryProvider extends ChangeNotifier {
Future<List<GenericAttachmentModel>> getAttachments({
num? tableItemId,
num? attachmentType,
}) async {
try {
isLoading = true;
final response = await ApiManager.instance.get("${URLs.getAttachments}?assetDeliveryExternalDeliveryId=$tableItemId");
final response = await ApiManager.instance.get("${URLs.getAttachments}?attachmentDocTypeValue=$attachmentType&assetDeliveryExternalDeliveryId=$tableItemId");
List<GenericAttachmentModel> list = [];
if (response.statusCode >= 200 && response.statusCode < 300) {
isLoading = false;

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

@ -13,6 +13,7 @@ import 'package:test_sa/modules/cm_module/cm_detail_provider.dart';
import 'package:test_sa/modules/cm_module/views/components/bottom_sheets/service_request_bottomsheet.dart';
import 'package:test_sa/modules/cx_module/chat/chat_page.dart';
import 'package:test_sa/modules/cx_module/chat/chat_provider.dart';
import 'package:test_sa/modules/cx_module/chat/chat_widget.dart';
import 'package:test_sa/modules/cx_module/survey/survey_page.dart';
import 'package:test_sa/modules/loan_module/pages/create_loan_request_page.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
@ -23,8 +24,9 @@ import 'views/components/service_request_detail_view.dart';
class CMDetailPage extends StatefulWidget {
final int requestId;
final int moduleId;
CMDetailPage({Key? key, required this.requestId}) : super(key: key);
CMDetailPage({Key? key, required this.requestId, required this.moduleId}) : super(key: key);
@override
_CMDetailPageState createState() {
@ -40,6 +42,7 @@ class _CMDetailPageState extends State<CMDetailPage> {
@override
void initState() {
super.initState();
_requestProvider = Provider.of<CMDetailProvider>(context, listen: false);
WidgetsBinding.instance.addPostFrameCallback((_) {
Provider.of<ChatProvider>(context, listen: false).reset();
getInitialData();
@ -89,84 +92,46 @@ class _CMDetailPageState extends State<CMDetailPage> {
Navigator.pop(context);
},
actions: [
if (context.userProvider.isEngineer || context.userProvider.isNurse)
Selector<CMDetailProvider, bool>(
selector: (_, myModel) => myModel.isLoading, // Selects only the userName
builder: (_, isLoading, __) {
if (isLoading) {
return const SizedBox();
} else {
CMDetailProvider provider = Provider.of<CMDetailProvider>(context, listen: false);
int? statusValue = provider.currentWorkOrder?.data?.status?.value;
if (statusValue == 2 || statusValue == 3 || statusValue == 5 || statusValue == 6) {
getChatToken(moduleId, provider.currentWorkOrder?.data?.workOrderNo ?? "");
return Consumer<ChatProvider>(builder: (pContext, requestProvider, _) {
return IconButton(
icon: const Icon(Icons.chat_bubble),
onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => ChatPage(
moduleId: moduleId,
requestId: widget.requestId,
title: _requestProvider.currentWorkOrder?.data?.workOrderNo ?? "",
readOnly: _requestProvider.isReadOnlyRequest,
)));
},
).toShimmer(context: context, isShow: requestProvider.chatLoginTokenLoading, radius: 30, height: 30, width: 30);
});
}
return const SizedBox();
}
}),
if (isNurse)
IconButton(
icon: 'qr'.toSvgAsset(height: 24, width: 24),
onPressed: () {
ServiceRequestBottomSheet.getQRCodeBottomSheet(context: context);
},
)
else ...[
Selector<CMDetailProvider, bool>(
selector: (_, myModel) => myModel.isLoading, // Selects only the userName
builder: (_, isLoading, __) {
if (isLoading) {
return const SizedBox();
} else {
CMDetailProvider provider = Provider.of<CMDetailProvider>(context, listen: false);
int? statusValue = provider.currentWorkOrder?.data?.status?.value;
if (statusValue == 2) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6),
margin: const EdgeInsets.only(left: 2),
decoration: BoxDecoration(color: context.isDark ? AppColor.neutral50 : AppColor.neutral30, borderRadius: BorderRadius.circular(16)),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.add, size: 20, color: AppColor.black1E),
Text(
"Loan",
style: AppTextStyles.bodyText2.copyWith(color: AppColor.black1E),
),
4.width,
],
),
).onPress(() {
Navigator.push(context, MaterialPageRoute(builder: (context) => CreateLoanRequestPage(workOrderId: widget.requestId)));
});
}
return const SizedBox();
Selector<CMDetailProvider, bool>(
selector: (_, myModel) => myModel.isLoading, // Selects only the userName
builder: (_, isLoading, __) {
if (isLoading) {
return const SizedBox();
} else {
CMDetailProvider provider = Provider.of<CMDetailProvider>(context, listen: false);
if ((context.userProvider.isEngineer || context.userProvider.isNurse) &&
provider.currentWorkOrder?.data?.assignedEmployee != null &&
(provider.currentWorkOrder?.data?.workOrderContactPerson?.isNotEmpty ?? false)) {
return ChatWidget(
moduleId: widget.moduleId,
isShow: context.settingProvider.isUserFlowMedical && (context.userProvider.isEngineer || context.userProvider.isNurse) && provider.currentWorkOrder?.data?.assignedEmployee != null,
isReadOnly: _requestProvider.isReadOnlyRequest,
requestId: widget.requestId.toInt(),
assigneeEmployeeNumber: provider.currentWorkOrder?.data?.assignedEmployee!.employeeId!,
myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!,
contactEmployeeINumber: provider.currentWorkOrder?.data?.workOrderContactPerson.first.employeeId,
);
}
}),
IconButton(
icon: const Icon(Icons.home),
onPressed: () {
// stopTimer();
Navigator.pop(context);
},
)
],
return const SizedBox();
}
}),
isNurse
? IconButton(
icon: 'qr'.toSvgAsset(
height: 24,
width: 24,
),
onPressed: () {
ServiceRequestBottomSheet.getQRCodeBottomSheet(context: context);
},
)
: IconButton(
icon: const Icon(Icons.home),
onPressed: () {
// stopTimer();
Navigator.pop(context);
},
),
],
),
body: DefaultTabController(

@ -300,7 +300,7 @@ class _CreateCMRequestState extends State<CreateCMRequest> with TickerProviderSt
bool checkPendingRequest = false;
void showPendingRequests() {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => PendingServiceRequestScreen(pendingAssetServiceRequest!)));
Navigator.of(context).push(MaterialPageRoute(builder: (_) => PendingServiceRequestScreen(pendingAssetServiceRequest!, 1)));
}
void showPendingRequestBottomSheet() async {

@ -199,8 +199,6 @@ class _ServiceRequestDetailViewState extends State<ServiceRequestDetailView> {
'${context.translation.assetName}: ${workOrder.assetNdModel!.name?.cleanupWhitespace?.capitalizeFirstOfEach}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
// 8.height,
Text(
'${context.translation.assetNumber}: ${workOrder.asset!.assetNumber}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),

@ -35,6 +35,7 @@ import 'model/user_chat_history_model.dart';
// import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as user;
// import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav;
//Need to refector this remove unused code.
class ChatApiClient {
static final ChatApiClient _instance = ChatApiClient._internal();
@ -81,8 +82,9 @@ class ChatApiClient {
}
Future<ChatParticipantModel?> loadParticipants(int moduleId, int referenceId, String? assigneeEmployeeNumber) async {
Response response =
await ApiClient().getJsonForResponse("${URLs.chatHubUrlApi}/chat/context/$moduleId/$referenceId?assigneeEmployeeNumber=$assigneeEmployeeNumber", token: chatLoginResponse!.token);
Response response = await ApiClient().getJsonForResponse(
"${URLs.chatHubUrlApi}/chat/context/$moduleId/$referenceId?assigneeEmployeeNumber=$assigneeEmployeeNumber&requestType=chat&transactionType=17",
token: chatLoginResponse!.token);
if (!kReleaseMode) {
// logger.i("login-res: " + response.body);
@ -94,6 +96,20 @@ class ChatApiClient {
}
}
Future<bool> resetCountApi(int moduleCode, int referenceNumber, String? employeeNumber) async {
final Map<String, dynamic> payload = {
'moduleCode': "$moduleCode",
'ReferenceNumber': "$referenceNumber",
};
final Response response = await ApiClient().postJsonForResponse(
URLs.resetMessageCount,
payload,
token: chatLoginResponse!.token,
);
return response.statusCode == 200;
}
Future<List<ChatAttachment>> viewAllDocuments(int moduleId, int referenceId) async {
Response response = await ApiClient().getJsonForResponse("${URLs.chatHubUrlApi}/attachments/conversation?referenceId=$referenceId&moduleCode=$moduleId", token: chatLoginResponse!.token);

@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:audio_waveforms/audio_waveforms.dart';
@ -29,6 +30,7 @@ import 'helper/chat_file_viewer.dart';
import 'model/get_single_user_chat_list_model.dart';
import 'model/user_chat_history_model.dart';
//Need to refactor this ...
enum ChatState { idle, voiceRecordingStarted, voiceRecordingCompleted }
class ChatPage extends StatefulWidget {
@ -66,7 +68,10 @@ class _ChatPageState extends State<ChatPage> {
@override
void initState() {
super.initState();
loadChatHistory();
WidgetsBinding.instance.addPostFrameCallback((_) {
loadChatHistory();
});
playerController.addListener(() async {
// if (playerController.playerState == PlayerState.playing && playerController.maxDuration == await playerController.getDuration()) {
// await playerController.stopPlayer();
@ -75,7 +80,7 @@ class _ChatPageState extends State<ChatPage> {
});
}
void loadChatHistory() {
void loadChatHistory() async {
// // String assigneeEmployeeNumber = Provider.of<ServiceRequestDetailProvider>(context, listen: false).currentWorkOrder?.data?.assignedEmployee?.employeeId ?? "";
// // String myEmployeeId = context.userProvider.user!.username!;
// //
@ -99,7 +104,14 @@ class _ChatPageState extends State<ChatPage> {
? Provider.of<CMDetailProvider>(context, listen: false).currentWorkOrder!.data!.workOrderCreatedBy!.employeeId!
: Provider.of<CMDetailProvider>(context, listen: false).currentWorkOrder!.data!.workOrderContactPerson.first.employeeId!))
: "");
Provider.of<ChatProvider>(context, listen: false).connectToHub(widget.moduleId, widget.requestId, myEmployeeId, receiver, widget.readOnly, isMounted: mounted);
final chatProvider = context.read<ChatProvider>();
// final chatProvider = Provider.of<C;
chatProvider.connectToHub(widget.moduleId, widget.requestId, myEmployeeId, receiver, widget.readOnly, isMounted: mounted);
bool readStatus = await chatProvider.resetCount(moduleId: widget.moduleId, referenceNo: widget.requestId, userId: context.userProvider.user?.id);
if (readStatus) {
chatProvider.chatParticipantModel?.unreadCount = 0;
}
}
@override
@ -231,7 +243,9 @@ class _ChatPageState extends State<ChatPage> {
}
return Column(mainAxisSize: MainAxisSize.min, children: [
if (showDateHeader) dateCard(currentMessage.createdDate?.toString().chatMsgDateWithYear ?? ""),
isSender ? senderMsgCard(showSenderName, chatProvider.chatResponseList[index]) : recipientMsgCard(showSenderName, chatProvider.chatResponseList[index])
isSender
? senderMsgCard(showSenderName, chatProvider.chatResponseList[index], index: index)
: recipientMsgCard(showSenderName, chatProvider.chatResponseList[index])
]);
},
itemCount: chatProvider.chatResponseList.length))
@ -607,7 +621,7 @@ class _ChatPageState extends State<ChatPage> {
.center;
}
Widget senderMsgCard(bool showHeader, SingleUserChatModel? chatResponse, {bool loading = false, String msg = ""}) {
Widget senderMsgCard(bool showHeader, SingleUserChatModel? chatResponse, {bool loading = false, String msg = "", int index = 0}) {
Widget senderHeader = Row(
mainAxisSize: MainAxisSize.min,
children: [

@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'dart:typed_data';
import 'package:audio_waveforms/audio_waveforms.dart';
@ -40,6 +41,7 @@ import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:signalr_netcore/hub_connection.dart';
import 'package:signalr_netcore/signalr_client.dart';
import 'package:test_sa/controllers/api_routes/api_manager.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/modules/cx_module/chat/model/chat_login_response_model.dart';
@ -54,6 +56,8 @@ import 'model/get_single_user_chat_list_model.dart';
import 'model/user_chat_history_model.dart';
// import 'get_single_user_chat_list_model.dart';
//Need to refactor this remove unused code.
HubConnection? chatHubConnection;
class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
@ -248,6 +252,9 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
chatHubConnection!.on("OnSubmitChatAsync", OnSubmitChatAsync);
chatHubConnection!.on("OnTypingAsync", OnTypingAsync);
chatHubConnection!.on("OnStopTypingAsync", OnStopTypingAsync);
//Need by Chat Backend for seen and un seen.
chatHubConnection!.on("OnSeenChatUserAsync", onSeenUserChatAsync);
chatHubConnection!.on("OnAckSeenAsync", onAckSeenAsync);
//group On message
@ -370,7 +377,19 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
// }
}
}
// notifyListeners();
}
Future<bool> resetCount({
required int moduleId,
required int referenceNo,
String? userId,
}) async {
try {
return await ChatApiClient().resetCountApi(moduleId, referenceNo, userId);
} catch (e, stack) {
debugPrint('resetCount error: $e');
rethrow;
}
}
void updateUserChatHistoryStatusAsync(List data) {
@ -453,6 +472,15 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
print(args);
}
Future<void> markMessageAsRead(int messageId) async {
final senderId = sender?.userId;
if (senderId == null) return;
chatHubConnection?.invoke(
"SendMessageReadAsync",
args: [messageId, senderId],
);
}
void onChatSeen(List<Object?>? args) {
dynamic items = args!.toList();
// for (var user in searchedChats!) {
@ -559,6 +587,57 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
notifyListeners();
}
Future<void> onSeenUserChatAsync(List<Object?>? parameters) async {
try {
if (parameters == null || parameters.isEmpty) {
log('onSeenUserChatAsync: parameters are null or empty');
return;
}
final parm = parameters.first;
if (parm is! List || parm.isEmpty) {
log('onSeenUserChatAsync: parm is not a valid list');
return;
}
final firstItem = parm.first;
if (firstItem is! Map<String, dynamic>) {
log('onSeenUserChatAsync: firstItem is not a Map');
return;
}
await chatHubConnection!.invoke(
"AckSeenAsync",
args: [
firstItem['currentUserId'],
[firstItem['userChatHistoryId']],
],
);
} catch (e, stackTrace) {
log('onSeenUserChatAsync error: $e');
log('StackTrace: $stackTrace');
}
}
Future<void> onAckSeenAsync(List<Object?>? parameters) async {
try {
if (parameters == null || parameters.isEmpty) {
log('onAckSeenAsync: parameters are null or empty');
return;
}
final parm = parameters.first;
log('parm onAckSeenAsync $parm');
if (chatResponseList.isEmpty) {
log('onAckSeenAsync: chatResponseList is empty');
return;
}
log('last list item id ${chatResponseList.first.toJson()}');
chatResponseList.first.isSeen = true;
notifyListeners();
} catch (e, stackTrace) {
log('onAckSeenAsync error: $e');
log('StackTrace: $stackTrace');
}
}
// Future<void> OnSubmitChatAsync(List<Object?>? parameters) async {
// // List data = jsonDecode(parameters!.first!.toString());
//

@ -1,9 +1,12 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/modules/cm_module/cm_detail_provider.dart';
import 'package:test_sa/new_views/common_widgets/custom_badge.dart';
import 'chat_page.dart';
import 'chat_provider.dart';
@ -40,8 +43,10 @@ class _ChatWidgetState extends State<ChatWidget> {
@override
void initState() {
super.initState();
Provider.of<ChatProvider>(context, listen: false).reset();
getChatToken();
WidgetsBinding.instance.addPostFrameCallback((_) {
Provider.of<ChatProvider>(context, listen: false).reset();
getChatToken();
});
}
void getChatToken() {
@ -73,25 +78,33 @@ class _ChatWidgetState extends State<ChatWidget> {
@override
Widget build(BuildContext context) {
return Consumer<ChatProvider>(builder: (pContext, requestProvider, _) {
return IconButton(
icon: const Icon(Icons.chat_bubble),
onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => ChatPage(
moduleId: widget.moduleId,
requestId: widget.requestId,
title: widget.title,
readOnly: widget.isReadOnly,
assigneeEmployeeNumber: widget.assigneeEmployeeNumber,
contactEmployeeINumber: widget.contactEmployeeINumber,
myLoginUserID: widget.myLoginUserID,
)));
},
).toShimmer(context: context, isShow: requestProvider.chatLoginTokenLoading, radius: 30, height: 30, width: 30);
});
;
return widget.isShow
? Consumer<ChatProvider>(builder: (pContext, chatProvider, _) {
final int unreadCount = (!chatProvider.chatLoginTokenLoading && (chatProvider.chatParticipantModel?.unreadCount ?? 0) > 0) ? chatProvider.chatParticipantModel!.unreadCount! : 0;
return CustomBadge2(
value: unreadCount,
top: 2,
right: 2,
minSize: 18,
child: IconButton(
icon: const Icon(Icons.chat_bubble),
onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => ChatPage(
moduleId: widget.moduleId,
requestId: widget.requestId,
title: widget.title,
readOnly: widget.isReadOnly,
assigneeEmployeeNumber: widget.assigneeEmployeeNumber,
contactEmployeeINumber: widget.contactEmployeeINumber,
myLoginUserID: widget.myLoginUserID,
)));
},
).toShimmer(context: context, isShow: chatProvider.chatLoginTokenLoading, radius: 30, height: 30, width: 30),
);
})
: const SizedBox();
}
}

@ -46,6 +46,7 @@ class ChatFilePicker extends StatelessWidget {
fromMediaPicker(BuildContext context, ImageSource imageSource) async {
XFile? pickedFile = await ImagePicker().pickImage(source: imageSource, imageQuality: 70, maxWidth: 800, maxHeight: 800);
if (pickedFile != null) {
//Has UI issues here top buttons not clickable smoothly
CroppedFile? croppedFile = await ImageCropper().cropImage(
sourcePath: pickedFile.path,
// aspectRatio: CropAspectRatio(ratioX: 1, ratioY: 1),

@ -24,6 +24,7 @@ class ChatFileViewer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FutureBuilder<File?>(
future: checkFileInLocalStorage(),
builder: (BuildContext context, AsyncSnapshot<File?> snapshot) {
@ -127,8 +128,9 @@ class ChatFileViewer extends StatelessWidget {
Future<File?> downloadFile() async {
try {
// print("downloadUrl:$downloadUrl");
return await ChatApiClient()
.downloadFileWithHttp(downloadUrl, fileName: fileTypeResponse.fileName, fileTypeDescription: fileTypeResponse.fileTypeDescription, fileSource: fileTypeResponse.fileTypeId!);
.downloadFileWithHttp(downloadUrl, fileName: fileTypeResponse.fileName, fileTypeDescription: fileTypeResponse.fileTypeDescription ?? "", fileSource: fileTypeResponse.fileTypeId!);
} catch (ex) {
print("downloadFile:$ex");
return null;

@ -1,12 +1,15 @@
import 'dart:developer';
class ChatParticipantModel {
int? id;
String? title;
String? conversationType;
List<Participants>? participants;
String? lastMessage;
dynamic lastMessage;
String? createdAt;
int? unreadCount;
ChatParticipantModel({this.id, this.title, this.conversationType, this.participants, this.lastMessage, this.createdAt});
ChatParticipantModel({this.id, this.title, this.conversationType, this.participants, this.createdAt, this.unreadCount});
ChatParticipantModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
@ -20,18 +23,20 @@ class ChatParticipantModel {
}
lastMessage = json['lastMessage'];
createdAt = json['createdAt'];
unreadCount = json['unreadCount'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['title'] = this.title;
data['conversationType'] = this.conversationType;
if (this.participants != null) {
data['participants'] = this.participants!.map((v) => v.toJson()).toList();
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['title'] = title;
data['conversationType'] = conversationType;
if (participants != null) {
data['participants'] = participants!.map((v) => v.toJson()).toList();
}
data['lastMessage'] = this.lastMessage;
data['createdAt'] = this.createdAt;
data['lastMessage'] = lastMessage;
data['createdAt'] = createdAt;
data['unreadCount'] = unreadCount;
return data;
}
}
@ -54,12 +59,12 @@ class Participants {
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['userId'] = this.userId;
data['userName'] = this.userName;
data['employeeNumber'] = this.employeeNumber;
data['role'] = this.role;
data['userStatus'] = this.userStatus;
final Map<String, dynamic> data = <String, dynamic>{};
data['userId'] = userId;
data['userName'] = userName;
data['employeeNumber'] = employeeNumber;
data['role'] = role;
data['userStatus'] = userStatus;
return data;
}
}

@ -170,6 +170,8 @@ class Asset {
String? siteName;
int? siteId;
String? modelDefinition;
int? departmentId;
String? departmentName;
Asset({
this.id,
@ -181,6 +183,8 @@ class Asset {
this.siteName,
this.siteId,
this.modelDefinition,
this.departmentId,
this.departmentName,
});
Asset.fromJson(Map<String, dynamic> json) {
@ -193,6 +197,8 @@ class Asset {
siteName = json['siteName'];
siteId = json['siteId'];
modelDefinition = json['modelDefinition'];
departmentId = json['departmentId'];
departmentName = json['departmentName'];
}
Map<String, dynamic> toJson() {
@ -206,6 +212,8 @@ class Asset {
data['siteName'] = siteName;
data['siteId'] = siteId;
data['modelDefinition'] = modelDefinition;
data['departmentId'] = departmentId;
data['departmentName'] = departmentName;
return data;
}
}

@ -42,6 +42,8 @@ class _CreateEquipmentInternalAuditFormState extends State<CreateEquipmentIntern
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
FocusNode auditCheckListNode = FocusNode();
int noFindingValue = 4;
@override
void initState() {
super.initState();
@ -78,7 +80,6 @@ class _CreateEquipmentInternalAuditFormState extends State<CreateEquipmentIntern
},
),
16.height,
//TODO need to check layout issue...
MultipleItemDropDownMenu<Lookup, InternalAuditCheckListProvider>(
context: context,
showAsBottomSheet: true,
@ -89,6 +90,15 @@ class _CreateEquipmentInternalAuditFormState extends State<CreateEquipmentIntern
title: 'Internal Audit Checklist'.addTranslation,
initialValue: _equipmentinternalAuditModel.findings,
onSelect: (value) {
if ((value ?? []).isNotEmpty) {
Lookup? noFindingElement;
try {
noFindingElement = value!.singleWhere((element) => element.value == noFindingValue);
} catch (ex) {}
if (noFindingElement != null) {
value!.removeWhere((element) => element.value != noFindingElement!.value);
}
}
_equipmentinternalAuditModel.findings = value ?? [];
},
),
@ -137,9 +147,9 @@ class _CreateEquipmentInternalAuditFormState extends State<CreateEquipmentIntern
}
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
_equipmentinternalAuditModel.auditorId = context.userProvider.user?.userID;
bool status = await internalAuditProvider.addEquipmentInternalAudit(context: context, request: _equipmentinternalAuditModel);
bool status = await internalAuditProvider.addEquipmentInternalAudit(context: context, request: _equipmentinternalAuditModel);
Navigator.pop(context);
if(status){
if (status) {
Navigator.pop(context);
}
}

@ -19,6 +19,7 @@ import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/views/widgets/images/files_list.dart';
import 'package:test_sa/views/widgets/loaders/app_loading.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
import 'package:test_sa/views/widgets/requests/request_status.dart';
class EquipmentInternalAuditDetailPage extends StatefulWidget {
static const String id = "/details-internal-audit";
@ -51,12 +52,10 @@ class _EquipmentInternalAuditDetailPageState extends State<EquipmentInternalAudi
Future<void> getAuditData() async {
model = await _internalAuditProvider.getEquipmentInternalAuditById(widget.auditId);
allAttachments.clear();
allAttachments = [
allAttachments = [
...(model?.attachments ?? []),
...(model?.engineerData?.attachments ?? []),
];
}
@override
@ -82,12 +81,43 @@ class _EquipmentInternalAuditDetailPageState extends State<EquipmentInternalAudi
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
StatusLabel(
label: model!.status!.name,
id: model!.status!.value,
radius: 4,
textColor: AppColor.getRequestStatusTextColorByName(
context,
model!.status!.name,
),
backgroundColor: AppColor.getRequestStatusColorByName(
context,
model!.status!.name,
),
// textColor: AppColor.green15,
// backgroundColor: AppColor.greenStatus(context),
),
Text(
model!.createdDate!.toString().toServiceRequestCardFormat,
textAlign: TextAlign.end,
style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
)
],
),
8.height,
assetDetails(),
8.height,
requestDetails(),
8.height,
if (model?.remarks?.isNotEmpty ?? false) ...[
const Divider().defaultStyle(context),
Text(
"Remarks",
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
model!.remarks!.bodyText(context),
8.height,
],
@ -103,7 +133,7 @@ class _EquipmentInternalAuditDetailPageState extends State<EquipmentInternalAudi
],
).paddingAll(0).toShadowContainer(context),
).expanded,
if (context.userProvider.isEngineer&&model?.status?.value!=3)
if (context.userProvider.isEngineer && model?.status?.value != 3)
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
@ -160,6 +190,7 @@ class _EquipmentInternalAuditDetailPageState extends State<EquipmentInternalAudi
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
6.height,
'${context.translation.requestNo}: ${model?.requestNo ?? '-'}'.bodyText(context),
if (model != null && model!.equipmentsFindings != null && model!.equipmentsFindings!.isNotEmpty)
...model!.equipmentsFindings!.map((item) {
final findingName = item.finding?.name ?? 'Unknown';
@ -177,14 +208,17 @@ class _EquipmentInternalAuditDetailPageState extends State<EquipmentInternalAudi
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Asset Details",
"Asset Information",
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
6.height,
'${context.translation.assetName}: ${model?.asset?.assetName ?? '-'}'.bodyText(context),
'${context.translation.assetNo}: ${model?.asset?.assetNumber ?? '-'}'.bodyText(context),
'${context.translation.serialNumber}: ${model?.asset?.assetNumber ?? '-'}'.bodyText(context),
'${context.translation.manufacture}: ${model?.asset?.manufacturerName ?? '-'}'.bodyText(context),
'${context.translation.model}: ${model?.asset?.modelName ?? '-'}'.bodyText(context),
'${context.translation.site}: ${model?.asset?.siteName ?? '-'}'.bodyText(context),
'${context.translation.department}: ${model?.asset?.departmentName ?? '-'}'.bodyText(context),
],
);
}

@ -21,7 +21,6 @@ class EquipmentInternalAuditItemView extends StatelessWidget {
@override
Widget build(BuildContext context) {
//TODO need to refactor this code repetation @waseem
if (requestData != null) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -44,8 +43,8 @@ class EquipmentInternalAuditItemView extends StatelessWidget {
),
8.height,
(requestData?.typeTransaction ?? "Internal Audit Request").heading5(context),
infoWidget(label: context.translation.assetNumber, value: requestData?.assetNumber, context: context),
infoWidget(label: context.translation.requestNo, value: requestData!.requestNo, context: context),
infoWidget(label: context.translation.assetNumber, value: requestData!.assetNumber, context: context),
8.height,
Row(
mainAxisSize: MainAxisSize.min,
@ -60,7 +59,7 @@ class EquipmentInternalAuditItemView extends StatelessWidget {
),
],
).toShadowContainer(context, withShadow: showShadow).onPress(() async {
Navigator.push(context, MaterialPageRoute(builder: (context) => EquipmentInternalAuditDetailPage(auditId: requestDetails!.id!)));
Navigator.push(context, MaterialPageRoute(builder: (context) => EquipmentInternalAuditDetailPage(auditId: requestData!.id!)));
});
}
return Column(
@ -85,9 +84,11 @@ class EquipmentInternalAuditItemView extends StatelessWidget {
8.height,
(requestDetails?.nameOfType ?? "Internal Audit Request").heading5(context),
8.height,
infoWidget(label: context.translation.requestNo, value: requestDetails!.requestNo, context: context),
infoWidget(label: context.translation.assetNumber, value: requestDetails!.assetNo, context: context),
infoWidget(label: context.translation.assetSN, value: requestDetails!.assetSN, context: context),
infoWidget(label: context.translation.model, value: requestDetails!.model, context: context),
infoWidget(label: context.translation.site, value: requestDetails!.site, context: context),
8.height,
Row(
mainAxisSize: MainAxisSize.min,

@ -20,6 +20,7 @@ import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/views/widgets/images/files_list.dart';
import 'package:test_sa/views/widgets/loaders/app_loading.dart';
import 'package:test_sa/views/widgets/requests/request_status.dart';
class SystemInternalAuditDetailPage extends StatefulWidget {
static const String id = "/details-system-internal-audit";
@ -79,17 +80,45 @@ class _SystemInternalAuditDetailPageState extends State<SystemInternalAuditDetai
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
assetInformation(),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
StatusLabel(
label: model!.status!.name,
id: model!.status!.value,
radius: 4,
textColor: AppColor.getRequestStatusTextColorByName(
context,
model!.status!.name,
),
backgroundColor: AppColor.getRequestStatusColorByName(
context,
model!.status!.name,
),
// textColor: AppColor.green15,
// backgroundColor: AppColor.greenStatus(context),
),
Text(
model!.createdDate!.toString().toServiceRequestCardFormat,
textAlign: TextAlign.end,
style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
)
],
),
8.height,
workOrderInformation(),
workOrderInfo(),
const Divider().defaultStyle(context),
8.height,
assetInfo(),
//TODO need to check for comments
if (model?.findingDescription?.isNotEmpty ?? false) ...[
const Divider().defaultStyle(context),
// Text(
// "Comments".addTranslation,
// style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
// ),
Text(
"Remarks",
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
model!.findingDescription!.bodyText(context),
],
//TODO need to check for attachments
@ -143,10 +172,33 @@ class _SystemInternalAuditDetailPageState extends State<SystemInternalAuditDetai
6.height,
labelValueText(context, context.translation.woNumber, details?.workOrderNo),
labelValueText(context, 'WO Type'.addTranslation, model?.workOrderType?.name),
labelValueText(context, context.translation.site, details?.woSite),
labelValueText(context, context.translation.assetName, details?.woAssetName),
labelValueText(context, context.translation.assetNo, details?.woAssetNo),
labelValueText(context, context.translation.manufacture, details?.woManufacturer),
labelValueText(context, context.translation.serialNumber, details?.wosn),
labelValueText(context, context.translation.model, details?.woModel),
labelValueText(context, context.translation.site, details?.woSite),
labelValueText(context, context.translation.department, details?.woDepartment),
],
);
}
Widget workOrderInfo() {
final details = model?.workOrderDetails;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"WO Info",
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
6.height,
labelValueText(context, context.translation.requestNo, model?.requestNo),
labelValueText(context, 'WO Type'.addTranslation, model?.workOrderType?.name),
labelValueText(context, context.translation.woNumber, details?.workOrderNo),
labelValueText(context, "Finding Type", model?.findingType?.name),
labelValueText(context, "Assigned Engineer", model?.workOrderDetails?.woAssignedEngineer),
],
);
}
@ -187,6 +239,28 @@ class _SystemInternalAuditDetailPageState extends State<SystemInternalAuditDetai
);
}
Widget assetInfo() {
final details = model?.workOrderDetails;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Asset Details",
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
6.height,
labelValueText(context, context.translation.assetName, details?.woAssetName),
labelValueText(context, context.translation.assetNo, details?.woAssetNo),
labelValueText(context, context.translation.manufacture, details?.woManufacturer),
labelValueText(context, context.translation.serialNumber, details?.wosn),
labelValueText(context, context.translation.model, details?.woModel),
labelValueText(context, context.translation.site, details?.woSite),
labelValueText(context, context.translation.department, details?.woDepartment),
],
);
}
Widget labelValueText(BuildContext context, String label, String? value) {
if (value == null || value.isEmpty) return const SizedBox.shrink();

@ -44,9 +44,9 @@ class SystemInternalAuditItemView extends StatelessWidget {
],
),
8.height,
(requestData?.typeTransaction ?? "Internal Audit Request").heading5(context),
infoWidget(label: context.translation.assetNo, value: requestData?.assetNumber, context: context),
(requestData?.typeTransaction ?? "System Audit Request").heading5(context),
infoWidget(label: context.translation.requestNo, value: requestData!.requestNo, context: context),
infoWidget(label: context.translation.assetNumber, value: requestData?.assetNumber, context: context),
8.height,
Row(
mainAxisSize: MainAxisSize.min,
@ -61,7 +61,7 @@ class SystemInternalAuditItemView extends StatelessWidget {
),
],
).toShadowContainer(context, withShadow: showShadow).onPress(() async {
Navigator.push(context, MaterialPageRoute(builder: (context) => SystemInternalAuditDetailPage(auditId: requestDetails!.id!)));
Navigator.push(context, MaterialPageRoute(builder: (context) => SystemInternalAuditDetailPage(auditId: requestData!.id!)));
});
}
return Column(
@ -85,9 +85,12 @@ class SystemInternalAuditItemView extends StatelessWidget {
),
8.height,
(requestDetails?.nameOfType ?? "Internal Audit Request").heading5(context),
infoWidget(label: context.translation.requestNo, value: requestDetails!.requestNo, context: context),
infoWidget(label: context.translation.assetNumber, value: requestDetails!.assetNo, context: context),
infoWidget(label: context.translation.assetSN, value: requestDetails!.assetSN, context: context),
infoWidget(label: context.translation.model, value: requestDetails!.model, context: context),
infoWidget(label: context.translation.site, value: requestDetails!.site, context: context),
8.height,
// infoWidget(label: context.translation.site, value: requestDetails!.site, context: context),
8.height,

@ -1,8 +1,7 @@
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/models/new_models/building.dart';
import 'package:test_sa/models/new_models/department.dart';
import 'package:test_sa/models/new_models/floor.dart';
import 'package:test_sa/models/new_models/mapped_sites.dart';
import 'package:test_sa/models/new_models/site.dart';
import 'package:test_sa/models/new_models/traf_department.dart';
import 'package:test_sa/modules/loan_module/models/medical_department_model.dart';
class LoanFormModel {
@ -21,6 +20,8 @@ class LoanFormModel {
Site? site;
MedicalDepartmentModel? department;
List<LoanAttachments>? loanAttachment;
MappedSite? mappedSite;
TrafDepartment? mappedDepartment;
LoanFormModel({
this.docName,
@ -38,6 +39,8 @@ class LoanFormModel {
this.loanAttachment,
this.site,
this.department,
this.mappedSite,
this.mappedDepartment,
});
//{
@ -91,8 +94,10 @@ class LoanFormModel {
"vendorNumber": vendorNumber,
"vendorContact": vendorNumber,
"vendorEmail": vendorEmail,
'siteId': site?.id,
'departmentId': department?.id,
// 'siteId': site?.id,
'siteId': mappedSite?.id,
// 'departmentId': department?.id,
'departmentId': mappedDepartment?.id,
"loanAttachments": loanAttachment != null ? loanAttachment!.map((v) => v.toJson()).toList() : [],
};
}

@ -1,18 +1,20 @@
import 'package:test_sa/models/base.dart';
class MedicalDepartmentModel extends Base {
MedicalDepartmentModel({
this.departmentName, this.departmentCode, this.ntCode, this.costCenterNumber, this.costCenterName, this.name, this.id, this.createdBy, this.createdDate, this.modifiedBy, this.modifiedDate
}) : super(identifier: id?.toString() ?? '', name: departmentName); // Handle potential null id
MedicalDepartmentModel(
{this.departmentName, this.departmentCode, this.ntCode, this.costCenterNumber, this.costCenterName, this.name, this.id, this.createdBy, this.createdDate, this.modifiedBy, this.modifiedDate})
: super(identifier: id?.toString() ?? '', name: departmentName); // Handle potential null id
MedicalDepartmentModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
identifier = id?.toString() ?? '';
name = json['departmentName'] ?? json['name'];
departmentName = json['departmentName'] ?? json['name'];
departmentCode = json['departmentCode'];
ntCode = json['ntCode'];
costCenterNumber = json['costCenterNumber'];
costCenterName = json['costCenterName'];
name = json['name'];
id = json['id'];
createdBy = json['createdBy'];
createdDate = json['createdDate'];
modifiedBy = json['modifiedBy'];
@ -30,6 +32,4 @@ class MedicalDepartmentModel extends Base {
String? createdDate;
String? modifiedBy;
String? modifiedDate;
}

@ -1,58 +1,42 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/device_transfer_provider.dart';
import 'package:test_sa/controllers/validator/validator.dart';
import 'package:test_sa/dashboard_latest/dashboard_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/helper/utils.dart';
import 'package:test_sa/models/device/asset.dart';
import 'package:test_sa/models/enums/user_types.dart';
import 'package:test_sa/models/generic_attachment_model.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/models/new_models/building.dart';
import 'package:test_sa/models/new_models/floor.dart';
import 'package:test_sa/models/new_models/mapped_sites.dart';
import 'package:test_sa/models/new_models/room_model.dart';
import 'package:test_sa/models/new_models/site.dart';
import 'package:test_sa/models/new_models/task_request/task_request_model.dart';
import 'package:test_sa/models/new_models/task_request/task_type_model.dart';
import 'package:test_sa/models/service_request/pending_service_request_model.dart';
import 'package:test_sa/models/new_models/traf_department.dart';
import 'package:test_sa/models/new_models/work_order_detail_model.dart';
import 'package:test_sa/modules/cm_module/cm_request_utils.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/modules/loan_module/models/loan_form_model.dart';
import 'package:test_sa/modules/loan_module/models/medical_department_model.dart';
import 'package:test_sa/modules/loan_module/provider/loan_period_provider.dart';
import 'package:test_sa/modules/loan_module/provider/loan_provider.dart';
import 'package:test_sa/modules/loan_module/provider/loan_provider.dart';
import 'package:test_sa/modules/loan_module/provider/medical_department_provider.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart';
import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart';
import 'package:test_sa/providers/department_provider.dart';
import 'package:test_sa/providers/gas_request_providers/site_provider.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
import 'package:test_sa/providers/task_request_provider/task_job_provider.dart';
import 'package:test_sa/providers/task_request_provider/task_request_provider.dart';
import 'package:test_sa/views/widgets/date_and_time/date_picker.dart';
import 'package:test_sa/views/widgets/equipment/asset_picker.dart';
import 'package:test_sa/providers/lookups/department_lookup_provider.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
import '../../../../../../new_views/common_widgets/default_app_bar.dart';
import '../../../models/new_models/department.dart';
class CreateLoanRequestPage extends StatefulWidget {
static const String id = "/create-loan";
int? workOrderId;
WorkOrderData? workOrder;
CreateLoanRequestPage({Key? key, this.workOrderId}) : super(key: key);
CreateLoanRequestPage({Key? key, this.workOrderId, this.workOrder}) : super(key: key);
@override
_CreateLoanRequestPageState createState() => _CreateLoanRequestPageState();
@ -62,7 +46,6 @@ class _CreateLoanRequestPageState extends State<CreateLoanRequestPage> with Tick
final List<GenericAttachmentModel> attachments = [];
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
Asset? device;
TaskType? selectedType;
final LoanFormModel _loanFormModel = LoanFormModel();
@ -70,11 +53,14 @@ class _CreateLoanRequestPageState extends State<CreateLoanRequestPage> with Tick
bool get isBackUpRequest => widget.workOrderId != null;
List<Lookup> loanTypes = [];
@override
void initState() {
super.initState();
Provider.of<MedicalDepartmentProvider>(context, listen: false).reset();
Provider.of<LoanPeriodProvider>(context, listen: false).reset();
loanTypes = [Lookup(id: 0, name: "Standard", value: 1), Lookup(id: 1, name: "Backup", value: 2)];
}
@override
@ -92,23 +78,55 @@ class _CreateLoanRequestPageState extends State<CreateLoanRequestPage> with Tick
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
SingleItemDropDownMenu<Lookup, NullableLoadingProvider>(
context: context,
title: "Loan Type",
enabled: false,
staticData: loanTypes,
initialValue: isBackUpRequest ? loanTypes[1] : loanTypes[0],
showShadow: false,
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
onSelect: (value) {},
),
16.height,
if (isBackUpRequest) ...[
scanAssetButton(),
'Asset Information'.addTranslation.bodyText(context).custom(color: AppColor.black10),
8.height,
Text(
'${context.translation.assetName}: ${widget.workOrder!.assetNdModel!.name?.cleanupWhitespace?.capitalizeFirstOfEach}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.assetNumber}: ${widget.workOrder!.asset!.assetNumber}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.serialNo}: ${widget.workOrder!.asset!.assetSerialNo}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.model}: ${widget.workOrder!.model?.name!.cleanupWhitespace?.capitalizeFirstOfEach}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.manufacture}: ${widget.workOrder!.manufacturer?.name?.cleanupWhitespace?.capitalizeFirstOfEach}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
16.height,
],
...doctorInfoSection(),
// ...contactInfoSection(),
// 16.height,
...itemDetailsSection(),
16.height,
...doctorInfoSection(),
...itemDetailsSection(),
16.height,
...vendorDetailsSection(),
16.height,
AttachmentPicker(
label: context.translation.attachments,
attachment: attachments,
buttonColor: AppColor.black10,
onlyImages: false,
showAsListView: true,
buttonIcon: 'image-plus'.toSvgAsset(color: context.isDark ? AppColor.primary10 : AppColor.neutral120),
onChange: (attachments) {
attachments = attachments;
@ -251,10 +269,10 @@ class _CreateLoanRequestPageState extends State<CreateLoanRequestPage> with Tick
return [
'Item Details'.addTranslation.bodyText(context).custom(color: AppColor.black10),
8.height,
SingleItemDropDownMenu<Site, SiteProvider>(
SingleItemDropDownMenu<MappedSite, MappedSiteProvider>(
context: context,
title: context.translation.site,
initialValue: _loanFormModel.site,
initialValue: _loanFormModel.mappedSite,
showShadow: false,
validator: (value) {
if (value == null) return "Please select a site";
@ -263,12 +281,17 @@ class _CreateLoanRequestPageState extends State<CreateLoanRequestPage> with Tick
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
onSelect: (value) {
_loanFormModel.site = value;
setState(() {});
if(value!=null){
_loanFormModel.mappedSite = value;
_loanFormModel.mappedDepartment = null;
Provider.of<DepartmentLookupProvider>(context,listen:false).getData(id:_loanFormModel.mappedSite?.id);
setState(() {});
}
},
),
8.height,
SingleItemDropDownMenu<MedicalDepartmentModel, MedicalDepartmentProvider>(
SingleItemDropDownMenu<TrafDepartment, DepartmentLookupProvider>(
context: context,
title: context.translation.department,
showShadow: false,
@ -277,11 +300,11 @@ class _CreateLoanRequestPageState extends State<CreateLoanRequestPage> with Tick
return null;
},
showAsBottomSheet: true,
initialValue: _loanFormModel.department,
initialValue: _loanFormModel.mappedDepartment,
requestById: context.userProvider.user?.clientId,
backgroundColor: AppColor.fieldBgColor(context),
onSelect: (value) {
_loanFormModel.department = value;
_loanFormModel.mappedDepartment = value;
setState(() {});
},
),
@ -473,24 +496,8 @@ class _CreateLoanRequestPageState extends State<CreateLoanRequestPage> with Tick
];
}
Widget scanAssetButton() {
return AssetPicker(
device: device,
showLoading: false,
borderColor: AppColor.black20,
onPick: (asset) async {
device = asset;
setState(() {});
},
);
}
Future<void> _submit() async {
FocusScope.of(context).unfocus();
if (isBackUpRequest && device == null) {
"Please scan or pick asset".showToast;
return;
}
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
@ -507,7 +514,12 @@ class _CreateLoanRequestPageState extends State<CreateLoanRequestPage> with Tick
body["requestorUserID"] = context.userProvider.user!.userID;
if (isBackUpRequest) {
body["assetId"] = device!.id;
body["assetId"] = widget.workOrder!.asset!.id;
body["assetName"] = widget.workOrder!.assetNdModel!.name;
body["assetNumber"] = widget.workOrder!.asset!.assetNumber;
body["assetSerialNumber"] = widget.workOrder!.asset!.assetSerialNo;
body["model"] = widget.workOrder!.model?.name;
body["manufacturer"] = widget.workOrder!.manufacturer?.name;
body["cMWOItemId"] = widget.workOrderId;
}
bool isSuccess = await loanProvider.addLoanRequest(body);

@ -1,4 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:http/http.dart';
import 'package:test_sa/controllers/api_routes/api_manager.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
@ -8,9 +9,10 @@ import 'dart:convert';
class LoanProvider extends ChangeNotifier {
Future<bool> addLoanRequest(Map<String, dynamic> body) async {
try {
Response response = await ApiManager.instance.post(URLs.addLoan, body: body);
Response response = await ApiManager.instance.post(URLs.addLoan, body: body, showToast: false);
if (response.statusCode >= 200 && response.statusCode < 300) {
// trafRequestDataModel = TrafRequestDataModel.fromJson(json.decode(response.body)["data"]);
String message = (jsonDecode(response.body)["data"] ?? "") + " " + (jsonDecode(response.body)["message"] ?? "");
Fluttertoast.showToast(msg: message ?? "", toastLength: Toast.LENGTH_LONG);
return true;
}
return false;

@ -62,10 +62,6 @@ class _CreateDeviceTransferRequestState extends State<CreateDeviceTransferReques
bool checkPendingRequest = false;
void showPendingRequests() {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => PendingServiceRequestScreen(pendingAssetServiceRequest!)));
}
void _onSubmit() async {
_transferModel.assetId = _pickedAsset?.id;
_transferModel.destSiteId = _assetDestination.site?.id;

@ -9,6 +9,7 @@ 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/models/device/device_transfer.dart';
import 'package:test_sa/modules/cx_module/chat/chat_widget.dart';
import 'package:test_sa/views/widgets/images/files_list.dart';
import 'package:test_sa/views/widgets/loaders/app_loading.dart';
import 'package:test_sa/views/widgets/loaders/loading_manager.dart';
@ -22,8 +23,9 @@ import 'update_device_transfer.dart';
class DeviceTransferDetails extends StatefulWidget {
final DeviceTransfer model;
final int moduleId;
const DeviceTransferDetails({Key? key, required this.model}) : super(key: key);
const DeviceTransferDetails({Key? key, required this.model, required this.moduleId}) : super(key: key);
@override
State<DeviceTransferDetails> createState() => _DeviceTransferDetailsState();
@ -131,6 +133,7 @@ class _DeviceTransferDetailsState extends State<DeviceTransferDetails> {
] else ...[
// sender card
_buildCard(
_model!,
isSender: true,
site: _model?.senderSiteName?.cleanupWhitespace.capitalizeFirstOfEach ?? "",
dept: _model?.senderDepartmentName?.cleanupWhitespace.capitalizeFirstOfEach ?? "",
@ -150,6 +153,7 @@ class _DeviceTransferDetailsState extends State<DeviceTransferDetails> {
8.height,
// receiver card
_buildCard(
_model!,
isSender: false,
site: _model?.destSiteName?.cleanupWhitespace.capitalizeFirstOfEach ?? "",
dept: _model?.destDepartmentName?.cleanupWhitespace.capitalizeFirstOfEach ?? "",
@ -215,7 +219,17 @@ class _DeviceTransferDetailsState extends State<DeviceTransferDetails> {
],
),
],
),
).expanded,
if (context.userProvider.isEngineer || context.userProvider.isNurse)
ChatWidget(
moduleId: widget.moduleId,
isShow: context.settingProvider.isUserFlowMedical && _model!.senderMachineStatusValue! != 4,
isReadOnly: _model!.senderMachineStatusValue! != 1,
requestId: widget.model.id!.toInt(),
assigneeEmployeeNumber: _model!.senderAssignedEmployeeNumber,
myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!,
contactEmployeeINumber: _model!.assetTransferContactPersons!.first.employeeNumber,
),
if ((_userProvider!.user?.type == UsersTypes.engineer))
if (isCurrentEngineerEligibleForEdit && isCurrentRequestEditAble)
"edit".toSvgAsset(height: 40, width: 40).onPress(() async {
@ -248,7 +262,8 @@ class _DeviceTransferDetailsState extends State<DeviceTransferDetails> {
).toShadowContainer(context);
}
_buildCard({required String site, String? building, String? dept, String? floor, required String comment, required bool isSender, StatusLabel? statusLabel, String? assignTo}) {
_buildCard(DeviceTransfer _deviceTransfer,
{required String site, String? building, String? dept, String? floor, required String comment, required bool isSender, StatusLabel? statusLabel, String? assignTo}) {
bool isCurrentEngineerEligibleForEdit = isSender ? _model?.senderAssignedEmployeeId == _userProvider!.user?.userID : _model?.receiverAssignedEmployeeId == _userProvider!.user?.userID;
bool isCurrentRequestClosed = isSender ? ((_model?.senderMachineStatusValue == 3)) : ((_model?.receiverMachineStatusValue == 3));
@ -265,7 +280,17 @@ class _DeviceTransferDetailsState extends State<DeviceTransferDetails> {
if (statusLabel != null) 8.height,
isSender ? context.translation.senderDetails.heading5(context) : context.translation.receiverDetails.heading5(context),
],
),
).expanded,
if (context.userProvider.isEngineer || context.userProvider.isNurse)
ChatWidget(
moduleId: widget.moduleId,
isShow: context.settingProvider.isUserFlowMedical && (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue!) != 0,
isReadOnly: (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue) != 1,
requestId: widget.model.id!.toInt(),
assigneeEmployeeNumber: isSender ? _deviceTransfer.senderAssignedEmployeeNumber! : _deviceTransfer.receiverAssignedEmployeeNumber!,
myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!,
contactEmployeeINumber: _deviceTransfer.assetTransferContactPersons!.first.employeeNumber,
),
if ((_userProvider!.user?.type == UsersTypes.engineer))
if (isCurrentEngineerEligibleForEdit && !isCurrentRequestClosed)
@ -273,7 +298,7 @@ class _DeviceTransferDetailsState extends State<DeviceTransferDetails> {
// ? (!(_model?.senderMachineStatusName?.toLowerCase()?.contains("close") ?? false) && !(_model?.senderMachineStatusName?.toLowerCase()?.contains("complete") ?? false))
// : (!(_model?.receiverMachineStatusName?.toLowerCase()?.contains("close") ?? false) && !(_model?.receiverMachineStatusName?.toLowerCase()?.contains("complete") ?? false)))
//
"edit".toSvgAsset(height: 48, width: 48).onPress(() async {
"edit".toSvgAsset(height: 48, width: 48).onPress(() async {
bool isReload = (await Navigator.of(context).push(MaterialPageRoute(builder: (_) => UpdateDeviceTransfer(model: _model!, isSender: isSender)))) as bool;
if (isReload ?? false) {
setState(() {});

@ -28,8 +28,9 @@ import 'update_gas_refill_request.dart';
class GasRefillDetailsPage extends StatefulWidget {
GasRefillModel model;
String? priority, date;
final int moduleId;
GasRefillDetailsPage({Key? key, this.priority, this.date, required this.model}) : super(key: key);
GasRefillDetailsPage({Key? key, this.priority, this.date, required this.model, required this.moduleId}) : super(key: key);
@override
State<GasRefillDetailsPage> createState() => _GasRefillDetailsPageState();
@ -59,21 +60,22 @@ class _GasRefillDetailsPageState extends State<GasRefillDetailsPage> {
appBar: DefaultAppBar(
title: context.translation.gasRefillDetails,
actions: [
// if (context.userProvider.isEngineer || context.userProvider.isNurse)
// Selector<GasRefillProvider, GasRefillModel?>(
// selector: (_, myModel) => myModel.gasRefillModel, // Selects only the userName
// builder: (_, _gasRefillModel, __) {
// if (_gasRefillModel == null) return const SizedBox();
// return ChatWidget(
// moduleId: 2,
// isShow: _gasRefillModel.status!.value! == 1,
// // isReadOnly: _gasRefillModel.status!.value! == 1,
// requestId: widget.model.id!.toInt(),
// assigneeEmployeeNumber: _gasRefillModel.assignedEmployee!.name!,
// myLoginUserID: context.userProvider.user!.username!,
// contactEmployeeINumber: _gasRefillModel.gasRefillContactPerson!.first.employeeCode!,
// );
// })
if (context.userProvider.isEngineer || context.userProvider.isNurse)
Selector<GasRefillProvider, GasRefillModel?>(
selector: (_, myModel) => myModel.gasRefillModel, // Selects only the userName
builder: (_, _gasRefillModel, __) {
if (_gasRefillModel == null) return const SizedBox();
return ChatWidget(
moduleId: widget.moduleId,
isShow: context.settingProvider.isUserFlowMedical && _gasRefillModel.status!.value! != 0,
isReadOnly: _gasRefillModel.status!.value! != 1,
requestId: widget.model.id!.toInt(),
assigneeEmployeeNumber: _gasRefillModel.assignedEmployee?.employeeId!,
myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!,
contactEmployeeINumber: _gasRefillModel.gasRefillContactPerson!.first.employeeCode!,
);
})
],
),
key: _scaffoldKey,

@ -11,6 +11,7 @@ import 'package:test_sa/models/all_requests_and_count_model.dart';
import 'package:test_sa/models/new_models/task_request/task_request_model.dart';
import 'package:test_sa/modules/cm_module/cm_request_utils.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/modules/cx_module/chat/chat_widget.dart';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/new_views/common_widgets/working_time_tile.dart';
import 'package:test_sa/providers/task_request_provider/task_request_provider.dart';
@ -29,8 +30,9 @@ class TaskRequestDetailsView extends StatefulWidget {
static const String id = "/task-request-detail";
final int taskId;
final RequestsDetails? requestDetails;
final int moduleId;
const TaskRequestDetailsView({Key? key, required this.taskId, this.requestDetails}) : super(key: key);
const TaskRequestDetailsView({Key? key, required this.taskId, this.requestDetails, required this.moduleId}) : super(key: key);
@override
_TaskRequestDetailsViewState createState() {
@ -65,7 +67,31 @@ class _TaskRequestDetailsViewState extends State<TaskRequestDetailsView> {
Widget build(BuildContext context) {
userProvider ??= Provider.of<UserProvider>(context, listen: false);
return Scaffold(
appBar: DefaultAppBar(title: context.translation.taskRequest),
appBar: DefaultAppBar(
title: context.translation.taskRequest,
actions: [
if (context.userProvider.isEngineer || context.userProvider.isNurse)
Selector<TaskRequestProvider, TaskData?>(
selector: (_, myModel) => myModel.taskRequestModel, // Selects only the userName
builder: (_, _taskReqModel, __) {
if (_taskReqModel == null) return const SizedBox();
if ((context.userProvider.isEngineer || context.userProvider.isNurse) &&
_taskReqModel.assignedEngineer?.employeeId != null &&
(_taskReqModel.taskJobContactPersons?.isNotEmpty ?? false)) {
return ChatWidget(
moduleId: widget.moduleId,
isShow: context.settingProvider.isUserFlowMedical && _taskReqModel.taskJobStatus!.value! != 1,
isReadOnly: _taskReqModel.taskJobStatus!.value! != 2,
requestId: widget.taskId.toInt(),
assigneeEmployeeNumber: _taskReqModel.assignedEngineer?.employeeId!,
myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!,
contactEmployeeINumber: _taskReqModel.taskJobContactPersons!.first.user?.employeeId,
);
} else
return const SizedBox();
})
],
),
body: Consumer<TaskRequestProvider>(builder: (context, taskProvider, child) {
TaskData? taskModel = taskProvider.taskRequestModel;
return taskProvider.isLoading

@ -68,7 +68,8 @@ class TaskRequestItemView extends StatelessWidget {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => TaskRequestDetailsView(
taskId: requestData!.id!,
requestDetails: RequestsDetails(nameOfType: requestData?.nameOfType, status: requestData?.statusName, priority: requestData?.priorityName, date: requestData?.transactionDate),
moduleId: requestData!.transactionNo!,
requestDetails: RequestsDetails(nameOfType: requestData?.nameOfType, status: requestData?.statusName, priority: requestData?.priorityName, date: requestData?.transactionDate, ),
)));
});
}
@ -122,6 +123,7 @@ class TaskRequestItemView extends StatelessWidget {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => TaskRequestDetailsView(
taskId: requestDetails!.id!,
moduleId: requestDetails!.transactionType!,
requestDetails: requestDetails,
)));
});

@ -18,13 +18,12 @@ class AppBottomNavigationBar extends StatelessWidget {
Widget build(BuildContext context) {
bool isEngineer = (Provider.of<UserProvider>(context, listen: false).user?.type) == UsersTypes.engineer;
bool isUserFMS = Provider.of<SettingProvider>(context, listen: false).isUserFMS;
bool commissioningEng = context.userProvider.isCommissioningEngineer;
List<NavItemModel> navItems = [
NavItemModel(0, "overview", context.translation.overview),
NavItemModel(1, "request_icon", context.translation.workOrder),
//TODO unCommit this to disable task
// if (!isEngineer||isUserFMS)
NavItemModel(2, "add_icon", context.translation.calendar, showLabel: false),
if (!commissioningEng) NavItemModel(2, "add_icon", context.translation.calendar, showLabel: false),
NavItemModel(3, "assets", context.translation.assets),
NavItemModel(4, "contact", context.translation.contact),
];

@ -5,12 +5,20 @@ import 'package:test_sa/new_views/app_style/app_color.dart';
class CustomBadge extends StatelessWidget {
final Widget child; // The widget that the badge will be overlaid on.
final int value; // The value or text to be displayed in the badge.
final Color color; // The background color of the badge.
final Color color;
final double? positionR;
final double? positionT;
final double? height;
final double? width;
const CustomBadge({
CustomBadge({
Key? key,
required this.child,
required this.value,
this.height,
this.width,
this.positionR,
this.positionT,
this.color = AppColor.red30, // Default color is red
}) : super(key: key);
@ -22,13 +30,14 @@ class CustomBadge extends StatelessWidget {
child, // The main widget
// if (value > 0)
Positioned(
right: -6,
top: -6,
right: positionR ?? -6,
top: positionT ?? -6,
//Need to check why opacity he used here this is not correct need to make this total dynamic and responsive
child: Opacity(
opacity: value > 0 ? 1 : 0,
child: Container(
height: 23,
constraints: const BoxConstraints(minWidth: 23),
height: height ?? 23,
constraints: BoxConstraints(minWidth: width ?? 23),
padding: const EdgeInsets.all(3),
alignment: Alignment.center,
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(30)),
@ -43,3 +52,139 @@ class CustomBadge extends StatelessWidget {
);
}
}
//Need to use this badge every where..
class CustomBadge2 extends StatelessWidget {
final Widget child;
final int value;
final double top;
final double right;
final double left;
final double bottom;
final double minSize;
final Color backgroundColor;
final Color textColor;
final TextStyle? textStyle;
final int maxValue;
final Duration animationDuration;
const CustomBadge2({
super.key,
required this.child,
required this.value,
this.top = -6,
this.right = -6,
this.left = double.nan,
this.bottom = double.nan,
this.minSize = 18,
this.backgroundColor = AppColor.red30,
this.textColor = AppColor.white10,
this.textStyle,
this.maxValue = 99,
this.animationDuration = const Duration(milliseconds: 200),
});
bool get _isVisible => value > 0;
@override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
children: [
child,
if (_isVisible)
Positioned(
top: top.isNaN ? null : top,
right: right.isNaN ? null : right,
left: left.isNaN ? null : left,
bottom: bottom.isNaN ? null : bottom,
child: AnimatedScale(
scale: _isVisible ? 1 : 0,
duration: animationDuration,
curve: Curves.easeOutBack,
child: AnimatedOpacity(
opacity: _isVisible ? 1 : 0,
duration: animationDuration,
child: _Badge(
value: value,
maxValue: maxValue,
minSize: minSize,
backgroundColor: backgroundColor,
textColor: textColor,
textStyle: textStyle ??
Theme.of(context).textTheme.labelSmall?.copyWith(
fontWeight: FontWeight.w700,
),
),
),
),
),
],
);
}
}
class _Badge extends StatelessWidget {
final int value;
final int maxValue;
final double minSize;
final Color backgroundColor;
final Color textColor;
final TextStyle? textStyle;
const _Badge({
required this.value,
required this.maxValue,
required this.minSize,
required this.backgroundColor,
required this.textColor,
this.textStyle,
});
bool get _isOverflow => value > maxValue;
@override
Widget build(BuildContext context) {
final TextStyle baseStyle = (textStyle ?? Theme.of(context).textTheme.labelMedium)!.copyWith(
color: textColor,
fontWeight: FontWeight.w700,
);
final double badgeSize = minSize;
return IgnorePointer(
child: SizedBox(
width: badgeSize,
height: badgeSize,
child: DecoratedBox(
decoration: const BoxDecoration(
shape: BoxShape.circle,
),
child: DecoratedBox(
decoration: BoxDecoration(
color: backgroundColor,
shape: BoxShape.circle,
),
child: Center(
child: _isOverflow
? FittedBox(
child: Text(
'$maxValue+',
style: baseStyle.copyWith(
fontSize: badgeSize * 0.45,
),
),
)
: CounterAnimatedText(
value: value,
style: baseStyle.copyWith(
fontSize: badgeSize * 0.6,
),
),
),
),
),
),
);
}
}

@ -113,12 +113,14 @@ class CreateRequestModel {
// list.add(CreateRequestModel(module.module!.name!, "add_icon", CreateTaskView.id));
// }
// });
if (context.userProvider.isQualityUser) {
if (context.userProvider.isQualityUser && context.settingProvider.isUserFlowMedical) {
list.add(CreateRequestModel("Equipment Internal Audit Checklist".addTranslation, "add_icon", CreateEquipmentInternalAuditForm.id));
list.add(CreateRequestModel("System Internal Audit Checklist".addTranslation, "add_icon", CreateSystemInternalAuditForm.id));
} else if (context.userProvider.isAssessor) {
list.add(CreateRequestModel("TRAF".addTranslation, "add_icon", CreateTRAFRequestPage.id));
} else if (context.userProvider.isEngineer) {
}
// else if (context.userProvider.isAssessor) {
// list.add(CreateRequestModel("TRAF".addTranslation, "add_icon", CreateTRAFRequestPage.id));
// }
else if (context.userProvider.isEngineer) {
if (Provider.of<SettingProvider>(context, listen: false).engineerCanCreateCM) {
list.add(CreateRequestModel(context.translation.correctiveMaintenance, "add_icon", CreateCMRequest.id));
}
@ -135,7 +137,7 @@ class CreateRequestModel {
list.add(CreateRequestModel(context.translation.transferAsset, "add_icon", CreateDeviceTransferRequest.id));
//TODO uncommit this to enable task.
list.add(CreateRequestModel(context.translation.task, "add_icon", CreateTaskView.id));
list.add(CreateRequestModel("TRAF".addTranslation, "add_icon", CreateTRAFRequestPage.id));
// list.add(CreateRequestModel("TRAF".addTranslation, "add_icon", CreateTRAFRequestPage.id));
list.add(CreateRequestModel('Loan Equipment Request'.addTranslation, "add_icon", CreateLoanRequestPage.id));
}
return list;

@ -1,8 +1,11 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.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/user_provider.dart';
import 'package:test_sa/controllers/providers/settings/app_settings.dart';
import 'package:test_sa/dashboard_latest/dashboard_view.dart';
@ -42,7 +45,7 @@ class _LandPageState extends State<LandPage> {
bool showAppbar = true;
late List<Widget> _pages;
UserProvider? _userProvider;
bool isFCM = true;
@override
void initState() {
_pages = <Widget>[];
@ -123,7 +126,14 @@ class _LandPageState extends State<LandPage> {
// if (_userProvider!.user!.type != UsersTypes.engineer) const CalendarPage(),
const MyAssetsPage(fromBottomBar: true),
];
if (isFCM) {
FirebaseNotificationManger.initialized(context);
NotificationManger.initialisation((notificationDetails) {
FirebaseNotificationManger.handleMessage(context, json.decode(notificationDetails.payload!));
}, (id, title, body, payload) async {});
isFCM = false;
}
checkLocalAuth();
}

@ -79,9 +79,9 @@ class _AllRequestsFilterPageState extends State<AllRequestsFilterPage> {
if (isEngineer) {
types[context.translation.recurrentWo] = 5;
types["Equipment Internal Audit".addTranslation] = 10;
types["System Internal Audit".addTranslation] = 11;
types["Medical Gas Inspection".addTranslation] = 14;
if (context.settingProvider.isUserFlowMedical) types["Equipment Internal Audit".addTranslation] = 10;
if (context.settingProvider.isUserFlowMedical) types["System Internal Audit".addTranslation] = 11;
if (context.settingProvider.isUserFlowMedical) types["Medical Gas Inspection".addTranslation] = 11;
}
if (context.settingProvider.isUserFlowMedical && isEngineer) {
@ -94,16 +94,24 @@ class _AllRequestsFilterPageState extends State<AllRequestsFilterPage> {
types[module.module!.name!] = module.module!.value!;
}
});
//Hide traf
// if (!isEngineer) {
// types['TRAF'] = 9;
// }
if (!isEngineer) {
types['TRAF'] = 9;
}
if (context.userProvider.isAssessor) {
types = {"TRAF": 9};
}
// if (context.userProvider.isAssessor) {
// types = {"TRAF": 9};
// }
if (context.userProvider.isQualityUser) {
types = {'Equipment Internal Audit': 10, 'System Internal Audit': 11};
if (context.settingProvider.isUserFlowMedical) types = {'Recall and Alert': 7};
if (context.settingProvider.isUserFlowMedical) types['Equipment Internal Audit'] = 10;
if (context.settingProvider.isUserFlowMedical) types['System Internal Audit'] = 11;
}
if (context.userProvider.isCommissioningEngineer) {
///TODO need to check what other requests type needed here.
types = {
'Asset Delivery': 13,
};
}
final statuses = {

@ -62,14 +62,15 @@ class _MyRequestsPageState extends State<MyRequestsPage> {
requestsList.add(Request(module.module!.value!, module.module!.name!.addTranslation));
}
});
if (context.userProvider.user!.type == UsersTypes.normal_user) {
requestsList.add(Request(9, 'TRAF'));
}
if (context.userProvider.isEngineer) {
//Hide TRAF.
// if (context.userProvider.user!.type == UsersTypes.normal_user) {
// requestsList.add(Request(9, 'TRAF'));
// }
if (context.userProvider.isEngineer &&context.settingProvider.isUserFlowMedical) {
requestsList.add(Request(10, 'Equipment Internal Audit'));
requestsList.add(Request(11, 'System Internal Audit'));
//Need to be added for new user role commissioning engineer
requestsList.add(Request(13, "Asset Delivery"));
// requestsList.add(Request(13, "Asset Delivery"));
requestsList.add(Request(14, "Medical Gas Inspection"));
}
@ -78,14 +79,22 @@ class _MyRequestsPageState extends State<MyRequestsPage> {
if (context.userProvider.isAssessor) {
requestsList = [
Request(null, context.translation.allWorkOrder),
Request(9, 'TRAF'),
// Request(9, 'TRAF'),
];
}
if (context.userProvider.isCommissioningEngineer) {
///TODO need to show only allowed ids for all workOrder
requestsList = [
Request(null, context.translation.allWorkOrder),
Request(13, "Asset Delivery"),
];
}
if (context.userProvider.isQualityUser) {
requestsList = [
Request(null, context.translation.allWorkOrder),
Request(10, 'Equipment Internal Audit'),
Request(11, 'System Internal Audit'),
if (context.settingProvider.isUserFlowMedical) Request(7, 'Recall and Alert'),
if (context.settingProvider.isUserFlowMedical) Request(10, 'Equipment Internal Audit'),
if (context.settingProvider.isUserFlowMedical) Request(11, 'System Internal Audit'),
];
}

@ -77,7 +77,7 @@ class DeviceItemView extends StatelessWidget {
),
],
).toShadowContainer(context, withShadow: showShadow).onPress(() {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => DeviceTransferDetails(model: DeviceTransfer(id: requestData!.id!))));
Navigator.of(context).push(MaterialPageRoute(builder: (_) => DeviceTransferDetails(model: DeviceTransfer(id: requestData!.id!), moduleId: requestData!.transactionNo!)));
});
}
@ -138,7 +138,11 @@ class DeviceItemView extends StatelessWidget {
),
],
).toShadowContainer(context, withShadow: showShadow).onPress(() {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => DeviceTransferDetails(model: DeviceTransfer(id: requestDetails!.id!))));
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => DeviceTransferDetails(
model: DeviceTransfer(id: requestDetails!.id!),
moduleId: requestDetails!.transactionType!,
)));
});
}

@ -74,6 +74,7 @@ class GasRefillItemView extends StatelessWidget {
builder: (_) => GasRefillDetailsPage(
priority: requestData!.priorityName,
date: requestData!.transactionDate!,
moduleId: requestData!.transactionNo!,
model: GasRefillModel(id: requestData!.id),
),
),
@ -135,6 +136,7 @@ class GasRefillItemView extends StatelessWidget {
builder: (_) => GasRefillDetailsPage(
priority: requestDetails!.priority!,
date: requestDetails!.date,
moduleId: requestDetails!.transactionType!,
model: GasRefillModel(id: requestDetails!.id),
),
),

@ -81,7 +81,7 @@ class ServiceRequestItemView extends StatelessWidget {
),
],
).toShadowContainer(context, withShadow: showShadow).onPress(() async {
await Navigator.of(context).push(MaterialPageRoute(builder: (_) => CMDetailPage(requestId: requestData!.id!)));
await Navigator.of(context).push(MaterialPageRoute(builder: (_) => CMDetailPage(requestId: requestData!.id!, moduleId: requestData!.transactionNo!)));
if (refreshData) {
Provider.of<DashBoardProvider>(context, listen: false).refreshDashboard(userType: Provider.of<SettingProvider>(context, listen: false).user!.type!, context: context);
}
@ -140,7 +140,7 @@ class ServiceRequestItemView extends StatelessWidget {
),
],
).toShadowContainer(context, withShadow: showShadow).onPress(() {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => CMDetailPage(requestId: requestDetails!.id!)));
Navigator.of(context).push(MaterialPageRoute(builder: (_) => CMDetailPage(requestId: requestDetails!.id!, moduleId: requestDetails!.transactionType!)));
});
}

@ -77,6 +77,7 @@ class TaskRequestProvider extends ChangeNotifier {
Future getTaskById({required int id, bool? showLoading = true}) async {
try {
isLoading = showLoading ?? true;
taskRequestModel = null;
notifyListeners();
final response = await ApiManager.instance.get(URLs.getTaskByIdUrl + "?taskJobId=$id");
stateCode = response.statusCode;
@ -129,7 +130,6 @@ class TaskRequestProvider extends ChangeNotifier {
isLoading = false;
notifyListeners();
if (stateCode == 200) {
return true;
}
return false;

@ -9,9 +9,10 @@ import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/views/widgets/sound/sound_player.dart';
class PendingServiceRequestScreen extends StatelessWidget {
final int moduleId;
final PendingAssetServiceRequest pendingAssetServiceRequest;
const PendingServiceRequestScreen(this.pendingAssetServiceRequest, {Key? key}) : super(key: key);
const PendingServiceRequestScreen(this.pendingAssetServiceRequest, this.moduleId, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -63,7 +64,7 @@ class PendingServiceRequestScreen extends StatelessWidget {
],
),
).onPress(() {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => CMDetailPage(requestId: pendingAssetServiceRequest.details![index].id ?? 0)));
Navigator.of(context).push(MaterialPageRoute(builder: (_) => CMDetailPage(requestId: pendingAssetServiceRequest.details![index].id ?? 0, moduleId: moduleId)));
})));
}
}

@ -122,7 +122,7 @@ class _AssetDetailPageState extends State<AssetDetailPage> {
"${context.translation.building}: ${assetModel.building?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}".bodyText(context),
"${context.translation.floor}: ${assetModel.floor?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}".bodyText(context),
"${context.translation.md}: ${assetModel.department?.departmentName?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}".bodyText(context),
"${context.translation.room}: ${assetModel.room?.value ?? "-"}".bodyText(context),
"${context.translation.room}: ${assetModel.room?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}".bodyText(context),
],
).expanded,
],

@ -1,10 +1,15 @@
import 'dart:io';
import 'package:ellipsized_text/ellipsized_text.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:open_file/open_file.dart';
import 'package:test_sa/controllers/api_routes/urls.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/views/widgets/buttons/app_back_button.dart';
import 'package:test_sa/views/widgets/loaders/image_loader.dart';
import 'package:url_launcher/url_launcher.dart';
@ -12,78 +17,180 @@ import 'package:url_launcher/url_launcher.dart';
class FilesList extends StatelessWidget {
final List<String> images;
final EdgeInsets? padding;
final bool showAsListView;
const FilesList({Key? key, this.images = const <String>[], this.padding}) : super(key: key);
const FilesList({Key? key, this.images = const <String>[], this.padding, this.showAsListView = false}) : super(key: key);
@override
Widget build(BuildContext context) {
return GridView.builder(
padding: const EdgeInsets.only(top: 8, bottom: 8),
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 6, mainAxisSpacing: 8, crossAxisSpacing: 8),
itemCount: images.length,
itemBuilder: (context, itemIndex) {
if (!images[itemIndex].contains(".")) {
return const Text("Bad\nFile");
}
var isImage = images[itemIndex].split(".").last.toLowerCase() == "png" || images[itemIndex].split(".").last.toLowerCase() == "jpg" || images[itemIndex].split(".").last.toLowerCase() == "jpeg";
var isPdf = images[itemIndex].split(".").last.toLowerCase() == "pdf";
var isExcel = images[itemIndex].split(".").last.toLowerCase() == "xlsx";
return showAsListView
? ListView.separated(
shrinkWrap: true,
padding: const EdgeInsets.only(top: 8),
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (cxt, itemIndex) {
if (!images[itemIndex].contains(".")) {
return const Text("Bad\nFile");
}
var isImage =
images[itemIndex].split(".").last.toLowerCase() == "png" || images[itemIndex].split(".").last.toLowerCase() == "jpg" || images[itemIndex].split(".").last.toLowerCase() == "jpeg";
var isPdf = images[itemIndex].split(".").last.toLowerCase() == "pdf";
var isExcel = images[itemIndex].split(".").last.toLowerCase() == "xlsx";
return Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
side: const BorderSide(width: 1, color: Color(0xFFEAF1F4)),
borderRadius: BorderRadius.circular(8),
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: (isImage
? (_isLocalUrl(images[itemIndex])
? Image.file(File(images[itemIndex]))
: ImageLoader(
url: images[itemIndex],
boxFit: BoxFit.cover,
height: 48,
width: 48,
))
: Image.asset("assets/images/${isPdf ? "pdf" : isExcel ? "excel" : "doc"}.png")),
),
).onPress(() async {
if (isImage) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
body: SafeArea(
child: Stack(
children: [
// InteractiveViewer(child: Image(image: getImageObject(itemIndex))).center,
InteractiveViewer(child: getImageWidget(itemIndex)).center,
const ABackButton(),
],
return Row(
children: [
Container(
width: 48.toScreenWidth,
height: 48.toScreenWidth,
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
side: const BorderSide(width: 1, color: Color(0xFFEAF1F4)),
borderRadius: BorderRadius.circular(8),
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: (isImage
? (_isLocalUrl(images[itemIndex])
? Image.file(File(images[itemIndex]))
: ImageLoader(
url: images[itemIndex],
boxFit: BoxFit.cover,
height: 48,
width: 48,
))
: Image.asset("assets/images/${isPdf ? "pdf" : isExcel ? "excel" : "doc"}.png")),
),
),
),
),
);
} else if (_isLocalUrl(images[itemIndex])) {
OpenFile.open(images[itemIndex]);
} else {
try {
if (!await launchUrl(Uri.parse(images[itemIndex]), mode: LaunchMode.externalApplication)) {
Fluttertoast.showToast(msg: "UnExpected Error with file.");
throw Exception('Could not launch ');
8.width,
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
EllipsizedText(
images[itemIndex].split("/").last,
style: AppTextStyles.heading6.copyWith(color: AppColor.black10),
type: EllipsisType.start, // or EllipsisType.middle
),
4.height,
RichText(
text: TextSpan(
text: 'Asset Guide | ',
style: AppTextStyles.bodyText.copyWith(color: AppColor.neutral120),
children: <TextSpan>[
TextSpan(
text: 'View',
recognizer: TapGestureRecognizer()
..onTap = () async {
if (isImage) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
body: SafeArea(
child: Stack(
children: [
// InteractiveViewer(child: Image(image: getImageObject(itemIndex))).center,
InteractiveViewer(child: getImageWidget(itemIndex)).center,
const ABackButton(),
],
),
),
),
),
);
} else if (_isLocalUrl(images[itemIndex])) {
OpenFile.open(images[itemIndex]);
} else {
try {
if (!await launchUrl(Uri.parse(images[itemIndex]), mode: LaunchMode.externalApplication)) {
Fluttertoast.showToast(msg: "UnExpected Error with file.");
throw Exception('Could not launch ');
}
} catch (ex) {
Fluttertoast.showToast(msg: "No Viewer found.");
}
}
},
style: AppTextStyles.bodyText.copyWith(color: AppColor.primary10, decoration: TextDecoration.underline),
),
],
),
),
],
).expanded
],
);
},
separatorBuilder: (cxt, index) => 8.height,
itemCount: images.length)
: GridView.builder(
padding: const EdgeInsets.only(top: 8, bottom: 8),
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 6, mainAxisSpacing: 8, crossAxisSpacing: 8),
itemCount: images.length,
itemBuilder: (context, itemIndex) {
if (!images[itemIndex].contains(".")) {
return const Text("Bad\nFile");
}
} catch (ex) {
Fluttertoast.showToast(msg: "No Viewer found.");
}
}
});
},
);
var isImage =
images[itemIndex].split(".").last.toLowerCase() == "png" || images[itemIndex].split(".").last.toLowerCase() == "jpg" || images[itemIndex].split(".").last.toLowerCase() == "jpeg";
var isPdf = images[itemIndex].split(".").last.toLowerCase() == "pdf";
var isExcel = images[itemIndex].split(".").last.toLowerCase() == "xlsx";
return Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
side: const BorderSide(width: 1, color: Color(0xFFEAF1F4)),
borderRadius: BorderRadius.circular(8),
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: (isImage
? (_isLocalUrl(images[itemIndex])
? Image.file(File(images[itemIndex]))
: ImageLoader(
url: images[itemIndex],
boxFit: BoxFit.cover,
height: 48,
width: 48,
))
: Image.asset("assets/images/${isPdf ? "pdf" : isExcel ? "excel" : "doc"}.png")),
),
).onPress(() async {
if (isImage) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
body: SafeArea(
child: Stack(
children: [
// InteractiveViewer(child: Image(image: getImageObject(itemIndex))).center,
InteractiveViewer(child: getImageWidget(itemIndex)).center,
const ABackButton(),
],
),
),
),
),
);
} else if (_isLocalUrl(images[itemIndex])) {
OpenFile.open(images[itemIndex]);
} else {
try {
if (!await launchUrl(Uri.parse(images[itemIndex]), mode: LaunchMode.externalApplication)) {
Fluttertoast.showToast(msg: "UnExpected Error with file.");
throw Exception('Could not launch ');
}
} catch (ex) {
Fluttertoast.showToast(msg: "No Viewer found.");
}
}
});
},
);
// return ListView.separated(
// padding: padding ?? EdgeInsets.symmetric(horizontal: 32 * AppStyle.getScaleFactor(context)),

@ -320,6 +320,7 @@ class AttachmentPicker extends StatefulWidget {
Color? buttonColor;
final Function(List<GenericAttachmentModel>)? onChange;
final bool showAsGrid;
final bool showAsListView;
AttachmentPicker(
{Key? key,
@ -332,6 +333,7 @@ class AttachmentPicker extends StatefulWidget {
this.onlyImages = false,
this.onChange,
this.showAsGrid = false,
this.showAsListView = false,
this.buttonColor})
: super(key: key);
@ -357,29 +359,54 @@ class _AttachmentPickerState extends State<AttachmentPicker> {
: onFilePicker),
16.height,
if (widget.attachment.isNotEmpty)
Wrap(
spacing: 8.toScreenWidth,
children: List.generate(
widget.attachment.length,
(index) {
File image = File(widget.attachment[index].name!);
return MultiFilesPickerItem(
file: image,
enabled: widget.enabled,
onRemoveTap: (image) {
if (!widget.enabled) {
return;
}
widget.attachment.removeAt(index);
if (widget.onChange != null) {
widget.onChange!(widget.attachment);
}
setState(() {});
widget.showAsListView
? ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemBuilder: (cxt, index) {
File image = File(widget.attachment[index].name!);
return MultiFilesPickerItem(
file: image,
showAsListView: widget.showAsListView,
enabled: widget.enabled,
onRemoveTap: (image) {
if (!widget.enabled) {
return;
}
widget.attachment.removeAt(index);
if (widget.onChange != null) {
widget.onChange!(widget.attachment);
}
setState(() {});
},
);
},
);
},
),
),
separatorBuilder: (cxt, index) => 8.height,
itemCount: widget.attachment.length)
: Wrap(
spacing: 8.toScreenWidth,
children: List.generate(
widget.attachment.length,
(index) {
File image = File(widget.attachment[index].name!);
return MultiFilesPickerItem(
file: image,
enabled: widget.enabled,
onRemoveTap: (image) {
if (!widget.enabled) {
return;
}
widget.attachment.removeAt(index);
if (widget.onChange != null) {
widget.onChange!(widget.attachment);
}
setState(() {});
},
);
},
),
),
],
);
}
@ -402,11 +429,6 @@ class _AttachmentPickerState extends State<AttachmentPicker> {
}
void showFileSourceSheet() async {
// if (widget.attachment.length >= 5) {
// Fluttertoast.showToast(msg: context.translation.maxImagesNumberIs5);
// return;
// }
ImageSource source = (await showModalBottomSheet(
context: context,
shape: const RoundedRectangleBorder(
@ -475,11 +497,6 @@ class _AttachmentPickerState extends State<AttachmentPicker> {
}
onFilePicker() async {
//TODO removed on request by Backend as they don't have anyissue with large number of files
// if (widget.attachment.length >= 5) {
// Fluttertoast.showToast(msg: context.translation.maxImagesNumberIs5);
// return;
// }
ImageSource? source = await showModalBottomSheet<ImageSource>(
context: context,
builder: (BuildContext context) {

@ -1,5 +1,7 @@
import 'dart:io';
import 'package:ellipsized_text/ellipsized_text.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:open_file/open_file.dart';
@ -18,8 +20,16 @@ class MultiFilesPickerItem extends StatelessWidget {
final bool enabled;
final Function(File) onRemoveTap;
final VoidCallback? onRemove;
final bool showAsListView;
const MultiFilesPickerItem({Key? key, required this.file, required this.enabled, required this.onRemoveTap, this.onRemove}) : super(key: key);
const MultiFilesPickerItem({
Key? key,
required this.file,
required this.enabled,
required this.onRemoveTap,
this.onRemove,
this.showAsListView = false,
}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -27,6 +37,96 @@ class MultiFilesPickerItem extends StatelessWidget {
bool isPdf = file.path.split(".").last.toLowerCase() == "pdf";
bool isExcel = file.path.split(".").last.toLowerCase() == "xlsx";
if (showAsListView) {
return Row(
children: [
Container(
width: 48.toScreenWidth,
height: 48.toScreenWidth,
decoration: BoxDecoration(
color: AppColor.neutral90,
border: Border.all(width: 1, color: context.isDark ? AppColor.neutral30 : AppColor.neutral30),
image: DecorationImage(
image: getImage(isImage, isPdf, isExcel, file),
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(8),
),
),
8.width,
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Flexible(
child: EllipsizedText(
file.path.split("/").last,
style: AppTextStyles.bodyText.copyWith(color: AppColor.black10),
type: EllipsisType.start, // or EllipsisType.middle
),
),
8.width,
if (enabled)
InkWell(
child: "remove".toSvgAsset(width: 16),
onTap: () {
onRemoveTap(file);
if (onRemove != null) {
onRemove!();
}
},
)
],
),
4.height,
RichText(
text: TextSpan(
text: 'Asset Guide | ',
style: AppTextStyles.bodyText.copyWith(color: AppColor.neutral120),
children: <TextSpan>[
TextSpan(
text: 'View',
recognizer: TapGestureRecognizer()
..onTap = () async {
if (isImage) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
appBar: const DefaultAppBar(),
body: SafeArea(
child: InteractiveViewer(
child: _isLocalUrl(file.path)
? Image.file(file)
: ImageLoader(
url: URLs.getFileUrl(file.path),
boxFit: BoxFit.cover,
),
).center,
),
),
),
);
} else if (_isLocalUrl(file.path)) {
OpenFile.open(file.path);
} else {
if (!await launchUrl(Uri.parse(URLs.getFileUrl(file.path)!), mode: LaunchMode.externalApplication)) {
Fluttertoast.showToast(msg: "UnExpected Error with file.");
throw Exception('Could not launch');
}
}
},
style: AppTextStyles.bodyText.copyWith(color: AppColor.primary10, decoration: TextDecoration.underline),
),
],
),
),
],
).expanded
],
);
}
return SizedBox(
width: 60.toScreenWidth,
height: 60.toScreenWidth,

@ -265,6 +265,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.0"
ellipsized_text:
dependency: "direct main"
description:
name: ellipsized_text
sha256: d4d372c2baf8d4adfb2ca3728c1e5955d1683e75fae774b311d557ad13317105
url: "https://pub.dev"
source: hosted
version: "1.2.0"
fake_async:
dependency: transitive
description:

@ -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.11+30
version: 1.5.0+32
environment:
sdk: ">=3.5.0 <4.0.0"
@ -98,6 +98,7 @@ dependencies:
tree_view_flutter: ^1.0.2
audio_waveforms: ^1.3.0
signalr_netcore: ^1.4.4
ellipsized_text: ^1.2.0
local_auth_darwin: any
dev_dependencies:

Loading…
Cancel
Save