Merge branch 'refs/heads/design_3.0_internal_audit_module' into design_3.0_cx_module

# Conflicts:
#	lib/controllers/api_routes/urls.dart
#	lib/main.dart
#	pubspec.lock
#	pubspec.yaml
design_3.0_cx_module
Sikander Saleem 4 months ago
commit f6f5048cc8

@ -0,0 +1,3 @@
<svg width="17" height="20" viewBox="0 0 17 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.82353 1.94999C3.67937 1.94999 1.94118 3.69608 1.94118 5.84998V11.2125C1.94118 14.7126 4.76573 17.55 8.25 17.55C11.7343 17.55 14.5588 14.7126 14.5588 11.2125V9.75003C14.5588 9.21155 14.9934 8.77503 15.5294 8.77503C16.0655 8.77503 16.5 9.21155 16.5 9.75003V11.2125C16.5 15.7896 12.8063 19.5 8.25 19.5C3.69365 19.5 0 15.7896 0 11.2125V5.84998C0 2.61913 2.60728 0 5.82353 0C9.03978 0 11.6471 2.61913 11.6471 5.84998V11.2125C11.6471 13.0971 10.1261 14.625 8.25 14.625C6.37386 14.625 4.85294 13.0971 4.85294 11.2125V7.31248C4.85294 6.774 5.28749 6.33748 5.82353 6.33748C6.35957 6.33748 6.79412 6.774 6.79412 7.31248V11.2125C6.79412 12.0202 7.44594 12.675 8.25 12.675C9.05406 12.675 9.70588 12.0202 9.70588 11.2125V5.84998C9.70588 3.69608 7.96769 1.94999 5.82353 1.94999Z" fill="#3DA5E5"/>
</svg>

After

Width:  |  Height:  |  Size: 938 B

@ -0,0 +1,3 @@
<svg width="14" height="8" viewBox="0 0 14 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.35372 0.305286C1.4756 0.46666 1.83951 0.948403 2.05624 1.22611C2.49031 1.78233 3.08342 2.52142 3.72323 3.25831C4.36627 3.99893 5.04178 4.72022 5.64189 5.25046C5.94281 5.51634 6.20714 5.71838 6.42527 5.84987C6.63042 5.97355 6.75161 5.99927 6.75161 5.99927C6.75161 5.99927 6.86923 5.97355 7.07437 5.84988C7.2925 5.71838 7.55684 5.51635 7.85775 5.25047C8.45786 4.72022 9.13337 3.99892 9.77642 3.2583C10.4162 2.5214 11.0093 1.7823 11.4434 1.22608C11.6601 0.948368 12.0235 0.467305 12.1454 0.305929C12.3911 -0.0275935 12.8611 -0.0995297 13.1946 0.146103C13.5281 0.391736 13.5994 0.861234 13.3537 1.19476L13.3518 1.1973C13.224 1.36655 12.8471 1.86557 12.6259 2.14892C12.1821 2.7177 11.5716 3.47861 10.9091 4.24171C10.2497 5.00109 9.52408 5.77979 8.85096 6.37454C8.51526 6.67116 8.17497 6.93788 7.8488 7.1345C7.54322 7.31872 7.15633 7.5 6.74982 7.5C6.34331 7.5 5.95642 7.31871 5.65084 7.1345C5.32468 6.93787 4.98439 6.67116 4.64869 6.37454C3.97557 5.77979 3.2499 5.0011 2.59058 4.24173C1.92803 3.47863 1.3176 2.71774 0.873719 2.14896C0.652433 1.86541 0.275507 1.36637 0.14793 1.19747L0.14628 1.19528C-0.0993546 0.861762 -0.0284591 0.391787 0.305062 0.146153C0.638573 -0.0994739 1.10808 -0.0282082 1.35372 0.305286Z" fill="#3B3D4A"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,3 @@
<svg width="8" height="14" viewBox="0 0 8 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.305286 12.1462C0.46666 12.0243 0.948403 11.6604 1.22611 11.4436C1.78233 11.0096 2.52142 10.4165 3.25831 9.77665C3.99893 9.13361 4.72022 8.4581 5.25046 7.85799C5.51634 7.55707 5.71838 7.29274 5.84987 7.07461C5.97355 6.86945 5.99927 6.74827 5.99927 6.74827C5.99927 6.74827 5.97355 6.63064 5.84988 6.4255C5.71838 6.20738 5.51635 5.94304 5.25047 5.64213C4.72022 5.04201 3.99892 4.3665 3.2583 3.72346C2.5214 3.08366 1.7823 2.49055 1.22608 2.05647C0.948368 1.83975 0.467305 1.47634 0.305929 1.35446C-0.0275935 1.10883 -0.0995297 0.63882 0.146103 0.305298C0.391736 -0.0282253 0.861234 -0.0994742 1.19476 0.146159L1.1973 0.148077C1.36655 0.275917 1.86557 0.652817 2.14892 0.873949C2.7177 1.31783 3.47861 1.92826 4.24171 2.59081C5.00109 3.25013 5.77979 3.9758 6.37454 4.64892C6.67116 4.98462 6.93788 5.32491 7.1345 5.65108C7.31872 5.95665 7.5 6.34355 7.5 6.75006C7.5 7.15657 7.31871 7.54346 7.1345 7.84904C6.93787 8.1752 6.67116 8.51549 6.37454 8.85119C5.77979 9.52431 5.0011 10.25 4.24173 10.9093C3.47863 11.5719 2.71774 12.1823 2.14896 12.6262C1.86541 12.8474 1.36637 13.2244 1.19747 13.3519L1.19528 13.3536C0.861762 13.5992 0.391787 13.5283 0.146153 13.1948C-0.0994739 12.8613 -0.0282082 12.3918 0.305286 12.1462Z" fill="#3B3D4A"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -270,7 +270,9 @@ class URLs {
//Traf
static get addTRAF => "$_baseUrl/TRAF/AddTRAF"; // get
static get getTRAFById => "$_baseUrl/TRAF/GetTRAFById"; // get
static get getAssetAnalytics => "$_baseUrl/Asset/GetAssetAnalytics"; // get
static get getAssetNDAutoCompleteByDynamicCodes => "$_baseUrl/AssetNameDefinition/GetAssetNDAutoCompleteByDynamicCodes"; // get// get
static get getAssetNameAutoComplete => "$_baseUrl/AssetNameDefinition/GetAssetNameAutoComplete"; // get// get
//gas refill
static get getGasTypes => "$_baseUrl/Lookups/GetLookup?lookupEnum=606"; // get
@ -307,11 +309,13 @@ class URLs {
static get getNurses => "$_baseUrl/Account/GetUserByRoleValue?value=R-7"; // get
static get getNursesBySiteId => "$_baseUrl/Account/GetUserByRoleValueSiteAndAssetGroupBySiteId?value=R-7"; // get
static get getUsersBasedOnSearch => "$_baseUrl/Account/GetUsersBasedOnSearch"; // get
static get getUserByRoleValueSiteMobileAutocomplete => "$_baseUrl/Account/GetUserByRoleValueSiteMobileAutocomplete"; // get
// pentry
static get getPentry => "$_baseUrl/return/pentry/details"; // get
static get updatePentry => "$_baseUrl/Visit/UpdateVisit"; // get
static get getPentryTaskStatus => "$_baseUrl/Lookups/GetLookup?lookupEnum=403"; // get
// get
static get getPentryVisitStatus => "$_baseUrl/Lookups/GetLookup?lookupEnum=402"; // get
static get getPentryStatus => "$_baseUrl/Lookups/GetLookup?lookupEnum=401"; // get
// contacts
@ -347,11 +351,22 @@ class URLs {
static get getClassification => '$_baseUrl/AssetInventory/Classification';
//internal Audit
static get getInternalAuditEquipmentById => "$_baseUrl/InternalAuditEquipments/GetInternalAuditEquipmentById";
static get getInternalAuditSystemById => "$_baseUrl/InternalAuditSystems/GetInternalAuditSystemByIdV2";
static get getInternalAuditChecklist => "$_baseUrl/Lookups/GetLookup?lookupEnum=43";
static get getInternalAuditWoType => "$_baseUrl/Lookups/GetLookup?lookupEnum=2500";
static get getInternalAuditFindingType => "$_baseUrl/Lookups/GetLookup?lookupEnum=2502";
static get addOrUpdateEquipmentInternalAudit => "$_baseUrl/InternalAuditEquipments/AddOrUpdateAuditEquipment";
static get addOrUpdateInternalAuditSystem => "$_baseUrl/InternalAuditSystems/AddOrUpdateInternalAuditSystem";
static get getWoAutoComplete => "$_baseUrl/InternalAuditSystems/AutoCompleteAllWorkOrder";
static get updateAuditEquipmentsEngineer => "$_baseUrl/InternalAuditEquipments/UpdateAuditEquipmentsEngineer";
static get updateAuditSystemEngineer => "$_baseUrl/InternalAuditSystems/UpdateAuditSystemEngineer";
static get loadAllWorkOrderDetailsByID => "$_baseUrl/InternalAuditSystems/LoadAllWorkOrderDetailsByID";
//chat
static get chatSdkToken => '$chatHubUrlApi/auth/sdk-token';
//survey
static get getQuestionnaire => '$_baseUrl/SurveyQuestionnaire/GetQuestionnaire';
static get submitSurvey => '$_baseUrl/SurveyQuestionnaire/SubmitSurvey';
}

@ -133,15 +133,24 @@ class AllRequestsProvider extends ChangeNotifier {
List<int> getStatues(BuildContext context) {
List<int> list = [1, 2, 3, 4];
//TODO need to refactor this code ....
if (context.userProvider.isAssessor) {
list = [9];
return list;
}
if (context.userProvider.isQualityUser) {
list = [10,11];
return list;
}
if (!context.userProvider.isNurse) {
list.add(5);
}
if (context.userProvider.isEngineer) {
list.add(10);
}
list.add(6); // task module
if (context.settingProvider.isUserFlowMedical && !context.userProvider.isNurse) {

@ -88,7 +88,7 @@ class OracleCodeProvider extends ChangeNotifier {
Future<List<AssetNDAutoCompleteByDynamicCodesModel>> getAssetByOracleCode(String oracleCode) async {
late Response response;
try {
response = await ApiManager.instance.post(URLs.getAssetNDAutoCompleteByDynamicCodes, body: {"codeValue": oracleCode});
response = await ApiManager.instance.post(URLs.getAssetNameAutoComplete, body: {"pageSize": 1, "pageNumber": 20, "searchText": oracleCode});
List<AssetNDAutoCompleteByDynamicCodesModel> page = [];
if (response.statusCode >= 200 && response.statusCode < 300) {
// client's request was successfully received

@ -46,10 +46,10 @@ class UserSearchProvider extends ChangeNotifier {
notifyListeners();
}
Future<List<UsersBasedOnSearchModel>> getUsersBasedOnSearch(String query) async {
Future<List<UsersBasedOnSearchModel>> getUsersBasedOnSearch(int siteId, String query) async {
late Response response;
try {
response = await ApiManager.instance.get(URLs.getUsersBasedOnSearch + "?roleValue=R-7&searchText=$query");
response = await ApiManager.instance.get(URLs.getUserByRoleValueSiteMobileAutocomplete + "?siteId=$siteId&roleValue=R-7&searchText=$query");
List<UsersBasedOnSearchModel> page = [];
if (response.statusCode >= 200 && response.statusCode < 300) {
// client's request was successfully received

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

@ -3,6 +3,7 @@ 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_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_wo/task_request_item_view.dart';
import 'package:test_sa/modules/traf_module/traf_request_item_view.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
@ -56,6 +57,11 @@ class RequestCategoryList extends StatelessWidget {
return InventorySessionItemView(requestData: request);
case 9:
return TrafRequestItemView(requestData: request);
//TODO need to verify this ...
case 10:
return EquipmentInternalAuditItemView(requestData: request);
case 11:
return EquipmentInternalAuditItemView(requestData: request);
default:
return Container(
height: 100,

@ -31,6 +31,12 @@ import 'package:test_sa/modules/asset_inventory_module/provider/asset_inventory_
import 'package:test_sa/modules/cm_module/service_request_detail_provider.dart';
import 'package:test_sa/modules/cm_module/views/nurse/create_new_request_view.dart';
import 'package:test_sa/modules/cx_module/chat/chat_provider.dart';
import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/create_equipment_internal_audit_form.dart';
import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/create_system_internal_audit_form.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_finding_type_provider.dart';
import 'package:test_sa/modules/internal_audit_module/provider/internal_audit_provider.dart';
import 'package:test_sa/modules/internal_audit_module/provider/internal_audit_wo_type_provider.dart';
import 'package:test_sa/modules/tm_module/tasks_wo/create_task_view.dart';
import 'package:test_sa/modules/traf_module/create_traf_request_page.dart';
import 'package:test_sa/modules/traf_module/traf_request_provider.dart';
@ -100,6 +106,8 @@ import 'controllers/providers/api/gas_refill_comments.dart';
import 'controllers/providers/api/user_provider.dart';
import 'controllers/providers/settings/setting_provider.dart';
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 'new_views/pages/gas_refill_request_form.dart';
import 'providers/lookups/classification_lookup_provider.dart';
@ -291,6 +299,10 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => PpmServiceProvider()),
ChangeNotifierProvider(create: (_) => CommentsProvider()),
ChangeNotifierProvider(create: (_) => GasRefillCommentsProvider()),
ChangeNotifierProvider(create: (_) => InternalAuditCheckListProvider()),
ChangeNotifierProvider(create: (_) => InternalAuditWoTypeProvider()),
ChangeNotifierProvider(create: (_) => InternalAuditProvider()),
ChangeNotifierProvider(create: (_) => InternalAuditFindingTypeProvider()),
///todo deleted
//ChangeNotifierProvider(create: (_) => RequestStatusProvider()),
@ -359,6 +371,10 @@ class MyApp extends StatelessWidget {
ProfilePage.id: (_) => const ProfilePage(),
ReportBugPage.id: (_) => const ReportBugPage(),
HelpCenterPage.id: (_) => const HelpCenterPage(),
CreateEquipmentInternalAuditForm.id: (_) => const CreateEquipmentInternalAuditForm(),
CreateSystemInternalAuditForm.id: (_) => const CreateSystemInternalAuditForm(),
UpdateEquipmentInternalAuditPage.id: (_) => UpdateEquipmentInternalAuditPage(),
UpdateSystemInternalAuditPage.id: (_) => UpdateSystemInternalAuditPage(),
// SwipeSuccessView.routeName: (_) => const SwipeSuccessView(),
// SwipeHistoryView.routeName: (_) => const SwipeHistoryView(),
},

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

@ -31,11 +31,11 @@ class ActivityMaintenanceHelperModel {
WorkOrderAssignedEmployee? assignedEmployee;
SuppEngineerWorkOrders? supEngineer;
ActivityMaintenanceAssistantEmployees? modelAssistantEmployees;
List<AssistantEmployeesModel>? assistantEmployList=[];
List<AssistantEmployees>? assistantEmployees;
List<ActivityMaintenanceTimers>? activityMaintenanceTimers = [];
TimerModel? activityMaintenanceTimerModel = TimerModel();
TimerModel? activityTimePicker;
List<AssistantEmployeesModel>? assistantEmployList=[];
List<TimerModel>? timerModelList = [];
ActivityMaintenanceHelperModel(

@ -6,17 +6,11 @@ class UsersBasedOnSearchModel {
int? languageId;
String? extensionNo;
String? phoneNumber;
String? positionName;
String? position;
bool? isActive;
UsersBasedOnSearchModel(
{this.userId,
this.userName,
this.email,
this.employeeId,
this.languageId,
this.extensionNo,
this.phoneNumber,
this.isActive});
UsersBasedOnSearchModel({this.userId, this.userName, this.email, this.employeeId, this.languageId, this.extensionNo, this.phoneNumber, this.positionName, this.position, this.isActive});
UsersBasedOnSearchModel.fromJson(Map<String, dynamic> json) {
userId = json['userId'];
@ -26,6 +20,8 @@ class UsersBasedOnSearchModel {
languageId = json['languageId'];
extensionNo = json['extensionNo'];
phoneNumber = json['phoneNumber'];
positionName = json['positionName'];
position = json['position'];
isActive = json['isActive'];
}
@ -38,6 +34,8 @@ class UsersBasedOnSearchModel {
data['languageId'] = this.languageId;
data['extensionNo'] = this.extensionNo;
data['phoneNumber'] = this.phoneNumber;
data['positionName'] = this.positionName;
data['position'] = this.position;
data['isActive'] = this.isActive;
return data;
}

@ -1,3 +1,4 @@
import 'dart:developer';
import 'dart:io';
import 'package:flutter/foundation.dart';
@ -107,6 +108,8 @@ class User {
return UsersTypes.assessor;
case "R-19": // Head Nurse Role
return UsersTypes.assessorTl;
case "R-3": // Quality User
return UsersTypes.qualityUser;
default:
return null;
}

@ -100,13 +100,13 @@ class AssetInventoryModel {
String? photo;
String? photoOriginName;
String? remarks;
Site? site;
Building? building;
Floor? floor;
Department? department;
Rooms? room;
SupplierDetails? supplier;
List<AssetInventoryDetailAssetAttachments> assetInventoryDetailAssetAttachments = [];
AssetInventoryModel({
this.id,
@ -160,6 +160,7 @@ class AssetInventoryModel {
this.floor,
this.room,
this.supplier,
this.assetInventoryDetailAssetAttachments = const [],
});
AssetInventoryModel.fromJson(Map<String, dynamic> json) {
@ -194,6 +195,13 @@ class AssetInventoryModel {
photo = json['photo'];
photoOriginName = json['photoOriginName'];
remarks = json['remarks'];
if (json['assetTransferAttachments'] != null) {
assetInventoryDetailAssetAttachments = [];
json['assetInventoryDetailAssetAttachments'].forEach((v) {
assetInventoryDetailAssetAttachments.add(AssetInventoryDetailAssetAttachments.fromJson(v));
});
}
//new data..
// newAssetNumber = json['newAssetNumber'] ?? json['assetNumber'];
// manufacturerId = json['manufacturerId'] ?? json['manufacturerId'];
@ -232,6 +240,9 @@ class AssetInventoryModel {
'newSupplierName': newSupplierName,
'photo': photo,
'remarks': remarks,
'assetInventoryDetailAssetAttachments': assetInventoryDetailAssetAttachments != null
? assetInventoryDetailAssetAttachments!.map((v) => v.toJson()).toList()
: [],
};
}
@ -290,8 +301,45 @@ class AssetInventoryModel {
department: department ?? other.department,
room: room ?? other.room,
supplier: supplier ?? other.supplier,
assetInventoryDetailAssetAttachments: assetInventoryDetailAssetAttachments??other.assetInventoryDetailAssetAttachments,
);
}
}
class AssetInventoryDetailAssetAttachments {
AssetInventoryDetailAssetAttachments({
this.id,
this.name,
this.originalName,
});
AssetInventoryDetailAssetAttachments.fromJson(dynamic json) {
id = json['id'];
name = json['name'];
originalName = json['originalName'];
}
num? id;
String? name;
String? originalName;
AssetInventoryDetailAssetAttachments copyWith({
num? id, // Parameter is now nullable
String? name, // Parameter is now nullable
String? originalName, // Parameter is now nullable
}) =>
AssetInventoryDetailAssetAttachments(
id: id ?? this.id,
name: name??this.name,
originalName: originalName??this.originalName,
);
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['id'] = id;
map['name'] = name;
map['originalName'] = originalName;
return map;
}
}

@ -24,6 +24,7 @@ class AssetDetailCardView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
@ -37,81 +38,75 @@ class AssetDetailCardView extends StatelessWidget {
],
),
8.height,
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'transaction Date: ${assetInventoryModel.transactionDate?.toServiceRequestDetailsFormat ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.assetNumber}: ${assetInventoryModel.assetNumber ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.serialNo}: ${assetInventoryModel.serialNo ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.manufacture}: ${assetInventoryModel.manufacturer ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.model}: ${assetInventoryModel.model ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.site}: ${assetInventoryModel.siteName ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.building}: ${assetInventoryModel.buildingName ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.floor}: ${assetInventoryModel.floorName ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.department}: ${assetInventoryModel.departmentName ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.supplier}: ${assetInventoryModel.supplierName ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${'Classifications'.addTranslation}: ${assetInventoryModel.status ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${'Remarks'.addTranslation}: ${assetInventoryModel.remarks ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
],
).expanded,
Container(
decoration: BoxDecoration(color: AppColor.neutral100, borderRadius: BorderRadius.circular(15)),
height: 115.toScreenHeight,
width: 115.toScreenWidth,
margin: const EdgeInsets.only(top: 8),
child: assetInventoryModel.photo != null && assetInventoryModel.photo!.isNotEmpty
? ClipRRect(
borderRadius: BorderRadius.circular(8),
child: ImageLoader(
url: URLs.getFileUrl(assetInventoryModel.photo),
boxFit: BoxFit.cover,
height: 48,
width: 48,
))
: 'image_placeholder'.toSvgAsset().center,
)
],
Text(
'Inventory Date: ${assetInventoryModel.transactionDate?.toServiceRequestDetailsFormat ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.assetNumber}: ${assetInventoryModel.assetNumber ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
), Text(
'${context.translation.assetName}: ${assetInventoryModel.assetName ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.serialNo}: ${assetInventoryModel.serialNo ?? '-'}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.manufacture}: ${assetInventoryModel.manufacturer ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.model}: ${assetInventoryModel.model ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.site}: ${assetInventoryModel.siteName ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.building}: ${assetInventoryModel.buildingName ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.floor}: ${assetInventoryModel.floorName ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.department}: ${assetInventoryModel.departmentName ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${context.translation.supplier}: ${assetInventoryModel.supplierName ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${'Classifications'.addTranslation}: ${assetInventoryModel.status ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
Text(
'${'Remarks'.addTranslation}: ${assetInventoryModel.remarks ?? ''}',
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120),
),
],
).toShadowContainer(context, borderRadius: 20, padding: 12);
}
}
//TODO old code.
// Container(
// decoration: BoxDecoration(color: AppColor.neutral100, borderRadius: BorderRadius.circular(15)),
// height: 115.toScreenHeight,
// width: 115.toScreenWidth,
// margin: const EdgeInsets.only(top: 8),
// child: assetInventoryModel.photo != null && assetInventoryModel.photo!.isNotEmpty
// ? ClipRRect(
// borderRadius: BorderRadius.circular(8),
// child: ImageLoader(
// url: URLs.getFileUrl(assetInventoryModel.photo),
// boxFit: BoxFit.cover,
// height: 48,
// width: 48,
// ))
// : 'image_placeholder'.toSvgAsset().center,
// )

@ -4,6 +4,7 @@ import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/new_models/site.dart';
import 'package:test_sa/modules/asset_inventory_module/models/session_model.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
@ -48,12 +49,15 @@ class _AssetInventoryDetailViewState extends State<AssetInventoryDetailView> {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
//
StatusLabel(
label: sessionModel.statusName,
id: sessionModel.statusId,
radius: 4,
textColor: AppColor.green15,
backgroundColor: AppColor.greenStatus(context),
textColor: AppColor.getRequestStatusTextColorByName(context, sessionModel.statusName),
backgroundColor: AppColor.getRequestStatusColorByName(context, sessionModel.statusName),
// textColor: AppColor.green15,
// backgroundColor: AppColor.greenStatus(context),
),
Text(
sessionModel.createdDate!.toString().toServiceRequestCardFormat,
@ -64,7 +68,7 @@ class _AssetInventoryDetailViewState extends State<AssetInventoryDetailView> {
),
8.height,
Text(
context.translation.requestDetails,
'Session Details'.addTranslation,
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
8.height,
@ -100,64 +104,139 @@ class _AssetInventoryDetailViewState extends State<AssetInventoryDetailView> {
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
8.height,
ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.zero,
itemCount: sites.length,
itemBuilder: (cxt, siteIndex) {
final site = sites[siteIndex];
final buildingNames = (site.buildings ?? []).map((b) => b.name).where((name) => name != null && name!.trim().isNotEmpty).join(', ');
final floorNames = (site.buildings ?? []).expand((b) => b.floors ?? []).map((f) => f.name).where((name) => name != null && name!.trim().isNotEmpty).join(', ');
final departmentNames =
(site.buildings ?? []).expand((b) => b.floors ?? []).expand((f) => f.departments ?? []).map((d) => d.name).where((name) => name != null && name!.trim().isNotEmpty).join(', ');
final roomNames = (site.buildings ?? [])
.expand((b) => b.floors ?? [])
.expand((f) => f.departments ?? [])
.expand((d) => d.rooms ?? [])
.map((r) => r.name)
.where((name) => name != null && name!.trim().isNotEmpty)
.join(', ');
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
SiteHierarchyView(
sites: sites,
),
],
).toShadowContainer(context, padding: 12, borderRadius: 20);
}
}
class SiteHierarchyView extends StatelessWidget {
final List<Site> sites;
const SiteHierarchyView({super.key, required this.sites});
@override
Widget build(BuildContext context) {
return ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: sites.length,
itemBuilder: (context, index) {
final site = sites[index];
return HierarchyTile(
title: site.name ?? '-',
children: (site.buildings ?? [])
.map((b) => HierarchyTile(
title: b.name ?? '-',
children: (b.floors ?? [])
.map((f) => HierarchyTile(
title: f.name ?? '-',
children: (f.departments ?? [])
.map((d) => HierarchyTile(
title: d.name ?? '-',
children: (d.rooms ?? [])
.map((r) => LeafTile(title: r.name ?? '-'))
.toList(),
))
.toList(),
))
.toList(),
))
.toList(),
);
},
);
}
}
class HierarchyTile extends StatefulWidget {
final String title;
final List<Widget> children;
final double indent;
const HierarchyTile({
super.key,
required this.title,
this.children = const [],
this.indent = 0,
});
@override
State<HierarchyTile> createState() => _HierarchyTileState();
}
class _HierarchyTileState extends State<HierarchyTile> {
bool expanded = false;
@override
Widget build(BuildContext context) {
final hasChildren = widget.children.isNotEmpty;
return Padding(
padding: EdgeInsets.only(left: widget.indent,top: 10.toScreenHeight),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: hasChildren ? () => setState(() => expanded = !expanded) : null,
child: Row(
children: [
if (hasChildren)
// expanded? 'expended_arrow_icon'.toSvgAsset(color: AppColor.neutral50,width: 8,height:13): 'collesped_arrow_icon'.toSvgAsset(color: AppColor.neutral50,width: 8,height:13)
Icon(
expanded ? Icons.expand_more : Icons.chevron_right,
size: 25.toScreenHeight,
color: AppColor.neutral50,
)
else
SizedBox(width: 18.toScreenWidth),
SizedBox(width: 15.toScreenWidth),
Text(
site.name ?? '-',
style: AppTextStyles.heading6.copyWith(
color: context.isDark ? AppColor.neutral30 : AppColor.neutral50,
),
),
Text(
'${context.translation.building}: ${buildingNames.isNotEmpty ? buildingNames : '-'}',
style: AppTextStyles.bodyText.copyWith(
color: context.isDark ? AppColor.neutral10 : AppColor.neutral120,
),
),
Text(
'${context.translation.floor}: ${floorNames.isNotEmpty ? floorNames : '-'}',
style: AppTextStyles.bodyText.copyWith(
color: context.isDark ? AppColor.neutral10 : AppColor.neutral120,
),
),
Text(
'${context.translation.department}: ${departmentNames.isNotEmpty ? departmentNames : '-'}',
style: AppTextStyles.bodyText.copyWith(
color: context.isDark ? AppColor.neutral10 : AppColor.neutral120,
),
),
Text(
'${context.translation.room}: ${roomNames.isNotEmpty ? roomNames : '-'}',
style: AppTextStyles.bodyText.copyWith(
color: context.isDark ? AppColor.neutral10 : AppColor.neutral120,
),
widget.title,
style:AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50),
),
],
);
},
separatorBuilder: (cxt, index) => const Divider().defaultStyle(context),
),
],
).toShadowContainer(context, padding: 12, borderRadius: 20);
),
),
if (expanded)
Padding(
padding: const EdgeInsets.only(left: 20, top: 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: widget.children
.map((child) => child is HierarchyTile
? HierarchyTile(
title: (child).title,
indent: 0,
children: (child).children,
)
: child)
.toList(),
),
),
],
),
);
}
}
class LeafTile extends StatelessWidget {
final String title;
const LeafTile({super.key, required this.title});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 22, top: 4, bottom: 4),
child: Text(
title,
style: const TextStyle(fontSize: 13, color: Colors.black87),
),
);
}
}

