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

# Conflicts:
#	lib/controllers/api_routes/urls.dart
#	lib/main.dart
#	lib/modules/medical_gas_inspection/create_medical_gas_request_page.dart
#	lib/modules/medical_gas_inspection/medical_gas_inspection_detail_page.dart
#	lib/new_views/pages/land_page/create_request_type_bottom_sheet.dart
design_3.0_demo_module
Sikander Saleem 2 days ago
commit 9eaae96c84

File diff suppressed because one or more lines are too long

@ -0,0 +1,4 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.8288 8.68721C13.6685 5.56426 11.8033 2.34682 8.65315 1.50569C6.30567 0.878883 3.91271 1.75062 2.49819 3.52772C2.39645 3.65553 2.41199 3.83848 2.5275 3.954L3.19897 4.62547C3.28768 4.71417 3.29587 4.86091 3.28768 4.99544C3.26581 5.35459 2.95694 5.62802 2.59779 5.60615L0.962417 5.50659C0.755718 5.49401 0.567312 5.38391 0.454883 5.21001C0.342453 5.03611 0.319393 4.81911 0.392762 4.62547C1.68326 1.21934 5.3844 -0.715769 8.98929 0.24678C12.829 1.27203 15.1157 5.20026 14.0871 9.02556C13.0588 12.85 9.10916 15.1116 5.27027 14.0866C2.41806 13.325 0.424623 10.963 0.00755845 8.23213C-0.0467641 7.87644 0.197544 7.54406 0.553237 7.48974C0.90893 7.43541 1.24131 7.67972 1.29564 8.03541C1.63682 10.2694 3.26872 12.2035 5.60641 12.8277C8.75742 13.669 11.9888 11.811 12.8288 8.68721Z" fill="#5A5E79"/>
<path d="M7.83333 4.5C7.83333 4.13181 7.53486 3.83333 7.16667 3.83333C6.79848 3.83333 6.5 4.13181 6.5 4.5V7.16667C6.5 7.34348 6.57024 7.51305 6.69526 7.63807L8.0286 8.9714C8.28895 9.23175 8.71106 9.23175 8.9714 8.9714C9.23175 8.71105 9.23175 8.28894 8.9714 8.0286L7.83333 6.89052V4.5Z" fill="#5A5E79"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.10204 2C9.65432 1.99901 10.1012 1.55048 10.1002 0.998201C10.0992 0.445917 9.65072 -0.000991314 9.09843 1.65146e-06C7.29011 0.00325987 5.81796 0.025199 4.63875 0.216071C3.43031 0.411675 2.4215 0.799884 1.61095 1.61043C0.735156 2.48623 0.353343 3.59229 0.173677 4.92863C-4.21107e-05 6.22073 -2.31424e-05 7.86717 5.77522e-07 9.92565V10.0738C-2.38577e-05 12.1323 -4.33922e-05 13.7787 0.173675 15.0709C0.353341 16.4072 0.735154 17.5133 1.61095 18.3891C2.48675 19.2648 3.59281 19.6467 4.92915 19.8263C6.22125 20 7.86769 20 9.92618 20H10.0744C12.1328 20 13.7793 20 15.0714 19.8263C16.4077 19.6467 17.5138 19.2649 18.3896 18.3891C19.2001 17.5785 19.5883 16.5697 19.7839 15.3613C19.9748 14.182 19.9967 12.7099 20 10.9016C20.001 10.3493 19.5541 9.90077 19.0018 9.89977C18.4495 9.89878 18.001 10.3457 18 10.898C17.9967 12.7393 17.9705 14.0476 17.8096 15.0417C17.6534 16.0066 17.3862 16.564 16.9754 16.9748C16.5331 17.4171 15.9194 17.6943 14.8049 17.8442C13.6616 17.9979 12.1499 18 10.0003 18C7.85061 18 6.33897 17.9979 5.19564 17.8442C4.08108 17.6943 3.46742 17.4171 3.02517 16.9748C2.58291 16.5326 2.30569 15.9189 2.15584 14.8044C2.00212 13.661 2 12.1494 2 9.99974C2 7.85009 2.00213 6.33845 2.15584 5.19512C2.30569 4.08056 2.58291 3.4669 3.02517 3.02464C3.43598 2.61383 3.99343 2.34656 4.95832 2.19038C5.95244 2.02946 7.26069 2.00332 9.10204 2Z" fill="#3DA5E5"/>
<path d="M14.6278 0.059696C15.5062 0.00814169 17.2561 -0.0671266 18.3023 0.118154C18.7322 0.194283 19.1066 0.401014 19.3861 0.698737L19.3948 0.708123C19.6424 0.975433 19.8141 1.31484 19.882 1.69821C20.067 2.74418 19.9919 4.49371 19.9404 5.37215C19.8724 6.53179 18.4836 7.007 17.698 6.22141L16.4671 4.99058L13.7317 7.70515C13.712 7.72477 13.6916 7.7434 13.6707 7.76105L9.70554 11.7087C9.31415 12.0983 8.68099 12.0969 8.29133 11.7055C7.90167 11.3142 7.90307 10.681 8.29446 10.2913L12.149 6.45379C12.1907 6.38574 12.2412 6.32154 12.3003 6.26282L15.0302 3.55371L13.7786 2.3021C12.9931 1.51658 13.4682 0.127754 14.6278 0.059696Z" fill="#3DA5E5"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@ -1,7 +1,7 @@
class URLs {
URLs._();
static const String appReleaseBuildNumber = "31";
static const String appReleaseBuildNumber = "32";
// static const host1 = "https://atomsm.hmg.com"; // production url
static const host1 = "https://atomsmdev.hmg.com"; // local DEV url
@ -18,6 +18,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;
@ -66,9 +67,11 @@ 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';
@ -393,4 +396,61 @@ class URLs {
static get getLoanById => '$_baseUrl/Loan/GetLoanById';
static get addLoan => '$_baseUrl/Loan/AddLoan';
//Asset Delivery Module
static get getAssetDeliveryDetailsById => '$_baseUrl/AssetDeliveryExternal/GetAssetDeliveryExternalById';
static get getAssetDeliveryTableListById => '$_baseUrl/AssetDeliveryExternalDelivery/GetDeliveriesByMasterId';
static get addNewDelivery => '$_baseUrl/AssetDeliveryExternalDelivery/AddNewDelivery';
static get changeDeliveryStatusToCancel => '$_baseUrl/AssetDeliveryExternalDelivery/ChangeStatusDeliveryToCancel';
static get changeDeliveryStatus => '$_baseUrl/AssetDeliveryExternalDelivery/ChangeStatusDelivery';
static get getAssetDeliveryInspectionData => '$_baseUrl/AssetDeliveryExternalDeliveryInspection/GetAssetDeliveryExternalDeliveryInspectionByDeliveryId';
static get getOracleUserList => '$_baseUrl/AssetDeliveryExternal/GetEmployeesFromOracle';
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';
static get getTechnicalInspectionByDetailId => '$_baseUrl/AssetDeliveryExternalTechnicalInspection/GetTechnicalInspectionByDetailId';
static get powerSupplyTypeLookUp => "$_baseUrl/Lookups/GetLookup?lookupEnum=527";
static get saveTechnicalInspection => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/SaveTechnicalInspection";
static get getTechnicalInspectionParentData => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/GetTechnicalInspectionItemByCostCenterId";
static get getAssetFromInternalTechnicalInspection => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/GetAssetFromInternal";
static get getAssetDetailsByAssetId => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/GetAssetByIdFromInternal";
static get saveTechnicalInspectionAsset => "$_baseUrl/AssetDeliveryExternalTechnicalInspection/SaveTechnicalInspectionItem";
static get getAttachments => "$_baseUrl/AssetDeliveryExternalDeliveryAttachment/GetAttachmentsBasedOnType";
static get saveAttachments => "$_baseUrl/AssetDeliveryExternalDeliveryAttachment/Save";
//End user Acceptance...
static get getEndUserAcceptanceCostCenter => "$_baseUrl/AssetDeliveryEndUserAcceptance/GetEndUserAcceptanceCostCenter";
static get getCostCenterAssetList => "$_baseUrl/AssetDeliveryEndUserAcceptance/GetItemByCostCenter";
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=415";
static get attachmentTypeLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=5009";
//Medical Gas Request..
// 4004
static get orderTypeLookUp => "$_baseUrl/Lookups/GetLookup?lookupEnum=4004";
// static get itemTypeLookUp => "$_baseUrl/Lookups/GetLookup?lookupEnum=4005";
static get itemTypeLookUp => "$_baseUrl/MedicalGas/GetItemTypeLookup?lookupEnum=4002";
static get getMedicalGasSupplierList => "$_baseUrl/MedicalGas/GetMedicalGasSupplierAsync";
static get getMedicalGasInfoBySupplier => "$_baseUrl/MedicalGas/GetMedicalGasInfoBySupplierAsync";
static get addMedicalGasRequest => "$_baseUrl/MedicalGas/AddUpdateMedicalGasRequestAsync";
static get getMedicalGasById => "$_baseUrl/MedicalGas/GetMedicalGasRequestByIdAsync";
static get updateDeliveryNote => "$_baseUrl/MedicalGas/MedicalGasRequestDeliveryNotesAsync";
}

@ -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,13 +141,13 @@ 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())), moduleId: 3);

@ -166,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;
}

@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/cupertino.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@ -10,6 +11,7 @@ import 'package:test_sa/models/device/asset_by_id_model.dart';
import 'package:test_sa/models/device/asset_search.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/models/user.dart';
import 'package:test_sa/modules/asset_delivery_module/models/technical_inpection_asset_model.dart';
import '../../../models/device/asset.dart';
@ -38,6 +40,7 @@ class AssetProvider extends ChangeNotifier {
List<Asset> _devices = [];
List<Asset> _searchDevices = [];
List<TechnicalInspectionAssetBasicInfoModel> technicalInspectionAsset= [];
List<Asset> get devices => _devices;
@ -99,6 +102,49 @@ class AssetProvider extends ChangeNotifier {
return response.statusCode;
}
Future<int> getAssetsForAssetDelivery({DeviceSearch? search, bool isSearchBy = false, int? lineId}) async {
if (_loading == true) return -2;
_loading = true;
notifyListeners();
late Response response;
try {
final Map<String, dynamic> body = {
"pageSize": isSearchBy ? searchPageItemNumber : pageItemNumber,
"pageNumber": isSearchBy ? (searchDevices.length / searchPageItemNumber).ceil() + 1 : devices.length ~/ pageItemNumber + 1,
///Uncommit this after testing...
"assetDeliveryExternalDetailId": lineId,
"assetName": search?.assetName,
// "assetDeliveryExternalDetailId": 2,
// "assetName": "Treatment Center Intego - Dental Chair",
"assetNumber": search?.assetNo,
"model": search?.model,
"manufacturer": search?.manufacturer,
};
response = await ApiManager.instance.post(URLs.getAssetFromInternalTechnicalInspection, body: body);
} catch (error) {
_loading = false;
_stateCode = -1;
notifyListeners();
return -1;
}
_stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
List equipmentListJson = json.decode(response.body)["data"];
var dList = equipmentListJson.map<TechnicalInspectionAssetBasicInfoModel>((asset) => TechnicalInspectionAssetBasicInfoModel.fromJson(asset)).toList() ?? [];
log('dlist ${dList.length}');
technicalInspectionAsset.addAll(dList);
nextPage = true;
} else {
nextPage = false;
}
_loading = false;
notifyListeners();
return response.statusCode;
}
Future<AssetByIdModel> getAssetById(int assetId, AppLocalizations appLocalizations) async {
late Response response;
try {

@ -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,13 +106,22 @@ class DashBoardProvider extends ChangeNotifier {
isAllCountLoading = true;
notifyListeners();
String url = '';
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;
}
// 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: {});
@ -235,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;
@ -243,25 +278,26 @@ class DashBoardProvider extends ChangeNotifier {
Response response;
String url = '';
// 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);
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;
}
try {
Map<String, dynamic> body = {"pageNumber": pageNum, "pageSize": pageItemNumber};
@ -383,6 +419,12 @@ class CategoryTabs {
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));

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

@ -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,3 +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,
}

@ -27,6 +27,12 @@ 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_last_calls_provider.dart';
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';
import 'package:test_sa/modules/asset_delivery_module/provider/power_supply_type_provider.dart';
import 'package:test_sa/modules/asset_inventory_module/provider/asset_inventory_provider.dart';
import 'package:test_sa/modules/cm_module/cm_detail_provider.dart';
import 'package:test_sa/modules/cm_module/create_cm_request.dart';
@ -45,6 +51,9 @@ 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/medical_department_provider.dart';
import 'package:test_sa/modules/medical_gas_inspection/create_medical_gas_request_page.dart';
import 'package:test_sa/modules/medical_gas_inspection/providers/item_type_provider.dart';
import 'package:test_sa/modules/medical_gas_inspection/providers/medical_gas_inspection_provider.dart';
import 'package:test_sa/modules/medical_gas_inspection/providers/order_type_provider.dart';
import 'package:test_sa/modules/tm_module/gas_refill/create_gas_refill_request.dart';
import 'package:test_sa/modules/tm_module/gas_refill/update_gas_refill_request.dart';
import 'package:test_sa/modules/traf_module/create_traf_request_page.dart';
@ -113,6 +122,7 @@ import 'dashboard_latest/dashboard_provider.dart';
import 'modules/internal_audit_module/pages/equipment_internal_audit/update_equipment_internal_audit_page.dart';
import 'modules/internal_audit_module/provider/internal_audit_checklist_provider.dart';
import 'modules/cx_module/survey/survey_provider.dart';
import 'modules/medical_gas_inspection/providers/medical_gas_supplier_provider.dart';
import 'modules/tm_module/device_transfer/create_device_transfer_request.dart';
import 'modules/tm_module/device_transfer/device_filter_screen.dart';
import 'modules/tm_module/device_transfer/device_search_screen.dart';
@ -134,6 +144,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 {});
@ -320,6 +331,16 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => LoanProvider()),
ChangeNotifierProvider(create: (_) => LoanPeriodProvider()),
ChangeNotifierProvider(create: (_) => MedicalDepartmentProvider()),
ChangeNotifierProvider(create: (_) => AssetDeliveryProvider()),
ChangeNotifierProvider(create: (_) => CostCenterProvider()),
ChangeNotifierProvider(create: (_) => PowerSupplyTypeProvider()),
ChangeNotifierProvider(create: (_) => EndUserStatusLookupProvider()),
ChangeNotifierProvider(create: (_) => EndUserRejectionReasonLookupProvider()),
ChangeNotifierProvider(create: (_) => AttachmentTypeLookupProvider()),
ChangeNotifierProvider(create: (_) => MedicalGasOrderTypeProvider()),
ChangeNotifierProvider(create: (_) => MedicalGasItemTypeProvider()),
ChangeNotifierProvider(create: (_) => MedicalGasInspectionProvider()),
ChangeNotifierProvider(create: (_) => MedicalGasSupplierProvider()),
ChangeNotifierProvider(create: (_) => DemoProvider()),
ChangeNotifierProvider(create: (_) => DemoPeriodLookupProvider()),
],
@ -389,6 +410,7 @@ class MyApp extends StatelessWidget {
UpdateEquipmentInternalAuditPage.id: (_) => UpdateEquipmentInternalAuditPage(),
UpdateSystemInternalAuditPage.id: (_) => UpdateSystemInternalAuditPage(),
CreateLoanRequestPage.id: (_) => CreateLoanRequestPage(),
CreateMedicalGasRequestPage.id: (_) => CreateMedicalGasRequestPage(),
// SwipeSuccessView.routeName: (_) => const SwipeSuccessView(),
// SwipeHistoryView.routeName: (_) => const SwipeHistoryView(),

@ -128,6 +128,8 @@ class RequestsDetails {
String? date;
String? siteTransferFrom;
String? siteTransferTo;
String? poInternal;
String? poExternal;
int? transactionType;
int? numberOfAssets;
int? numberOfSites;
@ -156,6 +158,8 @@ class RequestsDetails {
this.code,
this.siteTransferFrom,
this.siteTransferTo,
this.poInternal,
this.poExternal,
this.date,
this.transactionType});
@ -182,6 +186,8 @@ class RequestsDetails {
assetTransferTo = json['assetTransferTo'];
code = json['code'];
date = json['date'];
poInternal = json['poInternal'];
poExternal = json['poExternal'];
siteTransferFrom = json['siteTransferFrom'];
siteTransferTo = json['siteTransferTo'];
transactionType = json['transactionType'];

@ -1,6 +1,7 @@
import 'package:test_sa/models/lookup.dart';
class DeviceSearch {
DeviceSearch(
{this.code,
this.assetSerialNumber,

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

@ -8,7 +8,6 @@ class GenericAttachmentModel {
String ?createdBy;
String? originalName;
GenericAttachmentModel.fromJson(Map<String, dynamic> json) {
print('created by here is ${json['createdBy']}');
id = json['id'];
name = json['name'];
createdBy = json['createdBy'];

@ -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"],
);

@ -55,6 +55,9 @@ class Data {
String? assetNumber;
String? requestTypeName;
String? requestNo;
String? requestType;
String? poInternal;
String? poExternal;
int? transactionNo;
int? numberOfSites;
int? numberOfAssets;
@ -75,6 +78,9 @@ class Data {
this.assetNumber,
this.requestTypeName,
this.requestNo,
this.requestType,
this.poExternal,
this.poInternal,
this.transactionNo});
Data.fromJson(Map<String, dynamic> json) {
@ -86,6 +92,7 @@ class Data {
isHighPriority = json['isHighPriority'];
assetName = json['assetName'];
sessionType = json['sessionType'];
requestType = json['requestType'];
numberOfSites = json['numberOfSites'];
numberOfAssets = json['numberOfAssets'];
assetNumber = json['assetNumber'];
@ -93,6 +100,8 @@ class Data {
requestNo = json['requestNo'];
rejectReason = json['rejectReason'];
transactionNo = json['transactionNo'];
poInternal = json['poInternal'];
poExternal = json['poExternal'];
nameOfType = json['nameOfType'];
}

@ -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,10 +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;
}
@ -148,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;
@ -209,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);
},
),
],
),
),
);
},
);
}
}