@ -294,30 +294,17 @@ class _AssetInventoryFormViewState extends State<AssetInventoryFormView> {
],
12.height,
Text(
'Asset Photo'.addTranslation,
'Attachments'.addTranslation,
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.white936),
),
12.height,
attachments.isEmpty
? AttachmentPicker(
label: context.translation.attachImage,
attachment: attachments,
buttonColor: AppColor.black10,
onlyImages: true,
onChange: (value) {
setState(() {});
},
buttonIcon: 'image-plus'.toSvgAsset(color: AppColor.neutral120),
)
: MultiFilesPickerItem(
file: File(attachments.first.name ?? ''),
enabled: true,
onRemoveTap: (file) {
setState(() {
attachments.clear();
});
},
),
AttachmentPicker(
label: context.translation.attachFiles,
attachment: attachments,
buttonColor: AppColor.black10,
onlyImages: false,
buttonIcon: 'image-plus'.toSvgAsset(color: AppColor.neutral120),
),
12.height,
AppTextFormField(
backgroundColor: AppColor.fieldBgColor(context),
@ -338,7 +325,7 @@ class _AssetInventoryFormViewState extends State<AssetInventoryFormView> {
.expanded,
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(buttonColor: AppColor.primary10, label: context.translation.submitRequest, maxWidth: true, onPressed: _onSubmit),
child: AppFilledButton(buttonColor: AppColor.primary10, label: 'Submit'.addTranslation, maxWidth: true, onPressed: _onSubmit),
),
],
),
@ -348,7 +335,6 @@ class _AssetInventoryFormViewState extends State<AssetInventoryFormView> {
Future<void> searchAsset({required String assetNo}) async {
AssetInventoryProvider provider = Provider.of<AssetInventoryProvider>(context, listen: false);
Map<String, dynamic> payload = {
"assetNumber": assetNo,
"sessionId": widget.assetInventoryModel?.sessionId,
@ -415,11 +401,20 @@ class _AssetInventoryFormViewState extends State<AssetInventoryFormView> {
_scannedAssetModel?.departmentId = widget.assetInventoryModel?.department?.id;
_scannedAssetModel?.roomId = widget.assetInventoryModel?.room?.id;
_scannedAssetModel?.isNotRegistered = isRegistered;
if (attachments.isNotEmpty) {
String? photoName = attachments.first.name;
String fileName = ServiceRequestUtils.isLocalUrl(photoName ?? '') ? ("${photoName ?? ''.split("/").last}|${base64Encode(File(photoName ?? '').readAsBytesSync())}") : photoName ?? '';
_scannedAssetModel?.photo = fileName;
_scannedAssetModel?.assetInventoryDetailAssetAttachments = [];
// try {
for (var item in attachments) {
String fileName = ServiceRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? '';
_scannedAssetModel?.assetInventoryDetailAssetAttachments.add(AssetInventoryDetailAssetAttachments(id: item.id, name: fileName));
}
// // } catch (error) {
// // print(error);
// // }
// if (attachments.isNotEmpty) {
// String? photoName = attachments.first.name;
// String fileName = ServiceRequestUtils.isLocalUrl(photoName ?? '') ? ("${photoName ?? ''.split("/").last}|${base64Encode(File(photoName ?? '').readAsBytesSync())}") : photoName ?? '';
// _scannedAssetModel?.photo = fileName;
// }
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
assetInventoryProvider.saveAssetInSession(model: _scannedAssetModel).then((success) async {
///Need to use push and remove until...

@ -58,7 +58,7 @@ class _AssetInventoryPageState extends State<AssetInventoryPage> {
return Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: DefaultAppBar(
title: 'Inventory Session Request'.addTranslation,
title: 'Asset Inventory Session'.addTranslation,
onBackPress: () {
Navigator.pop(context);
},
@ -86,7 +86,7 @@ class _AssetInventoryPageState extends State<AssetInventoryPage> {
indicator: BoxDecoration(color: context.isDark ? AppColor.neutral60 : AppColor.neutral110, borderRadius: BorderRadius.circular(7)),
onTap: (index) {},
tabs: [
Tab(text: 'Request Details'.addTranslation, height: 57.toScreenHeight),
Tab(text: 'Session Details'.addTranslation, height: 57.toScreenHeight),
Tab(
text:
'${'Scan Assets'.addTranslation} ${provider.assetInventoryResponse?.totalRows != null && provider.assetInventoryResponse!.totalRows! > 0 ? '(${provider.assetInventoryResponse?.totalRows})' : ''}',

@ -26,12 +26,6 @@ class InventorySessionItemView extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StatusLabel(
label: requestData!.priorityName!,
textColor: AppColor.getRequestStatusTextColorByName(context, requestData!.priorityName!),
backgroundColor: AppColor.getRequestStatusColorByName(context, requestData!.priorityName!),
),
8.width,
StatusLabel(
label: requestData!.statusName!,
textColor: AppColor.getRequestStatusTextColorByName(context, requestData!.statusName!),
@ -47,7 +41,7 @@ class InventorySessionItemView extends StatelessWidget {
),
8.height,
(requestData?.typeTransaction ?? context.translation.taskRequest).heading5(context),
infoWidget(label: context.translation.requestType, value: requestData?.sessionType, context: context),
infoWidget(label: 'Session Type'.addTranslation, value: requestData?.sessionType, context: context),
infoWidget(label: 'No of Assets'.addTranslation, value: requestData?.numberOfAssets != null ? requestData?.numberOfAssets.toString() : '-', context: context),
infoWidget(label: 'No of Sites'.addTranslation, value: requestData?.numberOfSites != null ? requestData?.numberOfSites.toString() : '-', context: context),
8.height,
@ -73,12 +67,6 @@ class InventorySessionItemView extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StatusLabel(
label: requestDetails!.priority!,
textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails?.priority!),
backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails?.priority!),
),
8.width,
StatusLabel(
label: requestDetails!.status!,
textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails?.status!),
@ -94,7 +82,7 @@ class InventorySessionItemView extends StatelessWidget {
),
8.height,
(requestDetails?.nameOfType ?? context.translation.taskRequest).heading5(context),
infoWidget(label: context.translation.requestType, value: requestDetails?.sessionType, context: context),
infoWidget(label: 'Session Type'.addTranslation, value: requestDetails?.sessionType, context: context),
infoWidget(label: 'No of Assets'.addTranslation, value: requestDetails?.numberOfAssets != null ? requestDetails?.numberOfAssets.toString() : '-', context: context),
infoWidget(label: 'No of Sites'.addTranslation, value: requestDetails?.numberOfSites != null ? requestDetails?.numberOfSites.toString() : '-', context: context),
8.height,

@ -72,52 +72,44 @@ class _ServiceRequestDetailViewState extends State<ServiceRequestDetailView> {
? const CircularProgressIndicator(color: AppColor.primary10).center
: requestProvider.currentWorkOrder == null
? const NoDataFound()
: Stack(
children: [
Column(
: Column(
children: [
SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
workOrderDetailCard(context, requestProvider.currentWorkOrder!.data!, _userProvider, requestProvider),
initialVisitCard(requestDetailProvider: requestProvider, userProvider: _userProvider),
12.height,
assetDetailCard(requestDetailProvider: requestProvider, userProvider: _userProvider),
12.height,
if (context.userProvider.user!.type == UsersTypes.engineer &&
!requestProvider.isReadOnlyRequest &&
requestProvider.currentWorkOrder!.data!.nextStep?.workOrderNextStepEnum == WorkOrderNextStepEnum.activity) ...[
costCard(context, requestProvider),
12.height,
],
contactInfoCard(context, requestProvider.currentWorkOrder!.data!),
20.height,
],
),
).expanded,
SafeArea(
top: false,
right: false,
left: false,
child: FooterActionButton.requestDetailsFooterWidget(
workOrderNextStepStatus: requestProvider.currentWorkOrder!.data!.nextStep!.workOrderNextStepEnum!,
status: requestProvider.currentWorkOrder?.data?.status,
isEmpIsAssigned: requestProvider.currentWorkOrder!.data!.assignedEmployee != null,
activities: requestProvider.currentWorkOrder!.data?.activities ?? [],
userProvider: _userProvider,
context: context),
).toShadowContainer(context, padding: 0, showShadow: false, borderRadius: 0),
workOrderDetailCard(context, requestProvider.currentWorkOrder!.data!, _userProvider, requestProvider),
initialVisitCard(requestDetailProvider: requestProvider, userProvider: _userProvider),
12.height,
assetDetailCard(requestDetailProvider: requestProvider, userProvider: _userProvider),
12.height,
if (context.userProvider.user!.type == UsersTypes.engineer &&
!requestProvider.isReadOnlyRequest &&
requestProvider.currentWorkOrder!.data!.nextStep?.workOrderNextStepEnum == WorkOrderNextStepEnum.activity) ...[
costCard(context, requestProvider),
12.height,
],
contactInfoCard(context, requestProvider.currentWorkOrder!.data!),
20.height,
],
),
//no need to show timer as discussed with backend
// if (requestProvider.timer != null && requestProvider.timer!.isActive) ...[
// const TimerWidget(),
// ]
],
);
).expanded,
SafeArea(
top: false,
right: false,
left: false,
child: FooterActionButton.requestDetailsFooterWidget(
workOrderNextStepStatus: requestProvider.currentWorkOrder!.data!.nextStep!.workOrderNextStepEnum!,
status: requestProvider.currentWorkOrder?.data?.status,
isEmpIsAssigned: requestProvider.currentWorkOrder!.data!.assignedEmployee != null,
activities: requestProvider.currentWorkOrder!.data?.activities ?? [],
userProvider: _userProvider,
context: context),
).toShadowContainer(context, padding: 0, showShadow: false, borderRadius: 0),
],
);
});
}

@ -0,0 +1,53 @@
import 'package:test_sa/modules/internal_audit_module/models/internal_audit_attachment_model.dart';
class EngineerData {
int? id;
String? debrief;
String? startTime;
String? endTime;
double? totalHours;
bool? isComplete;
int? statusId;
int? requestId;
List<InternalAuditAttachments>? attachments;
EngineerData({
this.id,
this.debrief,
this.startTime,
this.endTime,
this.totalHours,
this.isComplete,
this.statusId,
this.requestId,
this.attachments,
});
EngineerData.fromJson(Map<String, dynamic> json) {
id = json['id'];
debrief = json['debrief'];
startTime = json['startTime'];
endTime = json['endTime'];
totalHours = (json['totalHours'] as num?)?.toDouble();
isComplete = json['isComplete'];
statusId = json['statusId'];
requestId = json['requestId'];
attachments = json['attachments'] != null ? (json['attachments'] as List).map((e) => InternalAuditAttachments.fromJson(e)).toList() : [];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['id'] = id;
data['debrief'] = debrief;
data['startTime'] = startTime;
data['endTime'] = endTime;
data['totalHours'] = totalHours;
data['isComplete'] = isComplete;
data['statusId'] = statusId;
data['requestId'] = requestId;
if (attachments != null) {
data['attachments'] = attachments!.map((e) => e.toJson()).toList();
}
return data;
}
}

@ -0,0 +1,300 @@
import 'package:test_sa/models/timer_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/engineer_data_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/internal_audit_attachment_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/internal_audit_timer_model.dart';
class EquipmentInternalAuditDataModel {
int? id;
String? requestNo;
String? createdDate;
int? requestNoSequence;
Auditor? auditor;
Status? status;
Asset? asset;
AssignEmployee? assignEmployee;
dynamic manufacture;
String? remarks;
List<EquipmentsFinding>? equipmentsFindings;
List<InternalAuditAttachments>? attachments;
EngineerData? engineerData;
EquipmentInternalAuditDataModel({
this.id,
this.requestNo,
this.createdDate,
this.requestNoSequence,
this.auditor,
this.status,
this.asset,
this.assignEmployee,
this.manufacture,
this.remarks,
this.equipmentsFindings,
this.attachments,
this.engineerData,
});
EquipmentInternalAuditDataModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
requestNo = json['requestNo'];
createdDate = json['createdDate'];
requestNoSequence = json['requestNoSequence'];
auditor = json['auditor'] != null ? Auditor.fromJson(json['auditor']) : null;
status = json['status'] != null ? Status.fromJson(json['status']) : null;
asset = json['asset'] != null ? Asset.fromJson(json['asset']) : null;
assignEmployee = json['assignEmployee'] != null ? AssignEmployee.fromJson(json['assignEmployee']) : null;
manufacture = json['manufacture'];
remarks = json['remarks'];
if (json['equipmentsFindings'] != null) {
equipmentsFindings = <EquipmentsFinding>[];
json['equipmentsFindings'].forEach((v) {
equipmentsFindings!.add(EquipmentsFinding.fromJson(v));
});
}
attachments = json['attachments'] != null
? (json['attachments'] as List)
.map((e) => InternalAuditAttachments.fromJson(e))
.where((e) => e.name != null) // optional filter if you want to skip null names
.toList()
: [];
engineerData = json['engineerData'] != null ? EngineerData.fromJson(json['engineerData']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['id'] = id;
data['requestNo'] = requestNo;
data['createdDate'] = createdDate;
data['requestNoSequence'] = requestNoSequence;
if (auditor != null) {
data['auditor'] = auditor!.toJson();
}
if (status != null) {
data['status'] = status!.toJson();
}
if (asset != null) {
data['asset'] = asset!.toJson();
}
if (assignEmployee != null) {
data['assignEmployee'] = assignEmployee!.toJson();
}
data['manufacture'] = manufacture;
data['remarks'] = remarks;
if (equipmentsFindings != null) {
data['equipmentsFindings'] =
equipmentsFindings!.map((v) => v.toJson()).toList();
}
data['attachments'] = attachments;
if (engineerData != null) data['engineerData'] = engineerData!.toJson();
return data;
}
}
class Auditor {
String? userId;
String? userName;
String? email;
String? employeeId;
int? languageId;
String? extensionNo;
String? phoneNumber;
bool? isActive;
Auditor({
this.userId,
this.userName,
this.email,
this.employeeId,
this.languageId,
this.extensionNo,
this.phoneNumber,
this.isActive,
});
Auditor.fromJson(Map<String, dynamic> json) {
userId = json['userId'];
userName = json['userName'];
email = json['email'];
employeeId = json['employeeId'];
languageId = json['languageId'];
extensionNo = json['extensionNo'];
phoneNumber = json['phoneNumber'];
isActive = json['isActive'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['userId'] = userId;
data['userName'] = userName;
data['email'] = email;
data['employeeId'] = employeeId;
data['languageId'] = languageId;
data['extensionNo'] = extensionNo;
data['phoneNumber'] = phoneNumber;
data['isActive'] = isActive;
return data;
}
}
class Status {
int? id;
String? name;
int? value;
Status({this.id, this.name, this.value});
Status.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
value = json['value'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['id'] = id;
data['name'] = name;
data['value'] = value;
return data;
}
}
class Asset {
int? id;
String? assetNumber;
String? assetSerialNo;
String? assetName;
String? modelName;
String? manufacturerName;
String? siteName;
int? siteId;
String? modelDefinition;
Asset({
this.id,
this.assetNumber,
this.assetSerialNo,
this.assetName,
this.modelName,
this.manufacturerName,
this.siteName,
this.siteId,
this.modelDefinition,
});
Asset.fromJson(Map<String, dynamic> json) {
id = json['id'];
assetNumber = json['assetNumber'];
assetSerialNo = json['assetSerialNo'];
assetName = json['assetName'];
modelName = json['modelName'];
manufacturerName = json['manufacturerName'];
siteName = json['siteName'];
siteId = json['siteId'];
modelDefinition = json['modelDefinition'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['id'] = id;
data['assetNumber'] = assetNumber;
data['assetSerialNo'] = assetSerialNo;
data['assetName'] = assetName;
data['modelName'] = modelName;
data['manufacturerName'] = manufacturerName;
data['siteName'] = siteName;
data['siteId'] = siteId;
data['modelDefinition'] = modelDefinition;
return data;
}
}
class AssignEmployee {
String? userId;
String? userName;
String? email;
String? employeeId;
int? languageId;
String? extensionNo;
String? phoneNumber;
bool? isActive;
AssignEmployee({
this.userId,
this.userName,
this.email,
this.employeeId,
this.languageId,
this.extensionNo,
this.phoneNumber,
this.isActive,
});
AssignEmployee.fromJson(Map<String, dynamic> json) {
userId = json['userId'];
userName = json['userName'];
email = json['email'];
employeeId = json['employeeId'];
languageId = json['languageId'];
extensionNo = json['extensionNo'];
phoneNumber = json['phoneNumber'];
isActive = json['isActive'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['userId'] = userId;
data['userName'] = userName;
data['email'] = email;
data['employeeId'] = employeeId;
data['languageId'] = languageId;
data['extensionNo'] = extensionNo;
data['phoneNumber'] = phoneNumber;
data['isActive'] = isActive;
return data;
}
}
class EquipmentsFinding {
int? id;
Finding? finding;
EquipmentsFinding({this.id, this.finding});
EquipmentsFinding.fromJson(Map<String, dynamic> json) {
id = json['id'];
finding = json['finding'] != null ? Finding.fromJson(json['finding']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['id'] = id;
if (finding != null) {
data['finding'] = finding!.toJson();
}
return data;
}
}
class Finding {
int? id;
String? name;
int? value;
Finding({this.id, this.name, this.value});
Finding.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
value = json['value'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['id'] = id;
data['name'] = name;
data['value'] = value;
return data;
}
}

@ -0,0 +1,41 @@
import 'package:flutter/src/widgets/framework.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/models/device/asset.dart';
import 'package:test_sa/models/fault_description.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/modules/internal_audit_module/models/internal_audit_attachment_model.dart';
class EquipmentInternalAuditFormModel {
int? id=0;
String? deviceArName;
String? auditorId;
String? woOrderNo;
List<String>? devicePhotos;
List<Lookup> findings =[];
List<InternalAuditAttachments> attachments = [];
String? remarks;
Asset? device;
EquipmentInternalAuditFormModel({
this.id,
this.deviceArName,
this.auditorId,
this.devicePhotos,
this.woOrderNo,
this.remarks,
});
Map<String, dynamic> toJson() {
return {
'id': id??0,
'assetId': device?.id,
'auditorId': auditorId,
'findings': findings.map((e) => {'findingId': e.value}).toList(),
'attachments': attachments.map((e) => e.toJson()).toList(),
'remarks': remarks,
};
}
}

@ -0,0 +1,27 @@
import 'dart:developer';
class InternalAuditAttachments {
InternalAuditAttachments({this.id, this.originalName, this.name, this.createdBy});
int? id;
String? name;
String? originalName;
String? createdBy;
InternalAuditAttachments.fromJson(Map<String, dynamic> json) {
log('name is ${json['name']}');
id = json['id'];
name = (json['name'] != null && !json['name'].toString().startsWith('data:image/jpeg')) ? json['name'] : null;
originalName = json['originalName'];
createdBy = json['createdBy'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['name'] = name;
data['originalName'] = originalName;
// data['createdBy'] = createdBy;
return data;
}
}

@ -0,0 +1,35 @@
class InternalAuditTimerModel {
int? id;
String? startDate;
String? endDate;
double? totalWorkingHour;
String? comment;
InternalAuditTimerModel({
this.id,
this.startDate,
this.endDate,
this.totalWorkingHour,
this.comment,
});
factory InternalAuditTimerModel.fromJson(Map<String, dynamic> json) {
return InternalAuditTimerModel(
id: json['id'] as int?,
startDate: json['startDate'],
endDate: json['endDate'],
totalWorkingHour: (json['totalWorkingHours'] as num?)?.toDouble(),
comment: json['comment'] as String?,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'startDate': startDate,
'endDate': endDate,
'totalWorkingHours': totalWorkingHour,
'comment': comment,
};
}
}

@ -0,0 +1,138 @@
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/modules/internal_audit_module/models/engineer_data_model.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_form_model.dart';
class SystemInternalAuditDataModel {
int? id;
int? assetGroupId;
String? requestNo;
int? requestNoSequence;
Auditor? auditor;
Lookup? status;
Lookup? findingType;
String? findingDescription;
Lookup? workOrderType;
Auditor? assignEmployee;
SystemAuditWorkOrderDetailModel? workOrderDetails; // Already exists as a separate model
String? createdBy;
String? createdDate;
String? modifiedBy;
String? modifiedDate;
List<InternalAuditAttachments>? attachments;
EngineerData? engineerData;
SystemInternalAuditDataModel({
this.id,
this.assetGroupId,
this.requestNo,
this.requestNoSequence,
this.auditor,
this.status,
this.findingType,
this.findingDescription,
this.workOrderType,
this.assignEmployee,
this.workOrderDetails,
this.createdBy,
this.createdDate,
this.modifiedBy,
this.modifiedDate,
this.attachments,
this.engineerData,
});
SystemInternalAuditDataModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
assetGroupId = json['assetGroupId'];
requestNo = json['requestNo'];
requestNoSequence = json['requestNoSequence'];
auditor = json['auditor'] != null ? Auditor.fromJson(json['auditor']) : null;
status = json['status'] != null ? Lookup.fromJson(json['status']) : null;
findingType = json['findingType'] != null ? Lookup.fromJson(json['findingType']) : null;
findingDescription = json['findingDescription'];
workOrderType = json['workOrderType'] != null ? Lookup.fromJson(json['workOrderType']) : null;
assignEmployee = json['assignEmployee'] != null ? Auditor.fromJson(json['assignEmployee']) : null;
workOrderDetails = json['workOrderDetails'] != null ? SystemAuditWorkOrderDetailModel.fromJson(json['workOrderDetails']) : null; // Keep as-is
createdBy = json['createdBy'];
createdDate = json['createdDate'];
modifiedBy = json['modifiedBy'];
modifiedDate = json['modifiedDate'];
attachments = json['attachments'] != null
? (json['attachments'] as List)
.map((e) => InternalAuditAttachments.fromJson(e))
.where((e) => e.name != null) // optional filter if you want to skip null names
.toList()
: [];
engineerData = json['engineerData'] != null ? EngineerData.fromJson(json['engineerData']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['id'] = id;
data['assetGroupId'] = assetGroupId;
data['requestNo'] = requestNo;
data['requestNoSequence'] = requestNoSequence;
if (auditor != null) data['auditor'] = auditor!.toJson();
if (status != null) data['status'] = status!.toJson();
if (findingType != null) data['findingType'] = findingType!.toJson();
data['findingDescription'] = findingDescription;
if (workOrderType != null) data['workOrderType'] = workOrderType!.toJson();
if (assignEmployee != null) data['assignEmployee'] = assignEmployee!.toJson();
data['workOrderDetails'] = workOrderDetails;
data['createdBy'] = createdBy;
data['createdDate'] = createdDate;
data['modifiedBy'] = modifiedBy;
data['modifiedDate'] = modifiedDate;
if (engineerData != null) data['engineerData'] = engineerData!.toJson();
return data;
}
}
class Auditor {
String? userId;
String? userName;
String? email;
String? employeeId;
int? languageId;
String? extensionNo;
String? phoneNumber;
bool? isActive;
Auditor({
this.userId,
this.userName,
this.email,
this.employeeId,
this.languageId,
this.extensionNo,
this.phoneNumber,
this.isActive,
});
Auditor.fromJson(Map<String, dynamic> json) {
userId = json['userId'];
userName = json['userName'];
email = json['email'];
employeeId = json['employeeId'];
languageId = json['languageId'];
extensionNo = json['extensionNo'];
phoneNumber = json['phoneNumber'];
isActive = json['isActive'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = {};
data['userId'] = userId;
data['userName'] = userName;
data['email'] = email;
data['employeeId'] = employeeId;
data['languageId'] = languageId;
data['extensionNo'] = extensionNo;
data['phoneNumber'] = phoneNumber;
data['isActive'] = isActive;
return data;
}
}

@ -0,0 +1,202 @@
import 'dart:developer';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/modules/internal_audit_module/models/internal_audit_attachment_model.dart';
class SystemInternalAuditFormModel {
int? id;
String? auditorId;
String? assignEmployeeId;
Lookup? findingType;
String? findingDescription;
Lookup? workOrderType;
int? correctiveMaintenanceId;
int? planPreventiveVisitId;
int? assetTransferId;
List<InternalAuditAttachments>? attachments = [];
int? taskJobId;
int? taskAlertJobId;
int? gasRefillId;
int? planRecurrentTaskId;
int? statusId;
SystemAuditWorkOrderDetailModel? workOrderDetailModel;
SystemInternalAuditFormModel({
this.id,
this.auditorId,
this.assignEmployeeId,
this.findingType,
this.findingDescription,
this.workOrderType,
this.correctiveMaintenanceId,
this.planPreventiveVisitId,
this.assetTransferId,
this.taskJobId,
this.taskAlertJobId,
this.gasRefillId,
this.planRecurrentTaskId,
this.workOrderDetailModel,
this.statusId,
this.attachments,
});
Map<String, dynamic> toJson() {
return {
// 'id': id,
'auditorId': auditorId,
'assignEmployeeId': assignEmployeeId,
'findingTypeId': findingType?.id,
'findingDescription': findingDescription,
'workOrderTypeId': workOrderType?.id,
'correctiveMaintenanceId': correctiveMaintenanceId,
'planPreventiveVisitId': planPreventiveVisitId,
'assetTransferId': assetTransferId,
'taskJobId': taskJobId,
'taskAlertJobId': taskAlertJobId,
'gasRefillId': gasRefillId,
'planRecurrentTaskId': planRecurrentTaskId,
'statusId': statusId,
'attachments': attachments?.map((e) => e.toJson()).toList(),
};
}
}
class WoAutoCompleteModel {
int? id;
String? workOrderNo;
String? displayName;
WoAutoCompleteModel({this.id, this.workOrderNo, this.displayName});
WoAutoCompleteModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
workOrderNo = json['workOrderNo'];
displayName = json['workOrderNo'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['woOrderNo'] = workOrderNo;
data['displayName'] = workOrderNo;
return data;
}
}
class SystemAuditWorkOrderDetailModel {
String? createdBy;
DateTime? createdDate;
String? modifiedBy;
DateTime? modifiedDate;
int? id;
String? workOrderNo;
String? woAssignedEngineer;
String? woAssetNo;
String? wosn;
String? woAssetName;
String? woModel;
String? woManufacturer;
String? woSite;
String? woDepartment;
String? wOPMScheduleDate;
String? wOPMActualVisitDate;
String? wOAssetTransferDestinationSite;
String? wOAssetTransferSenderEngineer;
String? wOAssetTransferReceiverEngineer;
String? wOTaskAlertJobImpactedStatus;
String? wOTaskAlertJobAcknowledgment;
String? wOGasRefillType;
String? wORecurrentTaskTaskType;
String? wORecurrentTaskPlanNo;
String? wORecurrentTaskPlanName;
String? wORecurrentTaskStatus;
SystemAuditWorkOrderDetailModel({
this.createdBy,
this.createdDate,
this.modifiedBy,
this.modifiedDate,
this.id,
this.workOrderNo,
this.woAssignedEngineer,
this.woAssetNo,
this.wosn,
this.woAssetName,
this.woModel,
this.woManufacturer,
this.woSite,
this.woDepartment,
this.wOPMScheduleDate,
this.wOPMActualVisitDate,
this.wOAssetTransferDestinationSite,
this.wOAssetTransferSenderEngineer,
this.wOAssetTransferReceiverEngineer,
this.wOTaskAlertJobImpactedStatus,
this.wOTaskAlertJobAcknowledgment,
this.wOGasRefillType,
this.wORecurrentTaskTaskType,
this.wORecurrentTaskPlanNo,
this.wORecurrentTaskPlanName,
this.wORecurrentTaskStatus,
});
SystemAuditWorkOrderDetailModel.fromJson(Map<String, dynamic> json) {
createdBy = json['createdBy'];
createdDate = json['createdDate'] != null ? DateTime.tryParse(json['createdDate']) : null;
modifiedBy = json['modifiedBy'];
modifiedDate = json['modifiedDate'] != null ? DateTime.tryParse(json['modifiedDate']) : null;
id = json['id'];
workOrderNo = json['workOrderNo'];
woAssignedEngineer = json['woAssignedEngineer'];
woAssetNo = json['woAssetNo'];
wosn = json['wosn'];
woAssetName = json['woAssetName'];
woModel = json['woModel'];
woManufacturer = json['woManufacturer'];
woSite = json['woSite'];
woDepartment = json['woDepartment'];
wOPMScheduleDate = json['wO_PM_ScheduleDate'];
wOPMActualVisitDate = json['wO_PM_ActualVisitDate'];
wOAssetTransferDestinationSite = json['wO_AssetTransfer_DestinationSite'];
wOAssetTransferSenderEngineer = json['wO_AssetTransfer_SenderEngineer'];
wOAssetTransferReceiverEngineer = json['wO_AssetTransfer_ReceiverEngineer'];
wOTaskAlertJobImpactedStatus = json['wO_TaskAlertJob_ImpactedStatus'];
wOTaskAlertJobAcknowledgment = json['wO_TaskAlertJob_Acknowledgment'];
wOGasRefillType = json['wO_GasRefill_Type'];
wORecurrentTaskTaskType = json['wO_RecurrentTask_TaskType'];
wORecurrentTaskPlanNo = json['wO_RecurrentTask_PlanNo'];
wORecurrentTaskPlanName = json['wO_RecurrentTask_PlanName'];
wORecurrentTaskStatus = json['wO_RecurrentTask_Status'];
}
Map<String, dynamic> toJson() {
return {
'createdBy': createdBy,
'createdDate': createdDate?.toIso8601String(),
'modifiedBy': modifiedBy,
'modifiedDate': modifiedDate?.toIso8601String(),
'id': id,
'workOrderNo': workOrderNo,
'woAssignedEngineer': woAssignedEngineer,
'woAssetNo': woAssetNo,
'wosn': wosn,
'woAssetName': woAssetName,
'woModel': woModel,
'woManufacturer': woManufacturer,
'woSite': woSite,
'woDepartment': woDepartment,
'wO_PM_ScheduleDate': wOPMScheduleDate,
'wO_PM_ActualVisitDate': wOPMActualVisitDate,
'wO_AssetTransfer_DestinationSite': wOAssetTransferDestinationSite,
'wO_AssetTransfer_SenderEngineer': wOAssetTransferSenderEngineer,
'wO_AssetTransfer_ReceiverEngineer': wOAssetTransferReceiverEngineer,
'wO_TaskAlertJob_ImpactedStatus': wOTaskAlertJobImpactedStatus,
'wO_TaskAlertJob_Acknowledgment': wOTaskAlertJobAcknowledgment,
'wO_GasRefill_Type': wOGasRefillType,
'wO_RecurrentTask_TaskType': wORecurrentTaskTaskType,
'wO_RecurrentTask_PlanNo': wORecurrentTaskPlanNo,
'wO_RecurrentTask_PlanName': wORecurrentTaskPlanName,
'wO_RecurrentTask_Status': wORecurrentTaskStatus,
};
}
}

@ -0,0 +1,61 @@
import 'package:test_sa/models/timer_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/internal_audit_attachment_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/internal_audit_timer_model.dart';
class AuditFormModel {
int? id;
String? createdDate;
int? requestId;
String? debrief;
DateTime? startTime;
DateTime? endTime;
double? totalHours;
List<InternalAuditAttachments>? attachments;
bool? isComplete;
TimerModel? auditTimerModel = TimerModel();
List<InternalAuditTimerModel>? auditTimers = [];
TimerModel? auditTimePicker;
List<TimerModel>? timerModelList = [];
AuditFormModel(
{this.id,
this.requestId,
this.debrief,
this.startTime,
this.endTime,
this.totalHours,
this.createdDate,
this.attachments,
this.isComplete,
this.auditTimerModel,
this.timerModelList,
this.auditTimePicker,
this.auditTimers});
AuditFormModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
requestId = json['requestId'];
debrief = json['debrief'];
startTime = json['startTime'] != null ? DateTime.tryParse(json['startTime']) : null;
endTime = json['endTime'] != null ? DateTime.tryParse(json['endTime']) : null;
totalHours = json['totalHours']?.toDouble();
if (json['attachments'] != null) {
attachments = (json['attachments'] as List).map((e) => InternalAuditAttachments.fromJson(e)).toList();
}
isComplete = json['isComplete'];
}
Map<String, dynamic> toJson() {
return {
'id': id,
'requestId': requestId,
'debrief': debrief,
'startTime': startTime?.toIso8601String(),
'endTime': endTime?.toIso8601String(),
// 'auditTimer': auditTimers,
'totalHours': totalHours,
'attachments': attachments?.map((e) => e.toJson()).toList(),
'isComplete': isComplete,
};
}
}

@ -0,0 +1,147 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
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/generic_attachment_model.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/modules/internal_audit_module/models/equipment_internal_audit_form_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/internal_audit_attachment_model.dart';
import 'package:test_sa/modules/internal_audit_module/provider/internal_audit_checklist_provider.dart';
import 'package:test_sa/modules/internal_audit_module/provider/internal_audit_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/views/widgets/equipment/asset_picker.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
class CreateEquipmentInternalAuditForm extends StatefulWidget {
static const String id = "/create-equipment-internal-audit-form";
const CreateEquipmentInternalAuditForm({Key? key}) : super(key: key);
@override
_CreateEquipmentInternalAuditFormState createState() => _CreateEquipmentInternalAuditFormState();
}
class _CreateEquipmentInternalAuditFormState extends State<CreateEquipmentInternalAuditForm> with TickerProviderStateMixin {
late TextEditingController _commentController;
final EquipmentInternalAuditFormModel _equipmentinternalAuditModel = EquipmentInternalAuditFormModel();
final List<GenericAttachmentModel> _attachments = [];
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
FocusNode auditCheckListNode = FocusNode();
@override
void initState() {
super.initState();
_commentController = TextEditingController();
}
@override
void dispose() {
_commentController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: DefaultAppBar(title: 'Equipment Internal Audit Checklist'.addTranslation),
body: Form(
key: _formKey,
child: Column(
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AssetPicker(
device: _equipmentinternalAuditModel.device,
borderColor: AppColor.black20,
buttonColor: AppColor.white936,
onPick: (asset) async {
_equipmentinternalAuditModel.device = asset;
setState(() {});
},
),
16.height,
//TODO need to check layout issue...
MultipleItemDropDownMenu<Lookup, InternalAuditCheckListProvider>(
context: context,
showAsBottomSheet: true,
backgroundColor: AppColor.neutral100,
showShadow: false,
showCancel: true,
requestById: context.userProvider.user?.clientId,
title: 'Internal Audit Checklist'.addTranslation,
initialValue: _equipmentinternalAuditModel.findings,
onSelect: (value) {
_equipmentinternalAuditModel.findings = value ?? [];
},
),
16.height,
AppTextFormField(
backgroundColor: AppColor.fieldBgColor(context),
labelText: 'Remarks'.addTranslation,
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
alignLabelWithHint: true,
textInputType: TextInputType.multiline,
showShadow: false,
onSaved: (value) {
_equipmentinternalAuditModel.remarks = value;
},
),
16.height,
AttachmentPicker(
label: context.translation.attachments,
attachment: _attachments,
buttonColor: AppColor.primary10,
onlyImages: false,
onChange: (value) {},
buttonIcon: 'attachment_icon'.toSvgAsset(color: AppColor.primary10),
),
],
).toShadowContainer(context),
).expanded,
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(label: context.translation.submitRequest, buttonColor: AppColor.primary10, onPressed: _submit),
),
],
),
),
);
}
Future<void> _submit() async {
InternalAuditProvider internalAuditProvider = Provider.of<InternalAuditProvider>(context, listen: false);
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
_equipmentinternalAuditModel.attachments = [];
for (var item in _attachments) {
String fileName = ServiceRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? '';
_equipmentinternalAuditModel.attachments.add(InternalAuditAttachments(id: item.id, name: fileName));
}
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
_equipmentinternalAuditModel.auditorId = context.userProvider.user?.userID;
bool status = await internalAuditProvider.addEquipmentInternalAudit(context: context, request: _equipmentinternalAuditModel);
Navigator.pop(context);
if(status){
Navigator.pop(context);
}
}
}
}

@ -0,0 +1,207 @@
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/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/internal_audit_module/models/equipment_internal_audit_data_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/internal_audit_attachment_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/provider/internal_audit_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/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';
class EquipmentInternalAuditDetailPage extends StatefulWidget {
static const String id = "/details-internal-audit";
final int auditId;
const EquipmentInternalAuditDetailPage({Key? key, required this.auditId}) : super(key: key);
@override
_EquipmentInternalAuditDetailPageState createState() {
return _EquipmentInternalAuditDetailPageState();
}
}
class _EquipmentInternalAuditDetailPageState extends State<EquipmentInternalAuditDetailPage> {
bool isWoType = true;
EquipmentInternalAuditDataModel? model;
late InternalAuditProvider _internalAuditProvider;
List<InternalAuditAttachments> allAttachments = [];
@override
void initState() {
super.initState();
_internalAuditProvider = Provider.of<InternalAuditProvider>(context, listen: false);
WidgetsBinding.instance.addPostFrameCallback((_) {
getAuditData();
});
}
Future<void> getAuditData() async {
model = await _internalAuditProvider.getEquipmentInternalAuditById(widget.auditId);
allAttachments.clear();
allAttachments = [
...(model?.attachments ?? []),
...(model?.engineerData?.attachments ?? []),
];
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
//TODO need to check when implementing provider needed or not .
return Scaffold(
appBar: const DefaultAppBar(title: "Request Details"),
body: Selector<InternalAuditProvider, bool>(
selector: (_, provider) => provider.isLoading,
builder: (_, isLoading, __) {
if (isLoading) return const ALoading();
if (model == null) return NoDataFound(message: context.translation.noDataFound).center;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
assetDetails(),
8.height,
requestDetails(),
8.height,
if (model?.remarks?.isNotEmpty ?? false) ...[
const Divider().defaultStyle(context),
model!.remarks!.bodyText(context),
8.height,
],
if (allAttachments.isNotEmpty) ...[
const Divider().defaultStyle(context),
Text(
"Attachments".addTranslation,
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
8.height,
FilesList(images: allAttachments.map((e) => URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []),
],
],
).paddingAll(0).toShadowContainer(context),
).expanded,
if (context.userProvider.isEngineer)
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
label: "Update",
onPressed: () async {
final result = await Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => UpdateEquipmentInternalAuditPage(model: model),
),
);
if (result == true) {
await getAuditData();
setState(() {}); // refresh UI with new model
}
// Navigator.of(context).push(MaterialPageRoute(builder: (_) => UpdateEquipmentInternalAuditPage(model: model)));
}),
),
],
);
},
));
}
Widget workOrderInformation() {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Divider().defaultStyle(context),
Text(
"WO Info",
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
6.height,
'${context.translation.woNumber}: ${'-'}'.bodyText(context),
'${'WO Type'.addTranslation}: ${'-'}'.bodyText(context),
'${context.translation.site}: ${'-'}'.bodyText(context),
'${context.translation.assetName}: ${'-'}'.bodyText(context),
'${context.translation.manufacture}: ${'-'}'.bodyText(context),
'${context.translation.model}: ${'-'}'.bodyText(context),
],
);
}
Widget requestDetails() {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Divider().defaultStyle(context),
Text(
"Request Details",
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
6.height,
if (model != null && model!.equipmentsFindings != null && model!.equipmentsFindings!.isNotEmpty)
...model!.equipmentsFindings!.map((item) {
final findingName = item.finding?.name ?? 'Unknown';
return checklistWidget(value: findingName.addTranslation);
}).toList()
else
Text('No findings available'.addTranslation),
],
);
}
Widget assetDetails() {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Asset Details",
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
6.height,
'${context.translation.assetName}: ${model?.asset?.assetName ?? '-'}'.bodyText(context),
'${context.translation.assetNo}: ${model?.asset?.assetNumber ?? '-'}'.bodyText(context),
'${context.translation.manufacture}: ${model?.asset?.manufacturerName ?? '-'}'.bodyText(context),
'${context.translation.model}: ${model?.asset?.modelName ?? '-'}'.bodyText(context),
],
);
}
Widget checklistWidget({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,115 @@
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/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_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 EquipmentInternalAuditItemView extends StatelessWidget {
final Data? requestData;
final RequestsDetails? requestDetails;
final bool showShadow;
const EquipmentInternalAuditItemView({Key? key, this.requestData, this.requestDetails, this.showShadow = true}) : super(key: key);
@override
Widget build(BuildContext context) {
//TODO need to refactor this code repetation @waseem
if (requestData != null) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StatusLabel(
label: requestData!.statusName!,
textColor: AppColor.getRequestStatusTextColorByName(context, requestData!.statusName!),
backgroundColor: AppColor.getRequestStatusColorByName(context, requestData!.statusName!),
),
1.width.expanded,
Text(
requestData!.transactionDate?.toServiceRequestCardFormat ?? "",
textAlign: TextAlign.end,
style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50),
),
],
),
8.height,
(requestData?.typeTransaction ?? "Internal Audit Request").heading5(context),
infoWidget(label: context.translation.assetNumber, value: requestData?.assetNumber, 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.push(context, MaterialPageRoute(builder: (context) => EquipmentInternalAuditDetailPage(auditId: requestDetails!.id!)));
});
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StatusLabel(
label: requestDetails!.status!,
textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails?.status!),
backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails?.status!),
),
1.width.expanded,
Text(
requestDetails!.date?.toServiceRequestCardFormat ?? "",
textAlign: TextAlign.end,
style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50),
),
],
),
8.height,
(requestDetails?.nameOfType ?? "Internal Audit Request").heading5(context),
8.height,
infoWidget(label: context.translation.assetNumber, value: requestDetails!.assetNo, context: context),
infoWidget(label: context.translation.assetSN, value: requestDetails!.assetSN, context: context),
infoWidget(label: context.translation.model, value: requestDetails!.model, context: context),
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.push(context, MaterialPageRoute(builder: (context) => EquipmentInternalAuditDetailPage(auditId: requestDetails!.id!)));
});
}
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,322 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/api_routes/api_manager.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/generic_attachment_model.dart';
import 'package:test_sa/models/timer_model.dart';
import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/modules/internal_audit_module/models/internal_audit_timer_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/update_audit_form_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/equipment_internal_audit_data_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/internal_audit_attachment_model.dart';
import 'package:test_sa/modules/internal_audit_module/provider/internal_audit_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/working_time_tile.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
import 'package:test_sa/views/widgets/loaders/loading_manager.dart';
import 'package:test_sa/views/widgets/timer/app_timer.dart';
import 'package:test_sa/views/widgets/total_working_time_detail_bottomsheet.dart';
class UpdateEquipmentInternalAuditPage extends StatefulWidget {
static const String id = "update-equipment-internal-audit";
EquipmentInternalAuditDataModel? model;
UpdateEquipmentInternalAuditPage({Key? key, this.model}) : super(key: key);
@override
State<UpdateEquipmentInternalAuditPage> createState() => _UpdateEquipmentInternalAuditPageState();
}
class _UpdateEquipmentInternalAuditPageState extends State<UpdateEquipmentInternalAuditPage> {
final bool _isLoading = false;
double totalWorkingHours = 0.0;
AuditFormModel formModel = AuditFormModel();
final TextEditingController _workingHoursController = TextEditingController();
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
List<GenericAttachmentModel> _attachments = [];
//TODO need to check if it's needed or not..
List<TimerHistoryModel> timerList = [];
@override
void initState() {
populateForm();
super.initState();
}
void populateForm() {
formModel.requestId = widget.model?.id;
formModel.id = widget.model?.id;
_attachments = widget.model?.engineerData?.attachments?.map((e) => GenericAttachmentModel(id: e.id?.toInt() ?? 0, name: e.name!)).toList() ?? [];
calculateWorkingTime();
}
void calculateWorkingTime() {
final helperModel = formModel;
final timers = helperModel.auditTimers ?? [];
totalWorkingHours = timers.fold<double>(0.0, (sum, item) {
if (item.startDate == null || item.endDate == null) return sum;
try {
final start = DateTime.parse(item.startDate!);
final end = DateTime.parse(item.endDate!);
final diffInHours = end.difference(start).inSeconds / 3600.0; // convert to hours
return sum + diffInHours;
} catch (_) {
return sum;
}
});
timerList = timers.map((e) {
return TimerHistoryModel(
id: e.id,
startTime: e.startDate,
endTime: e.endDate,
workingHours: e.startDate,
);
}).toList();
}
_onSubmit(BuildContext context) async {
bool isTimerPickerEnable = ApiManager.instance.assetGroup?.enabledEngineerTimer ?? false;
InternalAuditProvider provider = Provider.of<InternalAuditProvider>(context, listen: false);
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
formModel.auditTimers ??= [];
if (formModel.auditTimePicker != null) {
int durationInSecond = formModel.auditTimePicker!.endAt!.difference(formModel.auditTimePicker!.startAt!).inSeconds;
formModel.auditTimers?.add(
InternalAuditTimerModel(
id: 0,
startDate: formModel.auditTimePicker!.startAt!.toIso8601String(), // Handle potential null
endDate: formModel.auditTimePicker!.endAt?.toIso8601String(), // Handle potential null
totalWorkingHour: ((durationInSecond) / 60 / 60),
),
);
}
formModel.timerModelList?.forEach((timer) {
int durationInSecond = timer.endAt!.difference(timer.startAt!).inSeconds;
formModel.auditTimers?.add(
InternalAuditTimerModel(
id: 0,
startDate: timer.startAt!.toIso8601String(), // Handle potential null
endDate: timer.endAt?.toIso8601String(), // Handle potential null
totalWorkingHour: ((durationInSecond) / 60 / 60),
),
);
});
_formKey.currentState!.save();
formModel.attachments = [];
for (var item in _attachments) {
String fileName = ServiceRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name?.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? '';
formModel.attachments!.add(
InternalAuditAttachments(
id: item.id,
originalName: fileName,
name: fileName,
),
);
}
final success = await provider.updateEquipmentInternalAudit(model: formModel);
Navigator.pop(context);
if (formModel.isComplete == true) {
//Navigate to List screen.
} else if (formModel.isComplete == false) {
//Navigate to Detail screen.
if (success) {
Navigator.of(context).pop(true);
} else {
Fluttertoast.showToast(msg: 'Failed to update');
}
}
}
@override
void dispose() {
_workingHoursController.dispose();
super.dispose();
}
void updateTimer({TimerModel? timer}) {
if (timer?.startAt != null && timer?.endAt != null) {
final start = timer!.startAt!;
final end = timer.endAt!;
final difference = end.difference(start);
final totalHours = difference.inSeconds / 3600.0;
formModel.startTime = start;
formModel.endTime = end;
formModel.totalHours = totalHours;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: DefaultAppBar(
title: 'Update Information'.addTranslation,
onWillPopScope: () {
formModel.isComplete = false;
_onSubmit(context);
},
),
key: _scaffoldKey,
body: Form(
key: _formKey,
child: LoadingManager(
isLoading: _isLoading,
isFailedLoading: false,
stateCode: 200,
onRefresh: () async {},
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
8.height,
AppTextFormField(
labelText: 'Debrief'.addTranslation,
textInputType: TextInputType.multiline,
hintStyle: TextStyle(color: context.isDark ? AppColor.white10 : AppColor.black10),
labelStyle: TextStyle(color: context.isDark ? AppColor.white10 : AppColor.black10),
alignLabelWithHint: true,
initialValue: formModel.debrief,
backgroundColor: AppColor.fieldBgColor(context),
showShadow: false,
onSaved: (value) {
formModel.debrief = value;
},
),
8.height,
_timerWidget(context, totalWorkingHours),
16.height,
AttachmentPicker(
label: 'Upload Attachment',
attachment: _attachments,
buttonColor: AppColor.primary10,
onlyImages: false,
buttonIcon: 'attachment_icon'.toSvgAsset(
color: AppColor.primary10,
),
),
8.height,
],
).toShadowContainer(context),
).expanded,
FooterActionButton.footerContainer(
context: context,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
AppFilledButton(
label: context.translation.save,
buttonColor: context.isDark ? AppColor.neutral70 : AppColor.white60,
textColor: context.isDark ? AppColor.white10 : AppColor.black10,
onPressed: () {
formModel.isComplete = false;
_onSubmit(context);
}).expanded,
12.width,
AppFilledButton(
label: context.translation.complete,
buttonColor: AppColor.primary10,
onPressed: () {
formModel.isComplete = true;
_onSubmit(context);
}).expanded,
],
),
),
],
)),
),
).handlePopScope(
cxt: context,
onSave: () {
formModel.isComplete = false;
_onSubmit(context);
});
}
Widget _timerWidget(BuildContext context, double totalWorkingHours) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
//TODO multiple timer ..
// AppTimer(
// label: context.translation.timer,
// timer: formModel.auditTimerModel,
// // enabled: enableTimer,
// pickerTimer: formModel.auditTimePicker,
// pickerFromDate: DateTime.tryParse(widget.model?.createdDate ?? ''),
// onPick: (time) {
// formModel.auditTimePicker = time;
// setState(() {});
// log('Time picker start ${formModel.auditTimePicker?.startAt}');
// log('Time picker end ${formModel.auditTimePicker?.endAt}');
// },
// timerProgress: (isRunning) {},
// onChange: (timer) async {
// formModel.auditTimerModel = timer;
// log('start ${formModel.auditTimerModel?.startAt}');
// log('end ${formModel.auditTimerModel?.endAt}');
// if (timer.startAt != null && timer.endAt != null) {
// formModel.timerModelList = formModel.timerModelList ?? [];
// formModel.timerModelList!.add(timer);
// }
// setState(() {});
// log('list length ${formModel.timerModelList?.length}');
// return true;
// },
// ),
AppTimer(
label: context.translation.workingHours,
timer: formModel.auditTimerModel,
pickerFromDate: DateTime.tryParse(widget.model?.createdDate ?? ''),
pickerTimer: formModel.auditTimePicker,
showPicker: false,
onPick: (timer) {
updateTimer(timer: timer);
},
width: double.infinity,
decoration: BoxDecoration(
color: AppColor.fieldBgColor(context),
borderRadius: BorderRadius.circular(10),
),
timerProgress: (isRunning) {},
onChange: (timer) async {
updateTimer(timer: timer);
log('here onChange ${timer.startAt}');
return true;
},
),
if (totalWorkingHours > 0.0) ...[
12.height,
WorkingTimeTile(
timerList: timerList,
totalWorkingTime: totalWorkingHours,
),
],
],
);
}
}

@ -0,0 +1,278 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
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/device/asset.dart';
import 'package:test_sa/models/generic_attachment_model.dart';
import 'package:test_sa/models/helper_data_models/workorder/work_order_helper_models.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/modules/internal_audit_module/models/equipment_internal_audit_form_model.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_form_model.dart';
import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/system_audit_work_order_auto_complete_field.dart';
import 'package:test_sa/modules/internal_audit_module/provider/internal_audit_checklist_provider.dart';
import 'package:test_sa/modules/internal_audit_module/provider/internal_audit_finding_type_provider.dart';
import 'package:test_sa/modules/internal_audit_module/provider/internal_audit_provider.dart';
import 'package:test_sa/modules/internal_audit_module/provider/internal_audit_wo_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/views/widgets/equipment/asset_picker.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
import 'package:test_sa/views/widgets/parts/auto_complete_parts_field.dart';
import '../../../../../../../new_views/common_widgets/default_app_bar.dart';
class CreateSystemInternalAuditForm extends StatefulWidget {
static const String id = "/create-system-audit-view";
const CreateSystemInternalAuditForm({Key? key}) : super(key: key);
@override
_CreateSystemInternalAuditFormState createState() => _CreateSystemInternalAuditFormState();
}
//TODO remove unnecessary code
class _CreateSystemInternalAuditFormState extends State<CreateSystemInternalAuditForm> with TickerProviderStateMixin {
late TextEditingController _commentController;
late InternalAuditProvider _internalAuditProvider;
final SystemInternalAuditFormModel _model = SystemInternalAuditFormModel();
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final List<GenericAttachmentModel> _attachments = [];
late TextEditingController _woAutoCompleteController;
bool showLoading = false;
@override
void initState() {
super.initState();
_commentController = TextEditingController();
_woAutoCompleteController = TextEditingController();
_internalAuditProvider = Provider.of<InternalAuditProvider>(context, listen: false);
}
@override
void dispose() {
_commentController.dispose();
_woAutoCompleteController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: DefaultAppBar(title: 'System Internal Audit Checklist'.addTranslation),
body: Form(
key: _formKey,
child: Column(
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleItemDropDownMenu<Lookup, InternalAuditWoTypeProvider>(
context: context,
height: 56.toScreenHeight,
title: 'Wo Type'.addTranslation,
showShadow: false,
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
initialValue: _model.workOrderType,
onSelect: (status) {
if (status != null) {
_model.workOrderType = status;
_model.workOrderDetailModel = SystemAuditWorkOrderDetailModel();
_clearWorkOrderSelectionIds();
_woAutoCompleteController.clear();
setState(() {});
}
},
),
12.height,
SystemAuditWoAutoCompleteField(
woTypeId: _model.workOrderType?.value,
controller: _woAutoCompleteController,
clearAfterPick: false,
initialValue: '',
onPick: (wo) async {
InternalAuditProvider provider = Provider.of<InternalAuditProvider>(context, listen: false);
setState(() {
showLoading = true;
});
_model.workOrderDetailModel = await provider.loadAllWorkOrderDetailsByID(workOrderId: wo.id!, workOrderTypeId: _model.workOrderType!.value!);
updateWorkOrderReference(woTypeId: _model.workOrderType?.value, selectedId: wo.id);
setState(() {
showLoading = false;
});
},
),
12.height,
// if(_internalAuditModel.woOrderNo!=null)
_workOrderInfoWidget(context),
12.height,
SingleItemDropDownMenu<Lookup, InternalAuditFindingTypeProvider>(
context: context,
height: 56.toScreenHeight,
title: 'Finding Type'.addTranslation,
showShadow: false,
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
initialValue: _model.findingType,
onSelect: (status) {
if (status != null) {
_model.findingType = status;
setState(() {});
}
},
),
12.height,
AppTextFormField(
backgroundColor: AppColor.fieldBgColor(context),
labelText: 'Finding Description'.addTranslation,
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
alignLabelWithHint: true,
textInputType: TextInputType.multiline,
showShadow: false,
onSaved: (value) {
_model.findingDescription = value;
},
),
16.height,
AttachmentPicker(
label: context.translation.attachments,
attachment: _attachments,
buttonColor: AppColor.primary10,
onlyImages: false,
buttonIcon: 'attachment_icon'.toSvgAsset(color: AppColor.primary10),
),
],
).toShadowContainer(context),
).expanded,
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
label: context.translation.submitRequest,
onPressed: _submit,
),
),
],
),
),
);
}
Future<void> _submit() async {
InternalAuditProvider internalAuditProvider = Provider.of<InternalAuditProvider>(context, listen: false);
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
_model.attachments=[];
for (var item in _attachments) {
String fileName = ServiceRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? '';
_model.attachments?.add(InternalAuditAttachments(id: item.id, name: fileName));
}
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
_model.auditorId = context.userProvider.user?.userID;
bool status = await internalAuditProvider.addSystemInternalAudit(context: context, request: _model);
Navigator.pop(context);
if (status) {
Navigator.pop(context);
}
}
}
Widget _workOrderInfoWidget(BuildContext context) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: showLoading ? Colors.white : const Color(0xffF4F6FC),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: const Color(0xff212936).withOpacity(.03),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Text(
_model.workOrderType?.name ?? 'WO Type',
style: TextStyle(
fontSize: 14.toScreenWidth,
fontWeight: FontWeight.w500,
fontStyle: FontStyle.normal,
color: Colors.black87,
decoration: TextDecoration.none,
),
).expanded,
const Icon(
Icons.info,
color: Color(0xff7D859A),
size: 20,
),
],
),
6.height,
"${context.translation.site}: ${_model.workOrderDetailModel?.woSite ?? '-'}".bodyText(context),
"${'Eng. Name'.addTranslation}: ${_model.workOrderDetailModel?.woAssignedEngineer ?? '-'}".bodyText(context),
"${'Asset Name'.addTranslation}: ${_model.workOrderDetailModel?.woAssetName ?? '-'}".bodyText(context),
"${context.translation.model}: ${_model.workOrderDetailModel?.woModel ?? '-'}".bodyText(context),
"${context.translation.manufacture}: ${_model.workOrderDetailModel?.woManufacturer ?? '-'}".bodyText(context),
"${'SN'.addTranslation}: ${_model.workOrderDetailModel?.wosn ?? '-'}".bodyText(context),
"${context.translation.assetNo}: ${_model.workOrderDetailModel?.woAssetNo ?? '-'}".bodyText(context),
],
),
).toShimmer(isShow: showLoading, context: context);
}
void updateWorkOrderReference({int? woTypeId, int? selectedId}) {
_clearWorkOrderSelectionIds();
if (woTypeId == null || selectedId == null) return;
switch (woTypeId) {
case 1: // CM
_model.correctiveMaintenanceId = selectedId;
break;
case 2: // Gas Refill
_model.gasRefillId = selectedId;
break;
case 3: // Asset Transfer
_model.assetTransferId = selectedId;
break;
case 4: // PPM Request
_model.planPreventiveVisitId = selectedId;
break;
case 5: // Recurrent WOx
_model.planRecurrentTaskId = selectedId;
break;
case 6: // Task
_model.taskJobId = selectedId;
break;
case 7: // Recall & Alert
_model.taskAlertJobId = selectedId;
break;
}
}
void _clearWorkOrderSelectionIds() {
_model.correctiveMaintenanceId = null;
_model.planPreventiveVisitId = null;
_model.assetTransferId = null;
_model.taskJobId = null;
_model.taskAlertJobId = null;
_model.gasRefillId = null;
_model.planRecurrentTaskId = null;
}
}