@ -0,0 +1,386 @@
import 'package:test_sa/models/lookup.dart';
class AssetDeliveryDataModel {
int? id;
bool? isHMG;
int? operatingUnitId;
String? operatingUnit;
String? clientName;
int? poNumberId;
int? poNumber;
SiteModel? site;
Lookup? paymentTerm;
num? paymentTermAmount;
num? deliveryAmount;
num? total;
num? totalInvoice;
String? edd;
double? warrentyPeriod;
AssetDeliveryInternalMaster? assetDeliveryInternalMaster;
PurchaseOrderMaster? purchaseOrderMaster;
List<AssetDeliveryExternalDetail>? assetDeliveryExternalDetails;
List<AssetDeliveryTableModel>? assetDeliveryExternalDeliveries;
AssetDeliveryDataModel();
AssetDeliveryDataModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
isHMG = json['isHMG'];
operatingUnitId = json['operatingUnitId'];
clientName = json['clientName'];
operatingUnit = json['operatingUnit'];
poNumberId = json['poNumberId'];
poNumber = json['poNumber'];
total = json['total'];
totalInvoice = json['totalInvoice'];
edd = json['edd'];
deliveryAmount = json['deliveryAmount'];
site = json['site'] != null
? SiteModel.fromJson(json['site'])
: null;
paymentTerm = json['paymentTerm'] != null
? Lookup.fromJson(json['paymentTerm'])
: null;
paymentTermAmount = json['paymentTermAmount'];
warrentyPeriod = json['warrentyPeriod'];
assetDeliveryInternalMaster = json['assetDeliveryInternalMaster'] != null
? AssetDeliveryInternalMaster.fromJson(json['assetDeliveryInternalMaster'])
: null;
purchaseOrderMaster = json['purchaseOrderMaster'] != null
? PurchaseOrderMaster.fromJson(json['purchaseOrderMaster'])
: null;
if (json['assetDeliveryExternalDetails'] != null) {
assetDeliveryExternalDetails = [];
json['assetDeliveryExternalDetails'].forEach((v) {
assetDeliveryExternalDetails!.add(AssetDeliveryExternalDetail.fromJson(v));
});
}
if (json['assetDeliveryExternalDeliveries'] != null) {
assetDeliveryExternalDeliveries = [];
json['assetDeliveryExternalDeliveries'].forEach((v) {
assetDeliveryExternalDeliveries!.add(AssetDeliveryTableModel.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['id'] = id;
data['isHMG'] = isHMG;
data['operatingUnitId'] = operatingUnitId;
data['operatingUnit'] = operatingUnit;
data['clientName'] = clientName;
data['poNumberId'] = poNumberId;
data['poNumber'] = poNumber;
data['total'] = total;
data['edd'] = edd;
data['totalInvoice'] = totalInvoice;
data['deliveryAmount'] = deliveryAmount;
data['site'] = site;
data['paymentTermAmount'] = paymentTermAmount;
data['warrentyPeriod'] = warrentyPeriod;
if (paymentTerm != null) data['paymentTerm'] = paymentTerm!.toJson();
if (assetDeliveryInternalMaster != null) {
data['assetDeliveryInternalMaster'] =
assetDeliveryInternalMaster!.toJson();
}
if (assetDeliveryExternalDetails != null) {
data['assetDeliveryExternalDetails'] =
assetDeliveryExternalDetails!.map((v) => v.toJson()).toList();
}
if (assetDeliveryExternalDeliveries != null) {
data['assetDeliveryExternalDeliveries'] =
assetDeliveryExternalDeliveries!.map((v) => v.toJson()).toList();
}
return data;
}
}
class AssetDeliveryInternalMaster {
int? id;
int? operatingUnitId;
String? operatingUnit;
int? poNumberId;
int? poNumber;
String? supplier;
String? supplierSite;
String? type;
String? buyer;
num? total;
String? currency;
String? remark;
String? status;
String? paymentTerms;
double? warrantyPeriod;
String? assetDeliveryStatus;
String? statusDocument;
String? remarkDocument;
String? edd;
AssetDeliveryInternalMaster();
AssetDeliveryInternalMaster.fromJson(Map<String, dynamic> json) {
id = json['id'];
operatingUnitId = json['operatingUnitId'];
operatingUnit = json['operatingUnit'];
poNumberId = json['poNumberId'];
poNumber = json['poNumber'];
supplier = json['supplier'];
supplierSite = json['supplierSite'];
type = json['type'];
buyer = json['buyer'];
total = json['total'];
currency = json['currency'];
remark = json['remark'];
status = json['status'];
paymentTerms = json['paymentTerms'];
warrantyPeriod = json['warrantyPeriod'];
assetDeliveryStatus = json['assetDeliveryStatus'];
statusDocument = json['statusDocument'];
remarkDocument = json['remarkDocument'];
edd = json['edd'];
}
Map<String, dynamic> toJson() {
return {
'id': id,
'operatingUnitId': operatingUnitId,
'operatingUnit': operatingUnit,
'poNumberId': poNumberId,
'poNumber': poNumber,
'supplier': supplier,
'supplierSite': supplierSite,
'type': type,
'buyer': buyer,
'total': total,
'currency': currency,
'remark': remark,
'status': status,
'paymentTerms': paymentTerms,
'warrantyPeriod': warrantyPeriod,
'assetDeliveryStatus': assetDeliveryStatus,
'statusDocument': statusDocument,
'remarkDocument': remarkDocument,
'edd': edd,
};
}
}
class AssetDeliveryExternalDetail {
int? id;
AssetDeliveryInternalDetail? assetDeliveryInternalDetail;
String? itemNumber;
String? lineType;
String? unitMeasure;
num? qtyOrdered;
num? qtyReceived;
num? qtyBilled;
num? qtyCancelled;
num? unitPrice;
num? unitPriceWithTax;
String? itemDescription;
AssetDeliveryExternalDetail();
AssetDeliveryExternalDetail.fromJson(Map<String, dynamic> json) {
id = json['id'];
itemNumber = json['itemNumber'];
lineType = json['lineType'];
unitMeasure = json['unitMeasure'];
qtyOrdered = json['qtyOrdered'];
qtyReceived = json['qtyReceived'];
qtyBilled = json['qtyBilled'];
qtyCancelled = json['qtyCancelled'];
unitPrice = json['unitPrice'];
unitPriceWithTax = json['unitPriceWithTax'];
itemDescription = json['itemDescription'];
assetDeliveryInternalDetail =
json['assetDeliveryInternalDetail'] != null
? AssetDeliveryInternalDetail.fromJson(
json['assetDeliveryInternalDetail'])
: null;
}
Map<String, dynamic> toJson() {
return {
'id': id,
'itemNumber': itemNumber,
'lineType': lineType,
'unitMeasure': unitMeasure,
'qtyOrdered': qtyOrdered,
'qtyReceived': qtyReceived,
'qtyBilled': qtyBilled,
'qtyCancelled': qtyCancelled,
'unitPrice': unitPrice,
'unitPriceWithTax': unitPriceWithTax,
'itemDescription': itemDescription,
'assetDeliveryInternalDetail':
assetDeliveryInternalDetail?.toJson(),
};
}
}
class AssetDeliveryInternalDetail {
int? id;
int? lineId;
String? itemNumber;
String? itemDescription;
String? unitMeasure;
num? qtyOrdered;
num? qtyReceived;
num? qtyBilled;
num? qtyCancelled;
num? unitPrice;
num? unitPriceWithTax;
Lookup? itemCategory;
num? totalAssets;
num? childPerAsset;
AssetDeliveryInternalDetail();
AssetDeliveryInternalDetail.fromJson(Map<String, dynamic> json) {
id = json['id'];
lineId = json['lineId'];
itemNumber = json['itemNumber'];
itemDescription = json['itemDescription'];
unitMeasure = json['unitMeasure'];
qtyOrdered = json['qtyOrdered'];
qtyReceived = json['qtyReceived'];
qtyBilled = json['qtyBilled'];
qtyCancelled = json['qtyCancelled'];
unitPrice = json['unitPrice'];
unitPriceWithTax = json['unitPriceWithTax'];
itemCategory = json['itemCategory'] != null
? Lookup.fromJson(json['itemCategory'])
: null;
totalAssets = json['totalAssets'];
childPerAsset = json['childPerAsset'];
}
Map<String, dynamic> toJson() {
return {
'id': id,
'lineId': lineId,
'itemNumber': itemNumber,
'itemDescription': itemDescription,
'unitMeasure': unitMeasure,
'qtyOrdered': qtyOrdered,
'qtyReceived': qtyReceived,
'qtyBilled': qtyBilled,
'qtyCancelled': qtyCancelled,
'unitPrice': unitPrice,
'unitPriceWithTax': unitPriceWithTax,
'itemCategory': itemCategory,
'totalAssets': totalAssets,
'childPerAsset': childPerAsset,
};
}
}
class AssetDeliveryTableModel {
int? id;
Lookup? status;
String? number;
num? deliveryAmount;
num? invoiceAmount;
bool? isContainsTechnicalInspection;
AssetDeliveryTableModel();
AssetDeliveryTableModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
number = json['number'];
deliveryAmount = json['deliveryAmount'];
invoiceAmount = json['invoiceAmount'];
isContainsTechnicalInspection = json['isContainsTechnicalInspection'];
status = json['status'] != null
? Lookup.fromJson(json['status'])
: null;
}
Map<String, dynamic> toJson() {
return {
'id': id,
'number': number,
'deliveryAmount': deliveryAmount,
'invoiceAmount': invoiceAmount,
'isContainsTechnicalInspection': isContainsTechnicalInspection,
'status': status?.toJson(),
};
}
}
class PurchaseOrderMaster {
int? id;
num? poNumber;
int? siteId;
String? siteName;
num? amount;
String? remarks;
String? statuss;
List<PurchaseOrderDetails>? assetDeliveryPurchaseOrderDetails;
PurchaseOrderMaster({
this.id,
this.poNumber,
this.siteId,
this.siteName,
this.amount,
this.remarks,
this.statuss,
this.assetDeliveryPurchaseOrderDetails,
});
PurchaseOrderMaster.fromJson(Map<String, dynamic> json) {
id = json['id'];
poNumber = json['poNumber'];
siteId = json['siteId'];
siteName = json['siteName'];
amount = json['amount'];
remarks = json['remarks'];
statuss = json['statuss'];
if (json['assetDeliveryPurchaseOrderDetails'] != null &&
json['assetDeliveryPurchaseOrderDetails'] is List) {
assetDeliveryPurchaseOrderDetails =
(json['assetDeliveryPurchaseOrderDetails'] as List)
.map((e) => PurchaseOrderDetails.fromJson(e))
.toList();
} else {
assetDeliveryPurchaseOrderDetails = null;
}
}
}
class SiteModel{
int? id;
String? siteName;
SiteModel({
this.id,
this.siteName,
});
SiteModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
siteName = json['siteName'];
}
}
///Check this model if required and whats the parm
class PurchaseOrderDetails {
// No fields provided in your JSON placeholder
PurchaseOrderDetails();
PurchaseOrderDetails.fromJson(Map<String, dynamic> json);
}

@ -0,0 +1,57 @@
import 'package:test_sa/models/lookup.dart';
class CostCenterModel {
int? id;
int? assetDeliveryExternalDetailId;
Lookup? costCenter;
String? acceptedBy;
String? costCenterName;
DateTime? acceptanceDate;
String? acceptedByEmail;
String? acceptedBySignature;
double? quantity;
CostCenterModel({
this.id,
this.assetDeliveryExternalDetailId,
this.costCenter,
this.acceptedBy,
this.costCenterName,
this.acceptanceDate,
this.acceptedByEmail,
this.acceptedBySignature,
this.quantity,
});
CostCenterModel.fromJson(Map<String, dynamic> json) {
costCenterName = json['costCenter'] ?? json['costCenterName'];
acceptanceDate = json['acceptanceDate'] != null ? DateTime.tryParse(json['acceptanceDate']) : null;
acceptedBySignature = json['acceptedBySignature'];
acceptedByEmail = json['acceptedByEmail'];
quantity = json['qty'] ?? json['costCenterQty'];
acceptedBy = json['acceptedBy'];
costCenter = json['costCenter'] != null ? Lookup(name: json['costCenter']) : null;
id = json['id'] ?? json['costCenterId'];
assetDeliveryExternalDetailId = json['assetDeliveryExternalDetailId'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['id'] = id ?? 0;
data['qty'] = quantity;
data['costCenter'] = costCenterName;
return data;
}
Map<String, dynamic> toEndUserAcceptanceFormJson() {
final Map<String, dynamic> data = {};
data['assetDeliveryExternalDeliveryId'] = assetDeliveryExternalDetailId;
data['costCenterName'] = costCenterName;
data['acceptedBy'] = acceptedBy;
data['acceptanceDate'] = acceptanceDate?.toIso8601String();
data['acceptedByEmail'] = acceptedByEmail;
data['acceptedBySignature'] = acceptedBySignature;
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;
}
}

@ -0,0 +1,57 @@
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;
String? deliveryInspectionApprovedBy;
DeliveryInspectionFormModel({
this.assetDeliveryExternalDeliveryId,
this.deliveryInspectionInspectedBy,
this.inspectedByModel,
this.approvedByModel,
this.thirdApprovalModel,
this.deliveryInspectionInspectedDateFrom,
this.deliveryInspectionInspectedDateTo,
this.totalTime,
this.deliveryInspectionApprovedBy,
});
DeliveryInspectionFormModel.fromJson(Map<String, dynamic> json) {
assetDeliveryExternalDeliveryId = json['assetDeliveryExternalDeliveryId'];
deliveryInspectionInspectedBy = json['deliveryInspectionInspectedBy'];
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,
'deliveryInspectionInspectedDateFrom': deliveryInspectionInspectedDateFrom?.toIso8601String(),
'deliveryInspectionInspectedDateTo': deliveryInspectionInspectedDateTo?.toIso8601String(),
'totalTime': totalTime,
'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,61 @@
class DeliveryLineModel {
int? id;
int? assetDeliveryInternalDetailId;
String? itemNumber;
String? itemDescription;
num? qty;
num? remainingQty;
num? usingQtyInThisDelivery;
num? unitPrice;
num? total;
String? unitOfMeasure;
String? lineType;
String? itemCategory;
DeliveryLineModel({
this.id,
this.assetDeliveryInternalDetailId,
this.itemNumber,
this.itemDescription,
this.qty,
this.remainingQty,
this.usingQtyInThisDelivery,
this.unitPrice,
this.total,
this.unitOfMeasure,
this.lineType,
this.itemCategory,
});
DeliveryLineModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
assetDeliveryInternalDetailId = json['assetDeliveryInternalDetailId'];
itemNumber = json['itemNumber'];
itemDescription = json['itemDescription'];
qty = json['qty'];
remainingQty = json['remainingQty'];
usingQtyInThisDelivery = json['usingQtyInThisDelivery'];
unitPrice = json['unitPrice'];
total = json['total'];
unitOfMeasure = json['unitOfMeasure'];
lineType = json['lineType'];
itemCategory = json['itemCategory'];
}
Map<String, dynamic> toJson() {
return {
'id': id,
'assetDeliveryInternalDetailId': assetDeliveryInternalDetailId,
'itemNumber': itemNumber,
'itemDescription': itemDescription,
'qty': qty,
'remainingQty': remainingQty,
'usingQtyInThisDelivery': usingQtyInThisDelivery,
'unitPrice': unitPrice,
'total': total,
'unitOfMeasure': unitOfMeasure,
'lineType': lineType,
'itemCategory': itemCategory,
};
}
}

@ -0,0 +1,71 @@
import 'package:test_sa/models/lookup.dart';
class EndUserAssetDetailsModel {
int? id;
int? typeId;
int? assetDeliveryExternalDeliveryId;
String? type;
String? itemNumber;
String? costCenterName;
Lookup? status;
Lookup? rejectionReason;
String? description;
double? receivedQty;
num? rejectedQty;
String? assetNumber;
String? serialNo;
String? assetType;
String? replacedAssetNo;
EndUserAssetDetailsModel(
{this.costCenterName,
this.rejectedQty,
this.description,
this.id,
this.typeId,
this.status,
this.rejectionReason,
this.assetType,
this.assetNumber,
this.serialNo,
this.itemNumber,
this.type,
this.receivedQty,
this.replacedAssetNo,
this.assetDeliveryExternalDeliveryId});
EndUserAssetDetailsModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
typeId = json['typeId'];
type = json['type'];
itemNumber = json['itemNumber'];
status = json['status'] != null ? Lookup.fromJson(json['status']) : null;
rejectionReason = json['rejectReason'] != null ? Lookup.fromJson(json['rejectReason']) : null;
description = json['description'];
receivedQty = json['receivedQty'];
rejectedQty = json['rejectedQty'];
assetNumber = json['assetNumber'];
serialNo = json['serialNo'];
assetType = json['assetType'];
replacedAssetNo = json['replacedAssetNo'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['id'] = id;
data['assetDeliveryExternalDeliveryId'] = assetDeliveryExternalDeliveryId;
data['costCenterName'] = costCenterName;
data['typeId'] = typeId;
data['type'] = type;
data['itemNumber'] = itemNumber;
data['statusId'] = status?.id;
// data['status'] = status;
// data['rejectionReason'] = rejectionReason;
data['rejectReasonId'] = rejectionReason?.id;
data['description'] = description;
data['receivedQty'] = receivedQty;
data['rejectedQty'] = rejectedQty;
return data;
}
}

@ -0,0 +1,34 @@
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/models/new_models/work_order_detail_model.dart';
class AssetDeliveryHistoryModel {
WorkOrderAssignedEmployee? user;
Lookup? status;
DateTime? createdDate;
String ?timeDifference;
AssetDeliveryHistoryModel({
this.user,
this.status,
this.createdDate,
this.timeDifference,
});
AssetDeliveryHistoryModel.fromJson(Map<String, dynamic> json) {
user = json['user'] != null ? WorkOrderAssignedEmployee.fromJson(json['user']) : null;
status = json['status'] != null ? Lookup.fromJson(json['status']) : null;
createdDate = json['createdDate'] != null ? DateTime.tryParse(json['createdDate']) : null;
timeDifference = '';
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (user != null) {
data['user'] = user!.toJson();
}
if (status != null) {
data['status'] = status!.toJson();
}
return data;
}
}

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

@ -0,0 +1,26 @@
class OracleUserModel {
String? employeeNumber;
String? fullName;
String? job;
String? employeeMobileNumber;
String? employeeExtensionNumber;
String? employeeEmail;
OracleUserModel({
this.employeeNumber,
this.fullName,
this.job,
this.employeeMobileNumber,
this.employeeExtensionNumber,
this.employeeEmail,
});
OracleUserModel.fromJson(Map<String, dynamic> json) {
employeeNumber = json['employeeNumber'];
fullName = json['fullName'];
job = json['job'];
employeeMobileNumber = json['employeeMobileNumber'];
employeeExtensionNumber = json['employeeExtensionNumber'];
employeeEmail = json['employeeEmail'];
}
}

@ -0,0 +1,61 @@
class TechnicalInspectionAssetBasicInfoModel {
int? id;
String? assetNumber;
String? assetType;
String? assetOrigin;
String? assetName;
String? model;
String? manufacturer;
String? site;
String? building;
String? floor;
String? department;
String? room;
TechnicalInspectionAssetBasicInfoModel({
this.id,
this.assetNumber,
this.assetType,
this.assetOrigin,
this.assetName,
this.model,
this.manufacturer,
this.site,
this.building,
this.floor,
this.department,
this.room,
});
TechnicalInspectionAssetBasicInfoModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
assetNumber = json['assetNumber'];
assetType = json['assetType'];
assetOrigin = json['assetOrigin'];
assetName = json['assetName'];
model = json['model'];
manufacturer = json['manufacturer'];
site = json['site'];
building = json['building'];
floor = json['floor'];
department = json['department'];
room = json['room'];
}
Map<String, dynamic> toJson() {
return {
'id': id,
'assetNumber': assetNumber,
'assetType': assetType,
'assetOrigin': assetOrigin,
'assetName': assetName,
'model': model,
'manufacturer': manufacturer,
'site': site,
'building': building,
'floor': floor,
'department': department,
'room': room,
};
}
}

@ -0,0 +1,215 @@
import 'dart:developer';
import 'package:test_sa/models/device/supplier.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/room_model.dart';
import 'package:test_sa/models/new_models/site.dart';
import '../../../models/device/model_definition.dart';
import '../../../models/new_models/department.dart';
class TechnicalInspectionAssetModel {
num? id;
num? tableItemId;
num? lineId;
num? assetDeliveryInternalDetailAssetId;
num? assetDeliveryInternalDetailAssetChildId;
num? costCenterId;
String? assetNumber;
String? status;
Lookup? assetOrigin;
Lookup? replacedAsset;
AssetModelDefination? modelDefinition;
Supplier? supplier;
Lookup? assetType; // Now nullable
Site? site; // Now nullable
Building? building; // Now nullable
Floor? floor; // Now nullable
Department? department; // Now nullable
Rooms? room;
String? serialNo;
String? systemId;
DateTime? installationDate;
List<TechnicalInspectionAssetModel>? childAssetList;
bool? flagphysicalInspection;
bool? flagFunctionPerformance;
bool? flagGroundingResistance;
bool? flagChassiss;
bool? flagLeadsLeakage;
bool? flagAlert;
TechnicalInspectionAssetModel({
this.id,
this.assetDeliveryInternalDetailAssetId,
this.assetDeliveryInternalDetailAssetChildId,
this.tableItemId,
this.status,
this.lineId,
this.costCenterId,
this.assetNumber,
this.assetOrigin,
this.assetType,
this.replacedAsset,
this.modelDefinition,
this.supplier,
this.site,
this.building,
this.floor,
this.department,
this.room,
this.serialNo,
this.systemId,
this.installationDate,
this.childAssetList,
this.flagphysicalInspection,
this.flagFunctionPerformance,
this.flagGroundingResistance,
this.flagChassiss,
this.flagLeadsLeakage,
this.flagAlert,
});
TechnicalInspectionAssetModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
assetDeliveryInternalDetailAssetId = json['assetDeliveryInternalDetailAssetId'];
assetDeliveryInternalDetailAssetChildId = json['assetDeliveryInternalDetailAssetChildId'];
assetNumber = json['assetNumber'];
assetOrigin = json['assetOrigin'] != null ? Lookup.fromJson(json['assetOrigin']) : null;
replacedAsset = json['replacedAsset'] != null ? Lookup.fromJson(json['replacedAsset']) : null;
modelDefinition = json['modelDefinition'] != null ? AssetModelDefination.fromJson(json['modelDefinition']) : null;
supplier = json['supplier'] != null ? Supplier.fromJson(json['supplier']) : null;
assetType = json['assetType'] != null ? Lookup.fromJson(json['assetType']) : null;
site = json['site'] != null ? Site.fromJson(json['site']) : null;
building = json['building'] != null ? Building.fromJson(json['building']) : null;
floor = json['floor'] != null ? Floor.fromJson(json['floor']) : null;
department = json['department'] != null ? Department.fromJson(json['department']) : null;
room = json['room'] != null ? Rooms.fromJson(json['room']) : null;
serialNo = json['serialNo'];
systemId = json['systemId'];
installationDate = json['installationDate'] != null ? DateTime.tryParse(json['installationDate']) : null;
childAssetList =
json['assetDeliveryInternalDetailAssetChild'] != null ? (json['assetDeliveryInternalDetailAssetChild'] as List).map((e) => TechnicalInspectionAssetModel.fromJson(e)).toList() : [];
//Need to keep one parm from backend
// childAssetList = json['assetDeliveryExternalDeliveryLineTechnicalInspectionItemChild'] != null
// ? (json['assetDeliveryExternalDeliveryLineTechnicalInspectionItemChild'] as List).map((e) => TechnicalInspectionAssetModel.fromJson(e)).toList()
// : [];
flagphysicalInspection = json['flagphysicalInspection'];
flagFunctionPerformance = json['flagFunctionPerformance'];
flagGroundingResistance = json['flagGroundingResistance'];
flagChassiss = json['flagChassiss'];
flagLeadsLeakage = json['flagLeadsLeakage'];
flagAlert = json['flagAlert'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['id'] = id;
data['assetDeliveryInternalDetailAssetId'] = assetDeliveryInternalDetailAssetId;
data['assetDeliveryExternalDeliveryId'] = tableItemId;
data['assetDeliveryExternalDetailId'] = lineId;
data['costCenterId'] = costCenterId;
data['assetNumber'] = assetNumber;
data['assetOriginId'] = assetOrigin?.id;
data['assetTypeId'] = assetType?.id;
data['replacedAssetId'] = replacedAsset?.id;
data['modelDefinitionId'] = modelDefinition?.modelDefinitionId;
data['supplierId'] = supplier?.id;
data['siteId'] = site?.id;
data['buildingId'] = building?.id;
data['floorId'] = floor?.id;
data['departmentId'] = department?.id;
data['roomId'] = room?.id;
data['serialNo'] = serialNo;
data['systemId'] = systemId;
data['installationDate'] = installationDate?.toIso8601String();
data['assetDeliveryInternalDetailAssetChild'] = childAssetList?.map((e) => e.toChildJson()).toList();
data['flagphysicalInspection'] = flagphysicalInspection;
data['flagFunctionPerformance'] = flagFunctionPerformance;
data['flagGroundingResistance'] = flagGroundingResistance;
data['flagChassiss'] = flagChassiss;
data['flagLeadsLeakage'] = flagLeadsLeakage;
data['flagAlert'] = flagAlert;
return data;
}
Map<String, dynamic> toChildJson() {
final Map<String, dynamic> data = {};
data['id'] = id ?? 0;
data['assetDeliveryInternalDetailAssetChildId'] = assetDeliveryInternalDetailAssetChildId;
data['assetNumber'] = assetNumber;
data['assetOriginId'] = assetOrigin?.id;
data['assetTypeId'] = assetType?.id;
data['replacedAssetId'] = replacedAsset?.id;
data['modelDefinitionId'] = modelDefinition?.modelDefinitionId;
data['supplierId'] = supplier?.id;
data['siteId'] = site?.id;
data['buildingId'] = building?.id;
data['floorId'] = floor?.id;
data['departmentId'] = department?.id;
data['roomId'] = room?.id;
data['serialNo'] = serialNo;
data['systemId'] = systemId;
data['installationDate'] = installationDate?.toIso8601String();
data['flagphysicalInspection'] = flagphysicalInspection;
data['flagFunctionPerformance'] = flagFunctionPerformance;
data['flagGroundingResistance'] = flagGroundingResistance;
data['flagChassiss'] = flagChassiss;
data['flagLeadsLeakage'] = flagLeadsLeakage;
data['flagAlert'] = flagAlert;
return data;
}
}
class InspectionChecklistItem {
String? title;
String? value;
String? status; // "PASS" or "FAIL"
InspectionChecklistItem({this.title, this.status, this.value});
InspectionChecklistItem.fromJson(Map<String, dynamic> json) {
title = json['title'];
value = json['value'];
status = json['status'];
}
Map<String, dynamic> toJson() {
return {
'title': title,
'value': value,
'status': status,
};
}
}
class AssetModelDefination {
int? modelDefinitionId;
String? assetName;
String? model;
String? manufacturer;
AssetModelDefination({
this.modelDefinitionId,
this.assetName,
this.model,
this.manufacturer,
});
AssetModelDefination.fromJson(Map<String, dynamic> json) {
modelDefinitionId = json['modelDefinitionId'];
assetName = json['assetName'];
model = json['model'];
manufacturer = json['manufacturer'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['modelDefinitionId'] = modelDefinitionId;
data['assetName'] = assetName;
data['model'] = model;
data['manufacturer'] = manufacturer;
return data;
}
}

@ -0,0 +1,53 @@
import 'dart:developer';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/modules/asset_delivery_module/models/cost_center_model.dart';
class TechnicalInspectionFormModel {
Lookup? powerSupplyType;
num? powerReading;
num? maxCurrent;
num? voltage;
num? frequency;
List<CostCenterModel>? costCenters;
int? tableItemId;
int? lineId;
TechnicalInspectionFormModel({
this.powerSupplyType,
this.powerReading,
this.maxCurrent,
this.voltage,
this.frequency,
this.costCenters,
this.lineId,
this.tableItemId,
});
TechnicalInspectionFormModel.fromJson(Map<String, dynamic> json) {
powerSupplyType = json["powerSupplyType"] == null ? null : Lookup.fromJson(json["powerSupplyType"]);
powerReading = json['powerReading'];
maxCurrent = json['maxCurrent'];
voltage = json['voltage'];
frequency = json['frequency'];
if (json['costCenters'] != null) {
costCenters = <CostCenterModel>[];
json['costCenters'].forEach((v) {
costCenters!.add(CostCenterModel.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['powerSupplyTypeId'] = powerSupplyType?.id;
data['assetDeliveryExternalDeliveryId'] = tableItemId;
data['assetDeliveryExternalDetailId'] = lineId;
data['powerReading'] = powerReading;
data['maxCurrent'] = maxCurrent;
data['voltage'] = voltage;
data['frequency'] = frequency;
// data['costCenters'] = costCenters != null ? costCenters!.map((v) => v.toJson()).toList() : [];
return data;
}
}

@ -0,0 +1,90 @@
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/all_requests_and_count_model.dart';
import 'package:test_sa/models/new_models/dashboard_detail.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_page.dart';
import 'package:test_sa/modules/asset_inventory_module/pages/asset_inventory_page.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/views/widgets/requests/request_status.dart';
class AssetDeliveryItemView extends StatelessWidget {
final Data? requestData;
final RequestsDetails? requestDetails;
final bool showShadow;
const AssetDeliveryItemView({Key? key, this.requestData, this.requestDetails, this.showShadow = true}) : super(key: key);
@override
Widget build(BuildContext context) {
if (requestData != null) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
8.height,
(requestData?.typeTransaction ?? 'Asset Delivery').heading5(context),
infoWidget(label: 'PO Internal'.addTranslation, value: requestData?.poInternal ?? '-', context: context),
infoWidget(label: 'PO External'.addTranslation, value: requestData?.poExternal ?? '-', context: context),
// infoWidget(label: 'No of Sites'.addTranslation, value: requestData?.numberOfSites != null ? requestData?.numberOfSites.toString() : '-', context: context),
8.height,
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
context.translation.viewDetails,
style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)),
),
4.width,
Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14)
],
),
],
).toShadowContainer(context, withShadow: showShadow).onPress(() async {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => AssetDeliveryPage(
requestId: requestData!.id!,
poInternal: requestData?.poInternal,
poExternal: requestData?.poExternal,
)));
});
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
8.height,
(requestDetails?.nameOfType ?? 'Asset Delivery').heading5(context),
infoWidget(label: 'PO Internal'.addTranslation, value: requestDetails?.poInternal ?? '-', context: context),
infoWidget(label: 'PO External'.addTranslation, value: requestDetails?.poExternal ?? '-', context: context),
8.height,
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
context.translation.viewDetails,
style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)),
),
4.width,
Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14)
],
),
],
).toShadowContainer(context, withShadow: showShadow).onPress(() async {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => AssetDeliveryPage(
requestId: requestDetails!.id!,
poInternal: requestDetails?.poInternal,
poExternal: requestDetails?.poExternal,
)));
});
}
Widget infoWidget({required String label, String? value, required BuildContext context}) {
if (value != null && value.isNotEmpty) {
return '$label: $value'.bodyText(context);
}
return const SizedBox();
}
}

@ -0,0 +1,86 @@
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.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/delivery_line_model.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
class AssetDeliveryLineCard extends StatelessWidget {
DeliveryLineModel deliveryLineModel;
bool isViewOnly;
VoidCallback? editPress;
bool isTechnical;
AssetDeliveryLineCard({
super.key,
required this.isViewOnly,
required this.deliveryLineModel,
this.isTechnical = false,
this.editPress,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//need to check this @waseem.
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Item Category: ${deliveryLineModel.itemCategory ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
if (!isViewOnly)
'edit_icon'.toSvgAsset().onPress(() {
if (editPress != null) {
editPress!();
}
}),
],
),
Text(
'Item No: ${deliveryLineModel.itemNumber ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Item Description: ${deliveryLineModel.itemDescription ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Quantity: ${deliveryLineModel.qty ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
if(!isTechnical)...[
Text(
'Remaining Quantity: ${deliveryLineModel.remainingQty ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Quantity of this Delivery: ${deliveryLineModel.usingQtyInThisDelivery ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
],
Text(
'Measure Unit: ${deliveryLineModel.unitOfMeasure ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Unit price: ${deliveryLineModel.unitPrice ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Line Type: ${deliveryLineModel.lineType ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Total: ${deliveryLineModel.total ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
],
).toShadowContainer(context, borderRadius: 20, padding: 12);
}
}

@ -0,0 +1,364 @@
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/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';
import 'package:test_sa/modules/asset_delivery_module/pages/history_log_view.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.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/default_app_bar.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
class AssetDeliveryPage extends StatefulWidget {
final int? requestId;
final String? poInternal;
final String? poExternal;
const AssetDeliveryPage({
Key? key,
this.requestId,
this.poInternal,
this.poExternal,
}) : super(key: key);
@override
State<AssetDeliveryPage> createState() => _AssetDeliveryPageState();
}
class _AssetDeliveryPageState extends State<AssetDeliveryPage> {
AssetDeliveryDataModel? dataModel;
AssetDeliveryProvider? assetDeliveryProvider;
bool loading = false;
@override
void initState() {
assetDeliveryProvider = Provider.of<AssetDeliveryProvider>(context, listen: false);
WidgetsBinding.instance.addPostFrameCallback((_) {
getDetailsById();
});
super.initState();
}
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
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: DefaultAppBar(
title: 'Asset Delivery'.addTranslation,
onBackPress: () {
Navigator.pop(context);
},
),
body: Consumer<AssetDeliveryProvider>(
builder: (context, provider, child) {
final dataModel = provider.assetDeliveryDataModel;
if (loading) {
return const CircularProgressIndicator(color: AppColor.primary10).center;
}
if (dataModel == null) {
return const NoDataFound().center;
}
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,
),
),
],
).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),
);
},
),
);
}
TextStyle infoTextStyle(context) => AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120);
Widget requestDetailCard(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'PO No. Internal'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
widget.poInternal ?? '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
8.height,
Text(
'Client Name'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
dataModel?.clientName ?? '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
// Row(
// mainAxisSize: MainAxisSize.min,
// children: [
// Checkbox(
// value: dataModel!.isHMG,
// activeColor: AppColor.neutral120,
// materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
// visualDensity: const VisualDensity(horizontal: -4, vertical: -3),
// onChanged: (value) {},
// ),
// 8.width,
// Text(
// 'HMG'.addTranslation,
// style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
// ),
// ],
// ),
8.height,
if (dataModel?.isHMG == true) ...[
Text(
'Operation Units'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
dataModel?.operatingUnit ?? '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
] else ...[
Text(
'Site'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
dataModel?.site?.siteName ?? '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
],
8.height,
Text(
'PO No.'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
//Need to verify this with backend ...
// widget.poInternal?? '-',
Text(
dataModel?.isHMG == true
? dataModel?.poNumber != null
? dataModel!.poNumber.toString()
: '-'
: dataModel?.poNumber != null
? dataModel!.poNumber.toString()
: '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
8.height,
Text(
'Warranty Period'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
dataModel?.warrentyPeriod != null ? dataModel!.warrentyPeriod.toString() : '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
8.height,
Text(
'Payment Term'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
dataModel?.paymentTerm != null ? dataModel!.paymentTerm!.name.toString() : '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
8.height,
Text(
'Payment Terms Amount'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
dataModel?.paymentTermAmount != null ? dataModel!.paymentTermAmount.toString() : '-',
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),
),
8.height,
Text(
dataModel?.total != null ? dataModel!.total.toString() : '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
8.height,
Text(
'Total Invoice'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
dataModel?.totalInvoice != null ? dataModel!.totalInvoice.toString() : '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
],
);
}
Widget assetDeliveryTableList({required BuildContext context, required AssetDeliveryProvider assetDeliveryProvider}) {
List<AssetDeliveryTableModel> assetDeliveryTableList = assetDeliveryProvider.assetDeliveryTableList;
return assetDeliveryProvider.isDeliveryTableListLoading
? SizedBox(height: 200.toScreenHeight, child: const CircularProgressIndicator(color: AppColor.primary10).center)
: assetDeliveryTableList.isEmpty
? const NoDataFound().center
: ListView.separated(
itemCount: assetDeliveryTableList.length,
separatorBuilder: (_, __) => 8.height,
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (listContext, itemIndex) {
AssetDeliveryTableModel model = assetDeliveryTableList[itemIndex];
return AssetDeliveryTableCard(
deliveryTableModel: model,
onCardTap: () {
//status new
if (model.status?.value == 1) {
AssetDeliveryBottomSheet.changeStatusBottomSheet(
context: context,
cancelStatusTap: () async {
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await assetDeliveryProvider.changeDeliveryStatusToCancel(itemId: model.id).then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context);
await assetDeliveryProvider.getAssetDeliveryTableListById(requestId: widget.requestId);
}
});
},
statusChangeTap: () async {
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await assetDeliveryProvider.changeDeliveryStatus(itemId: model.id).then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context);
await assetDeliveryProvider.getAssetDeliveryTableListById(requestId: widget.requestId);
}
});
});
} else {
// Navigator.of(context).push(MaterialPageRoute(
// builder: (_) =>
// DeliveryInspectionFormView(
// deliveryTableModel: model,
// requestModel: dataModel,
// ),
// ));
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => AssetDeliveryStageTabPage(
title: 'Delivery Inspection'.addTranslation,
detailWidget: DeliveryInspectionFormView(
deliveryTableModel: model,
requestModel: dataModel,
),
tableId: model.id,
),
));
}
},
onViewHistoryTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AssetDeliveryHistoryLogView(
tableItemId: 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;
}
}
}

@ -0,0 +1,78 @@
import 'package:flutter/material.dart';
import 'package:test_sa/controllers/providers/api/device_transfer_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/modules/asset_delivery_module/models/asset_delivery_data_model.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/views/widgets/requests/request_status.dart';
class AssetDeliveryTableCard extends StatelessWidget {
AssetDeliveryTableModel deliveryTableModel;
VoidCallback onCardTap;
VoidCallback onViewHistoryTap;
AssetDeliveryTableCard({
super.key,
required this.deliveryTableModel,
required this.onCardTap,
required this.onViewHistoryTap,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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 ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Delivery Amount: ${deliveryTableModel.deliveryAmount ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Invoice Amount: ${deliveryTableModel.invoiceAmount ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Align(
alignment: Alignment.centerRight,
child: Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14),
),
],
).toShadowContainer(context, borderRadius: 20).onPress(() {
onCardTap();
});
}
}

@ -0,0 +1,18 @@
import 'package:test_sa/modules/asset_delivery_module/models/technical_inspection_asset_model.dart';
class AssetDeliveryUtils{
///This is static list for now if change in future backend provide Api as discuss with backend team
static List<InspectionChecklistItem> inspectionChecklist = [
InspectionChecklistItem(title: 'Physical Inspection As Per Manufacturer Instructions', value: 'flagphysicalInspection'),
InspectionChecklistItem(title: 'Functional Performance As Per Results Of Manufacturer Recommended Check List', value: 'flagFunctionPerformance'),
InspectionChecklistItem(title: 'Grounding Resistance: Ω', value: 'flagGroundingResistance'),
InspectionChecklistItem(title: 'Chassis Leakage Current: µA', value: 'flagChassiss'),
InspectionChecklistItem(title: 'Leads Leakage Current: µA', value: 'flagLeadsLeakage'),
InspectionChecklistItem(title: 'This Device Is Not Subject To Any Alert Or Recall Until The Date Of Preparation Of This Inspection Form', value: 'flagAlert'),
];
}

@ -0,0 +1,152 @@
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';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/generic_attachment_model.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.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/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 {
final int? tableItemId;
final int? attachmentType;
bool viewOnly = false;
final List<GenericAttachmentModel> attachmentList;
AssetDeliveryAttachmentView({
super.key,
required this.attachmentList,
this.viewOnly=false,
this.attachmentType,
this.tableItemId,
});
@override
State<AssetDeliveryAttachmentView> createState() => _AssetDeliveryAttachmentViewState();
}
class _AssetDeliveryAttachmentViewState extends State<AssetDeliveryAttachmentView> {
List<GenericAttachmentModel> _attachments = [];
bool isLoading = false;
late AssetDeliveryProvider assetDeliveryProvider;
@override
void initState() {
super.initState();
assetDeliveryProvider = Provider.of<AssetDeliveryProvider>(context, listen: false);
WidgetsBinding.instance.addPostFrameCallback((_) {
getInitialData();
});
}
void getInitialData() async {
///get previoues Attachment from Apis.
// isLoading = true;
setState(() {});
_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(
title: 'Attachments'.addTranslation,
onBackPress: () {
Navigator.pop(context);
},
),
body: widget.attachmentList.isEmpty
? const NoDataFound().center
: Column(
children: [
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: _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);
}
}
}

@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
class AssetDeliveryBottomSheet {
static buildBottomSheetParent({required BuildContext context, required Widget childWidget, bool? isDismissible}) {
return showModalBottomSheet(
context: context,
useSafeArea: true,
isScrollControlled: true,
isDismissible: isDismissible ?? true,
backgroundColor: Colors.transparent,
builder: (context) => SingleChildScrollView(
child: SafeArea(child: childWidget),
).bottomSheetContainer(context),
);
}
static Future changeStatusBottomSheet({required BuildContext context, required VoidCallback cancelStatusTap, required VoidCallback statusChangeTap}) {
return buildBottomSheetParent(
context: context,
childWidget: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox().indicatorWidget(),
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,
),
),
),
16.height,
Row(
children: [
AppFilledButton(
label: 'Cancel'.addTranslation,
maxWidth: true,
buttonColor: Colors.white,
// textColor: context.isDark ? AppColor.neutral30 : Colors.white,
textColor: AppColor.red30,
showBorder: true,
onPressed: () async {
cancelStatusTap();
},
).expanded,
12.width,
AppFilledButton(
buttonColor: AppColor.green70,
label: 'Change Status'.addTranslation,
maxWidth: true,
onPressed: () {
statusChangeTap();
}).expanded,
],
),
],
));
}
}

@ -0,0 +1,457 @@
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/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';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart';
import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart';
class CostCenterFormView extends StatefulWidget {
final List<CostCenterModel>? costCenterList;
final String? itemNo;
final String? itemDescription;
final int? lineId;
final int? tableItemId;
final int? requestId;
final bool? isHmg;
final double? cardPadding;
const CostCenterFormView({
super.key,
this.costCenterList,
this.lineId,
this.requestId,
this.tableItemId,
this.itemNo,
this.isHmg,
this.itemDescription,
this.cardPadding,
});
@override
State<CostCenterFormView> createState() => _CostCenterFormViewState();
}
class _CostCenterFormViewState extends State<CostCenterFormView> {
CostCenterSerialNoFormModel formModel = CostCenterSerialNoFormModel(costCenters: [], serialNos: []);
late AssetDeliveryProvider assetDeliveryProvider;
CostCenterType _selectedType = CostCenterType.costCenter;
@override
void initState() {
super.initState();
assetDeliveryProvider = Provider.of<AssetDeliveryProvider>(context, listen: false);
WidgetsBinding.instance.addPostFrameCallback((_) {
getInitialData();
});
}
void getInitialData() async {
formModel = await assetDeliveryProvider.getAssetDeliveryCostCenterListForLines(
lineId: widget.lineId,
tableItemId: widget.tableItemId,
);
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) {
await costCenterProvider.getData(id: widget.requestId);
}
}
setState(() {});
}
void addItem<T>(List<T> list, T Function() creator) {
setState(() {
list.add(creator());
});
}
void removeItem<T>(List<T> list, int index) {
setState(() {
list.removeAt(index);
});
}
void updateItem<T>(List<T> list, int index, void Function(T item) updater) {
setState(() {
updater(list[index]);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: DefaultAppBar(
title: 'Cost Center for Item'.addTranslation,
// onBackPress: () {
// Navigator.pop(context);
// },
),
body: Consumer<AssetDeliveryProvider>(builder: (context, provider, child) {
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 _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;
});
},
),
),
],
),
);
}
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),
],
),
);
}
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 {
final CostCenterModel model;
final bool? isHmg;
final int index;
final int? requestId;
final bool isReadOnly;
final void Function(void Function(CostCenterModel model)) onUpdate;
final VoidCallback onRemove;
const CostCenterItemCard({
super.key,
required this.model,
this.requestId,
this.isHmg,
required this.index,
required this.isReadOnly,
required this.onUpdate,
required this.onRemove,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
if (!isReadOnly)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
'Cost Center'.addTranslation.bodyText(context).custom(color: AppColor.textColor(context)),
if (!isReadOnly)
Container(
height: 28,
width: 28,
padding: const EdgeInsets.all(6),
child: "trash".toSvgAsset(height: 20, width: 20),
).onPress(onRemove),
],
),
8.height,
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
isHmg == true
? SingleItemDropDownMenu<Lookup, CostCenterProvider>(
context: context,
height: 56.toScreenHeight,
title: 'Cost Center'.addTranslation,
showShadow: false,
backgroundColor: AppColor.fieldBgColor(context),
requestById: requestId,
showAsBottomSheet: true,
matchBy: DropDownMatchType.name,
initialValue: model.costCenter,
onSelect: (status) {
if (status != null) {
model.costCenter = status;
model.costCenterName = status.name;
}
},
)
: AppTextFormField(
labelText: 'Cost Center'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
initialValue: model.costCenterName ?? '',
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
onChange: (value) {
model.costCenterName = value;
},
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
labelText: 'Quantity'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
initialValue: model.quantity != null ? model.quantity.toString() : '',
textAlign: TextAlign.center,
enable: true,
textInputType: TextInputType.number,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
onChange: (value) {
if (value.isNotEmpty) {
model.quantity = double.tryParse(value);
}
},
style: Theme.of(context).textTheme.titleMedium,
),
],
)
],
).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);
}
}

@ -0,0 +1,726 @@
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/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/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/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';
class DeliveryInspectionFormView extends StatefulWidget {
static const String id = "/delivery-inspection-form";
final AssetDeliveryTableModel deliveryTableModel;
final AssetDeliveryDataModel? requestModel;
DeliveryInspectionFormView({
Key? key,
required this.deliveryTableModel,
this.requestModel,
}) : super(key: key);
@override
State<DeliveryInspectionFormView> createState() => _DeliveryInspectionFormViewState();
}
class _DeliveryInspectionFormViewState extends State<DeliveryInspectionFormView> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
bool viewOnly = false;
AssetDeliveryProvider? assetDeliveryProvider;
DeliveryInspectionFormModel model = DeliveryInspectionFormModel();
bool isLoading = false;
@override
void initState() {
viewOnly = widget.deliveryTableModel.status?.value != 2;
WidgetsBinding.instance.addPostFrameCallback((_) {
getInitialData();
});
super.initState();
}
void getInitialData() async {
assetDeliveryProvider = Provider.of<AssetDeliveryProvider>(context, listen: false);
setState(() {
isLoading = true;
});
final results = await Future.wait([
assetDeliveryProvider!.getAssetDeliveryInspectionData(
itemId: widget.deliveryTableModel.id,
),
assetDeliveryProvider!.getDeliveryLinesListById(
itemId: widget.deliveryTableModel.id,
),
]);
model = results[0] as DeliveryInspectionFormModel;
setState(() {
isLoading = false;
});
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
// appBar: DefaultAppBar(
// title: 'Delivery Inspection'.addTranslation,
// onBackPress: () {
// Navigator.pop(context);
// },
// ),
body: isLoading
? const CircularProgressIndicator(color: AppColor.primary10).center
: Form(
key: _formKey,
child: Column(
children: [
SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!viewOnly) ...[inspectionDetailsForm()] else ...[inspectionDetails()],
8.height,
Text(
'Total Time'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
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(
"Lines",
style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
linesList(context),
],
).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: () {
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,
],
),
],
)
: 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) {
return Consumer<AssetDeliveryProvider>(builder: (context, provider, child) {
List<DeliveryLineModel> linesList = provider.deliveryLinesList;
return provider.isDeliveryLineListLoading
? SizedBox.expand(child: const CircularProgressIndicator(color: AppColor.primary10).center)
: linesList.isEmpty
? const NoDataFound().center
: ListView.separated(
itemCount: linesList.length,
separatorBuilder: (_, __) => 8.height,
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (listContext, itemIndex) {
DeliveryLineModel model = linesList[itemIndex];
return AssetDeliveryLineCard(
deliveryLineModel: model,
isViewOnly: viewOnly,
editPress: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CostCenterFormView(
itemNo: model.itemNumber,
lineId: model.id,
isHmg: widget.requestModel?.isHMG,
tableItemId: widget.deliveryTableModel.id,
requestId: widget.requestModel?.id,
itemDescription: model.itemDescription,
)));
},
);
},
);
});
}
void calculateTotalTime() {
final from = model.deliveryInspectionInspectedDateFrom;
final to = model.deliveryInspectionInspectedDateTo;
if (from == null || to == null) {
model.totalTime = null;
return;
}
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: [
_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,
_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);
}
},
),
],
// 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,
hideShadow: true,
height: 56.toScreenHeight,
backgroundColor: AppColor.fieldBgColor(context),
date: model.deliveryInspectionInspectedDateFrom,
from: DateTime(DateTime.now().year, 1, 1),
formatDateWithTime: true,
onDatePicker: (selectedDate) async {
final selectedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
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,
hideShadow: true,
height: 56.toScreenHeight,
backgroundColor: AppColor.fieldBgColor(context),
date: model.deliveryInspectionInspectedDateTo,
from: model.deliveryInspectionInspectedDateFrom,
formatDateWithTime: true,
onDatePicker: (selectedDate) async {
if (model.deliveryInspectionInspectedDateFrom == null) {
"Please select time to first".showToast;
return;
}
final selectedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
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,
children: [
Text(
'Inspected by'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
model.deliveryInspectionInspectedBy ?? '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
8.height,
Text(
'Approved by'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
model.deliveryInspectionApprovedBy ?? '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
8.height,
Text(
'Inspected Date/Time From'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
model.deliveryInspectionInspectedDateFrom != null ? model.deliveryInspectionInspectedDateFrom!.toString().toServiceRequestDetailsFormat : '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
8.height,
Text(
'Inspected Date/Time To'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
model.deliveryInspectionInspectedDateTo != null ? model.deliveryInspectionInspectedDateTo!.toString().toServiceRequestDetailsFormat : '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
],
);
}
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 {
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await assetDeliveryProvider!.changeDeliveryStatusToCancel(itemId: widget.deliveryTableModel.id).then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context);
await assetDeliveryProvider!.getAssetDeliveryTableListById(requestId: widget.requestModel?.id);
}
});
}
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) => AssetDeliveryStageTabPage(
title: 'Technical Inspection'.addTranslation,
detailWidget: TechnicalInspectionLinesListView(
deliveryTableModel: widget.deliveryTableModel,
requestModel: widget.requestModel,
),
tableId: widget.deliveryTableModel.id,
)));
} else {
Navigator.push(
context,
MaterialPageRoute(
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,
// )));
}
}
void _changeStatusTap() async {
_formKey.currentState!.save();
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
model.assetDeliveryExternalDeliveryId = widget.deliveryTableModel.id;
await assetDeliveryProvider!.changeDeliveryStatus(requestPayload: model.toJson()).then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context);
await assetDeliveryProvider!.getAssetDeliveryTableListById(requestId: widget.requestModel?.id);
}
});
}
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);
},
),
],
),
),
);
}
}

@ -0,0 +1,117 @@
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/text_extensions.dart';
import 'package:test_sa/modules/asset_delivery_module/models/oracle_user_model.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/app_style/app_text_style.dart';
import 'package:test_sa/views/app_style/sizing.dart';
class OracleUserAutoCompleteField extends StatefulWidget {
final String initialValue;
final String label;
final bool clearAfterPick;
final Function(OracleUserModel) onPick;
final Function(String)? onChanged;
const OracleUserAutoCompleteField({Key? key, required this.initialValue, required this.label, required this.onPick, this.clearAfterPick = true, this.onChanged}) : super(key: key);
@override
_AutoCompletePartsFieldState createState() => _AutoCompletePartsFieldState();
}
class _AutoCompletePartsFieldState extends State<OracleUserAutoCompleteField> {
AssetDeliveryProvider? assetDeliveryProvider;
// late TextEditingController _controller;
@override
void initState() {
// _controller = TextEditingController(text: widget.initialValue);
super.initState();
}
@override
void didUpdateWidget(covariant OracleUserAutoCompleteField oldWidget) {
// if (widget.initialValue != oldWidget.initialValue) {
// _controller = TextEditingController(text: widget.initialValue);
// }
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
// _controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
assetDeliveryProvider ??= Provider.of<AssetDeliveryProvider>(context);
final border = UnderlineInputBorder(borderSide: BorderSide.none, borderRadius: BorderRadius.circular(10));
return Container(
decoration: BoxDecoration(
color: AppColor.background(context),
borderRadius: BorderRadius.circular(AppStyle.borderRadius * AppStyle.getScaleFactor(context)),
// 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: fieldTextEditingController,
focusNode: fieldFocusNode,
style: AppTextStyles.bodyText.copyWith(color: AppColor.black10),
textAlign: TextAlign.start,
decoration: InputDecoration(
border: border,
disabledBorder: border,
focusedBorder: border,
enabledBorder: border,
errorBorder: border,
contentPadding: EdgeInsets.symmetric(vertical: 8.toScreenHeight, horizontal: 16.toScreenWidth),
constraints: const BoxConstraints(),
suffixIconConstraints: const BoxConstraints(minWidth: 0),
filled: true,
fillColor: AppColor.fieldBgColor(context),
errorStyle: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red60),
floatingLabelStyle: AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? null : AppColor.neutral20),
labelText: widget.label,
labelStyle: AppTextStyles.tinyFont.copyWith(color: AppColor.textColor(context)),
),
textInputAction: TextInputAction.search,
onChanged: (text) {
if (widget.onChanged != null) {
widget.onChanged!(text);
fieldTextEditingController.text = text;
}
},
onSubmitted: (String value) {
onFieldSubmitted();
},
);
},
onSelected: (OracleUserModel selection) {
// if (widget.clearAfterPick) {
// fieldTextEditingController.clear();
// } else {
// _controller.text = selection.fullName ?? '';
// }
widget.onPick(selection);
},
),
);
}
}

@ -0,0 +1,64 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:test_sa/common_widgets/lookup_autocomplete_field.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/modules/asset_delivery_module/models/cost_center_model.dart';
import 'package:test_sa/modules/asset_inventory_module/models/asset_inventory_model.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
class EndUserCostCenterCardView extends StatelessWidget {
CostCenterModel costModel;
VoidCallback? editPress;
bool isViewOnly;
EndUserCostCenterCardView({
super.key,
required this.costModel,
this.editPress,
required this.isViewOnly,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//need to check this @waseem.
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Text(
'Cost Center Name: ${costModel.costCenterName ?? '-'}',
overflow: TextOverflow.ellipsis,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
),
8.width,
if (!isViewOnly)
'edit_icon'.toSvgAsset(height: 18, width: 18).onPress(() {
if (editPress != null) {
editPress!();
}
}),
],
),
Text(
'Accepted By: ${costModel.acceptedBy ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),Text(
'Accepted By Email: ${costModel.acceptedByEmail ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),Text(
'Acceptance Date: ${costModel.acceptanceDate!=null? costModel.acceptanceDate.toString().toAssetDetailsFormat: '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
],
).toShadowContainer(context, borderRadius: 20, padding: 12);
}
}