@ -0,0 +1,149 @@
import 'dart:developer';
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/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/modules/internal_audit_module/models/system_internal_audit_form_model.dart';
import 'package:test_sa/modules/internal_audit_module/provider/internal_audit_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 SystemAuditWoAutoCompleteField extends StatefulWidget {
final String initialValue;
final int? woTypeId;
final TextEditingController controller;
final bool clearAfterPick;
final Function(WoAutoCompleteModel) onPick;
const SystemAuditWoAutoCompleteField({Key? key, required this.initialValue, this.woTypeId, required this.onPick, this.clearAfterPick = true, required this.controller}) : super(key: key);
@override
_SystemAuditWoAutoCompleteFieldState createState() => _SystemAuditWoAutoCompleteFieldState();
}
class _SystemAuditWoAutoCompleteFieldState extends State<SystemAuditWoAutoCompleteField> {
late InternalAuditProvider _provider;
//
// late TextEditingController _controller;
bool loading = false;
@override
void initState() {
// w controller = widget.controller ?? TextEditingController(text: widget.initialValue);
super.initState();
_provider = Provider.of<InternalAuditProvider>(context, listen: false);
}
@override
void didUpdateWidget(covariant SystemAuditWoAutoCompleteField oldWidget) {
// if (widget.initialValue != oldWidget.initialValue) {
// // _controller = widget.controller ?? TextEditingController(text: widget.initialValue);
// }
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
if (widget.controller == null) {
// _controller.dispose();
}
super.dispose();
}
@override
Widget build(BuildContext 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<WoAutoCompleteModel>(
optionsBuilder: (TextEditingValue textEditingValue) async {
if (textEditingValue.text.isEmpty) {
if (loading) {
setState(() {
loading = false;
});
}
return const Iterable<WoAutoCompleteModel>.empty();
}
if (!loading) {
setState(() {
loading = true;
});
}
List<WoAutoCompleteModel> workOrders = (await _provider.getWorkOrderByWoType(text: textEditingValue.text, woId:widget.woTypeId));
setState(() {
loading = false;
});
return workOrders;
},
displayStringForOption: (WoAutoCompleteModel option) => option.displayName ?? '',
fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) {
return TextField(
controller: widget.controller,
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(maxHeight: 24, maxWidth: 24 + 8),
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: context.translation.woNumber,
labelStyle: AppTextStyles.tinyFont.copyWith(color: AppColor.textColor(context)),
suffixIcon: loading ? const CircularProgressIndicator(color: AppColor.primary10, strokeWidth: 3.0).paddingOnly(end: 8) : null,
),
textInputAction: TextInputAction.search,
onChanged: (text) {
fieldTextEditingController.text = text;
},
onSubmitted: (String value) {
onFieldSubmitted();
},
);
},
onSelected: (WoAutoCompleteModel selection) {
if (widget.clearAfterPick) {
widget.controller.clear();
} else {
widget.controller.text = (selection.displayName ?? "");
}
widget.onPick(selection);
},
),
);
}
// String getUrl(int? woTypeId) {
// if (woTypeId == null) return '';
// String base = URLs.woAutoCompleteBase;
// var urlMap = {
// 1: '$base/ServiceRequest/WorkOrderAutoComplete', // CM
// 2: '$base/GasRefill/GetGasRefillAutoComplete', // Gas Refill
// 3: '$base/AssetTransfer/GetAssetTransferAutoComplete', // Asset Transfer
// 4: '$base/PlanPreventiveVisit/GetAutoCompletePlanPreventiveVisit', // PPM Request
// 5: '$base/PlanRecurrentTasks/GetPlanRecurrentTask', // Recurrent WO
// 6: '$base/TaskJobs/AutocompleteTaskJob', // Task
// 7: '$base/TaskJobs/AutocompleteTaskJob', // Recall & Alert
// };
// return urlMap[woTypeId] ?? '';
// }
}