@ -0,0 +1,118 @@
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/modules/asset_delivery_module/models/end_user_asset_details_model.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/views/widgets/requests/request_status.dart';
class EndUserAssetDetailCard extends StatelessWidget {
EndUserAssetDetailsModel assetModel;
bool isViewOnly;
VoidCallback? editPress;
EndUserAssetDetailCard({
super.key,
required this.isViewOnly,
required this.assetModel,
this.editPress,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
assetModel.status != null
? StatusLabel(
label: assetModel.status?.name,
radius: 4,
textColor: getAssetStatusColor(context: context, status: assetModel.status?.name ?? '', isBackground: false),
backgroundColor: getAssetStatusColor(context: context, status: assetModel.status?.name ?? ''),
)
: const SizedBox(),
if (!isViewOnly)
'edit_icon'.toSvgAsset().onPress(() {
if (editPress != null) {
editPress!();
}
}),
],
),
8.height,
if (assetModel.type == 'Accessory') ...[
Text(
'Type: ${assetModel.type ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Item Number: ${assetModel.itemNumber ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Received Quantity: ${assetModel.receivedQty ?? '-'}',
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),
),
Text(
'Rejected Quantity: ${assetModel.rejectedQty ?? '-'}',
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),
),
] else ...[
Text(
'Type: ${assetModel.type ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Item Number: ${assetModel.itemNumber ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Asset Number: ${assetModel.assetNumber ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Serial No: ${assetModel.serialNo ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Replaced Asset No: ${assetModel.replacedAssetNo ?? '-'}',
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),
),
],
],
).toShadowContainer(context, borderRadius: 20, padding: 12);
}
Color getAssetStatusColor({required BuildContext context, required String status, bool? isBackground = true}) {
switch (status) {
case 'Accept':
return isBackground! ? AppColor.green70.withOpacity(0.4) : AppColor.green15;
case 'Reject':
return isBackground! ? AppColor.red30.withOpacity(0.4) : AppColor.red30;
default:
return Colors.blueGrey;
}
}
}

@ -0,0 +1,198 @@
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/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';
import 'package:test_sa/modules/asset_delivery_module/pages/end_user_acceptance/cost_center_card_view.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/end_user_acceptance/update_end_user_cost_center_details_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_lazy_loading.dart';
import '../../../../new_views/common_widgets/app_filled_button.dart';
import '../../../../new_views/common_widgets/default_app_bar.dart';
class EndUserCostCenterLIstView extends StatefulWidget {
final AssetDeliveryTableModel deliveryTableModel;
final AssetDeliveryDataModel? requestModel;
EndUserCostCenterLIstView({
Key? key,
required this.deliveryTableModel,
this.requestModel,
}) : super(key: key);
@override
State<EndUserCostCenterLIstView> createState() => _EndUserCostCenterLIstViewState();
}
class _EndUserCostCenterLIstViewState extends State<EndUserCostCenterLIstView> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
bool viewOnly = false;
List<CostCenterModel> costCenterList = [];
AssetDeliveryProvider? assetDeliveryProvider;
bool isLoading = false;
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
getInitialData();
});
super.initState();
}
void getInitialData() async {
assetDeliveryProvider = Provider.of<AssetDeliveryProvider>(context, listen: false);
isLoading = true;
if (widget.deliveryTableModel.status?.value != null && widget.deliveryTableModel.status!.value! > 4) {
viewOnly = true;
}
setState(() {});
costCenterList = (await assetDeliveryProvider?.getEndUserCostCenterList(
tableItemId: widget.deliveryTableModel.id,
)) ??
[];
isLoading = false;
setState(() {});
}
@override
void dispose() {
super.dispose();
}
@override
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),
// ),
body: Form(
key: _formKey,
child: isLoading
? const CircularProgressIndicator(color: AppColor.primary10).center
: Column(
children: [
//Refactor this
SingleChildScrollView(padding: const EdgeInsets.all(16), child: linesList(context).toShadowContainer(context, borderRadius: 20, padding: 12)).expanded,
!viewOnly
? FooterActionButton.footerContainer(
context: context,
child: 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: AppColor.red30,
showBorder: true,
onPressed: _cancelTap,
).expanded,
12.width,
AppFilledButton(buttonColor: AppColor.green70, label: 'Change Status'.addTranslation, maxWidth: true, onPressed: _changeStatusTap).expanded,
],
),
],
))
: 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);
}),
),
],
),
),
);
}
//Need to refector don't use listview and singlechildview both @waseem.
Widget linesList(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Cost Center",
style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
ListView.separated(
itemCount: costCenterList.length,
separatorBuilder: (_, __) => 8.height,
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (listContext, itemIndex) {
return EndUserCostCenterCardView(
costModel: costCenterList[itemIndex],
isViewOnly: viewOnly,
editPress: () async {
bool isRefresh = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UpdateEndUserCostCenterDetailsView(
costCenterModel: costCenterList[itemIndex],
tableItemId: widget.deliveryTableModel.id,
isHmg: widget.requestModel?.isHMG,
)));
if (isRefresh) {
getInitialData();
}
},
);
},
),
],
);
}
void _attachmentTap() async {
HelperFunction.attachmentTap(context: context, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id);
}
void _cancelTap() async {
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await assetDeliveryProvider?.changeDeliveryStatusToCancel(itemId: widget.deliveryTableModel.id).then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context);
await assetDeliveryProvider?.getAssetDeliveryTableListById(requestId: widget.requestModel?.id);
}
});
}
void _changeStatusTap() async {
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await assetDeliveryProvider?.changeDeliveryStatus(itemId: widget.deliveryTableModel.id).then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context);
await assetDeliveryProvider?.getAssetDeliveryTableListById(requestId: widget.requestModel?.id);
}
});
}
void _nextTap() async {
// Navigator.push(context, MaterialPageRoute(builder: (context) => EndUserCostCenterLIstView()));
}
}

@ -0,0 +1,520 @@
// 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/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 {
final EndUserAssetDetailsModel? assetDetailsModel;
final String? costCenterName;
final int? tableId;
const 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>();
late AssetDeliveryProvider assetDeliveryProvider;
EndUserAssetDetailsModel model = EndUserAssetDetailsModel();
late TextEditingController quantityController;
late TextEditingController descriptionController;
@override
void initState() {
super.initState();
quantityController = TextEditingController();
descriptionController = TextEditingController();
WidgetsBinding.instance.addPostFrameCallback((_) {
_loadInitialData();
});
}
void _loadInitialData() {
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;
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(
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: _formBody(context),
).expanded,
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
label: 'Save'.addTranslation,
maxWidth: true,
onPressed: _saveTap,
),
),
],
),
),
);
}
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();
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);
}
}
}

@ -0,0 +1,331 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter/services.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/modules/asset_delivery_module/models/cost_center_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/end_user_asset_details_model.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/end_user_acceptance/end_user_asset_detail_card.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/end_user_acceptance/update_end_user_asset_details_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_lazy_loading.dart';
import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart';
import 'package:test_sa/views/widgets/date_and_time/date_picker.dart';
import 'package:test_sa/views/widgets/e_signature/e_signature.dart';
import '../../../../new_views/common_widgets/app_filled_button.dart';
import '../../../../new_views/common_widgets/default_app_bar.dart';
class UpdateEndUserCostCenterDetailsView extends StatefulWidget {
int? tableItemId;
CostCenterModel? costCenterModel;
bool? isHmg;
UpdateEndUserCostCenterDetailsView({
Key? key,
this.tableItemId,
this.costCenterModel,
this.isHmg,
}) : super(key: key);
@override
State<UpdateEndUserCostCenterDetailsView> createState() => _UpdateEndUserCostCenterDetailsViewState();
}
class _UpdateEndUserCostCenterDetailsViewState extends State<UpdateEndUserCostCenterDetailsView> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
bool viewOnly = false;
CostCenterModel model = CostCenterModel();
List<EndUserAssetDetailsModel> assetList = [];
late AssetDeliveryProvider assetDeliveryProvider;
Uint8List? newSignature;
String? endUserSignature;
bool isLoading = false;
late TextEditingController acceptedByController;
late TextEditingController acceptedByEmailController;
@override
void initState() {
acceptedByController = TextEditingController();
acceptedByEmailController = TextEditingController();
WidgetsBinding.instance.addPostFrameCallback((_) {
getInitialData();
});
super.initState();
}
void getInitialData() async {
assetDeliveryProvider = Provider.of<AssetDeliveryProvider>(context, listen: false);
if (widget.costCenterModel != null) {
model = CostCenterModel.fromJson(widget.costCenterModel!.toEndUserAcceptanceFormJson());
_fillControllers();
}
setState(() {
isLoading = true;
});
assetList = await assetDeliveryProvider.getEndUserAssetList(tableItemId: widget.tableItemId, costCenterName: widget.costCenterModel?.costCenterName);
setState(() {
isLoading = false;
});
}
void _fillControllers() {
acceptedByController.text = model.acceptedBy ?? '';
acceptedByEmailController.text = model.acceptedByEmail ?? '';
}
@override
void dispose() {
acceptedByController.dispose();
acceptedByEmailController.dispose();
super.dispose();
}
@override
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),
),
body: Form(
key: _formKey,
child: Column(
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
inspectionDetailsForm(),
8.height,
const Divider().defaultStyle(context),
///Need to implement later as discuss with Ahmed.
// Text(
// "Acceptance Form",
// style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
// ),
// 6.height,
// Row(
// children: [
// 'warning_icon'.toSvgAsset(height: 16, width: 16),
// 6.width,
// Text(
// 'Review the acceptance form'.addTranslation,
// style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
// ),
// 6.width,
// 'web_link_icon'.toSvgAsset(height: 20, width: 20),
// ],
// ),
16.height,
Text(
"Items",
style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
endUserAssetList(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 endUserAssetList(BuildContext context) {
return isLoading
? const CircularProgressIndicator(color: AppColor.primary10).center
: ListView.separated(
itemCount: assetList.length,
separatorBuilder: (_, __) => 8.height,
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (listContext, itemIndex) {
EndUserAssetDetailsModel model = assetList[itemIndex];
return EndUserAssetDetailCard(
assetModel: model,
isViewOnly: viewOnly,
editPress: () async {
bool isRefresh = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UpdateEndUserAssetDetailsView(
assetDetailsModel: model,
costCenterName: widget.costCenterModel?.costCenterName,
tableId: widget.tableItemId,
)));
if (isRefresh) {
getInitialData();
}
},
);
},
);
}
Widget inspectionDetailsForm() {
return Column(
children: [
ADatePicker(
label: 'Accepted Date'.addTranslation,
hideShadow: true,
height: 56.toScreenHeight,
backgroundColor: AppColor.fieldBgColor(context),
date: model.acceptanceDate,
from: DateTime(DateTime.now().year, 1, 1),
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.acceptanceDate = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute);
setState(() {});
}
});
},
),
8.height,
if (widget.isHmg == true) ...[
OracleUserAutoCompleteField(
clearAfterPick: false,
initialValue: model.acceptedBy ?? '',
label: 'Accepted By'.addTranslation,
onPick: (value) {
model.acceptedBy = value.fullName;
model.acceptedByEmail = value.employeeEmail;
acceptedByEmailController.text = value.employeeEmail ?? '';
setState(() {});
},
onChanged: (value) {},
),
8.height,
] else ...[
AppTextFormField(
labelText: 'Accepted By'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
controller: acceptedByController,
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,
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',
oldSignature: model.acceptedBySignature,
newSignature: newSignature,
backgroundColor: AppColor.neutral100,
showShadow: false,
onChange: (signature) {
updateSignature(signature: signature);
},
)
],
);
}
void updateSignature({Uint8List? signature}) {
if (signature == null || signature.isEmpty) {
setState(() {
newSignature = null;
model.acceptedBySignature = null;
});
return;
}
setState(() {
newSignature = signature;
model.acceptedBySignature = "${DateTime.now().toIso8601String()}.png|${base64Encode(signature)}";
});
}
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;
await assetDeliveryProvider.saveEndUserFormData(model: model).then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context, true);
}
});
}
}

@ -0,0 +1,168 @@
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/modules/asset_delivery_module/models/history_model.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart';
import 'package:test_sa/modules/cm_module/cm_request_utils.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/views/widgets/requests/request_status.dart';
class AssetDeliveryHistoryLogView extends StatefulWidget {
final int? tableItemId;
const AssetDeliveryHistoryLogView({Key? key,this.tableItemId}) : super(key: key);
@override
State<AssetDeliveryHistoryLogView> createState() => _AssetDeliveryHistoryLogViewState();
}
class _AssetDeliveryHistoryLogViewState extends State<AssetDeliveryHistoryLogView> {
List<AssetDeliveryHistoryModel> history = [];
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
getInitialData();
});
super.initState();
}
void getInitialData() async {
history = await Provider.of<AssetDeliveryProvider>(context, listen: false).getHistory(tableItemId: widget.tableItemId);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
// 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
: Stack(
children: [
Padding(
padding: EdgeInsets.only(left: 122.toScreenWidth),
child: const VerticalDivider(
color: AppColor.white40,
thickness: 2,
width: 20,
indent: 10,
endIndent: 10,
),
),
SafeArea(
child: Builder(builder: (cxt) {
List<Widget> items = [];
String lastStatus = "";
DateTime? nextDate;
for (int i = 0; i < history.length; i++) {
var object = history[i];
String currentStatus = object.status!.name!;
if (lastStatus.isEmpty) {
lastStatus = currentStatus;
}
if (lastStatus == currentStatus) {
if (i + 1 < history.length) {
nextDate = history[i + 1].createdDate;
String timeDifference = CMRequestUtils.calculateTimeDifference(object.createdDate!, nextDate!);
object.timeDifference = timeDifference;
} else {
object.timeDifference = ''; // Empty string for the last action
}
items.add(historyLogCard(cxt, object));
} else {
items.add(statusCard(cxt, lastStatus));
lastStatus = currentStatus;
if (i + 1 < history.length) {
nextDate = history[i + 1].createdDate;
String timeDifference = CMRequestUtils.calculateTimeDifference(object.createdDate!, nextDate!);
object.timeDifference = timeDifference;
} else {
object.timeDifference = ''; // Empty string for the last action
}
items.add(historyLogCard(cxt, object));
}
}
items.add(statusCard(cxt, lastStatus));
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(children: items),
);
}),
),
],
);
}),
);}
Widget historyLogCard(BuildContext context, AssetDeliveryHistoryModel object) {
String title = object.status!.name!;
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
object.createdDate.toString().toServiceRequestCardFormat,
textAlign: TextAlign.end,
style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
).expanded,
Expanded(
flex: 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title.bodyText(context).custom(color: AppColor.textColor(context)),
// object.timeDifference.isNotEmpty ? object.timeDifference?.tinyFont(context).custom(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120) : const SizedBox(),
],
),
),
],
).toShadowContainer(context, padding: 16).paddingOnly(bottom: 16);
}
Widget statusCard(BuildContext context, String status) {
return SizedBox(
width: 153.toScreenWidth,
child: StatusLabel(
radius: 4,
label: statusText(status.toLowerCase()),
labelInCenter: true,
textColor: AppColor.neutral50,
backgroundColor: AppColor.orange30,
),
).paddingOnly(bottom: 16, start: 40);
}
// Need to check status text we have
String statusText(String status) {
if (status == "new" || status == "open") {
return "New".addTranslation;
} else if (status == "in progress") {
return "Status update to In Progress".addTranslation;
} else if (status == "closed") {
return "Status update to Closed".addTranslation;
} else if (status == "completed") {
return "Status update to Completed".addTranslation;
} else if (status == "fixed") {
return "Status update to Fixed".addTranslation;
} else if (status == "reject") {
return "Status update to Rejected".addTranslation;
} else
return status;
}
}

@ -0,0 +1,194 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/devices_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/widget_extensions.dart';
import 'package:test_sa/models/device/asset.dart';
import 'package:test_sa/models/device/asset_search.dart';
import 'package:test_sa/models/device/model_definition.dart';
import 'package:test_sa/modules/asset_delivery_module/models/technical_inpection_asset_model.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/custom_app_bar.dart';
import 'package:test_sa/views/widgets/equipment/asset_item_listview.dart';
import 'package:test_sa/views/widgets/horizontal_list_widget.dart';
import 'package:test_sa/views/widgets/loaders/lazy_loading.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
class TechnicalInspectionSearchAssetView extends StatefulWidget {
/// add on route
static const String id = "search_asset_view";
final int? lineId;
const TechnicalInspectionSearchAssetView({
Key? key,
this.lineId,
}) : super(key: key);
@override
State<TechnicalInspectionSearchAssetView> createState() => _TechnicalInspectionSearchAssetViewState();
}
class _TechnicalInspectionSearchAssetViewState extends State<TechnicalInspectionSearchAssetView> {
int _selectedIndex = 0;
DeviceSearch? search;
late TextEditingController _searchController;
late AssetProvider _deviceProvider;
//check for this list if not need remove
final List<TechnicalInspectionAssetBasicInfoModel> _searchableList = [];
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
bool _isFirst = true;
@override
void initState() {
_searchController = TextEditingController();
super.initState();
}
@override
void dispose() {
_searchController.dispose();
_deviceProvider.searchReset();
super.dispose();
}
@override
Widget build(BuildContext context) {
List<String> searchBy = [
context.translation.assetName,
context.translation.assetNumber,
'model'.addTranslation,
'manufacturer'.addTranslation,
// 'assetType'.addTranslation,
// 'assetOrigin'.addTranslation,
];
_deviceProvider = Provider.of<AssetProvider>(context, listen: false);
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: CustomAppBar(
title: context.translation.searchAsset,
),
body: Column(
children: [
HorizontalListWidget(
list: searchBy,
callBackFunction: (index) {
setState(() {
_selectedIndex = index;
});
},
).paddingOnly(top: 16, bottom: 0),
Form(
key: _formKey,
child: AppTextFormField(
controller: _searchController,
textInputAction: TextInputAction.search,
labelText: "${context.translation.searchBy} ${searchBy[_selectedIndex]}",
onAction: _search,
onChange: (text) {
_searchController.text = text;
_searchController.selection = TextSelection.fromPosition(TextPosition(offset: _searchController.text.length));
setState(() {});
},
onSaved: (value) {
setState(() {
search = DeviceSearch();
});
_setValue(value);
},
suffixIcon: IconButton(
icon: const Icon(Icons.search),
splashColor: Colors.transparent,
onPressed: _searchController.text.isNotEmpty ? _search : null,
color: AppColor.neutral20,
).paddingOnly(end: 0),
).paddingOnly(top: 16, start: 16, end: 16, bottom: 8)),
Expanded(
child: _searchableList.isEmpty
? _isFirst
? const SizedBox()
: NoDataFound(message: context.translation.noDeviceFound)
: LazyLoading(
nextPage: _deviceProvider.nextPage,
onLazyLoad: () async {
if (_searchController.text.isNotEmpty) {
await _deviceProvider.getAssetsForAssetDelivery(search: search, lineId: widget.lineId, isSearchBy: true);
setState(() {
_searchableList.clear();
_searchableList.addAll(_deviceProvider.technicalInspectionAsset);
});
}
},
child: ListView.separated(
itemCount: _searchableList.length,
separatorBuilder: (listContext, itemIndex) => 8.height,
padding: const EdgeInsets.all(16),
itemBuilder: (listContext, itemIndex) {
return AssetItemListView(
device: Asset(
id: _searchableList[itemIndex].id,
assetNumber: _searchableList[itemIndex].assetNumber,
assetSerialNo: _searchableList[itemIndex].assetNumber,
modelDefinition: ModelDefinition(assetName: _searchableList[itemIndex].assetName, modelName: _searchableList[itemIndex].model),
),
onPressed: (device) {
Navigator.of(context).pop(device);
},
);
},
),
),
)
],
));
}
void _search({bool loadMore = false}) async {
FocusScope.of(context).unfocus();
_formKey.currentState?.save();
_searchableList.clear();
_deviceProvider.technicalInspectionAsset.clear();
setState(() {});
_deviceProvider.searchReset();
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await _deviceProvider.getAssetsForAssetDelivery(
search: search,
lineId: widget.lineId,
isSearchBy: true,
);
setState(() {
_searchableList.clear();
_searchableList.addAll(_deviceProvider.technicalInspectionAsset);
_isFirst = false;
});
Navigator.pop(context);
}
_setValue(String? value) {
search ??= DeviceSearch();
switch (_selectedIndex) {
case 0:
search!.assetName = value;
break;
case 1:
search!.assetNo = value;
break;
case 2:
search!.model = value;
break;
case 3:
search!.manufacturer = value;
break;
}
}
}

@ -0,0 +1,215 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/modules/asset_delivery_module/models/technical_inspection_asset_model.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 InspectionCheckListView extends StatefulWidget {
final List<InspectionChecklistItem>? initialValues;
const InspectionCheckListView({super.key, this.initialValues});
@override
State<InspectionCheckListView> createState() => _InspectionCheckListViewState();
}
class _InspectionCheckListViewState extends State<InspectionCheckListView> {
late List<InspectionChecklistItem> checklist;
@override
void initState() {
super.initState();
checklist = AssetDeliveryUtils.inspectionChecklist.map((e) => InspectionChecklistItem(title: e.title)).toList();
if (widget.initialValues != null && widget.initialValues!.isNotEmpty) {
for (final item in checklist) {
final matched = widget.initialValues!.firstWhere(
(e) => e.title == item.title,
orElse: () => InspectionChecklistItem(),
);
item.status = matched.status; // null-safe
}
}
}
void updateStatus(int index, String value) {
setState(() {
checklist[index].status = value;
});
}
List<InspectionChecklistItem> get selectedValues => checklist.where((e) => e.status != null).toList();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: DefaultAppBar(
title: 'Asset Check List'.addTranslation,
),
body: Column(
children: [
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(
buttonColor: AppColor.primary10,
label: 'Save'.addTranslation,
maxWidth: true,
onPressed: () {
Navigator.pop(context, selectedValues);
},
),
),
],
),
);
}
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!),
// ),
// ),
// ],
// ),
// ],
// );
}

@ -0,0 +1,138 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/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/new_views/app_style/app_color.dart';
import 'package:test_sa/views/widgets/requests/request_status.dart';
class TechnicalInspectionAssetCard extends StatelessWidget {
TechnicalInspectionAssetModel assetModel;
bool isViewOnly;
bool ischild;
VoidCallback? editPress;
TechnicalInspectionAssetCard({
super.key,
required this.isViewOnly,
this.ischild = false,
required this.assetModel,
this.editPress,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!ischild) ...[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
StatusLabel(
label: getAssetStatus(),
radius: 4,
textColor: getAssetStatusColor(context: context, status: getAssetStatus(), isBackground: false),
backgroundColor: getAssetStatusColor(context: context, status: getAssetStatus()),
),
if (!isViewOnly)
'edit_icon'.toSvgAsset().onPress(() {
if (editPress != null) {
editPress!();
}
}),
],
),
8.height,
],
if (ischild) ...[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Asset Number: ${assetModel.assetNumber ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
if (!isViewOnly)
'edit_icon'.toSvgAsset().onPress(() {
if (editPress != null) {
editPress!();
}
}),
],
),
2.height,
],
Text(
'Asset Name: ${assetModel.modelDefinition?.assetName ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
if (!ischild)
Text(
'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),
),
Text(
'System ID: ${assetModel.systemId ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Medical Department: ${assetModel.department?.name ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Installation Date: ${assetModel.installationDate ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'Asset Type: ${assetModel.assetType?.name ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
],
).toShadowContainer(context, borderRadius: 20, padding: 12);
}
Color getAssetStatusColor({required BuildContext context, required String status, bool? isBackground = true}) {
switch (status) {
case 'PASS':
return isBackground! ? AppColor.green70.withOpacity(0.4) : AppColor.green15;
case 'FAIL':
return isBackground! ? AppColor.red30.withOpacity(0.4) : AppColor.red30;
default:
return Colors.white10;
}
}
String getAssetStatus() {
final flags = [
assetModel.flagphysicalInspection,
assetModel.flagFunctionPerformance,
assetModel.flagGroundingResistance,
assetModel.flagChassiss,
assetModel.flagLeadsLeakage,
assetModel.flagAlert,
];
if (flags.any((f) => f == false)) {
return 'FAIL';
}
if (flags.every((f) => f == true)) {
return 'PASS';
}
return 'FAIL';
}
}

@ -0,0 +1,58 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:test_sa/common_widgets/lookup_autocomplete_field.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/modules/asset_delivery_module/models/cost_center_model.dart';
import 'package:test_sa/modules/asset_inventory_module/models/asset_inventory_model.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
class TechnicalInspectionCostCard extends StatelessWidget {
CostCenterModel costModel;
VoidCallback? editPress;
bool isViewOnly;
TechnicalInspectionCostCard({
super.key,
required this.costModel,
this.editPress,
required this.isViewOnly,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//need to check this @waseem.
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Text(
'Cost Center Name: ${costModel.costCenterName ?? '-'}',
overflow: TextOverflow.ellipsis,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
),
8.width,
if (!isViewOnly)
'edit_icon'.toSvgAsset(height: 18, width: 18).onPress(() {
if (editPress != null) {
editPress!();
}
}),
],
),
Text(
'Quantity: ${costModel.quantity ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
],
).toShadowContainer(context, borderRadius: 20, padding: 12);
}
}

@ -0,0 +1,284 @@
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/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';
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_lazy_loading.dart';
import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart';
import 'package:test_sa/providers/service_request_providers/last_situation_provider.dart';
import 'package:test_sa/views/widgets/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';
import '../../../../new_views/common_widgets/default_app_bar.dart';
class TechnicalInspectionLinesListView extends StatefulWidget {
final AssetDeliveryTableModel deliveryTableModel;
final AssetDeliveryDataModel? requestModel;
TechnicalInspectionLinesListView({
Key? key,
required this.deliveryTableModel,
this.requestModel,
}) : super(key: key);
@override
State<TechnicalInspectionLinesListView> createState() => _TechnicalInspectionLinesListViewState();
}
class _TechnicalInspectionLinesListViewState extends State<TechnicalInspectionLinesListView> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
bool viewOnly = false;
bool isLoading = false;
AssetDeliveryProvider? assetDeliveryProvider;
List<DeliveryLineModel> technicalInspectionLinesList = [];
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
getInitialData();
});
super.initState();
}
void getInitialData() async {
assetDeliveryProvider = Provider.of<AssetDeliveryProvider>(context, listen: false);
viewOnly = widget.deliveryTableModel.status?.value != 3;
isLoading = true;
setState(() {});
technicalInspectionLinesList = await assetDeliveryProvider!.getTechnicalInspectionLinesListById(
//TODO uncommit this after testing @waseem
itemId: widget.deliveryTableModel.id,
// itemId: 2,
);
isLoading = false;
setState(() {});
}
@override
void dispose() {
super.dispose();
}
@override
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),
// ),
body: Form(
key: _formKey,
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,
],
),
),
],
),
),
);
}
//May be need to show data.
Widget inspectionDetails() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Inspected by'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
'Mahmoud Shrouf'.addTranslation,
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
8.height,
Text(
'Approved by'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
'Mahmoud Shrouf'.addTranslation,
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
8.height,
Text(
'Inspected Date/Time From'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
'19 August, 2025'.addTranslation,
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
8.height,
Text(
'Inspected Date/Time To'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
Text(
'19 August, 2025'.addTranslation,
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
],
);
}
void _changeStatusTap() async {
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await assetDeliveryProvider?.changeDeliveryStatus(itemId: widget.deliveryTableModel.id).then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context);
await assetDeliveryProvider?.getAssetDeliveryTableListById(requestId: widget.requestModel?.id);
}
});
}
void _attachmentTap() async {
HelperFunction.attachmentTap(context: context, assetDeliveryProvider: assetDeliveryProvider!, deliveryTableItemId: widget.deliveryTableModel.id);
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => AssetDeliveryAttachmentView(
// tableItemId: widget.deliveryTableModel.id,
// )));
}
void _cancelTap() async {
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await assetDeliveryProvider?.changeDeliveryStatusToCancel(itemId: widget.deliveryTableModel.id).then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context);
await assetDeliveryProvider?.getAssetDeliveryTableListById(requestId: widget.requestModel?.id);
}
});
}
void _nextTap() async {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => EndUserCostCenterLIstView(
// requestModel: widget.requestModel,
// deliveryTableModel: widget.deliveryTableModel,
// )));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AssetDeliveryStageTabPage(
title: 'End-User Acceptance'.addTranslation,
detailWidget: EndUserCostCenterLIstView(
deliveryTableModel: widget.deliveryTableModel,
requestModel: widget.requestModel,
),
tableId: widget.deliveryTableModel.id,
)));
}
}

@ -0,0 +1,133 @@
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/modules/asset_delivery_module/models/technical_inspection_asset_model.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_parent_asset_view.dart';
import 'package:test_sa/modules/asset_delivery_module/provider/asset_delivery_provider.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
import '../../../../new_views/common_widgets/default_app_bar.dart';
class TechnicalInspectionParentAssetListView extends StatefulWidget {
final int? costCenterId;
final int? tableItemId;
final int? lineId;
TechnicalInspectionParentAssetListView({
Key? key,
this.costCenterId,
this.tableItemId,
this.lineId,
}) : super(key: key);
@override
State<TechnicalInspectionParentAssetListView> createState() => _TechnicalInspectionParentAssetListViewState();
}
class _TechnicalInspectionParentAssetListViewState extends State<TechnicalInspectionParentAssetListView> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
bool viewOnly = false;
AssetDeliveryProvider? assetDeliveryProvider;
List<TechnicalInspectionAssetModel> assetList = [];
bool isLoading = false;
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
getInitialData();
});
super.initState();
}
void getInitialData() async {
setState(() {
isLoading = true;
});
assetDeliveryProvider = Provider.of<AssetDeliveryProvider>(context, listen: false);
//TODO replace static id's after testing...
assetList = await assetDeliveryProvider!.getParentAssetList(
costCenterId: widget.costCenterId,
tableItemId: widget.tableItemId,
lineId: widget.lineId,
);
setState(() {
isLoading = false;
});
}
@override
void dispose() {
super.dispose();
}
@override
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),
),
body:Form(
key: _formKey,
child: parentAssetList(context), // parentAssetList already returns a ListView
),
);
}
Widget parentAssetList(BuildContext context) {
if (isLoading) {
return SizedBox.expand(
child: const CircularProgressIndicator(color: AppColor.primary10).center,
);
}
if (assetList.isEmpty) {
return const NoDataFound().center;
}
return ListView.separated(
padding: const EdgeInsets.all(16),
itemCount: assetList.length + 1, // +1 for the "Parents" header
separatorBuilder: (_, __) => 8.height,
itemBuilder: (context, index) {
if (index == 0) {
return Text(
"Parents",
style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
);
}
final asset = assetList[index - 1];
return TechnicalInspectionAssetCard(
assetModel: asset,
isViewOnly: viewOnly,
editPress: () async {
bool isRefreshed = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UpdateParentAssetView(
assetModel: asset,
lineId: widget.lineId,
tableItemId: widget.tableItemId,
costCenterItemId: widget.costCenterId,
),
),
);
if (isRefreshed) {
getInitialData();
}
},
);
},
);
}
}

@ -0,0 +1,472 @@
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/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';
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';
class UpdateChildAssetView extends StatefulWidget {
final TechnicalInspectionAssetModel assetModel;
final int? lineId;
final int index;
UpdateChildAssetView({
Key? key,
required this.assetModel,
required this.index,
this.lineId,
}) : super(key: key);
@override
State<UpdateChildAssetView> createState() => _UpdateChildAssetViewState();
}
class _UpdateChildAssetViewState extends State<UpdateChildAssetView> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
List<InspectionChecklistItem> checkList = [];
late SiteProvider siteProvider;
late AssetDeliveryProvider assetDeliveryProvider;
late TextEditingController assetNumberCtrl;
late TextEditingController serialNoCtrl;
late TextEditingController systemIdCtrl;
@override
void initState() {
siteProvider = Provider.of<SiteProvider>(context, listen: false);
assetDeliveryProvider = Provider.of<AssetDeliveryProvider>(context, listen: false);
assetNumberCtrl = TextEditingController();
serialNoCtrl = TextEditingController();
systemIdCtrl = TextEditingController();
WidgetsBinding.instance.addPostFrameCallback((_) {
populateForm();
});
super.initState();
}
void populateForm() async {
assetDeliveryProvider.isChildAssetDataLoading = true;
assetDeliveryProvider.setChildModel(widget.assetModel);
if (widget.assetModel.site != null) {
await assetDeliveryProvider.getSiteData(siteId: widget.assetModel.site?.id, model: assetDeliveryProvider.childFormModel);
}
populateTextFields(model: assetDeliveryProvider.childFormModel);
assetDeliveryProvider.isChildAssetDataLoading = false;
}
void populateTextFields({required TechnicalInspectionAssetModel model}) {
assetNumberCtrl.text = model.assetNumber ?? '';
serialNoCtrl.text = model.serialNo ?? '';
systemIdCtrl.text = model.systemId ?? '';
setState(() {});
}
@override
void dispose() {
assetNumberCtrl.dispose();
serialNoCtrl.dispose();
systemIdCtrl.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: DefaultAppBar(
title: 'Child Details'.addTranslation,
titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
body: Form(
key: _formKey,
child: Consumer<AssetDeliveryProvider>(builder: (context, provider, child) {
return provider.isChildAssetDataLoading
? const CircularProgressIndicator(color: AppColor.primary10).center
: Column(
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
inspectionDetailsForm(
lineId: widget.lineId,
provider: provider,
),
8.height,
const Divider().defaultStyle(context),
8.height,
testLines(
context: 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 testLines({required BuildContext context}) {
return 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);
}),
],
);
}
/// Need to make reuseable as used in parent and child both.
Widget inspectionDetailsForm({
int? lineId,
required AssetDeliveryProvider provider,
}) {
TechnicalInspectionAssetModel model = provider.childFormModel;
return Column(
children: [
AppTextFormField(
labelText: 'Asset Number'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
controller: assetNumberCtrl,
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) {
TechnicalInspectionAssetModel? assetModel = await provider.getAssetDetails(assetId: data?.id);
if (assetModel != null) {
provider.setChildModel(assetModel);
if (assetModel.site != null) {
await provider.getSiteData(siteId: assetModel.site?.id, model: provider.childFormModel);
}
provider.setParentModel(assetModel);
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,
showShadow: false,
onSaved: (value) {
model.serialNo = value;
},
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
labelText: 'System ID'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
controller: systemIdCtrl,
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: provider.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: provider.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: provider.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: provider.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 {
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);
}
Future<void> openInspectionChecklist(BuildContext context) async {
applyInspectionFlagsToChecklist(model: assetDeliveryProvider.childFormModel);
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => InspectionCheckListView(
initialValues: checkList,
)));
if (result != null) {
checkList = result;
setState(() {});
}
buildInspectionPayload(checkList);
}
bool _isPass(List<InspectionChecklistItem> list, String title) {
return list
.firstWhere(
(e) => e.title == title,
orElse: () => InspectionChecklistItem(status: 'FAIL'),
)
.status ==
'PASS';
}
buildInspectionPayload(List<InspectionChecklistItem> checklist) {
assetDeliveryProvider.childFormModel.flagphysicalInspection = _isPass(checklist, 'Physical Inspection As Per Manufacturer Instructions');
assetDeliveryProvider.childFormModel.flagFunctionPerformance = _isPass(checklist, 'Functional Performance As Per Results Of Manufacturer Recommended Check List');
assetDeliveryProvider.childFormModel.flagGroundingResistance = _isPass(checklist, 'Grounding Resistance: Ω');
assetDeliveryProvider.childFormModel.flagChassiss = _isPass(checklist, 'Chassis Leakage Current: µA');
assetDeliveryProvider.childFormModel.flagLeadsLeakage = _isPass(checklist, 'Leads Leakage Current: µA');
assetDeliveryProvider.childFormModel.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 TechnicalInspectionAssetModel model,
}) {
if (checkList.isEmpty) {
checkList = AssetDeliveryUtils.inspectionChecklist;
}
for (final item in checkList) {
switch (item.value) {
case 'flagphysicalInspection':
item.status = _statusFromBool(model.flagphysicalInspection);
break;
case 'flagFunctionPerformance':
item.status = _statusFromBool(model.flagFunctionPerformance);
break;
case 'flagGroundingResistance':
item.status = _statusFromBool(model.flagGroundingResistance);
break;
case 'flagChassiss':
item.status = _statusFromBool(model.flagChassiss);
break;
case 'flagLeadsLeakage':
item.status = _statusFromBool(model.flagLeadsLeakage);
break;
case 'flagAlert':
item.status = _statusFromBool(model.flagAlert);
break;
}
}
}
String _statusFromBool(bool? value) {
return value == true
? 'PASS'
: value == false
? 'FAIL'
: '';
}
}

@ -0,0 +1,521 @@
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/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';
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_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 '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';
class UpdateParentAssetView extends StatefulWidget {
final TechnicalInspectionAssetModel assetModel;
final int? lineId;
final int? tableItemId;
final int? costCenterItemId;
UpdateParentAssetView({
Key? key,
required this.assetModel,
this.lineId,
this.tableItemId,
this.costCenterItemId,
}) : 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;
late TextEditingController assetNumberCtrl;
late TextEditingController serialNoCtrl;
late TextEditingController systemIdCtrl;
@override
void initState() {
siteProvider = Provider.of<SiteProvider>(context, listen: false);
assetDeliveryProvider = Provider.of<AssetDeliveryProvider>(context, listen: false);
assetNumberCtrl = TextEditingController();
serialNoCtrl = TextEditingController();
systemIdCtrl = TextEditingController();
WidgetsBinding.instance.addPostFrameCallback((_) {
populateForm();
});
super.initState();
}
void populateForm() async {
assetDeliveryProvider.isLoading = true;
assetDeliveryProvider.setParentModel(widget.assetModel);
if (widget.assetModel.site != null) {
await assetDeliveryProvider.getSiteData(siteId: widget.assetModel.site?.id, model: assetDeliveryProvider.parentFormModel);
} else {
if (siteProvider.items.isEmpty) {
await siteProvider.getData();
}
}
populateTextFields(model: assetDeliveryProvider.parentFormModel);
assetDeliveryProvider.isLoading = false;
}
void populateTextFields({required TechnicalInspectionAssetModel model}) {
assetNumberCtrl.text = model.assetNumber ?? '';
serialNoCtrl.text = model.serialNo ?? '';
systemIdCtrl.text = model.systemId ?? '';
setState(() {});
}
@override
void dispose() {
assetNumberCtrl.dispose();
serialNoCtrl.dispose();
systemIdCtrl.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: Consumer<AssetDeliveryProvider>(builder: (context, provider, child) {
return provider.isLoading
? const CircularProgressIndicator(color: AppColor.primary10).center
: Column(
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
inspectionDetailsForm(lineId: widget.lineId, provider: provider),
8.height,
const Divider().defaultStyle(context),
8.height,
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,
childList(
context: context,
provider: provider,
)
],
).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({required BuildContext context, required AssetDeliveryProvider provider}) {
final List<TechnicalInspectionAssetModel> assetDeliveryTableList = provider.parentFormModel.childAssetList ?? [];
return ListView.separated(
itemCount: assetDeliveryTableList.length,
separatorBuilder: (_, __) => 8.height,
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (listContext, itemIndex) {
TechnicalInspectionAssetModel model = assetDeliveryTableList[itemIndex];
return TechnicalInspectionAssetCard(
assetModel: model,
ischild: true,
editPress: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UpdateChildAssetView(
assetModel: model,
index: itemIndex,
)));
},
isViewOnly: false,
);
},
);
}
/// Need to make reuseable as used in parent and child both.
Widget inspectionDetailsForm({
int? lineId,
required AssetDeliveryProvider provider,
}) {
TechnicalInspectionAssetModel model = provider.parentFormModel;
return Column(
children: [
//Replace with simple container...
AppTextFormField(
labelText: 'Asset Number'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
controller: assetNumberCtrl,
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) {
TechnicalInspectionAssetModel? assetModel = await provider.getAssetDetails(assetId: data?.id);
if (assetModel != null) {
provider.setParentModel(assetModel);
if (assetModel.site != null) {
await provider.getSiteData(siteId: assetModel.site?.id, model: provider.parentFormModel);
}
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,
showShadow: false,
onSaved: (value) {
model.serialNo = value;
},
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
labelText: 'System ID'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
controller: systemIdCtrl,
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: provider.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: provider.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: provider.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: provider.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,
//Need to check whare the picker start for now start from start of year
from: DateTime(DateTime.now().year, 1, 1),
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 {
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;
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await assetDeliveryProvider.saveTechnicalInspectionAssetData(model: assetDeliveryProvider.parentFormModel).then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context, true);
}
});
}
Future<void> openInspectionChecklist(BuildContext context) async {
applyInspectionFlagsToChecklist(model: assetDeliveryProvider.parentFormModel);
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => InspectionCheckListView(
initialValues: checkList,
)));
if (result != null) {
checkList = result;
setState(() {});
}
buildInspectionPayload(checkList);
}
bool _isPass(List<InspectionChecklistItem> list, String title) {
return list
.firstWhere(
(e) => e.title == title,
orElse: () => InspectionChecklistItem(status: 'FAIL'),
)
.status ==
'PASS';
}
buildInspectionPayload(List<InspectionChecklistItem> checklist) {
assetDeliveryProvider.parentFormModel.flagphysicalInspection = _isPass(checklist, 'Physical Inspection As Per Manufacturer Instructions');
assetDeliveryProvider.parentFormModel.flagFunctionPerformance = _isPass(checklist, 'Functional Performance As Per Results Of Manufacturer Recommended Check List');
assetDeliveryProvider.parentFormModel.flagGroundingResistance = _isPass(checklist, 'Grounding Resistance: Ω');
assetDeliveryProvider.parentFormModel.flagChassiss = _isPass(checklist, 'Chassis Leakage Current: µA');
assetDeliveryProvider.parentFormModel.flagLeadsLeakage = _isPass(checklist, 'Leads Leakage Current: µA');
assetDeliveryProvider.parentFormModel.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 TechnicalInspectionAssetModel model,
}) {
if (checkList.isEmpty) {
checkList = AssetDeliveryUtils.inspectionChecklist;
}
for (final item in checkList) {
switch (item.value) {
case 'flagphysicalInspection':
item.status = _statusFromBool(model.flagphysicalInspection);
break;
case 'flagFunctionPerformance':
item.status = _statusFromBool(model.flagFunctionPerformance);
break;
case 'flagGroundingResistance':
item.status = _statusFromBool(model.flagGroundingResistance);
break;
case 'flagChassiss':
item.status = _statusFromBool(model.flagChassiss);
break;
case 'flagLeadsLeakage':
item.status = _statusFromBool(model.flagLeadsLeakage);
break;
case 'flagAlert':
item.status = _statusFromBool(model.flagAlert);
break;
}
}
}
String _statusFromBool(bool? value) {
return value == true ? 'PASS' : 'FAIL';
}
}

@ -0,0 +1,285 @@
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/lookup.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/delivery_line_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/technical_inspection_form_model.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/technical_inpection_cost_center_card_view.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/technical_inpection/technical_inspection_parent_asset_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/power_supply_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/single_item_drop_down_menu.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
import '../../../../new_views/common_widgets/app_filled_button.dart';
import '../../../../new_views/common_widgets/default_app_bar.dart';
class UpdateTechnicalInspectionLinesView extends StatefulWidget {
static const String id = "/technical-inspection-form";
final AssetDeliveryTableModel deliveryTableModel;
final AssetDeliveryDataModel? requestModel;
final DeliveryLineModel? deliveryLineModel;
UpdateTechnicalInspectionLinesView({
Key? key,
required this.deliveryTableModel,
this.requestModel,
this.deliveryLineModel,
}) : super(key: key);
@override
State<UpdateTechnicalInspectionLinesView> createState() => _UpdateTechnicalInspectionLinesViewState();
}
class _UpdateTechnicalInspectionLinesViewState extends State<UpdateTechnicalInspectionLinesView> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
TechnicalInspectionFormModel model = TechnicalInspectionFormModel();
AssetDeliveryProvider? assetDeliveryProvider;
bool isLoading = false;
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
getInitialData();
});
super.initState();
}
@override
void dispose() {
super.dispose();
}
void getInitialData() async {
assetDeliveryProvider = Provider.of<AssetDeliveryProvider>(context, listen: false);
isLoading = true;
setState(() {});
model = await assetDeliveryProvider!.getTechnicalInspectionData(
lineId: widget.deliveryLineModel?.id,
tableId: widget.deliveryTableModel.id,
);
isLoading = false;
setState(() {});
}
@override
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),
),
body: Form(
key: _formKey,
child: isLoading
? const CircularProgressIndicator(color: AppColor.primary10).center
: Column(
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
itemDetails(context: context, itemNo: widget.deliveryLineModel?.itemNumber, itemDescription: widget.deliveryLineModel?.itemDescription),
12.height,
inspectionDetailsForm(),
8.height,
const Divider().defaultStyle(context),
Text(
"Cost Centers",
style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10),
),
8.height,
costCenterList(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 costCenterList(BuildContext context) {
List<CostCenterModel> costCenterList = model.costCenters ?? [];
return costCenterList.isEmpty
? const NoDataFound().center
: ListView.separated(
itemCount: costCenterList.length,
separatorBuilder: (_, __) => 8.height,
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (listContext, itemIndex) {
return TechnicalInspectionCostCard(
costModel: costCenterList[itemIndex],
editPress: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TechnicalInspectionParentAssetListView(
costCenterId: costCenterList[itemIndex].id,
tableItemId: widget.deliveryTableModel.id,
lineId: widget.deliveryLineModel?.id,
)));
},
isViewOnly: false,
);
},
);
}
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),
),
8.height,
Text(
itemDescription ?? '-',
style: AppTextStyles.bodyText2.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral120),
),
],
);
}
Widget inspectionDetailsForm() {
return Column(
children: [
SingleItemDropDownMenu<Lookup, PowerSupplyTypeProvider>(
context: context,
height: 56.toScreenHeight,
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,
onSelect: (value) {
if (value != null) {
model.powerSupplyType = value;
setState(() {});
}
},
),
8.height,
AppTextFormField(
labelText: 'Power Reading'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
initialValue: model.powerReading != null ? model.powerReading.toString() : '',
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
textInputType: TextInputType.number,
showShadow: false,
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
onSaved: (value) {
model.powerReading = int.tryParse(value);
},
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
labelText: 'Max Current'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
initialValue: model.maxCurrent != null ? model.maxCurrent.toString() : '',
textAlign: TextAlign.center,
labelStyle: AppTextStyles.textFieldLabelStyle,
textInputType: TextInputType.number,
showShadow: false,
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
onSaved: (value) {
model.maxCurrent = int.tryParse(value);
},
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
labelText: 'Voltage'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
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);
},
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
style: Theme.of(context).textTheme.titleMedium,
),
8.height,
AppTextFormField(
labelText: 'Frequency'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
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);
},
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
style: Theme.of(context).textTheme.titleMedium,
),
],
);
}
void _saveTap() async {
if (!_formKey.currentState!.validate()) return;
_formKey.currentState!.save();
model.tableItemId = widget.deliveryTableModel.id;
model.lineId = widget.deliveryLineModel?.id;
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await assetDeliveryProvider?.saveTechnicalInspection(model: model).then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context);
}
});
}
}