@ -0,0 +1,219 @@
import 'dart:developer';
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/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/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/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';
class SystemInternalAuditDetailPage extends StatefulWidget {
static const String id = "/details-system-internal-audit";
final int auditId;
SystemInternalAuditDetailPage({Key? key, required this.auditId}) : super(key: key);
@override
_SystemInternalAuditDetailPageState createState() {
return _SystemInternalAuditDetailPageState();
}
}
class _SystemInternalAuditDetailPageState extends State<SystemInternalAuditDetailPage> {
bool isWoType = true;
SystemInternalAuditDataModel? model;
late InternalAuditProvider _internalAuditProvider;
List<InternalAuditAttachments> allAttachments = [];
@override
void initState() {
super.initState();
_internalAuditProvider = Provider.of<InternalAuditProvider>(context, listen: false);
WidgetsBinding.instance.addPostFrameCallback((_) {
getAuditData();
});
}
Future<void> getAuditData() async {
model = await _internalAuditProvider.getInternalSystemAuditById(widget.auditId);
allAttachments.clear();
allAttachments = [
...(model?.attachments ?? []),
...(model?.engineerData?.attachments ?? []),
];
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
//TODO need to check when implementing provider needed or not .
return Scaffold(
appBar: const DefaultAppBar(title: "Request Details"),
body: Selector<InternalAuditProvider, bool>(
selector: (_, provider) => provider.isLoading,
builder: (_, isLoading, __) {
if (isLoading) return const ALoading();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
assetInformation(),
8.height,
workOrderInformation(),
8.height,
//TODO need to check for comments
if (model?.findingDescription?.isNotEmpty ?? false) ...[
const Divider().defaultStyle(context),
// Text(
// "Comments".addTranslation,
// style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
// ),
model!.findingDescription!.bodyText(context),
],
//TODO need to check for attachments
if (allAttachments.isNotEmpty) ...[
const Divider().defaultStyle(context),
Text(
"Attachments".addTranslation,
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
8.height,
FilesList(images: allAttachments.map((e) => URLs.getFileUrl(e.name ?? '') ?? '').toList() ?? []),
],
],
).paddingAll(0).toShadowContainer(context),
).expanded,
if (context.userProvider.isEngineer)
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
label: "Update",
onPressed: () async {
final result = await Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => UpdateSystemInternalAuditPage(model: model),
),
);
if (result == true) {
await getAuditData();
setState(() {}); // refresh UI with new model
}
}),
),
],
);
},
));
}
Widget workOrderInformation() {
final details = model?.workOrderDetails;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Divider().defaultStyle(context),
Text(
"WO Info",
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
6.height,
labelValueText(context, context.translation.woNumber, details?.workOrderNo),
labelValueText(context, 'WO Type'.addTranslation, model?.workOrderType?.name),
labelValueText(context, context.translation.site, details?.woSite),
labelValueText(context, context.translation.assetName, details?.woAssetName),
labelValueText(context, context.translation.manufacture, details?.woManufacturer),
labelValueText(context, context.translation.model, details?.woModel),
],
);
}
Widget requestDetails() {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Divider().defaultStyle(context),
Text(
"Request Details",
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
6.height,
checklistWidget(value: 'Asset Tag'.addTranslation),
checklistWidget(value: 'Expired PM Tag'.addTranslation),
],
);
}
Widget assetInformation() {
final details = model?.workOrderDetails;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Asset Details",
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
6.height,
labelValueText(context, context.translation.assetName, details?.woAssetName),
labelValueText(context, context.translation.assetNo, details?.woAssetNo),
labelValueText(context, context.translation.manufacture, details?.woManufacturer),
labelValueText(context, context.translation.model, details?.woModel),
],
);
}
Widget labelValueText(BuildContext context, String label, String? value) {
if (value == null || value.isEmpty) return const SizedBox.shrink();
return Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Text(
'$label: $value',
style: AppTextStyles.bodyText.copyWith(
color: context.isDark ? AppColor.neutral30 : AppColor.neutral120,
),
),
);
}
Widget checklistWidget({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,117 @@
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/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_detail_page.dart';
import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_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 SystemInternalAuditItemView extends StatelessWidget {
final Data? requestData;
final RequestsDetails? requestDetails;
final bool showShadow;
const SystemInternalAuditItemView({Key? key, this.requestData, this.requestDetails, this.showShadow = true}) : super(key: key);
@override
Widget build(BuildContext context) {
//TODO need to refactor this code repetation @waseem
if (requestData != null) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StatusLabel(
label: requestData!.statusName!,
textColor: AppColor.getRequestStatusTextColorByName(context, requestData!.statusName!),
backgroundColor: AppColor.getRequestStatusColorByName(context, requestData!.statusName!),
),
1.width.expanded,
Text(
requestData!.transactionDate?.toServiceRequestCardFormat ?? "",
textAlign: TextAlign.end,
style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50),
),
],
),
8.height,
(requestData?.typeTransaction ?? "Internal Audit Request").heading5(context),
infoWidget(label: context.translation.assetNo, value: requestData?.assetNumber, 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.push(context, MaterialPageRoute(builder: (context) => SystemInternalAuditDetailPage(auditId: requestDetails!.id!)));
});
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StatusLabel(
label: requestDetails!.status!,
textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails?.status!),
backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails?.status!),
),
1.width.expanded,
Text(
requestDetails!.date?.toServiceRequestCardFormat ?? "",
textAlign: TextAlign.end,
style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50),
),
],
),
8.height,
(requestDetails?.nameOfType ?? "Internal Audit Request").heading5(context),
infoWidget(label: context.translation.assetNumber, value: requestDetails!.assetNo, context: context),
infoWidget(label: context.translation.assetSN, value: requestDetails!.assetSN, context: context),
infoWidget(label: context.translation.model, value: requestDetails!.model, context: context),
8.height,
// infoWidget(label: context.translation.site, 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.push(context, MaterialPageRoute(builder: (context) => SystemInternalAuditDetailPage(auditId: requestDetails!.id!)));
});
}
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,316 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/api_routes/api_manager.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/generic_attachment_model.dart';
import 'package:test_sa/models/timer_model.dart';
import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/modules/internal_audit_module/models/internal_audit_timer_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/models/update_audit_form_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/internal_audit_attachment_model.dart';
import 'package:test_sa/modules/internal_audit_module/provider/internal_audit_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/working_time_tile.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
import 'package:test_sa/views/widgets/loaders/loading_manager.dart';
import 'package:test_sa/views/widgets/timer/app_timer.dart';
import 'package:test_sa/views/widgets/total_working_time_detail_bottomsheet.dart';
class UpdateSystemInternalAuditPage extends StatefulWidget {
static const String id = "update-system-internal-audit";
SystemInternalAuditDataModel? model;
UpdateSystemInternalAuditPage({Key? key, this.model}) : super(key: key);
@override
State<UpdateSystemInternalAuditPage> createState() => _UpdateSystemInternalAuditPageState();
}
class _UpdateSystemInternalAuditPageState extends State<UpdateSystemInternalAuditPage> {
final bool _isLoading = false;
double totalWorkingHours = 0.0;
AuditFormModel formModel = AuditFormModel();
final TextEditingController _workingHoursController = TextEditingController();
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
List<GenericAttachmentModel> _attachments = [];
//TODO need to check if it's needed or not..
List<TimerHistoryModel> timerList = [];
@override
void initState() {
populateForm();
super.initState();
}
void populateForm() {
formModel.requestId = widget.model?.id;
formModel.id = widget.model?.id;
_attachments = widget.model?.engineerData?.attachments?.map((e) => GenericAttachmentModel(id: e.id?.toInt() ?? 0, name: e.name!)).toList() ?? [];
calculateWorkingTime();
}
void calculateWorkingTime() {
final helperModel = formModel;
final timers = helperModel.auditTimers ?? [];
totalWorkingHours = timers.fold<double>(0.0, (sum, item) {
if (item.startDate == null || item.endDate == null) return sum;
try {
final start = DateTime.parse(item.startDate!);
final end = DateTime.parse(item.endDate!);
final diffInHours = end.difference(start).inSeconds / 3600.0; // convert to hours
return sum + diffInHours;
} catch (_) {
return sum;
}
});
timerList = timers.map((e) {
return TimerHistoryModel(
id: e.id,
startTime: e.startDate,
endTime: e.endDate,
workingHours: e.startDate,
);
}).toList();
}
_onSubmit(BuildContext context) async {
bool isTimerPickerEnable = ApiManager.instance.assetGroup?.enabledEngineerTimer ?? false;
InternalAuditProvider provider = Provider.of<InternalAuditProvider>(context, listen: false);
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
formModel.auditTimers ??= [];
if (formModel.auditTimePicker != null) {
int durationInSecond = formModel.auditTimePicker!.endAt!.difference(formModel.auditTimePicker!.startAt!).inSeconds;
formModel.auditTimers?.add(
InternalAuditTimerModel(
id: 0,
startDate: formModel.auditTimePicker!.startAt!.toIso8601String(), // Handle potential null
endDate: formModel.auditTimePicker!.endAt?.toIso8601String(), // Handle potential null
totalWorkingHour: ((durationInSecond) / 60 / 60),
),
);
}
formModel.timerModelList?.forEach((timer) {
int durationInSecond = timer.endAt!.difference(timer.startAt!).inSeconds;
formModel.auditTimers?.add(
InternalAuditTimerModel(
id: 0,
startDate: timer.startAt!.toIso8601String(), // Handle potential null
endDate: timer.endAt?.toIso8601String(), // Handle potential null
totalWorkingHour: ((durationInSecond) / 60 / 60),
),
);
});
_formKey.currentState!.save();
formModel.attachments = [];
for (var item in _attachments) {
String fileName = ServiceRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name?.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? '';
formModel.attachments!.add(
InternalAuditAttachments(
id: item.id,
// originalName: fileName,
name: fileName,
),
);
}
log('submit press');
log('data ${formModel.toJson()}');
final success = await provider.updateSystemInternalAudit(model: formModel);
Navigator.pop(context);
if (formModel.isComplete == true) {
//Navigate to List screen.
} else if (formModel.isComplete == false) {
if (success) {
Navigator.of(context).pop(true);
} else {
Fluttertoast.showToast(msg: 'Failed to update');
}
}
}
@override
void dispose() {
_workingHoursController.dispose();
super.dispose();
}
void updateTimer({TimerModel? timer}) {
if (timer?.startAt != null && timer?.endAt != null) {
final start = timer!.startAt!;
final end = timer.endAt!;
final difference = end.difference(start);
final totalHours = difference.inSeconds / 3600.0;
formModel.startTime = start;
formModel.endTime = end;
formModel.totalHours = totalHours;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: DefaultAppBar(
title: 'Update Information'.addTranslation,
onWillPopScope: () {
formModel.isComplete = false;
_onSubmit(context);
},
),
key: _scaffoldKey,
body: Form(
key: _formKey,
child: LoadingManager(
isLoading: _isLoading,
isFailedLoading: false,
stateCode: 200,
onRefresh: () async {},
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
8.height,
AppTextFormField(
labelText: 'Debrief'.addTranslation,
textInputType: TextInputType.multiline,
hintStyle: TextStyle(color: context.isDark ? AppColor.white10 : AppColor.black10),
labelStyle: TextStyle(color: context.isDark ? AppColor.white10 : AppColor.black10),
alignLabelWithHint: true,
initialValue: formModel.debrief,
backgroundColor: AppColor.fieldBgColor(context),
showShadow: false,
onSaved: (value) {
formModel.debrief = value;
},
),
8.height,
_timerWidget(context, totalWorkingHours),
16.height,
AttachmentPicker(
label: 'Upload Attachment',
attachment: _attachments,
buttonColor: AppColor.primary10,
onlyImages: false,
buttonIcon: 'attachment_icon'.toSvgAsset(
color: AppColor.primary10,
),
),
8.height,
],
).toShadowContainer(context),
).expanded,
FooterActionButton.footerContainer(
context: context,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
AppFilledButton(
label: context.translation.save,
buttonColor: context.isDark ? AppColor.neutral70 : AppColor.white60,
textColor: context.isDark ? AppColor.white10 : AppColor.black10,
onPressed: () {
log('button press ');
formModel.isComplete = false;
_onSubmit(context);
}).expanded,
12.width,
AppFilledButton(
label: context.translation.complete,
buttonColor: AppColor.primary10,
onPressed: () {
formModel.isComplete = true;
_onSubmit(context);
}).expanded,
],
),
),
],
)),
),
).handlePopScope(
cxt: context,
onSave: () {
formModel.isComplete = false;
_onSubmit(context);
});
}
Widget _timerWidget(BuildContext context, double totalWorkingHours) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
///this is for multiple timers...
// AppTimer(
// label: context.translation.timer,
// timer: formModel.auditTimerModel,
// // enabled: enableTimer,
// pickerTimer: formModel.auditTimePicker,
// pickerFromDate: DateTime.tryParse(widget.model?.createdDate ?? ''),
// onPick: (time) {
// formModel.auditTimePicker = time;
// },
// timerProgress: (isRunning) {},
// onChange: (timer) async {
// formModel.auditTimerModel = timer;
// if (timer.startAt != null && timer.endAt != null) {
// formModel.timerModelList = formModel.timerModelList ?? [];
// formModel.timerModelList!.add(timer);
// }
// return true;
// },
// ),
AppTimer(
label: context.translation.workingHours,
showPicker: false,
timer: formModel.auditTimerModel,
pickerFromDate: DateTime.tryParse(widget.model?.createdDate ?? ''),
pickerTimer: formModel.auditTimePicker,
onPick: (time) {
updateTimer(timer: time);
},
width: double.infinity,
decoration: BoxDecoration(
color: AppColor.fieldBgColor(context),
borderRadius: BorderRadius.circular(10),
),
timerProgress: (isRunning) {},
onChange: (timer) async {
updateTimer(timer: timer);
log('here onChange ${timer.startAt}');
return true;
},
),
if (totalWorkingHours > 0.0) ...[
12.height,
WorkingTimeTile(
timerList: timerList,
totalWorkingTime: totalWorkingHours,
),
],
],
);
}
}