@ -0,0 +1,736 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:test_sa/controllers/api_routes/api_manager.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/models/generic_attachment_model.dart';
import 'package:test_sa/models/module_permissions_model.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/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';
import 'package:test_sa/modules/asset_delivery_module/models/history_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/technical_inspection_asset_model.dart';
import 'package:test_sa/modules/asset_delivery_module/models/technical_inspection_form_model.dart';
import '../../../models/new_models/department.dart';
import '../../../models/new_models/site.dart';
import '../models/oracle_user_model.dart';
class AssetDeliveryProvider extends ChangeNotifier {
final pageItemNumber = 10;
final searchPageItemNumber = 10;
int pageNo = 1;
AssetDeliveryDataModel? assetDeliveryDataModel;
List<AssetDeliveryTableModel> assetDeliveryTableList = [];
List<DeliveryLineModel> deliveryLinesList = [];
ModulesPermissionsModel? modelPermission;
TechnicalInspectionAssetModel parentFormModel = TechnicalInspectionAssetModel();
TechnicalInspectionAssetModel childFormModel = TechnicalInspectionAssetModel();
void reset() {
pageNo = 1;
stateCode = null;
}
int? stateCode;
bool isDetailLoading = false;
bool nextPage = false;
bool isLoading = false;
bool isSiteLoading = false;
bool isDeliveryTableListLoading = false;
bool isDeliveryLineListLoading = false;
bool isChildAssetDataLoading = false;
void setParentModel(TechnicalInspectionAssetModel m) {
parentFormModel = m;
notifyListeners();
}
void setChildModel(TechnicalInspectionAssetModel m) {
childFormModel = m;
notifyListeners();
}
Future<AssetDeliveryDataModel?> getAssetDeliveryDetailById({int? requestId}) async {
try {
assetDeliveryDataModel = AssetDeliveryDataModel();
isLoading = true;
notifyListeners();
final response = await ApiManager.instance.get(URLs.getAssetDeliveryDetailsById + "?Id=$requestId");
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
assetDeliveryDataModel = AssetDeliveryDataModel.fromJson(json.decode(response.body)["data"]);
} else {
assetDeliveryDataModel = null;
}
isLoading = false;
notifyListeners();
return assetDeliveryDataModel;
} catch (e) {
log("getdelivery details [error] : $e");
isLoading = false;
assetDeliveryDataModel = null;
notifyListeners();
return assetDeliveryDataModel;
}
}
Future<void> getAssetDeliveryTableListById({int? requestId}) async {
try {
assetDeliveryTableList.clear();
isDeliveryTableListLoading = true;
notifyListeners();
final response = await ApiManager.instance.get(
"${URLs.getAssetDeliveryTableListById}?assetDeliveryExternalMasterId=$requestId",
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
final jsonData = json.decode(response.body)["data"];
if (jsonData is List) {
assetDeliveryTableList = jsonData.map((e) => AssetDeliveryTableModel.fromJson(e)).toList();
} else {
assetDeliveryTableList = [];
}
} else {
assetDeliveryTableList = [];
}
isDeliveryTableListLoading = false;
notifyListeners();
} catch (e) {
log("get Delivery Table details [error] : $e");
isDeliveryTableListLoading = false;
assetDeliveryTableList = [];
notifyListeners();
}
}
Future<void> getDeliveryLinesListById({int? itemId}) async {
try {
deliveryLinesList.clear();
isDeliveryLineListLoading = true;
notifyListeners();
final response = await ApiManager.instance.get(
"${URLs.getLineFromExternalPo}?assetDeliveryExternalDeliveryId=$itemId",
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
final jsonData = json.decode(response.body)["data"];
if (jsonData is List) {
deliveryLinesList = jsonData.map((e) => DeliveryLineModel.fromJson(e)).toList();
} else {
deliveryLinesList = [];
}
} else {
deliveryLinesList = [];
}
isDeliveryLineListLoading = false;
notifyListeners();
} catch (e) {
log("get Delivery Lines List [error] : $e");
isDeliveryLineListLoading = false;
deliveryLinesList = [];
notifyListeners();
}
}
Future<List<DeliveryLineModel>> getTechnicalInspectionLinesListById({int? itemId}) async {
List<DeliveryLineModel> linesList = [];
try {
isLoading = true;
notifyListeners();
final response = await ApiManager.instance.get(
"${URLs.getLineFromTechnicalInspection}?assetDeliveryExternalDeliveryId=$itemId",
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
final jsonData = json.decode(response.body)["data"];
if (jsonData is List) {
linesList = jsonData.map((e) => DeliveryLineModel.fromJson(e)).toList();
} else {
assetDeliveryTableList = [];
}
} else {
linesList = [];
}
isLoading = false;
notifyListeners();
return linesList;
} catch (e) {
log("get Delivery Lines List [error] : $e");
isLoading = false;
linesList = [];
notifyListeners();
return linesList;
}
}
Future<CostCenterSerialNoFormModel> getAssetDeliveryCostCenterListForLines({
int? lineId,
int? tableItemId,
}) async {
CostCenterSerialNoFormModel costCenterSerialNoFormModel = CostCenterSerialNoFormModel();
try {
isLoading = true;
notifyListeners();
final response = await ApiManager.instance.get(
"${URLs.getCostCenterAndSerialNoToLineDeliveryInspection}?AssetDeliveryExternalDetailId=$lineId&AssetDeliveryExternalDeliveryId=$tableItemId",
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
final jsonData = json.decode(response.body)["data"];
costCenterSerialNoFormModel = CostCenterSerialNoFormModel.fromJson(jsonData);
} else {
costCenterSerialNoFormModel.costCenters = [];
costCenterSerialNoFormModel.serialNos = [];
}
} catch (e) {
log("get Delivery Lines List [error] : $e");
costCenterSerialNoFormModel.costCenters = [];
costCenterSerialNoFormModel.serialNos = [];
} finally {
isLoading = false;
notifyListeners();
}
return costCenterSerialNoFormModel;
}
Future<bool> addNewDelivery({
int? requestId,
}) async {
isDeliveryTableListLoading = true;
Map<String, dynamic> payload = {
'assetDeliveryExternalMasterId': requestId,
};
try {
final response = await ApiManager.instance.post(URLs.addNewDelivery, body: payload);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
isDeliveryTableListLoading = false;
notifyListeners();
return true;
} else {
isDeliveryTableListLoading = false;
notifyListeners();
return false;
}
} catch (error) {
isDeliveryTableListLoading = false;
notifyListeners();
return false;
}
}
Future<bool> saveTechnicalInspection({
required TechnicalInspectionFormModel model,
}) async {
isLoading = true;
try {
final response = await ApiManager.instance.post(URLs.saveTechnicalInspection, body: model.toJson());
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
isLoading = false;
notifyListeners();
return true;
} else {
isLoading = false;
notifyListeners();
return false;
}
} catch (error) {
isLoading = false;
notifyListeners();
return false;
}
}
Future<bool> saveCostCenterToLineDeliveryInspection({
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.saveCostCenterAndSerialNoToLineDeliveryInspection, body: payload);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
isLoading = false;
notifyListeners();
return true;
} else {
isLoading = false;
notifyListeners();
return false;
}
} catch (error) {
isLoading = false;
notifyListeners();
return false;
}
}
Future<bool> changeDeliveryStatusToCancel({int? itemId}) async {
isLoading = true;
try {
final response = await ApiManager.instance.get(
"${URLs.changeDeliveryStatusToCancel}?assetDeliveryExternalDeliveryId=$itemId",
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
isLoading = false;
notifyListeners();
return true;
} else {
isLoading = false;
notifyListeners();
return false;
}
} catch (error) {
isLoading = false;
notifyListeners();
return false;
}
}
Future<bool> changeDeliveryStatus({
int? itemId,
Map<String, dynamic>? requestPayload,
}) async {
isLoading = true;
Map<String, dynamic> payload = {
'assetDeliveryExternalDeliveryId': itemId,
};
try {
final response = await ApiManager.instance.post(URLs.changeDeliveryStatus, body: requestPayload ?? payload);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
isLoading = false;
notifyListeners();
return true;
} else {
isLoading = false;
notifyListeners();
return false;
}
} catch (error) {
isLoading = false;
notifyListeners();
return false;
}
}
Future<DeliveryInspectionFormModel> getAssetDeliveryInspectionData({int? itemId}) async {
isLoading = true;
DeliveryInspectionFormModel deliveryInspectionFormModel = DeliveryInspectionFormModel();
try {
final response = await ApiManager.instance.get(
"${URLs.getAssetDeliveryInspectionData}?assetDeliveryExternalDeliveryId=$itemId",
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
deliveryInspectionFormModel = DeliveryInspectionFormModel.fromJson(json.decode(response.body)["data"]);
}
isLoading = false;
notifyListeners();
return deliveryInspectionFormModel;
} catch (e) {
log("getAssetDeliveryInspectionData details [error] : $e");
isLoading = false;
notifyListeners();
return deliveryInspectionFormModel;
}
}
Future<TechnicalInspectionFormModel> getTechnicalInspectionData({int? lineId, int? tableId}) async {
isLoading = true;
TechnicalInspectionFormModel technicalInspectionFormModel = TechnicalInspectionFormModel();
try {
final response = await ApiManager.instance.get(
"${URLs.getTechnicalInspectionByDetailId}?assetDeliveryExternalDetailId=$lineId&assetDeliveryExternalDeliveryId=$tableId",
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
technicalInspectionFormModel = TechnicalInspectionFormModel.fromJson(json.decode(response.body)["data"]);
}
isLoading = false;
notifyListeners();
return technicalInspectionFormModel;
} catch (e) {
log("getTechnicalInspectionByDetailId details [error] : $e");
isLoading = false;
notifyListeners();
return technicalInspectionFormModel;
}
}
Future<DeliveryInspectionFormModel> getDeliveryHistories({int? itemId}) async {
isLoading = true;
DeliveryInspectionFormModel deliveryInspectionFormModel = DeliveryInspectionFormModel();
try {
final response = await ApiManager.instance.get(
"${URLs.getAssetDeliveryInspectionData}?assetDeliveryExternalDeliveryId=$itemId",
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
deliveryInspectionFormModel = DeliveryInspectionFormModel.fromJson(json.decode(response.body)["data"]);
}
isLoading = false;
notifyListeners();
return deliveryInspectionFormModel;
} catch (e) {
log("getAssetDeliveryInspectionData details [error] : $e");
isLoading = false;
notifyListeners();
return deliveryInspectionFormModel;
}
}
Future<List<OracleUserModel>> getOracleUserAutoCompleteDetails({
String? query,
}) async {
try {
isLoading = true;
final response = await ApiManager.instance.post(URLs.getOracleUserList, body: {'employeeName': query});
List<OracleUserModel> list = [];
if (response.statusCode >= 200 && response.statusCode < 300) {
isLoading = false;
notifyListeners();
final data = json.decode(response.body)["data"];
list = (data as List).map((e) => OracleUserModel.fromJson(e)).toList();
}
isLoading = false;
notifyListeners();
return list;
} catch (error) {
log('error $error');
isLoading = false;
notifyListeners();
return [];
}
}
Future<List<GenericAttachmentModel>> getAttachments({
num? tableItemId,
num? attachmentType,
}) async {
try {
isLoading = true;
final response = await ApiManager.instance.get("${URLs.getAttachments}?attachmentDocTypeValue=$attachmentType&assetDeliveryExternalDeliveryId=$tableItemId");
List<GenericAttachmentModel> list = [];
if (response.statusCode >= 200 && response.statusCode < 300) {
isLoading = false;
notifyListeners();
final data = json.decode(response.body)["data"];
list = (data as List).map((e) => GenericAttachmentModel.fromJson(e)).toList();
}
isLoading = false;
notifyListeners();
return list;
} catch (error) {
log('error $error');
isLoading = false;
notifyListeners();
return [];
}
}
Future<List<TechnicalInspectionAssetModel>> getParentAssetList({int? costCenterId, int? tableItemId, int? lineId}) async {
List<TechnicalInspectionAssetModel> parentAssetList = [];
try {
isLoading = true;
notifyListeners();
final response = await ApiManager.instance.get(
"${URLs.getTechnicalInspectionParentData}?assetDeliveryExternalDeliveryId=$tableItemId&assetDeliveryExternalDetailId=$lineId&costCenterId=$costCenterId",
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
final jsonData = json.decode(response.body)["data"];
if (jsonData is List) {
parentAssetList = jsonData.map((e) => TechnicalInspectionAssetModel.fromJson(e)).toList();
}
} else {
parentAssetList = [];
}
isLoading = false;
notifyListeners();
return parentAssetList;
} catch (e) {
log("get parent List [error] : $e");
isLoading = false;
parentAssetList = [];
notifyListeners();
return parentAssetList;
}
}
Future<TechnicalInspectionAssetModel> getSiteData({
required num? siteId,
required TechnicalInspectionAssetModel model,
}) async {
isSiteLoading = true;
notifyListeners();
try {
final response = await ApiManager.instance.get(
"${URLs.getSiteAutoCompleteWithoutConditionSites}?id=$siteId",
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
final List? siteData = json.decode(response.body)["data"];
if (siteData != null && siteData.isNotEmpty) {
model.site = Site.fromJson(siteData.first);
}
model.building = model.site?.buildings?.firstWhere(
(e) => e.value == model.building?.value,
orElse: () => Building(),
);
model.floor = model.building?.floors?.firstWhere(
(e) => e.value == model.floor?.value,
orElse: () => Floor(),
);
model.department = model.floor?.departments?.firstWhere(
(e) => e.departmentName == model.department?.departmentName,
orElse: () => Department(),
);
model.room = model.department?.rooms?.firstWhere(
(e) => e.value == model.room?.value,
orElse: () => Rooms(),
);
}
isSiteLoading = false;
notifyListeners();
return model;
} catch (e) {
isSiteLoading = false;
stateCode = -1;
notifyListeners();
return model;
}
}
Future<TechnicalInspectionAssetModel?> getAssetDetails({required num? assetId}) async {
isLoading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.getAssetDetailsByAssetId + "?AssetDeliveryInternalDetailAssetId=$assetId");
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
final data = json.decode(response.body)["data"];
TechnicalInspectionAssetModel assetModel = TechnicalInspectionAssetModel.fromJson(data);
isLoading = false;
notifyListeners();
return assetModel;
} else {
return null;
}
} catch (error) {
isLoading = false;
stateCode = -1;
notifyListeners();
return null;
}
}
Future<bool> saveTechnicalInspectionAssetData({
required TechnicalInspectionAssetModel model,
}) async {
isLoading = true;
try {
final response = await ApiManager.instance.post(URLs.saveTechnicalInspectionAsset, body: model.toJson());
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
isLoading = false;
notifyListeners();
return true;
} else {
isLoading = false;
notifyListeners();
return false;
}
} catch (error) {
isLoading = false;
notifyListeners();
return false;
}
}
Future<bool> saveAttachment({
required Map<String, dynamic> payload,
}) async {
isLoading = true;
try {
final response = await ApiManager.instance.post(URLs.saveAttachments, body: payload);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
isLoading = false;
notifyListeners();
return true;
} else {
isLoading = false;
notifyListeners();
return false;
}
} catch (error) {
isLoading = false;
notifyListeners();
return false;
}
}
Future<List<AssetDeliveryHistoryModel>> getHistory({
num? tableItemId,
}) async {
try {
isLoading = true;
notifyListeners();
final response = await ApiManager.instance.get("${URLs.getDeliveryHistories}?assetDeliveryExternalDeliveryId=$tableItemId");
List<AssetDeliveryHistoryModel> list = [];
if (response.statusCode >= 200 && response.statusCode < 300) {
isLoading = false;
notifyListeners();
final data = json.decode(response.body)["data"];
list = (data as List).map((e) => AssetDeliveryHistoryModel.fromJson(e)).toList();
}
isLoading = false;
notifyListeners();
return list;
} catch (error) {
log('error $error');
isLoading = false;
notifyListeners();
return [];
}
}
//End user
Future<List<CostCenterModel>> getEndUserCostCenterList({int? tableItemId}) async {
List<CostCenterModel> costCenterList = [];
try {
isLoading = true;
notifyListeners();
final response = await ApiManager.instance.get(
"${URLs.getEndUserAcceptanceCostCenter}?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();
}
} else {
costCenterList = [];
}
isLoading = false;
notifyListeners();
return costCenterList;
} catch (e) {
log("get costCenter for End user [error] : $e");
isLoading = false;
costCenterList = [];
notifyListeners();
return costCenterList;
}
}
Future<List<EndUserAssetDetailsModel>> getEndUserAssetList({int? tableItemId, String? costCenterName}) async {
List<EndUserAssetDetailsModel> assetList = [];
try {
isLoading = true;
notifyListeners();
final response = await ApiManager.instance.get(
"${URLs.getCostCenterAssetList}?assetDeliveryExternalDeliveryId=$tableItemId&costCenterName=$costCenterName",
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
final jsonData = json.decode(response.body)["data"];
if (jsonData is List) {
assetList = jsonData.map((e) => EndUserAssetDetailsModel.fromJson(e)).toList();
}
} else {
assetList = [];
}
isLoading = false;
notifyListeners();
return assetList;
} catch (e) {
log("get End User AssetList [error] : $e");
isLoading = false;
assetList = [];
notifyListeners();
return assetList;
}
}
Future<bool> saveEndUserFormData({
required CostCenterModel model,
}) async {
isLoading = true;
try {
final response = await ApiManager.instance.post(URLs.saveEndUserFormData, body: model.toEndUserAcceptanceFormJson());
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
isLoading = false;
notifyListeners();
return true;
} else {
isLoading = false;
notifyListeners();
return false;
}
} catch (error) {
isLoading = false;
notifyListeners();
return false;
}
}
Future<bool> saveEndUserAssetDataData({
required EndUserAssetDetailsModel model,
}) async {
isLoading = true;
try {
final response = await ApiManager.instance.post(URLs.saveEndUserAssetData, body: model.toJson());
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
isLoading = false;
notifyListeners();
return true;
} else {
isLoading = false;
notifyListeners();
return false;
}
} catch (error) {
isLoading = false;
notifyListeners();
return 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;
}
}
}

@ -0,0 +1,45 @@
import 'dart:convert';
import 'dart:developer';
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/modules/loan_module/models/medical_department_model.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
class CostCenterProvider extends LoadingListNotifier<Lookup> {
@override
Future<void> getData({int? id}) async {
if (loading) return;
loading = true;
notifyListeners();
try {
final response = await ApiManager.instance.get(
URLs.getCostCenterToPo + "?assetDeliveryExternalMasterId=$id",
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
final List list = json.decode(response.body)['data'];
final Set<String> seenNames = {};
int index = 0;
items =
list.where((e) => e is Map && e['name'] != null && e['name'].toString().trim().isNotEmpty).map((e) => e['name'].toString().trim()).where((name) => seenNames.add(name)).map<Lookup>((name) {
final lookup = Lookup(
id: index,
value: index + 1,
name: name,
);
index++;
return lookup;
}).toList();
}
} catch (e, s) {
log('getData error', error: e, stackTrace: s);
stateCode = -1;
} finally {
loading = false;
notifyListeners();
}
}
}

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

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

@ -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 PowerSupplyTypeProvider 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.powerSupplyTypeLookUp);
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,85 +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(() {
CMDetailProvider provider = Provider.of<CMDetailProvider>(context, listen: false);
Navigator.push(context, MaterialPageRoute(builder: (context) => CreateLoanRequestPage(workOrderId: widget.requestId, workOrder: provider.currentWorkOrder!.data)));
});
}
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 {

@ -385,7 +385,7 @@ class ServiceRequestBottomSheet {
],
);
},
).toShimmer(isShow: snapshot.loading,context: context);
).toShimmer(isShow: snapshot.loading, context: context);
});
}
@ -645,7 +645,7 @@ class ServiceRequestBottomSheet {
borderRadius: BorderRadius.circular(14), // Circular border radius
),
// color: AppColor.neutral80,
color: AppColor.background(context),
color: AppColor.background(context),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start, // Align items at the top
children: [
@ -1191,30 +1191,26 @@ class ServiceRequestBottomSheet {
10.height,
StatefulBuilder(
builder: (context, setState) {
return Column(
children: [
ESignature(
title: '',
oldSignature: '',
newSignature: newSignature,
backgroundColor: AppColor.neutral100,
showShadow: false,
onChange: (signature) {
if (signature.isEmpty) return;
setState(() {
newSignature = signature;
nurseSignature = "${DateTime.now().toIso8601String()}.png|${base64Encode(signature)}";
});
},
onSaved: (signature) {
if (signature == null || signature.isEmpty) return;
setState(() {
newSignature = signature;
nurseSignature = "${DateTime.now().toIso8601String()}.png|${base64Encode(signature)}";
});
},
),
],
return ESignature(
title: '',
oldSignature: '',
newSignature: newSignature,
backgroundColor: AppColor.neutral100,
showShadow: false,
onChange: (signature) {
if (signature.isEmpty) return;
setState(() {
newSignature = signature;
nurseSignature = "${DateTime.now().toIso8601String()}.png|${base64Encode(signature)}";
});
},
onSaved: (signature) {
if (signature == null || signature.isEmpty) return;
setState(() {
newSignature = signature;
nurseSignature = "${DateTime.now().toIso8601String()}.png|${base64Encode(signature)}";
});
},
);
},
),

@ -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() {
@ -74,24 +79,31 @@ class _ChatWidgetState extends State<ChatWidget> {
@override
Widget build(BuildContext context) {
return widget.isShow
? 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);
? 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) {

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

@ -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,39 +1,33 @@
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/site.dart';
import 'package:test_sa/models/new_models/mapped_sites.dart';
import 'package:test_sa/models/new_models/task_request/task_type_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/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';
@ -275,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";
@ -287,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,
@ -301,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(() {});
},
),

@ -1,49 +1,37 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/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/site.dart';
import 'package:test_sa/models/new_models/task_request/task_type_model.dart';
import 'package:test_sa/models/new_models/work_order_detail_model.dart';
import 'package:test_sa/models/service_request/supplier_details.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/modules/medical_gas_inspection/models/medical_gas_data_model.dart';
import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_form_model.dart';
import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_supplier_model.dart';
import 'package:test_sa/modules/medical_gas_inspection/providers/item_type_provider.dart';
import 'package:test_sa/modules/medical_gas_inspection/providers/medical_gas_inspection_provider.dart';
import 'package:test_sa/modules/medical_gas_inspection/providers/medical_gas_supplier_provider.dart';
import 'package:test_sa/modules/medical_gas_inspection/providers/order_type_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_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 '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/work_order/vendor_provider.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
import '../../../../../../new_views/common_widgets/default_app_bar.dart';
import 'medical_gas_inspection_detail_page.dart';
class CreateMedicalGasRequestPage extends StatefulWidget {
static const String id = "/create-medical-gas";
final MedicalGasDataModel? dataModel;
CreateMedicalGasRequestPage({Key? key}) : super(key: key);
CreateMedicalGasRequestPage({Key? key, this.dataModel}) : super(key: key);
@override
_CreateMedicalGasRequestPageState createState() => _CreateMedicalGasRequestPageState();
@ -54,20 +42,40 @@ class _CreateMedicalGasRequestPageState extends State<CreateMedicalGasRequestPag
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
TaskType? selectedType;
final LoanFormModel _loanFormModel = LoanFormModel();
TextEditingController commentController = TextEditingController();
SupplierDetails? supplier;
// List<Lookup> loanTypes = [];
MedicalGasFormModel _medicalGasFormModel = MedicalGasFormModel();
MedicalGasSupplierModel? medicalGasInfo;
late MedicalGasInspectionProvider medicalGasInspectionProvider;
bool isGasInfoLoading = false;
@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)];
// Provider.of<MedicalGasSupplierProvider>(context, listen: false).reset();
medicalGasInspectionProvider = Provider.of<MedicalGasInspectionProvider>(context, listen: false);
_populateFormData();
}
_populateFormData() {
if (widget.dataModel != null) {
MedicalGasDataModel dataModel = widget.dataModel!;
_medicalGasFormModel = MedicalGasFormModel(
id: dataModel.id,
orderType: Lookup(id: dataModel.requestOrderTypeId, name: dataModel.requestOrderType),
itemType: Lookup(id: dataModel.itemTypeId, name: dataModel.itemType),
site: Site(id: dataModel.siteId, custName: dataModel.site),
supplier: MedicalGasSupplierModel(id: dataModel.supplierId, supplier: dataModel.supplier),
);
List<AttributeInfo> attributeInfo = [];
dataModel.requestDetailDtos.forEach((v) {
attributeInfo.add(AttributeInfo.fromJson(v.toJson()));
});
medicalGasInfo = MedicalGasSupplierModel(
supplierContact: dataModel.contact,
supplierEmail: dataModel.email,
attributeInfo: attributeInfo,
);
setState(() {});
}
}
@override
@ -85,42 +93,44 @@ class _CreateMedicalGasRequestPageState extends State<CreateMedicalGasRequestPag
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
SingleItemDropDownMenu<Lookup, NullableLoadingProvider>(
SingleItemDropDownMenu<Lookup, MedicalGasOrderTypeProvider>(
context: context,
title: "Order Type",
validator: (value) {
if (value == null) return "Mandatory";
return null;
},
enabled: false,
staticData: [],
initialValue: null,
initialValue: _medicalGasFormModel.orderType,
showShadow: false,
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
onSelect: (value) {},
onSelect: (value) {
_medicalGasFormModel.orderType = value;
},
),
8.height,
SingleItemDropDownMenu<Lookup, NullableLoadingProvider>(
SingleItemDropDownMenu<Lookup, MedicalGasItemTypeProvider>(
context: context,
title: "Item Type",
validator: (value) {
if (value == null) return "Mandatory";
return null;
},
enabled: false,
staticData: [],
initialValue: null,
initialValue: _medicalGasFormModel.itemType,
showShadow: false,
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
onSelect: (value) {},
onSelect: (value) {
_medicalGasFormModel.itemType = value;
setState(() {});
setGasInfo();
},
),
8.height,
SingleItemDropDownMenu<Site, SiteProvider>(
context: context,
title: context.translation.site,
initialValue: _loanFormModel.site,
initialValue: _medicalGasFormModel.site,
showShadow: false,
validator: (value) {
if (value == null) return "Please select a site";
@ -129,71 +139,72 @@ class _CreateMedicalGasRequestPageState extends State<CreateMedicalGasRequestPag
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
onSelect: (value) {
_loanFormModel.site = value;
_medicalGasFormModel.site = value;
setState(() {});
setGasInfo();
},
),
8.height,
SingleItemDropDownMenu<MedicalDepartmentModel, MedicalDepartmentProvider>(
context: context,
title: context.translation.department,
showShadow: false,
validator: (value) {
if (value == null) return "Please select a department";
return null;
},
showAsBottomSheet: true,
initialValue: _loanFormModel.department,
requestById: context.userProvider.user?.clientId,
backgroundColor: AppColor.fieldBgColor(context),
onSelect: (value) {
_loanFormModel.department = value;
setState(() {});
},
),
8.height,
SingleItemDropDownMenu<SupplierDetails, VendorProvider>(
// 8.height,
// SingleItemDropDownMenu<MedicalDepartmentModel, MedicalDepartmentProvider>(
// context: context,
// title: context.translation.department,
// showShadow: false,
// validator: (value) {
// if (value == null) return "Please select a department";
// return null;
// },
// showAsBottomSheet: true,
// initialValue: _medicalGasFormModel.department,
// requestById: context.userProvider.user?.clientId,
// backgroundColor: AppColor.fieldBgColor(context),
// onSelect: (value) {
// _medicalGasFormModel.department = value;
// setState(() {});
// },
// ),
// 8.height,
SingleItemDropDownMenu<MedicalGasSupplierModel, MedicalGasSupplierProvider>(
context: context,
title: context.translation.supplier,
backgroundColor: AppColor.fieldBgColor(context),
initialValue: supplier,
initialValue: _medicalGasFormModel.supplier,
showAsBottomSheet: true,
backgroundColor: AppColor.fieldBgColor(context),
showShadow: false,
showCancel: true,
onSelect: (_supplier) {
supplier = _supplier;
setState(() {});
},
),
8.height,
if (supplier != null) ...[
'Name: ${supplier?.name ?? "-"}'.addTranslation.bodyText(context).custom(color: AppColor.black10),
'Email: ${supplier?.email ?? "-"}'.addTranslation.bodyText(context).custom(color: AppColor.black10),
'Contact: ${supplier?.contact ?? "-"}'.addTranslation.bodyText(context).custom(color: AppColor.black10),
],
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;
setState(() {});
onSelect: (value) {
setState(() {
_medicalGasFormModel.supplier = value;
});
setGasInfo();
},
),
if (medicalGasInfo != null) medicalGasInfoWidget(),
],
).toShadowContainer(context, borderRadius: 20),
).expanded,
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
label: context.translation.submitRequest,
onPressed: _submit,
// buttonColor: AppColor.primary10,
child: Row(
children: [
AppFilledButton(
buttonColor: AppColor.primary10,
label: 'Save'.addTranslation,
onPressed: () {
_update(isSubmitted: false);
},
// buttonColor: AppColor.primary10,
).expanded,
8.width,
AppFilledButton(
buttonColor: AppColor.primary10,
label: 'Submit'.addTranslation,
onPressed: () {
_update(isSubmitted: true);
},
// buttonColor: AppColor.primary10,
).expanded,
],
),
),
],
@ -202,30 +213,87 @@ class _CreateMedicalGasRequestPageState extends State<CreateMedicalGasRequestPag
);
}
Future<void> _submit() async {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => MedicalGasInspectionDetailPage(inspectionId: 0)));
return;
FocusScope.of(context).unfocus();
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
void setGasInfo() async {
if (_medicalGasFormModel.itemType != null && _medicalGasFormModel.site != null && _medicalGasFormModel.supplier != null) {
setState(() {
isGasInfoLoading = true;
});
medicalGasInfo =
await medicalGasInspectionProvider.getMedicalGasInfo(itemTypeId: _medicalGasFormModel.itemType!.id, supplierId: _medicalGasFormModel.supplier!.id, siteId: _medicalGasFormModel.site!.id);
setState(() {
isGasInfoLoading = false;
});
}
}
_loanFormModel.loanAttachment = [];
for (var item in attachments) {
String fileName = CMRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? '';
_loanFormModel.loanAttachment?.add(LoanAttachments(id: 0, attachmentName: fileName, loanId: 0));
}
Utils.showLoading(context);
LoanProvider loanProvider = Provider.of<LoanProvider>(context, listen: false);
Map<String, dynamic> body = _loanFormModel.toJson();
body["id"] = 0;
body["LoanTypeId"] = 0;
body["requestorUserID"] = context.userProvider.user!.userID;
Widget medicalGasInfoWidget() {
return isGasInfoLoading
? const CircularProgressIndicator(color: AppColor.primary10).center
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
8.height,
'Supplier Contact: ${medicalGasInfo?.supplierContact ?? "-"}'.bodyText(context).custom(color: AppColor.black10),
8.height,
'Supplier Email: ${medicalGasInfo?.supplierEmail ?? "-"}'.bodyText(context).custom(color: AppColor.black10),
8.height,
'Items'.addTranslation.bodyText(context).custom(color: AppColor.textColor(context)),
8.height,
// MedicalGasItemsListView(
// itemsList: medicalGasInfo?.attributeInfo ?? [],
// ),
if (medicalGasInfo!.attributeInfo!.isNotEmpty)
ListView.separated(
separatorBuilder: (_, __) => 12.height,
itemCount: medicalGasInfo!.attributeInfo!.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
AttributeInfo model = medicalGasInfo!.attributeInfo![index];
return AppTextFormField(
labelText: model.itemName,
hintText: 'Enter Quantity'.addTranslation,
backgroundColor: AppColor.fieldBgColor(context),
initialValue: model.requestedQuantity != null ? model.requestedQuantity.toString() : '',
textAlign: TextAlign.center,
enable: true,
textInputType: TextInputType.number,
labelStyle: AppTextStyles.textFieldLabelStyle,
showShadow: false,
onChange: (value) {
if (value.isNotEmpty) {
model.requestedQuantity = int.tryParse(value);
}
},
style: Theme.of(context).textTheme.titleMedium,
);
},
)
],
);
}
bool isSuccess = await loanProvider.addLoanRequest(body);
Utils.hideLoading(context);
if (isSuccess) {
Navigator.pop(context);
Future<void> _update({bool isSubmitted = false}) async {
if (medicalGasInfo != null && medicalGasInfo!.attributeInfo!.isNotEmpty) {
_medicalGasFormModel.gasRequestDetail = [];
for (var item in medicalGasInfo!.attributeInfo!) {
_medicalGasFormModel.gasRequestDetail?.add(MedicalGasFormDetailModel(
id: widget.dataModel != null ? item.id : 0,
medicalGasAttributeId: item.medicalGasAttributeId ?? item.id,
requestedQuantity: item.requestedQuantity,
));
}
}
_medicalGasFormModel.isSubmitted = isSubmitted;
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await medicalGasInspectionProvider.saveMedicalGas(model: _medicalGasFormModel).then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context, true);
} else {
//This should be handled from backend.
Fluttertoast.showToast(msg: 'No gas available for selected site', toastLength: Toast.LENGTH_LONG);
}
});
}
}

@ -1,5 +1,3 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
@ -9,105 +7,135 @@ 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/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/modules/internal_audit_module/models/internal_audit_attachment_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/system_internal_audit_data_model.dart';
import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/update_equipment_internal_audit_page.dart';
import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/update_system_internal_audit_page.dart';
import 'package:test_sa/modules/internal_audit_module/provider/internal_audit_provider.dart';
import 'package:test_sa/modules/loan_module/models/loan_attachment_model.dart';
import 'package:test_sa/modules/loan_module/models/loan_request_model.dart';
import 'package:test_sa/modules/loan_module/provider/loan_provider.dart';
import 'package:test_sa/modules/medical_gas_inspection/create_medical_gas_request_page.dart';
import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_data_model.dart';
import 'package:test_sa/modules/medical_gas_inspection/providers/medical_gas_inspection_provider.dart';
import 'package:test_sa/modules/medical_gas_inspection/update_delivery_notes_page.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';
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';
import 'update_medical_gas_inspection_page.dart';
class MedicalGasInspectionDetailPage extends StatelessWidget {
class MedicalGasInspectionDetailPage extends StatefulWidget {
static const String id = "/medical-gas-inspection-detail-page";
final int inspectionId;
final String? prority;
MedicalGasInspectionDetailPage({Key? key, required this.inspectionId}) : super(key: key);
MedicalGasInspectionDetailPage({Key? key, required this.inspectionId, this.prority}) : super(key: key);
@override
State<MedicalGasInspectionDetailPage> createState() => _MedicalGasInspectionDetailPageState();
}
class _MedicalGasInspectionDetailPageState extends State<MedicalGasInspectionDetailPage> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_loadData();
});
}
MedicalGasDataModel? model;
bool _isLoading = true;
Future<void> _loadData() async {
final result = await context.read<MedicalGasInspectionProvider>().getMedicalGasById(requestId: widget.inspectionId);
setState(() {
model = result;
_isLoading = false;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const DefaultAppBar(title: "Request Details"),
body: SafeArea(
child: FutureBuilder<LoanRequestModel?>(
future: Provider.of<LoanProvider>(context, listen: false).getLoanById(0),
builder: (BuildContext context, AsyncSnapshot<LoanRequestModel?> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) return const CircularProgressIndicator(color: AppColor.primary10).center;
// if (snapshot.data == null) return const NoDataFound().center;
//
List<LoanAttachmentModel> allAttachments = snapshot.data?.loanAttachments ?? [];
return Column(
children: [
ListView(
padding: const EdgeInsets.all(16),
child: _isLoading
? SizedBox.expand(child: const CircularProgressIndicator(color: AppColor.primary10).center)
: model == null
? const NoDataFound().center
: Column(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
ListView(
padding: const EdgeInsets.all(16),
children: [
// Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// StatusLabel(
// label: snapshot.data!.loanStatusName!,
// textColor: AppColor.getRequestStatusTextColorByName(context, snapshot.data!.loanStatusName!),
// backgroundColor: AppColor.getRequestStatusColorByName(context, snapshot.data!.loanStatusName!),
// ),
// 1.width.expanded,
// Text(
// snapshot.data!.createdDate?.toServiceRequestCardFormat ?? "-",
// textAlign: TextAlign.end,
// style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50),
// ),
// ],
// ),
Text("Request Details", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)),
'${context.translation.requestNo}: ${"model?.requestNo" ?? '-'}'.bodyText(context),
'Order Type: ${"model?.Order Type" ?? '-'}'.bodyText(context),
'Item Type: ${"model?.Item Type" ?? '-'}'.bodyText(context),
'Site: ${"model?.Site" ?? '-'}'.bodyText(context),
'Cylinders: ${"model?.Cylinders" ?? '-'}'.bodyText(context),
'LOX: ${"model?.LOX" ?? '-'}'.bodyText(context),
'Supplier Name: ${"model?.Supplier Name" ?? '-'}'.bodyText(context),
'Supplier Contact: ${"model?.Supplier Contact" ?? '-'}'.bodyText(context),
'Supplier Email: ${"model?.Supplier Email" ?? '-'}'.bodyText(context),
if (allAttachments.isNotEmpty) ...[
const Divider().defaultStyle(context),
Text(
"Attachments".addTranslation,
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
FilesList(images: allAttachments.map((e) => URLs.getFileUrl(e.attachmentName ?? '') ?? '').toList() ?? []),
],
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//Need to return the status form api backend
Row(
children: [
if (widget.prority != null) ...[
StatusLabel(
label: widget.prority,
textColor: AppColor.getRequestStatusTextColorByName(context, widget.prority),
backgroundColor: AppColor.getRequestStatusColorByName(context, widget.prority),
),
8.width,
],
StatusLabel(
label: model?.requestStatus?.name,
textColor: AppColor.getHistoryLogStatusTextColorByName(model?.requestStatus?.name ?? ''),
backgroundColor: AppColor.getHistoryLogStatusColorByName(model?.requestStatus?.name ?? ''),
),
1.width.expanded,
Text(model!.requestedDate!.toServiceRequestCardFormat,
textAlign: TextAlign.end, style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50))
],
),
8.height,
Text("Request Details", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)),
'${context.translation.requestNo}: ${model?.requestNumber ?? '-'}'.bodyText(context),
'Requested by: ${model?.requestedBy ?? '-'}'.bodyText(context),
if (model?.lastModifiedBy != null)
'Last Modified by: ${model?.lastModifiedBy ?? '-'}'.bodyText(context),
'Order Type: ${model?.requestOrderType ?? '-'}'.bodyText(context),
'Item Type: ${model?.itemType ?? '-'}'.bodyText(context),
'Site: ${model?.site ?? '-'}'.bodyText(context),
'Supplier Name: ${model?.supplier ?? '-'}'.bodyText(context),
'Supplier Contact: ${model?.contact ?? '-'}'.bodyText(context),
'Supplier Email: ${model?.email ?? '-'}'.bodyText(context),
if (model!.deliveryNoteAttachmentDto.isNotEmpty) ...[
const Divider().defaultStyle(context),
Text(
"Attachments".addTranslation,
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
FilesList(images: model!.deliveryNoteAttachmentDto.map((e) => URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []),
],
],
).toShadowContainer(context, padding: 12),
],
).toShadowContainer(context, padding: 12),
).expanded,
if (model?.requestStatus?.value != null && model!.requestStatus!.value! < 3)
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
label: model?.isSubmitted == true ? "Upload Delivery Note" : 'Update',
onPressed: () async {
if (model?.isSubmitted == true) {
bool? isRefresh = await Navigator.of(context).push(MaterialPageRoute(builder: (_) => UpdateDeliveryNotes(dataModel: model)));
if (isRefresh == true) {
_loadData();
}
return;
}
bool? isRefresh = await Navigator.of(context).push(MaterialPageRoute(
builder: (_) => CreateMedicalGasRequestPage(
dataModel: model,
)));
if (isRefresh == true) {
_loadData();
}
}),
),
],
).expanded,
if (context.userProvider.isEngineer)
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
label: "Upload Delivery Note",
onPressed: () async {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => UpdateMedicalGasInspectionPage(inspectionId: 0)));
}),
),
],
);
}),
),
));
}
@ -124,20 +152,4 @@ class MedicalGasInspectionDetailPage extends StatelessWidget {
),
);
}
Widget checklistWidget(BuildContext context, {required String value}) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
value: true,
activeColor: AppColor.neutral120,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: const VisualDensity(horizontal: -4, vertical: -3),
onChanged: (value) {},
),
value.bodyText(context),
],
);
}
}

@ -0,0 +1,130 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/all_requests_and_count_model.dart';
import 'package:test_sa/models/new_models/dashboard_detail.dart';
import 'package:test_sa/modules/medical_gas_inspection/medical_gas_inspection_detail_page.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/views/widgets/requests/request_status.dart';
class MedicalGasInspectionItemView extends StatelessWidget {
final Data? requestData;
final RequestsDetails? requestDetails;
final bool showShadow;
const MedicalGasInspectionItemView({Key? key, this.requestData, this.requestDetails, this.showShadow = true}) : super(key: key);
@override
Widget build(BuildContext context) {
if (requestData != null) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (requestData!.priorityName != null) ...[
StatusLabel(
label: requestData!.priorityName!,
textColor: AppColor.getRequestStatusTextColorByName(context, requestData!.priorityName!),
backgroundColor: AppColor.getRequestStatusColorByName(context, requestData!.priorityName!),
),
8.width,
],
StatusLabel(
label: requestData!.statusName!,
textColor: AppColor.getHistoryLogStatusTextColorByName(requestData!.statusName!),
backgroundColor: AppColor.getHistoryLogStatusColorByName(requestData!.statusName!),
),
1.width.expanded,
Text(requestData!.transactionDate!.toServiceRequestCardFormat,
textAlign: TextAlign.end, style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50)),
],
),
(requestData?.typeTransaction ?? 'Medical Gas Request').heading5(context),
infoWidget(label: 'Request Number'.addTranslation, value: requestData?.requestNo ?? '-', context: context),
infoWidget(label: 'Request Type'.addTranslation, value: requestData?.requestType ?? '-', context: context),
// infoWidget(label: 'Site'.addTranslation, value: requestData?.si ?? '-', context: context),
// infoWidget(label: 'Gas Type'.addTranslation, value: requestDetails?.gasType ?? '-', context: context),
8.height,
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
context.translation.viewDetails,
style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)),
),
4.width,
Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14)
],
),
],
).toShadowContainer(context, withShadow: showShadow).onPress(() async {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => MedicalGasInspectionDetailPage(
inspectionId: requestData!.id!,
prority: requestData?.priorityName,
)));
});
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (requestDetails!.priority != null) ...[
StatusLabel(
label: requestDetails!.priority!,
textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails!.priority!),
backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails!.priority!),
),
8.width,
],
StatusLabel(
label: requestDetails!.status!,
textColor: AppColor.getHistoryLogStatusTextColorByName(requestDetails!.status!),
backgroundColor: AppColor.getHistoryLogStatusColorByName(requestDetails!.status!),
),
1.width.expanded,
Text(requestDetails!.date!.toServiceRequestCardFormat, textAlign: TextAlign.end, style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50)),
],
),
(requestDetails?.nameOfType ?? 'Medical Gas Request').heading5(context),
infoWidget(label: 'Request Number'.addTranslation, value: requestDetails?.requestNo ?? '-', context: context),
infoWidget(label: 'Request Type'.addTranslation, value: requestDetails?.requestType ?? '-', context: context),
infoWidget(label: 'Site'.addTranslation, value: requestDetails?.site ?? '-', context: context),
8.height,
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
context.translation.viewDetails,
style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)),
),
4.width,
Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14)
],
),
],
).toShadowContainer(context, withShadow: showShadow).onPress(() async {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => MedicalGasInspectionDetailPage(
inspectionId: requestDetails!.id!,
prority: requestDetails?.priority,
)));
});
}
Widget infoWidget({required String label, String? value, required BuildContext context}) {
if (value != null && value.isNotEmpty) {
return '$label: $value'.bodyText(context);
}
return const SizedBox();
}
}

@ -0,0 +1,94 @@
// 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/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/models/cost_center_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';
// import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_supplier_model.dart';
// import 'package:test_sa/new_views/app_style/app_color.dart';
// import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
// import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart';
// import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart';
// import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
// import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart';
//
// class MedicalGasItemsListView extends StatelessWidget {
// final List<AttributeInfo> itemsList;
//
// MedicalGasItemsListView({
// super.key,
// required this.itemsList,
// });
//
// late AssetDeliveryProvider assetDeliveryProvider;
//
// @override
// Widget build(BuildContext context) {
// // return ListView.separated(
// // separatorBuilder: (_, __) => 12.height,
// // itemCount: itemsList.length,
// // shrinkWrap: true,
// // itemBuilder: (context, index) {
// // AttributeInfo model = itemsList[index];
// // return AppTextFormField(
// // labelText: model.itemName,
// // hintText: 'Enter Quantity'.addTranslation,
// // backgroundColor: AppColor.fieldBgColor(context),
// // initialValue: model.requestedQuantity != null ? model.requestedQuantity.toString() : '',
// // textAlign: TextAlign.center,
// // enable: true,
// // textInputType: TextInputType.number,
// // labelStyle: AppTextStyles.textFieldLabelStyle,
// // showShadow: false,
// // onChange: (value) {
// // if (value.isNotEmpty) {
// // model.requestedQuantity = int.tryParse(value);
// // }
// // },
// // style: Theme.of(context).textTheme.titleMedium,
// // );
// // },
// // );
// }
// }
//
// // class ItemCard extends StatelessWidget {
// // final AttributeInfo model;
// // final int index;
// //
// // const ItemCard({
// // super.key,
// // required this.model,
// // required this.index,
// // });
// //
// // @override
// // Widget build(BuildContext context) {
// // return AppTextFormField(
// // labelText: model.itemName,
// // hintText: 'Enter Quantity'.addTranslation,
// // backgroundColor: AppColor.fieldBgColor(context),
// // initialValue: model.requestedQuantity != null ? model.requestedQuantity.toString() : '',
// // textAlign: TextAlign.center,
// // enable: true,
// // textInputType: TextInputType.number,
// // labelStyle: AppTextStyles.textFieldLabelStyle,
// // showShadow: false,
// // onChange: (value) {
// // if (value.isNotEmpty) {
// // model.requestedQuantity = int.tryParse(value);
// // }
// // },
// // style: Theme.of(context).textTheme.titleMedium,
// // );
// // }
// // }