@ -0,0 +1,36 @@
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 InternalAuditCheckListProvider extends LoadingListNotifier<Lookup> {
@override
Future getData({int? id}) async {
if (loading ?? false) return -2;
loading = true;
notifyListeners();
Response response;
try {
response = await ApiManager.instance.get(URLs.getInternalAuditChecklist);
} catch (error) {
loading = false;
stateCode = -1;
notifyListeners();
return -1;
}
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
// client's request was successfully received
List listJson = json.decode(response.body)["data"];
items = listJson.map((department) => Lookup.fromJson(department)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
}
}

@ -0,0 +1,34 @@
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 InternalAuditFindingTypeProvider extends LoadingListNotifier<Lookup> {
@override
Future getData({int? id}) async {
if (loading ?? false) return -2;
loading = true;
notifyListeners();
Response response;
try {
response = await ApiManager.instance.get(URLs.getInternalAuditFindingType);
} catch (error) {
loading = false;
stateCode = -1;
notifyListeners();
return -1;
}
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
// client's request was successfully received
List listJson = json.decode(response.body)["data"];
items = listJson.map((department) => Lookup.fromJson(department)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
}
}

@ -0,0 +1,211 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:http/http.dart';
import 'package:test_sa/controllers/api_routes/api_manager.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/models/device/asset_search.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/models/new_models/asset_nd_auto_complete_by_dynamic_codes_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/update_audit_form_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/equipment_internal_audit_data_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/equipment_internal_audit_form_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/models/system_internal_audit_form_model.dart';
import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart';
class InternalAuditProvider 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 isNextPageLoading = false;
bool isLoading = false;
Future<EquipmentInternalAuditDataModel?> getEquipmentInternalAuditById(int id) async {
try {
isLoading = true;
notifyListeners();
Response response = await ApiManager.instance.get(
"${URLs.getInternalAuditEquipmentById}?internalAuditEquipmentId=$id",
);
if (response.statusCode >= 200 && response.statusCode < 300) {
final decodedBody = jsonDecode(response.body);
EquipmentInternalAuditDataModel model = EquipmentInternalAuditDataModel.fromJson(decodedBody["data"]);
isLoading = false;
notifyListeners();
return model;
} else {
isLoading = false;
notifyListeners();
return null;
}
} catch (error) {
isLoading = false;
notifyListeners();
return null;
}
}
Future<SystemInternalAuditDataModel?> getInternalSystemAuditById(int id) async {
try {
isLoading = true;
notifyListeners();
Response response = await ApiManager.instance.get("${URLs.getInternalAuditSystemById}?AuditSystemId=$id");
if (response.statusCode >= 200 && response.statusCode < 300) {
final decodedBody = jsonDecode(response.body);
SystemInternalAuditDataModel model = SystemInternalAuditDataModel.fromJson(decodedBody["data"]);
isLoading = false;
notifyListeners();
return model;
} else {
isLoading = false;
notifyListeners();
return null;
}
} catch (error) {
isLoading = false;
notifyListeners();
return null;
}
}
Future<SystemAuditWorkOrderDetailModel?> loadAllWorkOrderDetailsByID({required int workOrderTypeId, required int workOrderId}) async {
try {
isLoading = true;
notifyListeners();
Response response = await ApiManager.instance.get("${URLs.loadAllWorkOrderDetailsByID}?workOrderTypeId=$workOrderTypeId&workOrderId=$workOrderId");
if (response.statusCode >= 200 && response.statusCode < 300) {
final decodedBody = jsonDecode(response.body);
SystemAuditWorkOrderDetailModel model = SystemAuditWorkOrderDetailModel.fromJson(decodedBody["data"]);
isLoading = false;
notifyListeners();
return model;
} else {
isLoading = false;
notifyListeners();
return null;
}
} catch (error) {
isLoading = false;
notifyListeners();
return null;
}
}
Future<bool> updateEquipmentInternalAudit({required AuditFormModel model}) async {
isLoading = true;
Response response;
try {
response = await ApiManager.instance.put(URLs.updateAuditEquipmentsEngineer, body: model.toJson());
stateCode = response.statusCode;
isLoading = false;
notifyListeners();
if (stateCode == 200) {
return true;
}
return false;
} catch (error) {
isLoading = false;
stateCode = -1;
notifyListeners();
return false;
}
}
Future<bool> updateSystemInternalAudit({required AuditFormModel model}) async {
isLoading = true;
Response response;
try {
response = await ApiManager.instance.put(URLs.updateAuditSystemEngineer, body: model.toJson());
stateCode = response.statusCode;
isLoading = false;
notifyListeners();
log('status code ${stateCode}');
if (stateCode == 200) {
return true;
}
return false;
} catch (error) {
isLoading = false;
stateCode = -1;
notifyListeners();
return false;
}
}
Future<bool> addEquipmentInternalAudit({
required BuildContext context,
required EquipmentInternalAuditFormModel request,
}) async {
bool status = false;
Response response;
try {
response = await ApiManager.instance.post(URLs.addOrUpdateEquipmentInternalAudit, body: request.toJson());
if (response.statusCode >= 200 && response.statusCode < 300) {
status = true;
notifyListeners();
} else {
Fluttertoast.showToast(msg: "${context.translation.failedRequestMessage} :${json.decode(response.body)['message']}");
status = false;
}
return status;
} catch (error) {
print(error);
status = false;
notifyListeners();
return status;
}
}
Future<bool> addSystemInternalAudit({
required BuildContext context,
required SystemInternalAuditFormModel request,
}) async {
bool status = false;
Response response;
try {
response = await ApiManager.instance.post(URLs.addOrUpdateInternalAuditSystem, body: request.toJson());
if (response.statusCode >= 200 && response.statusCode < 300) {
status = true;
notifyListeners();
} else {
Fluttertoast.showToast(msg: "${context.translation.failedRequestMessage} :${json.decode(response.body)['message']}");
status = false;
}
return status;
} catch (error) {
print(error);
status = false;
notifyListeners();
return status;
}
}
Future<List<WoAutoCompleteModel>> getWorkOrderByWoType({String? text, required int? woId}) async {
late Response response;
try {
Response response = await ApiManager.instance.get("${URLs.getWoAutoComplete}?workOrderTypeId=$woId&search=$text");
// response = await ApiManager.instance.post(url, body: {"search": text});
List<WoAutoCompleteModel> woOrderList = [];
if (response.statusCode >= 200 && response.statusCode < 300) {
List categoriesListJson = json.decode(response.body)["data"];
woOrderList = categoriesListJson.map((wo) => WoAutoCompleteModel.fromJson(wo)).toList();
}
return woOrderList;
} catch (error) {
return [];
}
}
}

@ -0,0 +1,35 @@
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 InternalAuditWoTypeProvider extends LoadingListNotifier<Lookup> {
@override
Future getData({int? id}) async {
if (loading ?? false) return -2;
loading = true;
notifyListeners();
Response response;
try {
response = await ApiManager.instance.get(URLs.getInternalAuditWoType);
} 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((department) => Lookup.fromJson(department)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
}
}

@ -271,7 +271,7 @@ class _UpdateTaskRequestState extends State<UpdateTaskRequest> {
taskModel?.timerModelList?.forEach((timer) {
int durationInSecond = timer.endAt!.difference(timer.startAt!).inSeconds;
taskModel.taskJobActivityEngineerTimers?.add(
TaskJobActivityEngineerTimer(
TaskJobActivityEngineerTimer (
id: 0, startDate: timer.startAt!.toIso8601String(), endDate: timer.endAt?.toIso8601String(), totalWorkingHour: ((durationInSecond) / 60 / 60), comment: timer.comments ?? comments),
);
});

@ -0,0 +1,172 @@
class AssetAnalytics {
num? assetId;
String? assetNumber;
String? assetName;
num? lastPrice;
String? lastPONo;
String? lastPODate;
num? sameAssetQuantity;
String? slaStatus;
String? contractNumber;
String? contractStartDate;
String? contractEndDate;
num? assetAgeInDays;
String? assetAgeDescription;
num? totalMaintenanceCost;
num? sparePartCost;
num? laborCost;
num? exchangeCost;
num? travelCost;
num? netBookValue;
num? purchasedPrice;
num? lifeSpan;
num? mtbf;
String? mtbfDescription;
num? totalCMWorkOrders;
num? downTimeHours;
String? downTimeDescription;
num? upTimeHours;
String? upTimeDescription;
num? operationHours;
String? installationDate;
String? modelName;
String? manufacturerName;
String? serialNumber;
String? departmentName;
String? siteName;
num? id;
String? createdBy;
String? createdDate;
String? modifiedBy;
String? modifiedDate;
AssetAnalytics(
{this.assetId,
this.assetNumber,
this.assetName,
this.lastPrice,
this.lastPONo,
this.lastPODate,
this.sameAssetQuantity,
this.slaStatus,
this.contractNumber,
this.contractStartDate,
this.contractEndDate,
this.assetAgeInDays,
this.assetAgeDescription,
this.totalMaintenanceCost,
this.sparePartCost,
this.laborCost,
this.exchangeCost,
this.travelCost,
this.netBookValue,
this.purchasedPrice,
this.lifeSpan,
this.mtbf,
this.mtbfDescription,
this.totalCMWorkOrders,
this.downTimeHours,
this.downTimeDescription,
this.upTimeHours,
this.upTimeDescription,
this.operationHours,
this.installationDate,
this.modelName,
this.manufacturerName,
this.serialNumber,
this.departmentName,
this.siteName,
this.id,
this.createdBy,
this.createdDate,
this.modifiedBy,
this.modifiedDate});
AssetAnalytics.fromJson(Map<String, dynamic> json) {
assetId = json['assetId'];
assetNumber = json['assetNumber'];
assetName = json['assetName'];
lastPrice = json['lastPrice'];
lastPONo = json['lastPONo'];
lastPODate = json['lastPODate'];
sameAssetQuantity = json['sameAssetQuantity'];
slaStatus = json['slaStatus'];
contractNumber = json['contractNumber'];
contractStartDate = json['contractStartDate'];
contractEndDate = json['contractEndDate'];
assetAgeInDays = json['assetAgeInDays'];
assetAgeDescription = json['assetAgeDescription'];
totalMaintenanceCost = json['totalMaintenanceCost'];
sparePartCost = json['sparePartCost'];
laborCost = json['laborCost'];
exchangeCost = json['exchangeCost'];
travelCost = json['travelCost'];
netBookValue = json['netBookValue'];
purchasedPrice = json['purchasedPrice'];
lifeSpan = json['lifeSpan'];
mtbf = json['mtbf'];
mtbfDescription = json['mtbfDescription'];
totalCMWorkOrders = json['totalCMWorkOrders'];
downTimeHours = json['downTimeHours'];
downTimeDescription = json['downTimeDescription'];
upTimeHours = json['upTimeHours'];
upTimeDescription = json['upTimeDescription'];
operationHours = json['operationHours'];
installationDate = json['installationDate'];
modelName = json['modelName'];
manufacturerName = json['manufacturerName'];
serialNumber = json['serialNumber'];
departmentName = json['departmentName'];
siteName = json['siteName'];
id = json['id'];
createdBy = json['createdBy'];
createdDate = json['createdDate'];
modifiedBy = json['modifiedBy'];
modifiedDate = json['modifiedDate'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['assetId'] = this.assetId;
data['assetNumber'] = this.assetNumber;
data['assetName'] = this.assetName;
data['lastPrice'] = this.lastPrice;
data['lastPONo'] = this.lastPONo;
data['lastPODate'] = this.lastPODate;
data['sameAssetQuantity'] = this.sameAssetQuantity;
data['slaStatus'] = this.slaStatus;
data['contractNumber'] = this.contractNumber;
data['contractStartDate'] = this.contractStartDate;
data['contractEndDate'] = this.contractEndDate;
data['assetAgeInDays'] = this.assetAgeInDays;
data['assetAgeDescription'] = this.assetAgeDescription;
data['totalMaintenanceCost'] = this.totalMaintenanceCost;
data['sparePartCost'] = this.sparePartCost;
data['laborCost'] = this.laborCost;
data['exchangeCost'] = this.exchangeCost;
data['travelCost'] = this.travelCost;
data['netBookValue'] = this.netBookValue;
data['purchasedPrice'] = this.purchasedPrice;
data['lifeSpan'] = this.lifeSpan;
data['mtbf'] = this.mtbf;
data['mtbfDescription'] = this.mtbfDescription;
data['totalCMWorkOrders'] = this.totalCMWorkOrders;
data['downTimeHours'] = this.downTimeHours;
data['downTimeDescription'] = this.downTimeDescription;
data['upTimeHours'] = this.upTimeHours;
data['upTimeDescription'] = this.upTimeDescription;
data['operationHours'] = this.operationHours;
data['installationDate'] = this.installationDate;
data['modelName'] = this.modelName;
data['manufacturerName'] = this.manufacturerName;
data['serialNumber'] = this.serialNumber;
data['departmentName'] = this.departmentName;
data['siteName'] = this.siteName;
data['id'] = this.id;
data['createdBy'] = this.createdBy;
data['createdDate'] = this.createdDate;
data['modifiedBy'] = this.modifiedBy;
data['modifiedDate'] = this.modifiedDate;
return data;
}
}

@ -105,7 +105,7 @@ class _AssetAutoCompleteFieldState extends State<AssetAutoCompleteField> {
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: context.translation.oracleCode,
labelText: "Name/Oracle Code",
labelStyle: AppTextStyles.tinyFont.copyWith(color: AppColor.textColor(context)),
suffixIcon: loading ? const CircularProgressIndicator(color: AppColor.primary10, strokeWidth: 3.0).paddingOnly(end: 8) : null,
),

@ -165,6 +165,10 @@ class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
AppTextFormField(
initialValue: "",
labelText: "How would the requested technology solve the current situation and/or serve the purpose?",
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)),
@ -178,6 +182,10 @@ class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
AppTextFormField(
initialValue: "",
labelText: "What is the current practice?",
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
@ -191,6 +199,10 @@ class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
initialValue: "",
makeMultiLinesNull: true,
labelText: "Census Q1",
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
@ -204,6 +216,10 @@ class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
initialValue: "",
makeMultiLinesNull: true,
labelText: "Census Q2",
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
@ -217,6 +233,10 @@ class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
initialValue: "",
makeMultiLinesNull: true,
labelText: "Census Q3",
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
@ -230,6 +250,10 @@ class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
initialValue: "",
makeMultiLinesNull: true,
labelText: "Census Q4",
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
@ -264,7 +288,8 @@ class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
dense: true,
contentPadding: EdgeInsets.zero,
title: Text("Name: ${userBasedOnSearch[index].userName ?? ""}"),
subtitle: Text("Email: ${userBasedOnSearch[index].email ?? ""}\nPhone: ${userBasedOnSearch[index].phoneNumber ?? ""}"),
subtitle: Text(
"Position: ${userBasedOnSearch[index].position ?? "-"}\nEmail: ${userBasedOnSearch[index].email ?? ""}\nPhone: ${userBasedOnSearch[index].phoneNumber ?? ""}"),
trailing: const Icon(Icons.delete_rounded, color: Color(0xffF63939), size: 20).onPress(() {
userBasedOnSearch.removeAt(index);
setState(() {});
@ -286,7 +311,7 @@ class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
initialValue: isUsedSolelyOrShared,
onSelect: (value) {
isUsedSolelyOrShared = value;
trafRequest?.usingSolelyOrSharedId = value?.value;
trafRequest?.usingSolelyOrSharedId = value?.id;
if (isUsedSolelyOrShared?.value != 1) {
departments = [];
trafRequest?.trafDepartments = [];
@ -329,7 +354,7 @@ class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
initialValue: otherServicesEffects,
onSelect: (value) {
otherServicesEffects = value;
trafRequest?.isEffectedId = value?.value;
trafRequest?.isEffectedId = value?.id;
if (otherServicesEffects?.value != 1) {
trafRequest?.effectedServices = null;
FocusScope.of(context).unfocus();
@ -347,6 +372,10 @@ class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
textInputType: TextInputType.multiline,
alignLabelWithHint: true,
node: otherServicesEffectsNode,
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
labelText: "List down these services and stat how would it be effected",
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
@ -367,7 +396,7 @@ class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
initialValue: useInCombination,
onSelect: (value) {
useInCombination = value;
trafRequest?.isCombinationId = value?.value;
trafRequest?.isCombinationId = value?.id;
if (useInCombination?.value != 1) {
trafRequest?.usedWithCombination = null;
FocusScope.of(context).unfocus();
@ -384,6 +413,10 @@ class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
textInputType: TextInputType.multiline,
alignLabelWithHint: true,
node: useInCombinationNode,
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
labelText: "kindly describe in detail",
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
@ -430,11 +463,46 @@ class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
}
void _verifyAndSubmit() async {
if (requestType == null) {
"Select request type".showToast;
return;
} else if (requestType?.value == 1 && trafRequest?.assetNDId == null) {
"Oracle Code missing".showToast;
return;
} else if (requestType?.value == 2 && trafRequest?.assetId == null) {
"Pick asset".showToast;
return;
}
if (!_formKey.currentState!.validate()) return;
trafRequest?.employeeId = context.userProvider.user?.userID;
trafRequest?.siteId = context.userProvider.user?.clientId;
trafRequest?.trafContacts = [];
trafRequest?.trafContacts = userBasedOnSearch.map((item) => TrafContacts(id: 0, trafId: 0, name: item.userName, userId: item.userId)).toList();
if ((trafRequest?.trafContacts ?? []).isEmpty) {
"Please specify users going to use technology".showToast;
return;
}
if (isUsedSolelyOrShared == null) {
"Please specify technology solely or shared".showToast;
return;
}
if (isUsedSolelyOrShared?.value == 1 && (trafRequest?.trafDepartments ?? []).isEmpty) {
"Please specify departments and relations".showToast;
return;
}
if (otherServicesEffects == null) {
"Please specify other services will be effected or not".showToast;
return;
}
if (useInCombination == null) {
"Please specify equipment used in combination or not".showToast;
return;
}
Utils.showLoading(context);
bool isSuccess = await Provider.of<TrafRequestProvider>(context, listen: false).addTraf(trafRequest!.toJson());
Utils.hideLoading(context);

@ -15,6 +15,7 @@ import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/views/widgets/loaders/app_loading.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
import 'asset_analytics_model.dart';
import 'traf_request_model.dart';
class TrafRequestDetailPage extends StatefulWidget {
@ -93,14 +94,14 @@ class _TrafRequestDetailPageState extends State<TrafRequestDetailPage> {
),
8.height,
'${context.translation.employeeId}: ${data.employeeIdForDisplay ?? '-'}'.bodyText(context),
'${context.translation.name}: ${data.employeeName ?? '-'}'.bodyText(context), // todo ask shaheer
'${context.translation.email}: ${data.employeeEmail ?? '-'}'.bodyText(context), // todo ask shaheer
'Position: ${data.positionName ?? '-'}'.bodyText(context), // todo ask shaheer
'${context.translation.name}: ${data.employeeName ?? '-'}'.bodyText(context),
'${context.translation.email}: ${data.employeeEmail ?? '-'}'.bodyText(context),
'Position: ${data.positionName ?? '-'}'.bodyText(context),
'${context.translation.site}: ${data.siteName ?? '-'}'.bodyText(context),
'${context.translation.department}: ${data.departments?.map((item) => item.departmentName).toList() ?? '-'}'.bodyText(context),
'Requester Extension: ${data.requesterExtensionName ?? '-'}'.bodyText(context),
'${context.translation.extensionNo}: ${data.requesterExtensionNumber ?? '-'}'.bodyText(context),
'${context.translation.phoneNumber}: ${data.requesterContactNumber ?? '-'}'.bodyText(context),
'${context.translation.department}: ${data.departmentName}'.bodyText(context),
// 'Requester Extension: ${data.requesterExtensionName ?? '-'}'.bodyText(context),
'Number/Contact Information: ${data.requesterContactNumber ?? '-'}'.bodyText(context),
'Extension: ${data.requesterExtensionNumber ?? '-'}'.bodyText(context),
],
).toShadowContainer(context, borderRadius: 20, padding: 12);
}
@ -131,10 +132,22 @@ class _TrafRequestDetailPageState extends State<TrafRequestDetailPage> {
.bodyText(context),
4.height,
'Is the requesting department going to use the technology solely or shared with other departments?:\n${data.usingSolelyOrSharedName ?? '-'}'.bodyText(context),
if ((data.departments ?? []).isNotEmpty) ...[
4.height,
'Technology used with other departments:\n${data.departments?.map((item) => item.departmentName).toList() ?? '-'}'.bodyText(context),
],
4.height,
'Would other services be effected by acquiring the new equipment?:\n${data.effectedServices ?? '-'}'.bodyText(context),
'Would other services be effected by acquiring the new equipment?:\n${data.isEffectedName ?? '-'}'.bodyText(context),
if ((data.effectedServices ?? "").isNotEmpty) ...[
4.height,
'List of services would it be effected:\n${data.effectedServices ?? '-'}'.bodyText(context),
],
4.height,
'Is the equipment going to be used with combination of other equipment to accomplish a specific procedure?:\n${data.usedWithCombination ?? '-'}'.bodyText(context),
'Is the equipment going to be used with combination of other equipment to accomplish a specific procedure?:\n${data.isCombinationName ?? '-'}'.bodyText(context),
if ((data.usedWithCombination ?? "").isNotEmpty) ...[
4.height,
'Equipment going to be used with combination of other equipments:\n${data.usedWithCombination ?? '-'}'.bodyText(context),
],
],
).toShadowContainer(context, borderRadius: 20, padding: 12);
}
@ -153,16 +166,29 @@ class _TrafRequestDetailPageState extends State<TrafRequestDetailPage> {
'${context.translation.model}: ${data.modelName ?? '-'}'.bodyText(context),
'${context.translation.manufacture}: ${data.manufacturerName ?? '-'}'.bodyText(context),
if (data.requestTypeId == 733) ...[
'Last price & PO: ${data.poNumber ?? '-'}'.bodyText(context),
'The quantity of the same asset: ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Existing asset under SLA: ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Age of the asset: ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Total Maintenance Cost (TMC): ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Net Book Value (NBV): ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Mean Time Between Failure (MTBF): ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Down Time (DT): ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Up Time (UT): ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Purchased Price: ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
FutureBuilder<AssetAnalytics?>(
future: Provider.of<TrafRequestProvider>(context, listen: false).getAssetAnalytics(data.assetId!),
builder: (cxt, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox.square(dimension: 24, child: CircularProgressIndicator()).center;
}
if (snapshot.hasData) {
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
'Last price: ${snapshot.data?.lastPrice ?? '-'}'.bodyText(context),
'PO: ${snapshot.data?.lastPONo ?? '-'}'.bodyText(context),
'The quantity of the same asset: ${snapshot.data?.sameAssetQuantity ?? '-'}'.bodyText(context), // todo check with shaheer
'Existing asset under SLA: ${snapshot.data?.slaStatus ?? '-'}'.bodyText(context), // todo check with shaheer
'Age of the asset: ${snapshot.data?.assetAgeDescription ?? '-'}'.bodyText(context), // todo check with shaheer
'Total Maintenance Cost (TMC): ${snapshot.data?.totalMaintenanceCost ?? '-'}'.bodyText(context), // todo check with shaheer
'Net Book Value (NBV): ${snapshot.data?.netBookValue ?? '-'}'.bodyText(context), // todo check with shaheer
'Mean Time Between Failure (MTBF): ${snapshot.data?.mtbfDescription ?? '-'}'.bodyText(context), // todo check with shaheer
'Down Time (DT): ${snapshot.data?.downTimeDescription ?? '-'}'.bodyText(context), // todo check with shaheer
'Up Time (UT): ${snapshot.data?.upTimeDescription ?? '-'}'.bodyText(context),
'Purchased Price: ${snapshot.data?.purchasedPrice ?? '-'}'.bodyText(context),
]);
}
return const SizedBox();
})
],
],
).toShadowContainer(context, borderRadius: 20, padding: 12);

@ -51,6 +51,8 @@ class TrafRequestDataModel {
String? employeeEmail;
int? positionId;
String? positionName;
String? departmentId;
String? departmentName;
List<Departments>? departments;
List<Departments>? trafDepartments;
int? siteId;
@ -147,6 +149,8 @@ class TrafRequestDataModel {
this.employeeEmail,
this.positionId,
this.positionName,
this.departmentId,
this.departmentName,
this.departments,
this.trafDepartments,
this.siteId,
@ -243,6 +247,8 @@ class TrafRequestDataModel {
employeeEmail = json['employeeEmail'];
positionId = json['positionId'];
positionName = json['positionName'];
departmentId = json['departmentId'];
departmentName = json['departmentName'];
if (json['departments'] != null) {
departments = <Departments>[];
json['departments'].forEach((v) {
@ -366,6 +372,8 @@ class TrafRequestDataModel {
data['employeeEmail'] = this.employeeEmail;
data['positionId'] = this.positionId;
data['positionName'] = this.positionName;
data['departmentId'] = this.departmentId;
data['departmentName'] = this.departmentName;
if (this.departments != null) {
data['departments'] = this.departments!.map((v) => v.toJson()).toList();
}

@ -5,6 +5,7 @@ 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 'asset_analytics_model.dart';
import 'traf_request_model.dart';
class TrafRequestProvider extends ChangeNotifier {
@ -43,4 +44,16 @@ class TrafRequestProvider extends ChangeNotifier {
return -1;
}
}
Future<AssetAnalytics?> getAssetAnalytics(int assetId) async {
try {
Response response = await ApiManager.instance.get("${URLs.getAssetAnalytics}?assetId=$assetId");
if (response.statusCode >= 200 && response.statusCode < 300) {
return AssetAnalytics.fromJson(json.decode(response.body)["data"]);
}
} catch (error) {
print(error);
}
return null;
}
}

@ -81,13 +81,13 @@ class _UsersAutoCompleteFieldState extends State<SearchUserAutoCompleteField> {
loading = true;
});
}
List<UsersBasedOnSearchModel> workOrders = (await _TechnologyUsersProvider.getUsersBasedOnSearch(textEditingValue.text));
List<UsersBasedOnSearchModel> workOrders = (await _TechnologyUsersProvider.getUsersBasedOnSearch(context.userProvider.user!.clientId!, textEditingValue.text));
setState(() {
loading = false;
});
return workOrders;
},
displayStringForOption: (UsersBasedOnSearchModel option) => widget.byName ? option.userName ?? "" : option.employeeId ?? "",
displayStringForOption: (UsersBasedOnSearchModel option) => widget.byName ? "${option.userName ?? "-"} (Nurse)" ?? "" : option.employeeId ?? "",
fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) {
return TextField(
controller: _controller,

@ -210,7 +210,7 @@ class _MultipleItemDropDownMenuState<T extends Base, X extends LoadingListNotifi
: null,
)
.expanded,
if (widget.enabled) const PositionedDirectional(end: 0, child: Icon(Icons.keyboard_arrow_down_rounded)),
if (widget.enabled) const Icon(Icons.keyboard_arrow_down_rounded),
],
),
),

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/settings/setting_provider.dart';
@ -7,6 +9,8 @@ import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/models/enums/user_types.dart';
import 'package:test_sa/modules/cm_module/views/nurse/create_new_request_view.dart';
import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/create_equipment_internal_audit_form.dart';
import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/create_system_internal_audit_form.dart';
import 'package:test_sa/modules/tm_module/tasks_wo/create_task_view.dart';
import 'package:test_sa/modules/traf_module/create_traf_request_page.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
@ -90,8 +94,11 @@ class CreateRequestModel {
static List<CreateRequestModel> requestsList(BuildContext context) {
List<CreateRequestModel> list = [];
if (context.userProvider.isAssessor) {
log('user type is ${context.userProvider.isQualityUser}');
if (context.userProvider.isQualityUser) {
list.add(CreateRequestModel("Equipment Internal Audit Checklist".addTranslation, "add_icon", CreateEquipmentInternalAuditForm.id));
list.add(CreateRequestModel("System Internal Audit Checklist".addTranslation, "add_icon", CreateSystemInternalAuditForm.id));
} else if (context.userProvider.isAssessor) {
list.add(CreateRequestModel("TRAF".addTranslation, "add_icon", CreateTRAFRequestPage.id));
} else if (context.userProvider.isEngineer) {
if (Provider.of<SettingProvider>(context, listen: false).engineerCanCreateCM) {

@ -71,6 +71,8 @@ class _AllRequestsFilterPageState extends State<AllRequestsFilterPage> {
if (isEngineer) {
types[context.translation.recurrentWo] = 5;
types["Equipment Internal Audit".addTranslation] = 10;
types["System Internal Audit".addTranslation] = 11;
}
if (context.settingProvider.isUserFlowMedical && isEngineer) {
@ -80,9 +82,13 @@ class _AllRequestsFilterPageState extends State<AllRequestsFilterPage> {
if (!isEngineer) {
types['TRAF'] = 9;
}
if (context.userProvider.isAssessor) {
types = {"TRAF": 9};
}
if (context.userProvider.isQualityUser) {
types = {'Equipment Internal Audit': 10, 'System Internal Audit': 11};
}
final statuses = {
"All WO": 0,

@ -1,5 +1,7 @@
//request main page
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/all_requests_provider.dart';
@ -42,9 +44,11 @@ class _MyRequestsPageState extends State<MyRequestsPage> {
Request(2, context.translation.gasRefill),
Request(3, context.translation.transferAsset),
Request(4, context.translation.preventiveMaintenance),
];
if (Provider.of<UserProvider>(context, listen: false).user!.type != UsersTypes.normal_user) {
requestsList.add(Request(5, context.translation.recurrentWo));
}
//TODO unCommit this to enable task
requestsList.add(Request(6, context.translation.taskRequest));
@ -58,6 +62,11 @@ class _MyRequestsPageState extends State<MyRequestsPage> {
if (context.userProvider.user!.type == UsersTypes.normal_user) {
requestsList.add(Request(9, 'TRAF'));
}
if (context.userProvider.isEngineer) {
// TODO need to replace with actual number.
requestsList.add(Request(10, 'Equipment Internal Audit'));
requestsList.add(Request(11, 'System Internal Audit'));
}
if (context.userProvider.isAssessor) {
requestsList = [
@ -65,6 +74,14 @@ class _MyRequestsPageState extends State<MyRequestsPage> {
Request(9, 'TRAF'),
];
}
if (context.userProvider.isQualityUser) {
requestsList = [
Request(null, context.translation.allWorkOrder),
//TODO need to replace with actual number.
Request(10, 'Equipment Internal Audit'),
Request(11, 'System Internal Audit'),
];
}
_provider = Provider.of<AllRequestsProvider>(context, listen: false);
_provider!.reset();

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/models/new_models/dashboard_detail.dart';
import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_item_view.dart';
import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_item_view.dart';
import 'package:test_sa/modules/asset_inventory_module/pages/inventory_session_item_view.dart';
import 'package:test_sa/modules/tm_module/tasks_wo/task_request_item_view.dart';
import 'package:test_sa/modules/traf_module/traf_request_item_view.dart';
@ -66,6 +68,10 @@ class RequestPaginatedListview extends StatelessWidget {
return InventorySessionItemView(requestData: request);
case 9:
return TrafRequestItemView(requestData: request);
case 10:
return EquipmentInternalAuditItemView(requestData: request);
case 11:
return SystemInternalAuditItemView(requestData: request);
default:
return Container(
height: 100,

@ -1,12 +1,17 @@
//request tab page
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/widget_extensions.dart';
import 'package:test_sa/models/all_requests_and_count_model.dart';
import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_item_view.dart';
import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_item_view.dart';
import 'package:test_sa/modules/asset_inventory_module/pages/inventory_session_item_view.dart';
import 'package:test_sa/modules/tm_module/tasks_wo/task_request_item_view.dart';
import 'package:test_sa/modules/traf_module/traf_request_item_view.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/pages/land_page/requests/device_item_view.dart';
import 'package:test_sa/new_views/pages/land_page/requests/gas_refill_item_view.dart';
import 'package:test_sa/new_views/pages/land_page/requests/ppm_item_view.dart';
@ -29,9 +34,19 @@ class RequestItemViewList extends StatelessWidget {
shrinkWrap: true,
itemBuilder: (cxt, index) {
if (isLoading) return const SizedBox().toRequestShimmer(cxt, isLoading);
// if (list[index].transactionType == null) {
// return EquipmentInternalAuditItemView(requestDetails: list[index]);
// // return Container(
// // height: 100,
// // width: double.infinity,
// // color: AppColor.neutral40,
// // );
// }
switch (list[index].transactionType) {
case 1:
return ServiceRequestItemView(requestDetails: list[index]);
// case 1:
// return InternalAuditItemView(requestDetails: list[index]);
case 2:
return GasRefillItemView(requestDetails: list[index]);
case 3:
@ -48,6 +63,11 @@ class RequestItemViewList extends StatelessWidget {
return InventorySessionItemView(requestDetails: list[index]);
case 9:
return TrafRequestItemView(requestDetails: list[index]);
//Need to verify the type No..
case 10:
return EquipmentInternalAuditItemView(requestDetails: list[index]);
case 11:
return SystemInternalAuditItemView(requestDetails: list[index]);
default:
Container(
height: 100,

@ -22,6 +22,7 @@ class AppTimer extends StatefulWidget {
final TextStyle? style;
final BoxDecoration? decoration;
final bool enabled;
final bool showPicker;
final String? label;
final double? width;
@ -40,6 +41,7 @@ class AppTimer extends StatefulWidget {
this.onPick,
this.timerProgress,
this.enabled = true,
this.showPicker = true,
}) : super(key: key);
@override
@ -126,7 +128,8 @@ class _AppTimerState extends State<AppTimer> {
@override
void initState() {
canPickTime = ApiManager.instance.assetGroup?.enabledEngineerTimer ?? false;
///this is temp check for internal audit
canPickTime = widget.showPicker == false ? false : ApiManager.instance.assetGroup?.enabledEngineerTimer ?? false;
_startAt = widget.timer?.startAt;
_endAt = widget.timer?.endAt;
_running = _startAt != null && _endAt == null;

@ -1634,6 +1634,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.0"
tree_view_flutter:
dependency: "direct main"
description:
name: tree_view_flutter
sha256: "3365e29b7d1b1e6c5098a2a0e77a208a12a9345dfa73865cb4c87657cf387801"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
tuple:
dependency: transitive
description:

@ -95,8 +95,8 @@ dependencies:
safe_device: ^1.2.1
toggle_switch: ^2.3.0
clipboard: ^2.0.2
tree_view_flutter: ^1.0.2
audio_waveforms: ^1.3.0
signalr_netcore: ^1.4.4
local_auth_darwin: any

Loading…
Cancel
Save