@ -0,0 +1,92 @@
import 'package:test_sa/models/generic_attachment_model.dart';
import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_data_model.dart';
class DeliveryNotesFormModel {
int? id;
DateTime? deliveredDate;
String? deliveryNoteNumber;
bool? isPressureChecked;
bool? isInspectedGasLeak;
bool? isSubmitted;
List<GasDeliveredQuantityModel> gasDeliveredQuantityDtos = [];
List<MedicalGasRequestDetailModel> deliveredItemList = [];
List<GenericAttachmentModel> attachments = [];
DeliveryNotesFormModel({
this.id,
this.deliveredDate,
this.deliveryNoteNumber,
this.isPressureChecked,
this.isInspectedGasLeak,
this.isSubmitted,
List<GasDeliveredQuantityModel>? gasDeliveredQuantityDtos,
List<MedicalGasRequestDetailModel>? deliveredItemList,
List<GenericAttachmentModel>? attachments,
}) {
this.gasDeliveredQuantityDtos = gasDeliveredQuantityDtos ?? [];
this.deliveredItemList = deliveredItemList ?? [];
this.attachments = attachments ?? [];
}
DeliveryNotesFormModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
deliveredDate = json['deliveredDate'] != null ? DateTime.parse(json['deliveredDate']) : null;
deliveryNoteNumber = json['deliveryNoteNumber'];
isPressureChecked = json['isPressureChecked'];
isInspectedGasLeak = json['isInspectedGasLeak'];
isSubmitted = json['isSubmitted'];
if (json['gasDeliveredQuantityDtos'] != null) {
gasDeliveredQuantityDtos = [];
json['gasDeliveredQuantityDtos'].forEach((v) {
gasDeliveredQuantityDtos.add(GasDeliveredQuantityModel.fromJson(v));
});
}
if (json['cUMedicalGasDeliveryNoteAttachmentDtos'] != null) {
attachments = [];
json['cUMedicalGasDeliveryNoteAttachmentDtos'].forEach((v) {
attachments.add(GenericAttachmentModel.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
return {
'id': id,
'deliveredDate': deliveredDate?.toIso8601String(),
'deliveryNoteNumber': deliveryNoteNumber,
'isPressureChecked': isPressureChecked,
'isInspectedGasLeak': isInspectedGasLeak,
'isSubmitted': isSubmitted,
'gasDeliveredQuantityDtos': gasDeliveredQuantityDtos.map((e) => e.toJson()).toList(),
'cUMedicalGasDeliveryNoteAttachmentDtos': attachments.map((e) => e.toJson()).toList(),
};
}
}
class GasDeliveredQuantityModel {
int? id;
num? medicalGasAttributeId;
num? deliveredQuantity;
GasDeliveredQuantityModel({
this.id,
this.medicalGasAttributeId,
this.deliveredQuantity,
});
GasDeliveredQuantityModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
medicalGasAttributeId = json['medicalGasAttributeId'];
deliveredQuantity = json['deliveredQuantity'];
}
Map<String, dynamic> toJson() {
return {
'id': id,
'medicalGasAttributeId': medicalGasAttributeId,
'deliveredQuantity': deliveredQuantity,
};
}
}

@ -0,0 +1,174 @@
import 'package:test_sa/models/base.dart';
import 'package:test_sa/models/generic_attachment_model.dart';
import 'package:test_sa/models/lookup.dart';
class MedicalGasDataModel {
int? id;
String? requestNumber;
String? requestedBy;
String? requestedDate;
int? requestOrderTypeId;
String? requestOrderType;
int? itemTypeId;
String? itemType;
int? siteId;
String? site;
int? supplierId;
String? supplier;
String? contact;
String? email;
bool? isSubmitted;
String? requestOrderStatus;
bool? isPressureChecked;
bool? isInspectedGasLeak;
Lookup? requestStatus;
DateTime? deliveredDate;
String? deliveryNote;
String? lastModifiedBy;
/// non-nullable lists
List<MedicalGasRequestDetailModel> requestDetailDtos = [];
List<GenericAttachmentModel> deliveryNoteAttachmentDto = [];
MedicalGasDataModel({
this.id,
this.requestNumber,
this.requestedBy,
this.requestedDate,
this.requestOrderTypeId,
this.requestOrderType,
this.itemTypeId,
this.itemType,
this.siteId,
this.site,
this.requestStatus,
this.supplierId,
this.supplier,
this.contact,
this.email,
this.isSubmitted,
this.requestOrderStatus,
this.isPressureChecked,
this.isInspectedGasLeak,
this.deliveredDate,
this.deliveryNote,
this.lastModifiedBy,
List<MedicalGasRequestDetailModel>? requestDetailDtos,
List<GenericAttachmentModel>? deliveryNoteAttachmentDto,
}) {
this.requestDetailDtos = requestDetailDtos ?? [];
this.deliveryNoteAttachmentDto = deliveryNoteAttachmentDto ?? [];
}
MedicalGasDataModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
requestNumber = json['requestNumber'];
requestedBy = json['requestedBy'];
requestedDate = json['requestedDate'];
requestOrderTypeId = json['requestOrderTypeId'];
requestOrderType = json['requestOrderType'];
itemTypeId = json['itemTypeId'];
itemType = json['itemType'];
siteId = json['siteId'];
site = json['site'];
supplierId = json['supplierId'];
supplier = json['supplier'];
contact = json['contact'];
email = json['email'];
isSubmitted = json['isSubmitted'];
deliveryNote = json['deliveryNote'];
lastModifiedBy = json['lastModifiedBy'];
requestOrderStatus = json['requestOrderStatus'];
isPressureChecked = json['isPressureChecked'];
isInspectedGasLeak = json['isInspectedGasLeak'];
deliveredDate = json['deliveredDate'] != null ? DateTime.parse(json['deliveredDate']) : null;
requestStatus = json['requestOrderStatusDto'] != null ? Lookup.fromJson(json['requestOrderStatusDto']) : null;
if (json['requestDetailDtos'] != null) {
requestDetailDtos = [];
json['requestDetailDtos'].forEach((v) {
requestDetailDtos.add(MedicalGasRequestDetailModel.fromJson(v));
});
}
if (json['deliveryNoteAttachmentDto'] != null) {
deliveryNoteAttachmentDto = [];
json['deliveryNoteAttachmentDto'].forEach((v) {
deliveryNoteAttachmentDto.add(GenericAttachmentModel.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
return {
'id': id,
'requestNumber': requestNumber,
'requestedBy': requestedBy,
'requestedDate': requestedDate,
'requestOrderTypeId': requestOrderTypeId,
'requestOrderType': requestOrderType,
'itemTypeId': itemTypeId,
'itemType': itemType,
'siteId': siteId,
'site': site,
'supplierId': supplierId,
'deliveryNote': deliveryNote,
'lastModifiedBy': lastModifiedBy,
'supplier': supplier,
'contact': contact,
'email': email,
'isSubmitted': isSubmitted,
'requestOrderStatus': requestOrderStatus,
'isPressureChecked': isPressureChecked,
'isInspectedGasLeak': isInspectedGasLeak,
'deliveredDate': deliveredDate?.toIso8601String(),
'requestDetailDtos': requestDetailDtos.map((e) => e.toJson()).toList(),
'requestOrderStatusDto': requestStatus?.toJson(),
'deliveryNoteAttachmentDto': deliveryNoteAttachmentDto.map((e) => e.toJson()).toList(),
};
}
}
class MedicalGasRequestDetailModel extends Base {
int? id;
int? medicalGasAttributeId;
String? itemName;
String? oracleName;
String? oracleCode;
num? requestedQuantity;
num? deliveredQuantity;
MedicalGasRequestDetailModel({
this.id,
this.medicalGasAttributeId,
this.itemName,
this.oracleName,
this.oracleCode,
this.requestedQuantity,
this.deliveredQuantity,
}) : super(identifier: id?.toString(), name: itemName);
MedicalGasRequestDetailModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
medicalGasAttributeId = json['medicalGasAttributeId'];
itemName = json['itemName'];
identifier = id.toString();
name = itemName;
oracleName = json['oracleName'];
oracleCode = json['oracleCode'];
requestedQuantity = json['requestedQuantity'];
deliveredQuantity = json['deliveredQuantity'];
}
Map<String, dynamic> toJson() {
return {
'id': id,
'medicalGasAttributeId': medicalGasAttributeId,
'itemName': itemName,
'oracleName': oracleName,
'oracleCode': oracleCode,
'requestedQuantity': requestedQuantity,
'deliveredQuantity': deliveredQuantity,
};
}
}

@ -0,0 +1,87 @@
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/models/new_models/site.dart';
import 'package:test_sa/modules/loan_module/models/medical_department_model.dart';
import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_supplier_model.dart';
class MedicalGasFormModel {
int? id;
int? requestOrderTypeId;
Lookup? orderType;
Lookup? itemType;
int? itemTypeId;
int? siteId;
Site? site;
MedicalDepartmentModel? department;
MedicalGasSupplierModel? supplier;
int? supplierId;
bool? isSubmitted;
List<MedicalGasFormDetailModel>? gasRequestDetail;
MedicalGasFormModel({
this.id,
this.requestOrderTypeId,
this.itemTypeId,
this.siteId,
this.supplierId,
this.supplier,
this.isSubmitted,
this.orderType,
this.itemType,
this.site,
this.department,
this.gasRequestDetail,
});
MedicalGasFormModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
requestOrderTypeId = json['requestOrderTypeId'];
itemTypeId = json['itemTypeId'];
siteId = json['siteId'];
supplierId = json['supplierId'];
isSubmitted = json['isSubmitted'];
if (json['gasRequestDetailDtos'] != null) {
gasRequestDetail = [];
json['gasRequestDetailDtos'].forEach((v) {
gasRequestDetail!.add(MedicalGasFormDetailModel.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
return {
'id': id ?? 0,
'requestOrderTypeId': orderType?.id,
'itemTypeId': itemType?.id,
'siteId': site?.id,
'supplierId': supplier?.id,
'isSubmitted': isSubmitted,
'gasRequestDetailDtos': gasRequestDetail?.map((e) => e.toJson()).toList(),
};
}
}
class MedicalGasFormDetailModel {
int? id;
int? medicalGasAttributeId;
num? requestedQuantity;
MedicalGasFormDetailModel({
this.id,
this.medicalGasAttributeId,
this.requestedQuantity,
});
MedicalGasFormDetailModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
medicalGasAttributeId = json['medicalGasAttributeId'];
requestedQuantity = json['requestedQuantity'];
}
Map<String, dynamic> toJson() {
return {
'id': id,
'medicalGasAttributeId': medicalGasAttributeId,
'requestedQuantity': requestedQuantity??0,
};
}
}

@ -0,0 +1,72 @@
import 'package:test_sa/models/base.dart';
class MedicalGasSupplierModel extends Base {
int? id;
String? supplier;
String? supplierEmail;
String? supplierContact;
List<AttributeInfo>? attributeInfo;
MedicalGasSupplierModel({this.id, this.supplier, this.supplierContact, this.supplierEmail, this.attributeInfo}) : super(identifier: id?.toString() ?? '', name: supplier);
MedicalGasSupplierModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
identifier = id?.toString() ?? '';
supplier = json['supplier'];
name=supplier;
supplierEmail = json['supplierEmail'];
supplierContact = json['supplierContact'];
if (json['attributeInfoDtos'] != null) {
attributeInfo = [];
json['attributeInfoDtos'].forEach((v) {
attributeInfo!.add(AttributeInfo.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
return {
'id': id,
'supplier': supplier,
'supplierEmail': supplierEmail,
'supplierContact': supplierContact,
'attributeInfoDtos': attributeInfo?.map((e) => e.toJson()).toList(),
};
}
}
class AttributeInfo {
int? id;
int? medicalGasAttributeId;
num? requestedQuantity;
String? itemName;
String? oracleName;
String? oracleCode;
AttributeInfo({this.id, this.medicalGasAttributeId, this.requestedQuantity, this.itemName, this.oracleName, this.oracleCode});
AttributeInfo.fromJson(Map<String, dynamic> json) {
id = json['id'];
medicalGasAttributeId = json['medicalGasAttributeId'];
requestedQuantity = json['requestedQuantity'];
itemName = json['itemName'];
oracleName = json['oracleName'];
oracleCode = json['oracleCode'];
}
Map<String, dynamic> toJson() {
//{
// "id": 0,
// "medicalGasAttributeId": 0,
// "requestedQuantity": 0
// }
return {
'id': id,
'medicalGasAttributeId': medicalGasAttributeId,
'requestedQuantity': requestedQuantity,
// 'oracleCode': oracleCode,
};
}
}

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

@ -0,0 +1,119 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:test_sa/controllers/api_routes/api_manager.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/modules/medical_gas_inspection/models/delivery_notes_form_model.dart';
import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_data_model.dart';
import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_form_model.dart';
import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_supplier_model.dart';
class MedicalGasInspectionProvider extends ChangeNotifier {
final pageItemNumber = 10;
final searchPageItemNumber = 10;
int pageNo = 1;
void reset() {
pageNo = 1;
stateCode = null;
}
int? stateCode;
bool isDetailLoading = false;
bool nextPage = false;
bool isLoading = false;
bool isGasInfoLoading = false;
Future<MedicalGasDataModel?> getMedicalGasById({int? requestId}) async {
MedicalGasDataModel? assetDeliveryDataModel = MedicalGasDataModel();
try {
isLoading = true;
notifyListeners();
final response = await ApiManager.instance.get(URLs.getMedicalGasById + "?medicalGasRequestId=$requestId");
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
assetDeliveryDataModel = MedicalGasDataModel.fromJson(json.decode(response.body)["data"]);
} else {
assetDeliveryDataModel = null;
}
isLoading = false;
notifyListeners();
return assetDeliveryDataModel;
} catch (e) {
log("getMedicalGas details [error] : $e");
isLoading = false;
assetDeliveryDataModel = null;
notifyListeners();
return assetDeliveryDataModel;
}
}
Future<bool> saveMedicalGas({
required MedicalGasFormModel model,
}) async {
isLoading = true;
try {
final response = await ApiManager.instance.post(URLs.addMedicalGasRequest, body: model.toJson());
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
isLoading = false;
notifyListeners();
return true;
} else {
isLoading = false;
notifyListeners();
return false;
}
} catch (error) {
isLoading = false;
notifyListeners();
return false;
}
}
Future<MedicalGasSupplierModel?> getMedicalGasInfo({int? supplierId, int? itemTypeId, num? siteId}) async {
MedicalGasSupplierModel? supplierInfo;
Map<String, dynamic> payload = {"supplierId": supplierId, "itemTypeId": itemTypeId, "siteId": siteId};
try {
isGasInfoLoading = true;
notifyListeners();
final response = await ApiManager.instance.post("${URLs.getMedicalGasInfoBySupplier}", body: payload);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
final jsonData = json.decode(response.body)["data"];
supplierInfo = MedicalGasSupplierModel.fromJson(jsonData);
}
isGasInfoLoading = false;
notifyListeners();
return supplierInfo;
} catch (e) {
log("get Supplier List [error] : $e");
isGasInfoLoading = false;
notifyListeners();
return supplierInfo;
}
}
Future<bool> updatedDeliveryNotes({
required DeliveryNotesFormModel model,
}) async {
isLoading = true;
try {
final response = await ApiManager.instance.post(URLs.updateDeliveryNote, body: model.toJson());
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
isLoading = false;
notifyListeners();
return true;
} else {
isLoading = false;
notifyListeners();
return false;
}
} catch (error) {
isLoading = false;
notifyListeners();
return false;
}
}
}

@ -0,0 +1,34 @@
import 'dart:convert';
import 'dart:developer';
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/modules/medical_gas_inspection/models/medical_gas_supplier_model.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
class MedicalGasSupplierProvider extends LoadingListNotifier<MedicalGasSupplierModel> {
@override
Future getData({int? id}) async {
loading = true;
notifyListeners();
Response response;
try {
response = await ApiManager.instance.get(URLs.getMedicalGasSupplierList);
} catch (error) {
loading = false;
stateCode = -1;
notifyListeners();
return -1;
}
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
List listJson = json.decode(response.body)["data"];
items = listJson.map((supplier) => MedicalGasSupplierModel.fromJson(supplier)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
}
}

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

@ -0,0 +1,384 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/all_requests_provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/modules/medical_gas_inspection/models/delivery_notes_form_model.dart';
import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_data_model.dart';
import 'package:test_sa/modules/medical_gas_inspection/providers/medical_gas_inspection_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_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/multiple_item_drop_down_menu.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
import 'package:test_sa/views/widgets/date_and_time/date_picker.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
class UpdateDeliveryNotes extends StatefulWidget {
final MedicalGasDataModel? dataModel;
UpdateDeliveryNotes({Key? key, required this.dataModel}) : super(key: key);
@override
_UpdateDeliveryNotesState createState() {
return _UpdateDeliveryNotesState();
}
}
class _UpdateDeliveryNotesState extends State<UpdateDeliveryNotes> {
DeliveryNotesFormModel formModel = DeliveryNotesFormModel();
late MedicalGasInspectionProvider medicalGasInspectionProvider;
bool pressureTestAcknowledgement = false;
bool leakTestAcknowledgement = false;
// List<MedicalGasRequestDetailModel> selectedItemList = [];
@override
void initState() {
super.initState();
medicalGasInspectionProvider = Provider.of<MedicalGasInspectionProvider>(context, listen: false);
_populateFormData();
}
@override
void dispose() {
super.dispose();
}
_populateFormData() {
if (widget.dataModel != null) {
MedicalGasDataModel dataModel = widget.dataModel!;
formModel = DeliveryNotesFormModel(
id: dataModel.id,
deliveredItemList: dataModel.requestDetailDtos,
isInspectedGasLeak: dataModel.isInspectedGasLeak,
isPressureChecked: dataModel.isPressureChecked,
deliveredDate: dataModel.deliveredDate,
attachments: dataModel.deliveryNoteAttachmentDto,
isSubmitted: dataModel.isSubmitted,
deliveryNoteNumber: dataModel.deliveryNote);
//Need to Confirm this condition
// selectedItemList = dataModel.requestDetailDtos.where((item) => item.deliveredQuantity != null && item.deliveredQuantity! > 0).toList();
setState(() {});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const DefaultAppBar(
title: "Upload Delivery Note",
// onWillPopScope: () {
// _update(context: context);
// },
),
body: SafeArea(
child: Column(
children: [
ListView(
padding: const EdgeInsets.all(16),
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ADatePicker(
label: "Delivered Date",
hideShadow: true,
backgroundColor: AppColor.fieldBgColor(context),
from: DateTime.now().subtract(const Duration(days: 365 * 3)),
date: formModel.deliveredDate,
formatDateWithTime: true,
onDatePicker: (selectedDate) {
showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
).then((selectedTime) {
if (selectedTime != null) {
formModel.deliveredDate = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute);
setState(() {});
}
});
},
),
16.height,
AppTextFormField(
initialValue: formModel.deliveryNoteNumber,
labelText: "Delivery Notes",
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
floatingLabelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
textInputType: TextInputType.multiline,
makeMultiLinesNull: true,
onChange: (value) {
formModel.deliveryNoteNumber = value;
},
),
16.height,
//Older design code
// MultipleItemDropDownMenu<MedicalGasRequestDetailModel, NullableLoadingProvider>(
// context: context,
// showAsBottomSheet: true,
// backgroundColor: AppColor.neutral100,
// showShadow: false,
// showCancel: true,
// requestById: context.userProvider.user?.clientId,
// title: 'Delivered Items'.addTranslation,
// staticData: formModel.deliveredItemList,
// initialValue: selectedItemList,
// onSelect: (value) {
// if ((value ?? []).isNotEmpty) {
// WidgetsBinding.instance.addPostFrameCallback((_) {
// if (!mounted) return;
// setState(() {
// selectedItemList = value ?? [];
// });
// });
// }
// },
// ),
// if (selectedItemList.isNotEmpty) ...[
// 8.height,
// ListView.separated(
// separatorBuilder: (_, __) => 12.height,
// itemCount: selectedItemList.length,
// shrinkWrap: true,
// physics: const NeverScrollableScrollPhysics(),
// itemBuilder: (context, index) {
// MedicalGasRequestDetailModel model = selectedItemList[index];
// return AppTextFormField(
// labelText: '${model.name} (Req Qty =${model.requestedQuantity})',
// hintText: 'Enter Delivered Quantity'.addTranslation,
// backgroundColor: AppColor.fieldBgColor(context),
// initialValue: model.deliveredQuantity != null ? model.deliveredQuantity?.toInt().toString() : '',
// textAlign: TextAlign.center,
// enable: true,
// textInputType: TextInputType.number,
// labelStyle: AppTextStyles.textFieldLabelStyle,
// showShadow: false,
// onChange: (value) {
// if (value.isNotEmpty) {
// model.deliveredQuantity = double.tryParse(value);
// }
// },
// style: Theme.of(context).textTheme.titleMedium,
// );
// },
// )
// ],
if (formModel.deliveredItemList.isNotEmpty) ...[
_ItemRowLayout(
left: 'Items'.addTranslation.bodyText(context).custom(color: AppColor.textColor(context)),
right: 'Delivered Qty'.addTranslation.bodyText(context).custom(color: AppColor.textColor(context)),
),
8.height,
ListView.separated(
separatorBuilder: (_, __) => 12.height,
itemCount: formModel.deliveredItemList.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
MedicalGasRequestDetailModel model = formModel.deliveredItemList[index];
return itemQuantityFieldWidget(index: index, model: model, context: context);
},
)
],
16.height,
AttachmentPicker(
label: 'Upload Attachment',
attachment: formModel.attachments,
buttonColor: AppColor.primary10,
// showAsListView: true,
onlyImages: false,
buttonIcon: 'attachment_icon'.toSvgAsset(
color: AppColor.primary10,
),
),
16.height,
//should create a reusable widget.
Row(
children: [
Checkbox(
value: formModel.isPressureChecked,
visualDensity: const VisualDensity(horizontal: -4.0, vertical: -4.0),
activeColor: AppColor.blueStatus(context),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onChanged: (value) {
setState(() {
formModel.isPressureChecked = value!;
});
}),
12.width,
"I Acknowledge that the received medical gases cylinders have been tested for pressure."
.addTranslation
.bodyText(context)
.custom(color: context.isDark ? AppColor.primary50 : AppColor.neutral120)
.expanded,
],
),
8.height,
Row(
// mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Checkbox(
value: formModel.isInspectedGasLeak,
visualDensity: const VisualDensity(horizontal: -4.0, vertical: -4.0),
activeColor: AppColor.blueStatus(context),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onChanged: (value) {
setState(() {
formModel.isInspectedGasLeak = value!;
});
}),
12.width,
"I Acknowledge that the received medical gases cylinders have been inspected for leak test."
.addTranslation
.bodyText(context)
.custom(color: context.isDark ? AppColor.primary50 : AppColor.neutral120)
.expanded,
],
),
],
).toShadowContainer(context, padding: 12),
],
).expanded,
FooterActionButton.footerContainer(
context: context,
child: Row(
children: [
AppFilledButton(
buttonColor: AppColor.primary10,
label: 'Save'.addTranslation,
onPressed: () {
_update(context: context);
},
// buttonColor: AppColor.primary10,
).expanded,
8.width,
AppFilledButton(
buttonColor: AppColor.primary10,
label: 'Submit'.addTranslation,
onPressed: () {
_update(context: context, isSubmitted: true);
},
// buttonColor: AppColor.primary10,
).expanded,
],
),
),
],
),
));
// .handlePopScope(
// cxt: context,
// onSave: () {
// _update(context: context);
// });
}
void _update({required BuildContext context, bool isSubmitted = false}) async {
formModel.gasDeliveredQuantityDtos = [];
formModel.isSubmitted = isSubmitted;
for (var item in formModel.deliveredItemList) {
formModel.gasDeliveredQuantityDtos.add(GasDeliveredQuantityModel(
id: widget.dataModel != null ? item.id : 0,
medicalGasAttributeId: item.medicalGasAttributeId ?? item.id,
deliveredQuantity: item.deliveredQuantity,
));
}
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await medicalGasInspectionProvider.updatedDeliveryNotes(model: formModel).then((status) async {
Navigator.pop(context);
if (status) {
Navigator.pop(context, true);
if (formModel.isSubmitted == true) {
AllRequestsProvider allRequestsProvider = Provider.of<AllRequestsProvider>(context, listen: false);
allRequestsProvider.reset();
await allRequestsProvider.getAllRequests(context, typeTransaction: 14);
}
}
});
}
Widget itemQuantityFieldWidget({
required int index,
required MedicalGasRequestDetailModel model,
required BuildContext context,
}) {
final border = OutlineInputBorder(
borderRadius: BorderRadius.circular(4),
borderSide: const BorderSide(color: AppColor.white70, width: 0),
);
return _ItemRowLayout(
left: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
model.itemName!.bodyText2(context).custom(
color: context.isDark ? Colors.white : AppColor.white936,
fontWeight: FontWeight.w500,
),
'Req Qty ${model.requestedQuantity}'.bodyText2(context).custom(
color: context.isDark ? Colors.white : AppColor.white936,
fontWeight: FontWeight.w500,
),
],
),
right: TextFormField(
keyboardType: TextInputType.number,
initialValue: model.deliveredQuantity != null ? model.deliveredQuantity?.toStringAsFixed(0) : '',
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 5.toScreenWidth),
filled: true,
fillColor: AppColor.fieldBgColor(context),
constraints: BoxConstraints(
maxHeight: 30.toScreenHeight,
),
border: border,
enabledBorder: border,
focusedBorder: border,
),
onChanged: (value) {
model.deliveredQuantity = int.tryParse(value);
},
),
);
}
}
class _ItemRowLayout extends StatelessWidget {
final Widget left;
final Widget right;
const _ItemRowLayout({
required this.left,
required this.right,
});
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(child: left), // Items column
SizedBox(
width: 100.toScreenWidth, // Delivered Qty column
child: right,
),
],
);
}
}

@ -69,7 +69,7 @@ class _RecurrentWorkOrderViewState extends State<RecurrentWorkOrderView> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
RecurrentTaskInfoWidget(model: requestProvider.recurrentWoData),
requestProvider.recurrentWoData!.planRecurrentMedicalTaskRooms!.isNotEmpty
requestProvider.recurrentWoData!.planRecurrentMedicalTaskRooms != null && requestProvider.recurrentWoData!.planRecurrentMedicalTaskRooms!.isNotEmpty
? Column(
children: [
8.height,

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

@ -223,11 +223,11 @@ class _DeviceTransferDetailsState extends State<DeviceTransferDetails> {
if (context.userProvider.isEngineer || context.userProvider.isNurse)
ChatWidget(
moduleId: widget.moduleId,
isShow: _model!.senderMachineStatusValue! != 4,
isShow: context.settingProvider.isUserFlowMedical && _model!.senderMachineStatusValue! != 4,
isReadOnly: _model!.senderMachineStatusValue! != 1,
requestId: widget.model.id!.toInt(),
assigneeEmployeeNumber: _model!.senderAssignedEmployeeNumber,
myLoginUserID: context.userProvider.user!.username!,
myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!,
contactEmployeeINumber: _model!.assetTransferContactPersons!.first.employeeNumber,
),
if ((_userProvider!.user?.type == UsersTypes.engineer))
@ -284,11 +284,11 @@ class _DeviceTransferDetailsState extends State<DeviceTransferDetails> {
if (context.userProvider.isEngineer || context.userProvider.isNurse)
ChatWidget(
moduleId: widget.moduleId,
isShow: (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue!) != 0,
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!.username!,
myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!,
contactEmployeeINumber: _deviceTransfer.assetTransferContactPersons!.first.employeeNumber,
),
if ((_userProvider!.user?.type == UsersTypes.engineer))

@ -68,11 +68,11 @@ class _GasRefillDetailsPageState extends State<GasRefillDetailsPage> {
return ChatWidget(
moduleId: widget.moduleId,
isShow: _gasRefillModel.status!.value! != 0,
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!.username!,
myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!,
contactEmployeeINumber: _gasRefillModel.gasRefillContactPerson!.first.employeeCode!,
);
})

@ -75,16 +75,20 @@ class _TaskRequestDetailsViewState extends State<TaskRequestDetailsView> {
selector: (_, myModel) => myModel.taskRequestModel, // Selects only the userName
builder: (_, _taskReqModel, __) {
if (_taskReqModel == null) return const SizedBox();
return ChatWidget(
moduleId: widget.moduleId,
isShow: _taskReqModel.taskJobStatus!.value! != 1,
isReadOnly: _taskReqModel.taskJobStatus!.value! != 2,
requestId: widget.taskId.toInt(),
assigneeEmployeeNumber: _taskReqModel.assignedEngineer?.employeeId!,
myLoginUserID: context.userProvider.user!.username!,
contactEmployeeINumber: _taskReqModel.taskJobContactPersons!.first.user?.employeeId,
);
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();
})
],
),

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.dart';
@ -119,6 +121,8 @@ class AppColor {
return orange60;
case "high priority":
return white10;
case "emergency":
return white10;
case "yes":
return white10;
case "low priority":
@ -134,6 +138,8 @@ class AppColor {
switch ((status).toLowerCase()) {
case "in progress":
return white936;
case "waiting for delivery":
return white936;
case "new":
return primary63;
case "open":
@ -193,6 +199,14 @@ class AppColor {
return red30;
case "returned":
return red30;
case "waiting for delivery":
return orange30;
case "waiting for medical gas specialist review":
return orange30;
case "reviewed":
return orange30;
case "returned for correction":
return orange30;
default:
return Colors.white;
}
@ -210,6 +224,8 @@ class AppColor {
return greenStatus(context);
case "high priority":
return redStatus(context);
case "emergency":
return redStatus(context);
case "yes":
return redStatus(context);
case "low priority":

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

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save