diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index d310d230..19110cd5 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -232,6 +232,14 @@ class URLs { static get getServiceReportReasonsNew => "$_baseUrl/Lookups/GetLookupReasonNew?lookupEnum=505"; + static get getTrafRequestTypeLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=416"; + + static get getYesNoRequestTypeLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=4"; + + static get getClassificationTypeLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=450"; + + static get getRecommendationTypeLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=451"; + static get getWoFrames => "$_baseUrl/Lookups/GetLookup?lookupEnum=1254"; static get getServiceReportRetirementType => "$_baseUrl/Lookups/GetLookup?lookupEnum=415"; @@ -253,6 +261,11 @@ class URLs { static get getCallRequestForWorkOrder => "$_baseUrl/CallRequest/GetCallRequestForWorkOrder"; // get static get attachmentBaseUrl => "https://atomsmdev.hmg.com/v2/mobile/Files/DownloadFile?fileName="; + //Traf + static get addTRAF => "$_baseUrl/TRAF/AddTRAF"; // get + static get getTRAFById => "$_baseUrl/TRAF/GetTRAFById"; // get + static get getAssetNDAutoCompleteByDynamicCodes => "$_baseUrl/AssetNameDefinition/GetAssetNDAutoCompleteByDynamicCodes"; // get// get + //gas refill static get getGasTypes => "$_baseUrl/Lookups/GetLookup?lookupEnum=606"; // get // todo check edits with backend @@ -287,6 +300,7 @@ class URLs { static get getTaskEvaluatorUser => "$_baseUrl/Account/GetUserByRoleValue?value=R-2"; // get 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 // pentry static get getPentry => "$_baseUrl/return/pentry/details"; // get @@ -324,4 +338,5 @@ class URLs { static get getAssetsTemp => '$_baseUrl/AssetInventory/GetAssetsTemp'; static get convertDetailToComplete => '$_baseUrl/AssetInventory/ConvertDetailToComplete'; + static get getDepartmentBasedOnSite => "$_baseUrl/TRAFDataSource/GetDepartmentBasedOnSite"; // add } diff --git a/lib/controllers/providers/api/all_requests_provider.dart b/lib/controllers/providers/api/all_requests_provider.dart index b1af1d28..470f803d 100644 --- a/lib/controllers/providers/api/all_requests_provider.dart +++ b/lib/controllers/providers/api/all_requests_provider.dart @@ -134,18 +134,27 @@ class AllRequestsProvider extends ChangeNotifier { List getStatues(BuildContext context) { List list = [1, 2, 3, 4]; - if (context.userProvider.user!.type != UsersTypes.normal_user) { + if (context.userProvider.isAssessor) { + list = [9]; + return list; + } + + if (!context.userProvider.isNurse) { list.add(5); } list.add(6); // task module - if (context.settingProvider.isUserFlowMedical && context.userProvider.user!.type != UsersTypes.normal_user) { + if (context.settingProvider.isUserFlowMedical && !context.userProvider.isNurse) { list.add(7); // task mod } - if (context.userProvider.user!.type != UsersTypes.normal_user) { + if (!context.userProvider.isNurse) { list.add(8); // } + if (context.userProvider.isNurse) { + list.add(9); // + } + return list; } diff --git a/lib/controllers/providers/api/oracle_code_provider.dart b/lib/controllers/providers/api/oracle_code_provider.dart new file mode 100644 index 00000000..ea8b0978 --- /dev/null +++ b/lib/controllers/providers/api/oracle_code_provider.dart @@ -0,0 +1,120 @@ +import 'dart:convert'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart'; +import 'package:test_sa/controllers/api_routes/api_manager.dart'; +import 'package:test_sa/controllers/api_routes/urls.dart'; +import 'package:test_sa/models/new_models/asset_nd_auto_complete_by_dynamic_codes_model.dart'; +import 'package:test_sa/models/service_request/spare_parts.dart'; + +class OracleCodeProvider extends ChangeNotifier { + // number of items call in each request + final pageItemNumber = 20; + + //reset provider data + void reset() { + _parts = null; + _stateCode = null; + } + + // state code of current request to defied error message + // like 400 customer request failed + // 500 service not available + int? _stateCode; + + int? get stateCode => _stateCode; + + // true if there is next pagein product list and false if not + bool _nextPage = true; + + bool get nextPage => _nextPage; + + // contain user data + // when user not login or register _user = null + List? _parts; + + List? get parts => _parts; + + // when categories in-process _loading = true + // done _loading = true + // failed _loading = false + bool _loading = false; + + bool get isLoading => _loading; + + set isLoading(bool isLoading) { + _loading = isLoading; + notifyListeners(); + } + + /// return -2 if request in progress + /// return -1 if error happen when sending request + /// return state code if request complete may be 200, 404 or 403 + /// for more details check http state manager + /// lib\controllers\http_status_manger\http_status_manger.dart + // Future getParts({String? title}) async { + // if (_loading == true) return -2; + // _loading = true; + // notifyListeners(); + // late Response response; + // try { + // response = await ApiManager.instance.post(URLs.getPartNumber, body: {if (title != null && title.isNotEmpty) "partName": title}); + // _stateCode = response.statusCode; + // if (response.statusCode >= 200 && response.statusCode < 300) { + // // client's request was successfully received + // List categoriesListJson = json.decode(utf8.decode(response.bodyBytes)); + // List page = categoriesListJson.map((part) => SparePart.fromJson(part)).toList(); + // _parts ??= []; + // _parts!.addAll(page.map((e) => SparePartsWorkOrders(sparePart: e)).toList()); + // _nextPage = page.length >= pageItemNumber; + // } + // _loading = false; + // notifyListeners(); + // return response.statusCode; + // } catch (error) { + // _loading = false; + // _stateCode = -1; + // notifyListeners(); + // return -1; + // } + // } + + /// return -2 if request in progress + /// return -1 if error happen when sending request + /// return state code if request complete may be 200, 404 or 403 + /// for more details check http state manager + /// lib\controllers\http_status_manger\http_status_manger.dart + Future> getAssetByOracleCode(String oracleCode) async { + late Response response; + try { + response = await ApiManager.instance.post(URLs.getAssetNDAutoCompleteByDynamicCodes, body: {"codeValue": oracleCode}); + List page = []; + if (response.statusCode >= 200 && response.statusCode < 300) { + // client's request was successfully received + List categoriesListJson = json.decode(response.body)["data"]; + page = categoriesListJson.map((part) => AssetNDAutoCompleteByDynamicCodesModel.fromJson(part)).toList(); + } + return page; + } catch (error) { + return []; + } + } +// // Implement this only for spare part request for now show and search on display name .... +// +// Future> getPartsListByDisplayName({num? assetId, String? displayName}) async { +// late Response response; +// try { +// response = await ApiManager.instance.post(URLs.getPartNumber, body: {"displayName": displayName, }); +// List page = []; +// if (response.statusCode >= 200 && response.statusCode < 300) { +// // client's request was successfully received +// List categoriesListJson = json.decode(response.body)["data"]; +// page = categoriesListJson.map((part) => SparePart.fromJson(part,true)).toList(); +// } +// return page; +// } catch (error) { +// return []; +// } +// } +} diff --git a/lib/controllers/providers/api/search_user_provider.dart b/lib/controllers/providers/api/search_user_provider.dart new file mode 100644 index 00000000..2804e8a6 --- /dev/null +++ b/lib/controllers/providers/api/search_user_provider.dart @@ -0,0 +1,64 @@ +import 'dart:convert'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart'; +import 'package:test_sa/controllers/api_routes/api_manager.dart'; +import 'package:test_sa/controllers/api_routes/urls.dart'; +import 'package:test_sa/models/new_models/asset_nd_auto_complete_by_dynamic_codes_model.dart'; +import 'package:test_sa/models/new_models/users_based_on_search_model.dart'; +import 'package:test_sa/models/service_request/spare_parts.dart'; + +class UserSearchProvider extends ChangeNotifier { + // number of items call in each request + final pageItemNumber = 20; + + //reset provider data + void reset() { + _parts = null; + _stateCode = null; + } + + // state code of current request to defied error message + // like 400 customer request failed + // 500 service not available + int? _stateCode; + + int? get stateCode => _stateCode; + + // true if there is next pagein product list and false if not + bool _nextPage = true; + + bool get nextPage => _nextPage; + + // contain user data + // when user not login or register _user = null + List? _parts; + + List? get parts => _parts; + + bool _loading = false; + + bool get isLoading => _loading; + + set isLoading(bool isLoading) { + _loading = isLoading; + notifyListeners(); + } + + Future> getUsersBasedOnSearch(String query) async { + late Response response; + try { + response = await ApiManager.instance.get(URLs.getUsersBasedOnSearch + "?searchText=$query"); + List page = []; + if (response.statusCode >= 200 && response.statusCode < 300) { + // client's request was successfully received + List categoriesListJson = json.decode(response.body); + page = categoriesListJson.map((part) => UsersBasedOnSearchModel.fromJson(part)).toList(); + } + return page; + } catch (error) { + return []; + } + } +} diff --git a/lib/controllers/providers/api/user_provider.dart b/lib/controllers/providers/api/user_provider.dart index 7a846817..481d06d5 100644 --- a/lib/controllers/providers/api/user_provider.dart +++ b/lib/controllers/providers/api/user_provider.dart @@ -8,6 +8,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 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/models/new_models/general_response_model.dart'; import 'package:test_sa/models/new_models/update_password.dart'; import 'package:test_sa/models/new_models/verify_otp_model.dart'; @@ -39,6 +40,12 @@ class UserProvider extends ChangeNotifier { File? profileImage; + bool get isEngineer => user!.type == UsersTypes.engineer; + + bool get isNurse => user!.type == UsersTypes.normal_user; + + bool get isAssessor => user!.type == UsersTypes.assessor || user!.type == UsersTypes.assessorTl; + VerifyOtpModel _verifyOtpModel = VerifyOtpModel(); SwipeTransaction _swipeTransactionModel = SwipeTransaction(); List _swipeHistory = []; diff --git a/lib/dashboard_latest/dashboard_provider.dart b/lib/dashboard_latest/dashboard_provider.dart index a0e8a47f..c09cb621 100644 --- a/lib/dashboard_latest/dashboard_provider.dart +++ b/lib/dashboard_latest/dashboard_provider.dart @@ -106,7 +106,7 @@ class DashBoardProvider extends ChangeNotifier { isAllCountLoading = true; notifyListeners(); String url = ''; - if (usersType == UsersTypes.engineer) { + if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) { url = URLs.engineerDashboardCountUrl; } else { url = URLs.nurseDashboardCountUrl; @@ -241,7 +241,7 @@ class DashBoardProvider extends ChangeNotifier { Response response; String url = ''; - if (usersType == UsersTypes.engineer) { + if (usersType == UsersTypes.engineer || usersType == UsersTypes.assessor || usersType == UsersTypes.assessorTl) { //need to check pagination for not assigned task @waseem. //these checks are to call different apis for dashboard for engineer... diff --git a/lib/dashboard_latest/widgets/request_category_list.dart b/lib/dashboard_latest/widgets/request_category_list.dart index 744958e5..07d5d92d 100644 --- a/lib/dashboard_latest/widgets/request_category_list.dart +++ b/lib/dashboard_latest/widgets/request_category_list.dart @@ -4,6 +4,7 @@ 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/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'; @@ -49,10 +50,12 @@ class RequestCategoryList extends StatelessWidget { return RecurrentWoItemView(requestData: request); case 6: return TaskRequestItemView(requestData: request); - case 7: + case 7: return TaskRequestItemView(requestData: request); - case 8: + case 8: return InventorySessionItemView(requestData: request); + case 9: + return TrafRequestItemView(requestData: request); default: return Container( height: 100, diff --git a/lib/main.dart b/lib/main.dart index d9fabe55..59cb86ee 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,8 +16,10 @@ import 'package:test_sa/controllers/providers/api/devices_provider.dart'; import 'package:test_sa/controllers/providers/api/gas_refill_provider.dart'; import 'package:test_sa/controllers/providers/api/hospitals_provider.dart'; import 'package:test_sa/controllers/providers/api/notifications_provider.dart'; +import 'package:test_sa/controllers/providers/api/oracle_code_provider.dart'; import 'package:test_sa/controllers/providers/api/parts_provider.dart'; import 'package:test_sa/controllers/providers/api/ppm_provider.dart'; +import 'package:test_sa/controllers/providers/api/search_user_provider.dart'; import 'package:test_sa/controllers/providers/api/service_requests_provider.dart'; import 'package:test_sa/controllers/providers/api/status_drop_down/employee/nurse_provider.dart'; import 'package:test_sa/controllers/providers/api/status_drop_down/report/service_report_assistants_employee_provider.dart'; @@ -29,6 +31,9 @@ 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/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'; +import 'package:test_sa/modules/traf_module/update_traf_request_page.dart'; import 'package:test_sa/new_views/app_style/app_themes.dart'; import 'package:test_sa/new_views/pages/help_center_page.dart'; import 'package:test_sa/new_views/pages/land_page/land_page.dart'; @@ -48,6 +53,7 @@ import 'package:test_sa/providers/gas_request_providers/gas_status_provider.dart import 'package:test_sa/providers/gas_request_providers/gas_types_provider.dart'; import 'package:test_sa/providers/gas_request_providers/site_provider.dart'; import 'package:test_sa/providers/loading_list_notifier.dart'; +import 'package:test_sa/providers/lookups/recommendation_lookup_provider.dart'; import 'package:test_sa/providers/ppm_asset_availability_provider.dart'; import 'package:test_sa/providers/ppm_checklist_status_provider.dart'; import 'package:test_sa/providers/ppm_device_status_provider.dart'; @@ -94,6 +100,10 @@ import 'controllers/providers/api/user_provider.dart'; import 'controllers/providers/settings/setting_provider.dart'; import 'dashboard_latest/dashboard_provider.dart'; import 'new_views/pages/gas_refill_request_form.dart'; +import 'providers/lookups/classification_lookup_provider.dart'; +import 'providers/lookups/department_lookup_provider.dart'; +import 'providers/lookups/request_type_lookup_provider.dart'; +import 'providers/lookups/yes_no_lookup_provider.dart'; import 'providers/service_request_providers/loan_availability_provider.dart'; import 'providers/service_request_providers/reject_reason_provider.dart'; @@ -122,7 +132,8 @@ void main() async { } else { await Firebase.initializeApp(); } - SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(statusBarColor: Colors.transparent, + SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( + statusBarColor: Colors.transparent, systemNavigationBarColor: Colors.white, systemNavigationBarIconBrightness: Brightness.dark, )); @@ -180,21 +191,18 @@ class MyApp extends StatelessWidget { //new providers according to new Api's.. ChangeNotifierProvider(create: (_) => DashBoardProvider()), ChangeNotifierProvider(create: (_) => ServiceRequestDetailProvider()), - // ChangeNotifierProvider(create: (_) => PreventiveMaintenanceVisitsProvider()), + ChangeNotifierProvider(create: (_) => ClassificationLookupProvider()), + ChangeNotifierProvider(create: (_) => RecommendationLookupProvider()), ChangeNotifierProvider(create: (_) => PpmProvider()), ChangeNotifierProvider(create: (_) => PartsProvider()), - ///todo deleted - //ChangeNotifierProvider(create: (_) => ServiceReportReasonsProvider()), - //ChangeNotifierProvider(create: (_) => ServiceReportStatusProvider()), - ///todo deleted - //ChangeNotifierProvider(create: (_) => ServiceReportEquipmentStatusProvider()), - //ChangeNotifierProvider(create: (_) => ServiceReportTypesProvider()), + ChangeNotifierProvider(create: (_) => RequestTypeLookupProvider()), + ChangeNotifierProvider(create: (_) => YesNoLookupProvider()), ChangeNotifierProvider(create: (_) => ServiceStatusProvider()), ChangeNotifierProvider(create: (_) => ServiceReportLastCallsProvider()), - ///todo deleted - //ChangeNotifierProvider(create: (_) => GasCylinderSizesProvider()), + ChangeNotifierProvider(create: (_) => OracleCodeProvider()), + ///todo deleted //ChangeNotifierProvider(create: (_) => GasCylinderTypesProvider()), ChangeNotifierProvider(create: (_) => GasStatusProvider()), @@ -203,8 +211,10 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (_) => DeviceTransferProvider()), ChangeNotifierProvider(create: (_) => AssetTransferStatusProvider()), - ///todo deleted - //ChangeNotifierProvider(create: (_) => AssignedToProvider()), + ChangeNotifierProvider(create: (_) => TrafRequestProvider()), + ChangeNotifierProvider(create: (_) => DepartmentLookupProvider()), + ChangeNotifierProvider(create: (_) => UserSearchProvider()), + ///todo deleted //ChangeNotifierProvider(create: (_) => PentryTaskStatusProvider()), //ChangeNotifierProvider(create: (_) => PentryVisitStatusProvider()), @@ -314,7 +324,8 @@ class MyApp extends StatelessWidget { GasRefillRequestForm.routeName: (_) => const GasRefillRequestForm(), // ServiceRequestsPage.id: (_) => const ServiceRequestsPage(), CreateTaskView.id: (_) => const CreateTaskView(), - //ReportIssuesPage.id: (_) => ReportIssuesPage(), + CreateTRAFRequestPage.id: (_) => CreateTRAFRequestPage(), + UpdateTrafRequestPage.id: (_) => UpdateTrafRequestPage(), RequestGasRefill.id: (_) => const RequestGasRefill(), UpdateGasRefillRequest.id: (_) => const UpdateGasRefillRequest(), // CreateServiceRequestPage.id: (_) => const CreateServiceRequestPage(), diff --git a/lib/models/enums/user_types.dart b/lib/models/enums/user_types.dart index e84e2493..63f1d9d7 100644 --- a/lib/models/enums/user_types.dart +++ b/lib/models/enums/user_types.dart @@ -2,4 +2,6 @@ enum UsersTypes { engineer, // 0 normal_user, // 1 nurse, // 1 + assessor, + assessorTl } diff --git a/lib/models/new_models/asset_nd_auto_complete_by_dynamic_codes_model.dart b/lib/models/new_models/asset_nd_auto_complete_by_dynamic_codes_model.dart new file mode 100644 index 00000000..4d5ae14e --- /dev/null +++ b/lib/models/new_models/asset_nd_auto_complete_by_dynamic_codes_model.dart @@ -0,0 +1,27 @@ +class AssetNDAutoCompleteByDynamicCodesModel { + int? id; + String? assetName; + int? codeTypeId; + String? codeValue; + String? displayName; + + AssetNDAutoCompleteByDynamicCodesModel({this.id, this.assetName, this.codeTypeId, this.codeValue, this.displayName}); + + AssetNDAutoCompleteByDynamicCodesModel.fromJson(Map json) { + id = json['id']; + assetName = json['assetName']; + codeTypeId = json['codeTypeId']; + codeValue = json['codeValue']; + displayName = json['displayName']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['assetName'] = this.assetName; + data['codeTypeId'] = this.codeTypeId; + data['codeValue'] = this.codeValue; + data['displayName'] = this.displayName; + return data; + } +} diff --git a/lib/models/new_models/traf_department.dart b/lib/models/new_models/traf_department.dart new file mode 100644 index 00000000..5a2e256d --- /dev/null +++ b/lib/models/new_models/traf_department.dart @@ -0,0 +1,101 @@ +import 'package:test_sa/models/new_models/room_model.dart'; + +import '../base.dart'; + +class TrafDepartment extends Base { + TrafDepartment({ + this.departmentName, this.departmentCode, this.ntCode, this.costCenterNumber, this.costCenterName, this.name, this.id, this.createdBy, this.createdDate, this.modifiedBy, this.modifiedDate + }) : super(identifier: id?.toString() ?? '', name: departmentName); // Handle potential null id + + TrafDepartment.fromJson(Map json) { + departmentName = json['departmentName'] ?? json['name']; + departmentCode = json['departmentCode']; + ntCode = json['ntCode']; + costCenterNumber = json['costCenterNumber']; + costCenterName = json['costCenterName']; + name = json['name']; + id = json['id']; + createdBy = json['createdBy']; + createdDate = json['createdDate']; + modifiedBy = json['modifiedBy']; + modifiedDate = json['modifiedDate']; + } + + num? id; // Now nullable + String? departmentName; // Now nullable + String? departmentCode; // Now nullable + String? ntCode; + String? costCenterNumber; + String? costCenterName; + String? name; + String? createdBy; + String? createdDate; + String? modifiedBy; + String? modifiedDate; + + // TrafDepartment copyWith({ + // num? id, // Parameters are now nullable + // String? departmentName, + // String? departmentCode, + // String? departmentId, + // String? ntCode, + // List? rooms, + // }) => + // TrafDepartment( + // id: id ?? this.id, + // departmentName: departmentName ?? this.departmentName, + // departmentCode: departmentCode ?? this.departmentCode, + // departmentId: departmentId ?? this.departmentId, + // ntCode: ntCode ?? this.ntCode, + // rooms: rooms ?? this.rooms, + // ); + + +} + +// class TrafDepartment extends Base { +// String? departmentName; +// String? departmentCode; +// Null? ntCode; +// Null? costCenterNumber; +// Null? costCenterName; +// String? name; +// int? id; +// String? createdBy; +// String? createdDate; +// Null? modifiedBy; +// Null? modifiedDate; +// +// TrafDepartment( +// {this.departmentName, this.departmentCode, this.ntCode, this.costCenterNumber, this.costCenterName, this.name, this.id, this.createdBy, this.createdDate, this.modifiedBy, this.modifiedDate}); +// +// TrafDepartment.fromJson(Map json) { +// departmentName = json['departmentName']; +// departmentCode = json['departmentCode']; +// ntCode = json['ntCode']; +// costCenterNumber = json['costCenterNumber']; +// costCenterName = json['costCenterName']; +// name = json['name']; +// id = json['id']; +// createdBy = json['createdBy']; +// createdDate = json['createdDate']; +// modifiedBy = json['modifiedBy']; +// modifiedDate = json['modifiedDate']; +// } +// +// Map toJson() { +// final Map data = new Map(); +// data['departmentName'] = this.departmentName; +// data['departmentCode'] = this.departmentCode; +// data['ntCode'] = this.ntCode; +// data['costCenterNumber'] = this.costCenterNumber; +// data['costCenterName'] = this.costCenterName; +// data['name'] = this.name; +// data['id'] = this.id; +// data['createdBy'] = this.createdBy; +// data['createdDate'] = this.createdDate; +// data['modifiedBy'] = this.modifiedBy; +// data['modifiedDate'] = this.modifiedDate; +// return data; +// } +// } diff --git a/lib/models/new_models/users_based_on_search_model.dart b/lib/models/new_models/users_based_on_search_model.dart new file mode 100644 index 00000000..54480f91 --- /dev/null +++ b/lib/models/new_models/users_based_on_search_model.dart @@ -0,0 +1,44 @@ +class UsersBasedOnSearchModel { + String? userId; + String? userName; + String? email; + String? employeeId; + int? languageId; + String? extensionNo; + String? phoneNumber; + bool? isActive; + + UsersBasedOnSearchModel( + {this.userId, + this.userName, + this.email, + this.employeeId, + this.languageId, + this.extensionNo, + this.phoneNumber, + this.isActive}); + + UsersBasedOnSearchModel.fromJson(Map 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 toJson() { + final Map data = new Map(); + data['userId'] = this.userId; + data['userName'] = this.userName; + data['email'] = this.email; + data['employeeId'] = this.employeeId; + data['languageId'] = this.languageId; + data['extensionNo'] = this.extensionNo; + data['phoneNumber'] = this.phoneNumber; + data['isActive'] = this.isActive; + return data; + } +} diff --git a/lib/models/user.dart b/lib/models/user.dart index e8bb7044..0bb4ab23 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -103,11 +103,16 @@ class User { return UsersTypes.normal_user; case "R-33": // Head Nurse Role return UsersTypes.normal_user; + case "R-32": // Head Nurse Role + return UsersTypes.assessor; + case "R-19": // Head Nurse Role + return UsersTypes.assessorTl; default: return null; } } + Map toUpdateProfileJson() { Map jsonObject = {}; // if (departmentId != null) jsonObject["department"] = departmentId; diff --git a/lib/modules/traf_module/asset_auto_complete_field.dart b/lib/modules/traf_module/asset_auto_complete_field.dart new file mode 100644 index 00000000..0abb378b --- /dev/null +++ b/lib/modules/traf_module/asset_auto_complete_field.dart @@ -0,0 +1,132 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/oracle_code_provider.dart'; +import 'package:test_sa/controllers/providers/api/parts_provider.dart'; +import 'package:test_sa/controllers/providers/api/user_provider.dart'; +import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/new_models/asset_nd_auto_complete_by_dynamic_codes_model.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/app_style/sizing.dart'; + +import '../../../extensions/text_extensions.dart'; +import '../../../models/service_request/spare_parts.dart'; +import '../../../new_views/app_style/app_text_style.dart'; + +class AssetAutoCompleteField extends StatefulWidget { + final String initialValue; + final num? assetId; + final bool clearAfterPick, byName; + final Function(AssetNDAutoCompleteByDynamicCodesModel) onPick; + + const AssetAutoCompleteField({Key? key, required this.byName, required this.initialValue, this.assetId, required this.onPick, this.clearAfterPick = true}) : super(key: key); + + @override + _AssetAutoCompleteFieldState createState() => _AssetAutoCompleteFieldState(); +} + +class _AssetAutoCompleteFieldState extends State { + late OracleCodeProvider _oracleCodeProvider; + + late TextEditingController _controller; + + bool loading = false; + + @override + void initState() { + _controller = TextEditingController(text: widget.initialValue); + super.initState(); + _oracleCodeProvider = Provider.of(context, listen: false); + } + + @override + void didUpdateWidget(covariant AssetAutoCompleteField oldWidget) { + if (widget.initialValue != oldWidget.initialValue) { + _controller = TextEditingController(text: widget.initialValue); + } + super.didUpdateWidget(oldWidget); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + 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( + optionsBuilder: (TextEditingValue textEditingValue) async { + if (textEditingValue.text.isEmpty) { + if (loading) { + setState(() { + loading = false; + }); + } + return const Iterable.empty(); + } + if (!loading) { + setState(() { + loading = true; + }); + } + List workOrders = (await _oracleCodeProvider.getAssetByOracleCode(textEditingValue.text)); + setState(() { + loading = false; + }); + return workOrders; + }, + displayStringForOption: (AssetNDAutoCompleteByDynamicCodesModel option) => widget.byName ? option.displayName ?? "" : option.codeValue ?? "", + fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) { + return TextField( + controller: _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.oracleCode, + 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: (AssetNDAutoCompleteByDynamicCodesModel selection) { + if (widget.clearAfterPick) { + _controller.clear(); + } else { + _controller.text = widget.byName ? (selection.displayName ?? "") : (selection.codeValue ?? ""); + } + widget.onPick(selection); + }, + ), + ); + } +} diff --git a/lib/modules/traf_module/create_traf_request_page.dart b/lib/modules/traf_module/create_traf_request_page.dart new file mode 100644 index 00000000..e2d066a9 --- /dev/null +++ b/lib/modules/traf_module/create_traf_request_page.dart @@ -0,0 +1,443 @@ +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/helper/utils.dart'; +import 'package:test_sa/models/device/asset.dart'; +import 'package:test_sa/models/lookup.dart'; +import 'package:test_sa/models/new_models/traf_department.dart'; +import 'package:test_sa/models/new_models/users_based_on_search_model.dart'; +import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; +import 'package:test_sa/modules/traf_module/asset_auto_complete_field.dart'; +import 'package:test_sa/modules/traf_module/traf_request_detail_page.dart'; +import 'package:test_sa/modules/traf_module/update_traf_request_page.dart'; +import 'package:test_sa/modules/traf_module/users_auto_complete_field.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; +import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; +import 'package:test_sa/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/new_views/common_widgets/single_item_drop_down_menu.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; +import 'package:test_sa/providers/lookups/department_lookup_provider.dart'; +import 'package:test_sa/providers/lookups/request_type_lookup_provider.dart'; +import 'package:test_sa/providers/lookups/yes_no_lookup_provider.dart'; +import 'package:test_sa/views/widgets/equipment/asset_picker.dart'; + +import 'traf_request_model.dart'; +import 'traf_request_provider.dart'; + +class CreateTRAFRequestPage extends StatefulWidget { + static const String id = "/create-TRAF"; + + CreateTRAFRequestPage({Key? key}) : super(key: key); + + @override + _CreateTRAFRequestPageState createState() { + return _CreateTRAFRequestPageState(); + } +} + +class _CreateTRAFRequestPageState extends State { + final GlobalKey _formKey = GlobalKey(); + + bool _acknowledgement = false; + Asset? asset; + + Lookup? requestType; + Lookup? isUsedSolelyOrShared; + Lookup? otherServicesEffects; + Lookup? useInCombination; + + TrafRequestDataModel? trafRequest; + + List abc = []; + + List departments = []; + List userBasedOnSearch = []; + + FocusNode otherServicesEffectsNode = FocusNode(); + FocusNode useInCombinationNode = FocusNode(); + FocusNode departmentNode = FocusNode(); + + @override + void initState() { + super.initState(); + trafRequest = TrafRequestDataModel(id: 0, attachments: [], departments: [], qty: 1); + resetProviders(); + } + + void resetProviders() { + Provider.of(context, listen: false).reset(); + // Provider.of(context, listen: false).reset(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const DefaultAppBar(title: "TRAF Request"), + body: Form( + key: _formKey, + child: Column( + children: [ + ListView(padding: const EdgeInsets.all(16), children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // SingleItemDropDownMenu( + // context: context, + // height: 56.toScreenHeight, + // title: context.translation.taskType, + // showShadow: false, + // backgroundColor: AppColor.fieldBgColor(context), + // showAsBottomSheet: true, + // initialValue: selectedType, + // onSelect: (type) {}, + // ), + SingleItemDropDownMenu( + context: context, + showAsBottomSheet: true, + backgroundColor: AppColor.neutral100, + showShadow: false, + title: context.translation.requestType, + initialValue: requestType, + onSelect: (value) { + requestType = value; + trafRequest?.requestTypeId = value?.id; + if (requestType?.value == 1) { + trafRequest?.assetId = null; + // trafRequest?.qty = 0; + } else if (requestType?.value == 2) { + trafRequest?.assetNDId = null; + } + setState(() {}); + }, + ), + if (requestType?.value == 1) ...[ + 12.height, + AssetAutoCompleteField( + clearAfterPick: false, + byName: true, + initialValue: "", + onPick: (asset) { + trafRequest?.assetNDId = asset.id; + // model.partCatalogItem = PartCatalogItem(id: part.sparePart?.id, partNumber: part.sparePart?.partNo, partName: part.sparePart?.partName, oracleCode: part.sparePart?.oracleCode); + // setState(() {}); + }, + ), + ], + if (requestType?.value == 2) ...[ + 12.height, + AssetPicker( + device: asset, + editable: false, + showLoading: false, + borderColor: AppColor.black20, + backgroundColor: AppColor.white936, + onPick: (asset) async { + this.asset = asset; + trafRequest?.assetId = asset.id?.toInt(); + // trafRequest?.qty = 1; + setState(() {}); + // pendingAssetServiceRequest = null; + // _serviceRequest.device = asset; + // await checkAssetForPendingServiceRequest(asset.id!.toInt()); + // if (pendingAssetServiceRequest != null && pendingAssetServiceRequest!.details!.isNotEmpty) { + // showPendingRequestBottomSheet(); + // } + }, + ), + ], + 12.height, + Text( + "Request Details", + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50, fontWeight: FontWeight.w600), + ), + 12.height, + AppTextFormField( + initialValue: "", + labelText: "How would the requested technology solve the current situation and/or serve the purpose?", + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + floatingLabelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + makeMultiLinesNull: true, + onChange: (value) { + trafRequest?.purposeAnswer = value; + }, + ), + 12.height, + AppTextFormField( + initialValue: "", + labelText: "What is the current practice?", + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + makeMultiLinesNull: true, + onChange: (value) { + trafRequest?.currentPractise = value; + }, + ), + 12.height, + AppTextFormField( + initialValue: "", + makeMultiLinesNull: true, + labelText: "Census Q1", + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.number, + onChange: (value) { + trafRequest?.censusQ1 = int.tryParse(value); + }, + ), + 12.height, + AppTextFormField( + initialValue: "", + makeMultiLinesNull: true, + labelText: "Census Q2", + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.number, + onChange: (value) { + trafRequest?.censusQ2 = int.tryParse(value); + }, + ), + 12.height, + AppTextFormField( + initialValue: "", + makeMultiLinesNull: true, + labelText: "Census Q3", + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.number, + onChange: (value) { + trafRequest?.censusQ3 = int.tryParse(value); + }, + ), + 12.height, + AppTextFormField( + initialValue: "", + makeMultiLinesNull: true, + labelText: "Census Q4", + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.number, + onChange: (value) { + trafRequest?.censusQ4 = int.tryParse(value); + }, + ), + 12.height, + SearchUserAutoCompleteField( + clearAfterPick: true, + byName: true, + initialValue: "", + onPick: (user) { + userBasedOnSearch.add(user); + FocusScope.of(context).unfocus(); + // model.partCatalogItem = PartCatalogItem(id: part.sparePart?.id, partNumber: part.sparePart?.partNo, partName: part.sparePart?.partName, oracleCode: part.sparePart?.oracleCode); + setState(() {}); + }, + ), + if (userBasedOnSearch.isNotEmpty) ...[ + // 12.height, + ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.only(left: 12, right: 12), + itemBuilder: (cxt, index) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + dense: true, + contentPadding: EdgeInsets.zero, + title: Text("Name: ${userBasedOnSearch[index].userName ?? ""}"), + subtitle: Text("Email: ${userBasedOnSearch[index].email ?? ""}\nPhone: ${userBasedOnSearch[index].phoneNumber ?? ""}"), + trailing: const Icon(Icons.delete_rounded, color: Color(0xffF63939), size: 20).onPress(() { + userBasedOnSearch.removeAt(index); + setState(() {}); + }), + ), + ], + ), + separatorBuilder: (cxt, index) => Divider(thickness: 1, height: 1, color: context.isDark ? AppColor.neutral20 : AppColor.neutral30), + itemCount: userBasedOnSearch.length) + ], + 12.height, + SingleItemDropDownMenu( + context: context, + showAsBottomSheet: true, + backgroundColor: AppColor.neutral100, + showShadow: false, + height: 80, + title: "Is the requesting department going to use the technology solely or shared with other departments?", + initialValue: isUsedSolelyOrShared, + onSelect: (value) { + isUsedSolelyOrShared = value; + trafRequest?.usingSolelyOrSharedId = value?.value; + if (isUsedSolelyOrShared?.value != 1) { + departments = []; + trafRequest?.trafDepartments = []; + Provider.of(context, listen: false).reset(); + FocusScope.of(context).unfocus(); + } else { + departmentNode.requestFocus(); + } + setState(() {}); + }, + ), + if (isUsedSolelyOrShared?.value == 1) ...[ + 12.height, + Focus( + focusNode: departmentNode, + child: MultipleItemDropDownMenu( + context: context, + showAsBottomSheet: true, + backgroundColor: AppColor.neutral100, + showShadow: false, + showCancel: true, + requestById: context.userProvider.user?.clientId, + title: "Please specify departments and relations", + initialValue: departments, + onSelect: (value) { + departments = value ?? []; + trafRequest?.trafDepartments = departments.map((element) => Departments(id: 0, trafId: 0, departmentId: element.id!.toInt())).toList(); + }, + ), + ), + ], + 12.height, + SingleItemDropDownMenu( + context: context, + showAsBottomSheet: true, + backgroundColor: AppColor.neutral100, + showShadow: false, + height: 80, + title: "Would other services be effected by acquiring the new equipment?", + initialValue: otherServicesEffects, + onSelect: (value) { + otherServicesEffects = value; + trafRequest?.isEffectedId = value?.value; + if (otherServicesEffects?.value != 1) { + trafRequest?.effectedServices = null; + FocusScope.of(context).unfocus(); + } else { + otherServicesEffectsNode.requestFocus(); + } + setState(() {}); + }, + ), + if (otherServicesEffects?.value == 1) ...[ + 12.height, + AppTextFormField( + initialValue: "", + // makeMultiLinesNull: true, + textInputType: TextInputType.multiline, + alignLabelWithHint: true, + node: otherServicesEffectsNode, + labelText: "List down these services and stat how would it be effected", + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + onChange: (value) { + trafRequest?.effectedServices = value; + }, + ), + ], + 12.height, + SingleItemDropDownMenu( + context: context, + showAsBottomSheet: true, + backgroundColor: AppColor.neutral100, + showShadow: false, + height: 80, + title: "Is the equipment going to be used with combination of other equipment to accomplish a specific procedure?", + initialValue: useInCombination, + onSelect: (value) { + useInCombination = value; + trafRequest?.isCombinationId = value?.value; + if (useInCombination?.value != 1) { + trafRequest?.usedWithCombination = null; + FocusScope.of(context).unfocus(); + } else { + useInCombinationNode.requestFocus(); + } + setState(() {}); + }, + ), + if (useInCombination?.value == 1) ...[ + 12.height, + AppTextFormField( + initialValue: "", + textInputType: TextInputType.multiline, + alignLabelWithHint: true, + node: useInCombinationNode, + labelText: "kindly describe in detail", + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + onChange: (value) { + trafRequest?.usedWithCombination = value; + }, + ), + ] + ], + ).toShadowContainer(context, padding: 12, borderRadius: 20), + 16.height, + Row( + children: [ + Checkbox( + value: _acknowledgement, + visualDensity: const VisualDensity(horizontal: -4.0, vertical: -4.0), + activeColor: AppColor.blueStatus(context), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + onChanged: (value) { + setState(() { + _acknowledgement = value!; + }); + }), + 12.width, + "I acknowledge the information filled above is correct".addTranslation.bodyText(context).custom(color: context.isDark ? AppColor.primary50 : AppColor.neutral120).expanded, + ], + ), + ]).expanded, + FooterActionButton.footerContainer( + context: context, + child: AppFilledButton( + buttonColor: AppColor.primary10, + label: context.translation.submitRequest, + disableButton: !_acknowledgement, + onPressed: _verifyAndSubmit, + // buttonColor: AppColor.primary10, + ), + ), + ], + ), + ), + ); + } + + void _verifyAndSubmit() async { + 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(); + + Utils.showLoading(context); + await Provider.of(context, listen: false).addTraf(trafRequest!.toJson()); + Utils.hideLoading(context); + Navigator.pop(context); + } +} diff --git a/lib/modules/traf_module/traf_request_detail_page.dart b/lib/modules/traf_module/traf_request_detail_page.dart new file mode 100644 index 00000000..7283bf98 --- /dev/null +++ b/lib/modules/traf_module/traf_request_detail_page.dart @@ -0,0 +1,166 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/user_provider.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/enums/user_types.dart'; +import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; +import 'package:test_sa/modules/traf_module/traf_request_provider.dart'; +import 'package:test_sa/modules/traf_module/update_traf_request_page.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; +import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; +import 'package:test_sa/views/widgets/loaders/app_loading.dart'; + +import 'traf_request_model.dart'; + +class TrafRequestDetailPage extends StatefulWidget { + static const String id = "/details-TRAF"; + + final int trafId; + + TrafRequestDetailPage({Key? key, required this.trafId}) : super(key: key); + + @override + _TrafRequestDetailPageState createState() { + return _TrafRequestDetailPageState(); + } +} + +class _TrafRequestDetailPageState extends State { + @override + void initState() { + super.initState(); + Provider.of(context, listen: false).getTRAFById(widget.trafId); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + bool isEngineer = (Provider.of(context, listen: false).user?.type) == UsersTypes.engineer; + return Scaffold( + appBar: const DefaultAppBar(title: "TRAF Request"), + body: Selector( + selector: (_, myModel) => myModel.isLoading, // Selects only the userName + builder: (_, isLoading, __) { + if (isLoading) return const ALoading(); + TrafRequestProvider trafProvider = Provider.of(context, listen: false); + return Column( + children: [ + ListView( + padding: const EdgeInsets.all(16), + children: [ + requesterInformation(trafProvider.trafRequestDataModel!), + 12.height, + requestInformation(trafProvider.trafRequestDataModel!), + 12.height, + assetInformation(trafProvider.trafRequestDataModel!), + ], + ).expanded, + // // if (isEngineer) + // FooterActionButton.footerContainer( + // context: context, + // child: AppFilledButton( + // buttonColor: AppColor.primary10, + // label: "Update", + // onPressed: () { + // Navigator.pushNamed(context, UpdateTrafRequestPage.id); + // }), + // ), + ], + ); + }, + )); + } + + Widget requesterInformation(TrafRequestDataModel data) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Requester Information", + style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ), + 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.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), + ], + ).toShadowContainer(context, borderRadius: 20, padding: 12); + } + + Widget requestInformation(TrafRequestDataModel data) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Request Information", + style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ), + 8.height, + 'TRAF No: ${data.reqCode ?? '-'}'.bodyText(context), + 'Request type: ${data.requestTypeName ?? '-'}'.bodyText(context), + 4.height, + 'How would the requested technology solve the current situation and/or serve the purpose?: ${data.purposeAnswer ?? '-'}'.bodyText(context), + 4.height, + 'What is the current practice?: ${data.currentPractise ?? '-'}'.bodyText(context), + 4.height, + 'Census Q1: ${data.censusQ1 ?? '-'}'.bodyText(context), + 'Census Q2: ${data.censusQ2 ?? '-'}'.bodyText(context), + 'Census Q3: ${data.censusQ3 ?? '-'}'.bodyText(context), + 'Census Q4: ${data.censusQ4 ?? '-'}'.bodyText(context), + 4.height, + 'List down names & contact information of users going to use the technology & specify if they are part-time or full time?:\n${data.trafContacts?.map((item) => item.name).toList() ?? '-'}' + .bodyText(context), + 4.height, + 'Is the requesting department going to use the technology solely or shared with other departments?:\n${data.usingSolelyOrSharedName ?? '-'}'.bodyText(context), + 4.height, + 'Would other services be effected by acquiring the new equipment?:\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), + ], + ).toShadowContainer(context, borderRadius: 20, padding: 12); + } + + Widget assetInformation(TrafRequestDataModel data) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Asset Information", + style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ), + 8.height, + '${context.translation.assetName}: ${data.assetName ?? '-'}'.bodyText(context), + '${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 + ], + ], + ).toShadowContainer(context, borderRadius: 20, padding: 12); + } +} diff --git a/lib/modules/traf_module/traf_request_item_view.dart b/lib/modules/traf_module/traf_request_item_view.dart new file mode 100644 index 00000000..1bf03eb5 --- /dev/null +++ b/lib/modules/traf_module/traf_request_item_view.dart @@ -0,0 +1,125 @@ +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/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/widgets/requests/request_status.dart'; + +import 'traf_request_detail_page.dart'; + +class TrafRequestItemView extends StatelessWidget { + final Data? requestData; + final RequestsDetails? requestDetails; + final bool showShadow; + + const TrafRequestItemView({Key? key, this.requestData, this.requestDetails, this.showShadow = true}) : super(key: key); + + @override + Widget build(BuildContext context) { + if (requestData != null) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 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!), + 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 ?? "TRAF Request").heading5(context), + infoWidget(label: context.translation.requestType, value: requestData?.requestTypeName, context: context), + infoWidget(label: "TRAF No", value: requestData?.requestNo, 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) => TrafRequestDetailPage(trafId: requestData!.id!))); + }); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // StatusLabel( + // label: requestDetails!.priority!, + // textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails?.priority!), + // backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails?.priority!), + // ), + // 8.width, + StatusLabel( + label: requestDetails!.status!, + textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails?.status!), + backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails?.status!), + ), + 1.width.expanded, + Text( + requestDetails!.date?.toServiceRequestCardFormat ?? "", + textAlign: TextAlign.end, + style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50), + ), + ], + ), + 8.height, + (requestDetails?.nameOfType ?? "TRAF Request").heading5(context), + 8.height, + infoWidget(label: context.translation.requestType, value: requestDetails!.requestType, context: context), + infoWidget(label: "TRAF No", value: requestDetails!.requestNo, context: context), + 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) => TrafRequestDetailPage(trafId: 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(); + } +} diff --git a/lib/modules/traf_module/traf_request_model.dart b/lib/modules/traf_module/traf_request_model.dart new file mode 100644 index 00000000..b5de3280 --- /dev/null +++ b/lib/modules/traf_module/traf_request_model.dart @@ -0,0 +1,574 @@ +class TrafRequestModel { + List? trafRequestDataModel; + int? totalRows; + int? count; + String? message; + String? title; + String? innerMessage; + int? responseCode; + bool? isSuccess; + + TrafRequestModel({this.trafRequestDataModel, this.totalRows, this.count, this.message, this.title, this.innerMessage, this.responseCode, this.isSuccess}); + + TrafRequestModel.fromJson(Map json) { + if (json['TrafRequestDataModel'] != null) { + trafRequestDataModel = []; + json['TrafRequestDataModel'].forEach((v) { + trafRequestDataModel!.add(new TrafRequestDataModel.fromJson(v)); + }); + } + totalRows = json['totalRows']; + count = json['count']; + message = json['message']; + title = json['title']; + innerMessage = json['innerMessage']; + responseCode = json['responseCode']; + isSuccess = json['isSuccess']; + } + + Map toJson() { + final Map data = new Map(); + if (this.trafRequestDataModel != null) { + data['TrafRequestDataModel'] = this.trafRequestDataModel!.map((v) => v.toJson()).toList(); + } + data['totalRows'] = this.totalRows; + data['count'] = this.count; + data['message'] = this.message; + data['title'] = this.title; + data['innerMessage'] = this.innerMessage; + data['responseCode'] = this.responseCode; + data['isSuccess'] = this.isSuccess; + return data; + } +} + +class TrafRequestDataModel { + int? reqNo; + String? reqCode; + String? employeeId; + String? employeeIdForDisplay; + String? employeeName; + String? employeeEmail; + int? positionId; + String? positionName; + List? departments; + List? trafDepartments; + int? siteId; + String? siteName; + String? requesterExtensionNumber; + String? requesterExtensionName; + int? requesterExtensionPositionId; + String? requesterExtensionPositionName; + int? requestTypeId; + String? requestTypeName; + int? assetNDId; + int? modelId; + String? modelName; + int? manufacturerId; + String? manufacturerName; + int? assetId; + String? assetSerialNo; + String? assetNumber; + String? assetName; + int? assetAssetNDId; + int? qty; + String? purposeAnswer; + String? currentPractise; + int? censusQ1; + int? censusQ2; + int? censusQ3; + int? censusQ4; + List? trafContacts; + int? usingSolelyOrSharedId; + String? usingSolelyOrSharedName; + int? isEffectedId; + String? isEffectedName; + String? effectedServices; + int? isCombinationId; + String? isCombinationName; + String? usedWithCombination; + String? comment; + List? attachments; + int? isBudgetId; + String? isBudgetName; + String? firstLineManagerId; + String? firstLineManagerName; + int? firstLineManagerApprovalId; + String? firstLineManagerApprovalName; + int? tlapiId; + String? tlapiName; + String? assessorEmployeeId; + String? assessorEmployeeName; + String? secondLineManagerId; + String? secondLineManagerName; + int? secondLineManagerApprovalId; + String? secondLineManagerApprovalName; + String? hospitalManagementId; + String? hospitalManagementName; + int? hospitalManagementApprovalId; + String? hospitalManagementApprovalName; + List? oracleCodes; + String? assessorTeamLeaderId; + String? assessorTeamLeaderName; + int? assessorTeamLeaderApprovalId; + String? assessorTeamLeaderApprovalName; + int? statusOfDRId; + String? statusOfDRName; + int? statusOfRequesterId; + String? statusOfRequesterName; + bool? approved; + String? approvalUserId; + String? approvalUserName; + String? approvalDate; + String? docDate; + String? approvalDocDate; + String? isAssigned; + String? poNumber; + int? apiDirectorId; + String? apiDirectorName; + int? apiDirectorApprovalId; + String? apiDirectorApprovalName; + List? trafAssessIds; + List? trafPRIds; + List? trafOfferIds; + int? id; + String? createdBy; + String? createdDate; + String? modifiedBy; + String? modifiedDate; + + TrafRequestDataModel( + {this.reqNo, + this.reqCode, + this.employeeId, + this.employeeIdForDisplay, + this.employeeName, + this.employeeEmail, + this.positionId, + this.positionName, + this.departments, + this.trafDepartments, + this.siteId, + this.siteName, + this.requesterExtensionNumber, + this.requesterExtensionName, + this.requesterExtensionPositionId, + this.requesterExtensionPositionName, + this.requestTypeId, + this.requestTypeName, + this.assetNDId, + this.modelId, + this.modelName, + this.manufacturerId, + this.manufacturerName, + this.assetId, + this.assetSerialNo, + this.assetNumber, + this.assetName, + this.assetAssetNDId, + this.qty, + this.purposeAnswer, + this.currentPractise, + this.censusQ1, + this.censusQ2, + this.censusQ3, + this.censusQ4, + this.trafContacts, + this.usingSolelyOrSharedId, + this.usingSolelyOrSharedName, + this.isEffectedId, + this.isEffectedName, + this.effectedServices, + this.isCombinationId, + this.isCombinationName, + this.usedWithCombination, + this.comment, + this.attachments, + this.isBudgetId, + this.isBudgetName, + this.firstLineManagerId, + this.firstLineManagerName, + this.firstLineManagerApprovalId, + this.firstLineManagerApprovalName, + this.tlapiId, + this.tlapiName, + this.assessorEmployeeId, + this.assessorEmployeeName, + this.secondLineManagerId, + this.secondLineManagerName, + this.secondLineManagerApprovalId, + this.secondLineManagerApprovalName, + this.hospitalManagementId, + this.hospitalManagementName, + this.hospitalManagementApprovalId, + this.hospitalManagementApprovalName, + this.oracleCodes, + this.assessorTeamLeaderId, + this.assessorTeamLeaderName, + this.assessorTeamLeaderApprovalId, + this.assessorTeamLeaderApprovalName, + this.statusOfDRId, + this.statusOfDRName, + this.statusOfRequesterId, + this.statusOfRequesterName, + this.approved, + this.approvalUserId, + this.approvalUserName, + this.approvalDate, + this.docDate, + this.approvalDocDate, + this.isAssigned, + this.poNumber, + this.apiDirectorId, + this.apiDirectorName, + this.apiDirectorApprovalId, + this.apiDirectorApprovalName, + this.trafAssessIds, + this.trafPRIds, + this.trafOfferIds, + this.id, + this.createdBy, + this.createdDate, + this.modifiedBy, + this.modifiedDate}); + + TrafRequestDataModel.fromJson(Map json) { + reqNo = json['reqNo']; + reqCode = json['reqCode']; + employeeId = json['employeeId']; + employeeIdForDisplay = json['employeeIdForDisplay']; + employeeName = json['employeeName']; + employeeEmail = json['employeeEmail']; + positionId = json['positionId']; + positionName = json['positionName']; + if (json['departments'] != null) { + departments = []; + json['departments'].forEach((v) { + departments!.add(new Departments.fromJson(v)); + }); + } + if (json['trafDepartments'] != null) { + trafDepartments = []; + json['trafDepartments'].forEach((v) { + trafDepartments!.add(new Departments.fromJson(v)); + }); + } + siteId = json['siteId']; + siteName = json['siteName']; + requesterExtensionNumber = json['requesterExtensionNumber']; + requesterExtensionName = json['requesterExtensionName']; + requesterExtensionPositionId = json['requesterExtensionPositionId']; + requesterExtensionPositionName = json['requesterExtensionPositionName']; + requestTypeId = json['requestTypeId']; + requestTypeName = json['requestTypeName']; + assetNDId = json['assetNDId']; + modelId = json['modelId']; + modelName = json['modelName']; + manufacturerId = json['manufacturerId']; + manufacturerName = json['manufacturerName']; + assetId = json['assetId']; + assetSerialNo = json['assetSerialNo']; + assetNumber = json['assetNumber']; + assetName = json['assetName']; + assetAssetNDId = json['assetAssetNDId']; + qty = json['qty']; + purposeAnswer = json['purposeAnswer']; + currentPractise = json['currentPractise']; + censusQ1 = json['censusQ1']; + censusQ2 = json['censusQ2']; + censusQ3 = json['censusQ3']; + censusQ4 = json['censusQ4']; + if (json['trafContacts'] != null) { + trafContacts = []; + json['trafContacts'].forEach((v) { + trafContacts!.add(new TrafContacts.fromJson(v)); + }); + } + usingSolelyOrSharedId = json['usingSolelyOrSharedId']; + usingSolelyOrSharedName = json['usingSolelyOrSharedName']; + isEffectedId = json['isEffectedId']; + isEffectedName = json['isEffectedName']; + effectedServices = json['effectedServices']; + isCombinationId = json['isCombinationId']; + isCombinationName = json['isCombinationName']; + usedWithCombination = json['usedWithCombination']; + comment = json['comment']; + if (json['attachments'] != null) { + attachments = []; + json['attachments'].forEach((v) { + attachments!.add(new Attachments.fromJson(v)); + }); + } + isBudgetId = json['isBudgetId']; + isBudgetName = json['isBudgetName']; + firstLineManagerId = json['firstLineManagerId']; + firstLineManagerName = json['firstLineManagerName']; + firstLineManagerApprovalId = json['firstLineManagerApprovalId']; + firstLineManagerApprovalName = json['firstLineManagerApprovalName']; + tlapiId = json['tlapiId']; + tlapiName = json['tlapiName']; + assessorEmployeeId = json['assessorEmployeeId']; + assessorEmployeeName = json['assessorEmployeeName']; + secondLineManagerId = json['secondLineManagerId']; + secondLineManagerName = json['secondLineManagerName']; + secondLineManagerApprovalId = json['secondLineManagerApprovalId']; + secondLineManagerApprovalName = json['secondLineManagerApprovalName']; + hospitalManagementId = json['hospitalManagementId']; + hospitalManagementName = json['hospitalManagementName']; + hospitalManagementApprovalId = json['hospitalManagementApprovalId']; + hospitalManagementApprovalName = json['hospitalManagementApprovalName']; + if (json['oracleCodes'] != null) { + oracleCodes = []; + json['oracleCodes'].forEach((v) { + oracleCodes!.add(new OracleCodes.fromJson(v)); + }); + } + assessorTeamLeaderId = json['assessorTeamLeaderId']; + assessorTeamLeaderName = json['assessorTeamLeaderName']; + assessorTeamLeaderApprovalId = json['assessorTeamLeaderApprovalId']; + assessorTeamLeaderApprovalName = json['assessorTeamLeaderApprovalName']; + statusOfDRId = json['statusOfDRId']; + statusOfDRName = json['statusOfDRName']; + statusOfRequesterId = json['statusOfRequesterId']; + statusOfRequesterName = json['statusOfRequesterName']; + approved = json['approved']; + approvalUserId = json['approvalUserId']; + approvalUserName = json['approvalUserName']; + approvalDate = json['approvalDate']; + docDate = json['docDate']; + approvalDocDate = json['approvalDocDate']; + isAssigned = json['isAssigned']; + poNumber = json['poNumber']; + apiDirectorId = json['apiDirectorId']; + apiDirectorName = json['apiDirectorName']; + apiDirectorApprovalId = json['apiDirectorApprovalId']; + apiDirectorApprovalName = json['apiDirectorApprovalName']; + trafAssessIds = json['trafAssessIds'].cast(); + trafPRIds = json['trafPRIds'].cast(); + trafOfferIds = json['trafOfferIds'].cast(); + id = json['id']; + createdBy = json['createdBy']; + createdDate = json['createdDate']; + modifiedBy = json['modifiedBy']; + modifiedDate = json['modifiedDate']; + } + + Map toJson() { + final Map data = new Map(); + data['reqNo'] = this.reqNo; + data['reqCode'] = this.reqCode; + data['employeeId'] = this.employeeId; + data['employeeIdForDisplay'] = this.employeeIdForDisplay; + data['employeeName'] = this.employeeName; + data['employeeEmail'] = this.employeeEmail; + data['positionId'] = this.positionId; + data['positionName'] = this.positionName; + if (this.departments != null) { + data['departments'] = this.departments!.map((v) => v.toJson()).toList(); + } + if (this.trafDepartments != null) { + data['trafDepartments'] = this.trafDepartments!.map((v) => v.toJson()).toList(); + } + data['siteId'] = this.siteId; + data['siteName'] = this.siteName; + data['requesterExtensionNumber'] = this.requesterExtensionNumber; + data['requesterExtensionName'] = this.requesterExtensionName; + data['requesterExtensionPositionId'] = this.requesterExtensionPositionId; + data['requesterExtensionPositionName'] = this.requesterExtensionPositionName; + data['requestTypeId'] = this.requestTypeId; + data['requestTypeName'] = this.requestTypeName; + data['assetNDId'] = this.assetNDId; + data['modelId'] = this.modelId; + data['modelName'] = this.modelName; + data['manufacturerId'] = this.manufacturerId; + data['manufacturerName'] = this.manufacturerName; + data['assetId'] = this.assetId; + data['assetSerialNo'] = this.assetSerialNo; + data['assetNumber'] = this.assetNumber; + data['assetName'] = this.assetName; + data['assetAssetNDId'] = this.assetAssetNDId; + data['qty'] = this.qty; + data['purposeAnswer'] = this.purposeAnswer; + data['currentPractise'] = this.currentPractise; + data['censusQ1'] = this.censusQ1; + data['censusQ2'] = this.censusQ2; + data['censusQ3'] = this.censusQ3; + data['censusQ4'] = this.censusQ4; + if (this.trafContacts != null) { + data['trafContacts'] = this.trafContacts!.map((v) => v.toJson()).toList(); + } + data['usingSolelyOrSharedId'] = this.usingSolelyOrSharedId; + data['usingSolelyOrSharedName'] = this.usingSolelyOrSharedName; + data['isEffectedId'] = this.isEffectedId; + data['isEffectedName'] = this.isEffectedName; + data['effectedServices'] = this.effectedServices; + data['isCombinationId'] = this.isCombinationId; + data['isCombinationName'] = this.isCombinationName; + data['usedWithCombination'] = this.usedWithCombination; + data['comment'] = this.comment; + if (this.attachments != null) { + data['attachments'] = this.attachments!.map((v) => v.toJson()).toList(); + } else { + data['attachments'] = []; + } + data['isBudgetId'] = this.isBudgetId; + data['isBudgetName'] = this.isBudgetName; + data['firstLineManagerId'] = this.firstLineManagerId; + data['firstLineManagerName'] = this.firstLineManagerName; + data['firstLineManagerApprovalId'] = this.firstLineManagerApprovalId; + data['firstLineManagerApprovalName'] = this.firstLineManagerApprovalName; + data['tlapiId'] = this.tlapiId; + data['tlapiName'] = this.tlapiName; + data['assessorEmployeeId'] = this.assessorEmployeeId; + data['assessorEmployeeName'] = this.assessorEmployeeName; + data['secondLineManagerId'] = this.secondLineManagerId; + data['secondLineManagerName'] = this.secondLineManagerName; + data['secondLineManagerApprovalId'] = this.secondLineManagerApprovalId; + data['secondLineManagerApprovalName'] = this.secondLineManagerApprovalName; + data['hospitalManagementId'] = this.hospitalManagementId; + data['hospitalManagementName'] = this.hospitalManagementName; + data['hospitalManagementApprovalId'] = this.hospitalManagementApprovalId; + data['hospitalManagementApprovalName'] = this.hospitalManagementApprovalName; + if (this.oracleCodes != null) { + data['oracleCodes'] = this.oracleCodes!.map((v) => v.toJson()).toList(); + } + data['assessorTeamLeaderId'] = this.assessorTeamLeaderId; + data['assessorTeamLeaderName'] = this.assessorTeamLeaderName; + data['assessorTeamLeaderApprovalId'] = this.assessorTeamLeaderApprovalId; + data['assessorTeamLeaderApprovalName'] = this.assessorTeamLeaderApprovalName; + data['statusOfDRId'] = this.statusOfDRId; + data['statusOfDRName'] = this.statusOfDRName; + data['statusOfRequesterId'] = this.statusOfRequesterId; + data['statusOfRequesterName'] = this.statusOfRequesterName; + data['approved'] = this.approved; + data['approvalUserId'] = this.approvalUserId; + data['approvalUserName'] = this.approvalUserName; + data['approvalDate'] = this.approvalDate; + data['docDate'] = this.docDate; + data['approvalDocDate'] = this.approvalDocDate; + data['isAssigned'] = this.isAssigned; + data['poNumber'] = this.poNumber; + data['apiDirectorId'] = this.apiDirectorId; + data['apiDirectorName'] = this.apiDirectorName; + data['apiDirectorApprovalId'] = this.apiDirectorApprovalId; + data['apiDirectorApprovalName'] = this.apiDirectorApprovalName; + data['trafAssessIds'] = this.trafAssessIds; + data['trafPRIds'] = this.trafPRIds; + data['trafOfferIds'] = this.trafOfferIds; + data['id'] = this.id; + data['createdBy'] = this.createdBy; + data['createdDate'] = this.createdDate; + data['modifiedBy'] = this.modifiedBy; + data['modifiedDate'] = this.modifiedDate; + return data; + } +} + +class TrafContacts { + int? id; + int? trafId; + String? userId; + String? name; + String? telephone; + String? notes; + + TrafContacts({this.id, this.trafId, this.userId, this.name, this.telephone, this.notes}); + + TrafContacts.fromJson(Map json) { + id = json['id']; + trafId = json['trafId']; + userId = json['userId']; + name = json['name']; + telephone = json['telephone']; + notes = json['notes']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['trafId'] = this.trafId; + data['userId'] = this.userId; + data['name'] = this.name; + data['telephone'] = this.telephone; + data['notes'] = this.notes; + return data; + } +} + +class OracleCodes { + int? id; + int? assetNDId; + int? codeTypeId; + String? codeTypeName; + int? codeTypeValue; + String? codeValue; + + OracleCodes({this.id, this.assetNDId, this.codeTypeId, this.codeTypeName, this.codeTypeValue, this.codeValue}); + + OracleCodes.fromJson(Map json) { + id = json['id']; + assetNDId = json['assetNDId']; + codeTypeId = json['codeTypeId']; + codeTypeName = json['codeTypeName']; + codeTypeValue = json['codeTypeValue']; + codeValue = json['codeValue']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['assetNDId'] = this.assetNDId; + data['codeTypeId'] = this.codeTypeId; + data['codeTypeName'] = this.codeTypeName; + data['codeTypeValue'] = this.codeTypeValue; + data['codeValue'] = this.codeValue; + return data; + } +} + +class Departments { + int? id; + int? trafId; + int? departmentId; + String? departmentName; + + Departments({this.id, this.trafId, this.departmentId, this.departmentName}); + + Departments.fromJson(Map json) { + id = json['id']; + trafId = json['trafId']; + departmentId = json['departmentId']; + departmentName = json['departmentName']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['trafId'] = this.trafId; + data['departmentId'] = this.departmentId; + data['departmentName'] = this.departmentName; + return data; + } +} + +class Attachments { + int? id; + int? trafId; + String? attachmentName; + + Attachments({this.id, this.trafId, this.attachmentName}); + + Attachments.fromJson(Map json) { + id = json['id']; + trafId = json['trafId']; + attachmentName = json['attachmentName']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['trafId'] = this.trafId; + data['attachmentName'] = this.attachmentName; + return data; + } +} diff --git a/lib/modules/traf_module/traf_request_provider.dart b/lib/modules/traf_module/traf_request_provider.dart new file mode 100644 index 00000000..1f1dd385 --- /dev/null +++ b/lib/modules/traf_module/traf_request_provider.dart @@ -0,0 +1,50 @@ +import 'dart:convert'; + +import 'package:flutter/foundation.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 'traf_request_model.dart'; + +class TrafRequestProvider extends ChangeNotifier { + bool isLoading = false; + + TrafRequestDataModel? trafRequestDataModel; + + Future addTraf(Map body) async { + try { + isLoading = true; + notifyListeners(); + Response response = await ApiManager.instance.post(URLs.addTRAF, body: body); + if (response.statusCode >= 200 && response.statusCode < 300) { + // trafRequestDataModel = TrafRequestDataModel.fromJson(json.decode(response.body)["data"]); + } + isLoading = false; + notifyListeners(); + return 0; + } catch (error) { + isLoading = false; + notifyListeners(); + return -1; + } + } + + Future getTRAFById(int trafId) async { + try { + isLoading = true; + notifyListeners(); + Response response = await ApiManager.instance.get("${URLs.getTRAFById}?tRAFId=$trafId"); + if (response.statusCode >= 200 && response.statusCode < 300) { + trafRequestDataModel = TrafRequestDataModel.fromJson(json.decode(response.body)["data"]); + } + isLoading = false; + notifyListeners(); + return 0; + } catch (error) { + isLoading = false; + notifyListeners(); + return -1; + } + } +} diff --git a/lib/modules/traf_module/update_traf_request_page.dart b/lib/modules/traf_module/update_traf_request_page.dart new file mode 100644 index 00000000..e16e5726 --- /dev/null +++ b/lib/modules/traf_module/update_traf_request_page.dart @@ -0,0 +1,245 @@ +import 'dart:convert'; +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/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/traf_module/traf_request_model.dart'; +import 'package:test_sa/modules/traf_module/traf_request_provider.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; +import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; +import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; +import 'package:test_sa/new_views/common_widgets/multiple_item_drop_down_menu.dart'; +import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; +import 'package:test_sa/providers/lookups/classification_lookup_provider.dart'; +import 'package:test_sa/providers/lookups/recommendation_lookup_provider.dart'; +import 'package:test_sa/providers/lookups/request_type_lookup_provider.dart'; +import 'package:test_sa/views/widgets/equipment/asset_picker.dart'; +import 'package:test_sa/models/device/asset.dart'; +import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; + +class UpdateTrafRequestPage extends StatefulWidget { + static const String id = "/update-TRAF"; + + UpdateTrafRequestPage({Key? key}) : super(key: key); + + @override + _UpdateTrafRequestPageState createState() { + return _UpdateTrafRequestPageState(); + } +} + +class _UpdateTrafRequestPageState extends State { + Lookup? requestType; + Lookup? assessorRecommendation; + Lookup? classificationType; + late TrafRequestProvider trafRequestProvider; + + late TrafRequestDataModel trafRequest; + + List abc = []; + + List _deviceList = []; + Asset? asset; + List attachments = []; + + @override + void initState() { + super.initState(); + Provider.of(context, listen: false).reset(); + Provider.of(context, listen: false).reset(); + trafRequestProvider = Provider.of(context, listen: false); + trafRequest = trafRequestProvider.trafRequestDataModel!; + attachments = trafRequest.attachments?.map((item) => GenericAttachmentModel(id: item.id, name: item.attachmentName)).toList() ?? []; + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const DefaultAppBar(title: "Update TRAF Request"), + body: Column( + children: [ + SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SingleItemDropDownMenu( + context: context, + showAsBottomSheet: true, + backgroundColor: AppColor.neutral100, + showShadow: false, + title: "Classification", + initialValue: classificationType, + onSelect: (value) { + classificationType = value; + // trafRequest?.cla = value?.value; + // setState(() {}); + }, + ), + 12.height, + SingleItemDropDownMenu( + context: context, + showAsBottomSheet: true, + backgroundColor: AppColor.neutral100, + showShadow: false, + title: context.translation.requestType, + initialValue: requestType, + onSelect: (value) { + requestType = value; + setState(() {}); + }, + ), + if (requestType?.id == 732) ...[ + 12.height, + AppTextFormField( + initialValue: "", + makeMultiLinesNull: true, + labelText: "Recommended Quantity", + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.number, + onChange: (value) { + trafRequest.qty = int.tryParse(value); + }, + ), + ], + if (requestType?.id == 733) ...[ + 12.height, + AssetPicker( + device: asset, + editable: false, + showLoading: false, + + // borderColor: AppColor.black20, + // backgroundColor: AppColor.white936, + onPick: (asset) async { + this.asset = asset; + setState(() {}); + // pendingAssetServiceRequest = null; + // _serviceRequest.device = asset; + // await checkAssetForPendingServiceRequest(asset.id!.toInt()); + // if (pendingAssetServiceRequest != null && pendingAssetServiceRequest!.details!.isNotEmpty) { + // showPendingRequestBottomSheet(); + // } + }, + ), + ], + // 12.height, + // MultipleItemDropDownMenu( + // context: context, + // showAsBottomSheet: true, + // backgroundColor: AppColor.neutral100, + // showShadow: false, + // showCancel: true, + // title: "Please specify departments and relations", + // initialValue: abc, + // onSelect: (value) { + // abc = value ?? []; + // // setState(() { + // // + // // }); + // }, + // ), + 12.height, + Text( + "Quantity to be Transfer", + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50, fontWeight: FontWeight.w500), + ), + 8.height, + AssetPicker( + deviceList: _deviceList, + showLoading: false, + borderColor: AppColor.black20, + buttonColor: AppColor.white936, + multiSelection: true, + onAssetRemove: (itemId) { + _deviceList.removeWhere((asset) => asset.id?.toInt() == itemId); + setState(() {}); + }, + onMultiAssetPick: (assetList) { + _deviceList.addAll(assetList); + final seenIds = {}; + _deviceList = _deviceList.where((item) => seenIds.add(item.id)).toList(); + setState(() {}); + }, + ), + 12.height, + SingleItemDropDownMenu( + context: context, + showAsBottomSheet: true, + backgroundColor: AppColor.neutral100, + showShadow: false, + title: "Assessor Recommendation", + initialValue: assessorRecommendation, + onSelect: (value) { + assessorRecommendation = value; + trafRequest?.requestTypeId = value?.value; + // trafRequest?.requestTypeId = value?.value; + // setState(() {}); + }, + ), + 12.height, + AppTextFormField( + initialValue: "", + textInputType: TextInputType.multiline, + alignLabelWithHint: true, + labelText: "Assessor comment", + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + onChange: (value) { + trafRequest?.comment = value; // todo confirm parameter shaheer + }, + ), + 12.height, + AttachmentPicker( + label: context.translation.attachFiles, + attachment: attachments, + buttonColor: AppColor.black10, + onlyImages: false, + buttonIcon: 'image-plus'.toSvgAsset(color: AppColor.neutral120), + onChange: (attachment) { + attachments = attachment; + }, + ), + ], + ).toShadowContainer(context, borderRadius: 20, padding: 12), + ).expanded, + FooterActionButton.footerContainer( + context: context, + child: AppFilledButton( + buttonColor: AppColor.primary10, + label: "Update", + onPressed: () { + trafRequest.attachments = []; + for (var item in attachments) { + String fileName = + ServiceRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? ''; + trafRequest.attachments!.add( + Attachments(id: item.id, trafId: item.id, attachmentName: fileName), + ); + } + } + // buttonColor: AppColor.primary10, + ), + ), + ], + )); + } +} diff --git a/lib/modules/traf_module/users_auto_complete_field.dart b/lib/modules/traf_module/users_auto_complete_field.dart new file mode 100644 index 00000000..12060844 --- /dev/null +++ b/lib/modules/traf_module/users_auto_complete_field.dart @@ -0,0 +1,134 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/oracle_code_provider.dart'; +import 'package:test_sa/controllers/providers/api/parts_provider.dart'; +import 'package:test_sa/controllers/providers/api/search_user_provider.dart'; +import 'package:test_sa/controllers/providers/api/user_provider.dart'; +import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/new_models/asset_nd_auto_complete_by_dynamic_codes_model.dart'; +import 'package:test_sa/models/new_models/users_based_on_search_model.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/app_style/sizing.dart'; + +import '../../../extensions/text_extensions.dart'; +import '../../../models/service_request/spare_parts.dart'; +import '../../../new_views/app_style/app_text_style.dart'; + +class SearchUserAutoCompleteField extends StatefulWidget { + final String initialValue; + final num? assetId; + final bool clearAfterPick, byName; + final Function(UsersBasedOnSearchModel) onPick; + + const SearchUserAutoCompleteField({Key? key, required this.byName, required this.initialValue, this.assetId, required this.onPick, this.clearAfterPick = true}) : super(key: key); + + @override + _UsersAutoCompleteFieldState createState() => _UsersAutoCompleteFieldState(); +} + +class _UsersAutoCompleteFieldState extends State { + late UserSearchProvider _TechnologyUsersProvider; + + late TextEditingController _controller; + + bool loading = false; + + @override + void initState() { + _controller = TextEditingController(text: widget.initialValue); + super.initState(); + _TechnologyUsersProvider = Provider.of(context, listen: false); + } + + @override + void didUpdateWidget(covariant SearchUserAutoCompleteField oldWidget) { + if (widget.initialValue != oldWidget.initialValue) { + _controller = TextEditingController(text: widget.initialValue); + } + super.didUpdateWidget(oldWidget); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + 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( + optionsBuilder: (TextEditingValue textEditingValue) async { + if (textEditingValue.text.isEmpty) { + if (loading) { + setState(() { + loading = false; + }); + } + return const Iterable.empty(); + } + if (!loading) { + setState(() { + loading = true; + }); + } + List workOrders = (await _TechnologyUsersProvider.getUsersBasedOnSearch(textEditingValue.text)); + setState(() { + loading = false; + }); + return workOrders; + }, + displayStringForOption: (UsersBasedOnSearchModel option) => widget.byName ? option.userName ?? "" : option.employeeId ?? "", + fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) { + return TextField( + controller: _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: "Users going to use technology", + 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: (UsersBasedOnSearchModel selection) { + if (widget.clearAfterPick) { + _controller.clear(); + } else { + _controller.text = widget.byName ? (selection.userName ?? "") : (selection.employeeId ?? ""); + } + widget.onPick(selection); + }, + ), + ); + } +} diff --git a/lib/new_views/common_widgets/app_text_form_field.dart b/lib/new_views/common_widgets/app_text_form_field.dart index 2434230c..02cb825b 100644 --- a/lib/new_views/common_widgets/app_text_form_field.dart +++ b/lib/new_views/common_widgets/app_text_form_field.dart @@ -25,6 +25,7 @@ class AppTextFormField extends StatefulWidget { final TextStyle? style; final TextStyle? labelStyle; final TextStyle? hintStyle; + final TextStyle? floatingLabelStyle; final bool enable; final TextAlign? textAlign; final FocusNode? node; @@ -58,6 +59,7 @@ class AppTextFormField extends StatefulWidget { this.hintText, this.labelText, this.hintStyle, + this.floatingLabelStyle, this.textInputType = TextInputType.text, this.initialValue, // Provide default value this.enable = true, @@ -183,12 +185,16 @@ class _AppTextFormFieldState extends State { onChanged: widget.onChange, obscureText: widget.obscureText ?? false, keyboardType: widget.textInputType, - maxLines: widget.makeMultiLinesNull ? null:widget.textInputType == TextInputType.multiline ? 4 : 1, + maxLines: widget.makeMultiLinesNull + ? null + : widget.textInputType == TextInputType.multiline + ? 4 + : 1, obscuringCharacter: "*", controller: widget.controller, textInputAction: widget.textInputType == TextInputType.multiline ? null : widget.textInputAction ?? TextInputAction.next, onEditingComplete: widget.onAction ?? () => FocusScope.of(context).nextFocus(), - style: widget.style ?? AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500,color:context.isDark?AppColor.white10: AppColor.black10), + style: widget.style ?? AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.white10 : AppColor.black10), onTap: widget.onTap, decoration: InputDecoration( alignLabelWithHint: widget.alignLabelWithHint, @@ -208,9 +214,12 @@ class _AppTextFormFieldState extends State { ? (widget.enableColor ?? AppColor.neutral40) : AppColor.background(context)), errorStyle: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red60), - floatingLabelStyle: AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.white10 : AppColor.neutral20), + floatingLabelStyle: widget.floatingLabelStyle ?? AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.white10 : AppColor.neutral20), hintText: widget.hintText ?? "", - labelText: (widget.showSpeechToText && _speechToText.isListening) ? "Listening..." : widget.labelText ?? "", + // labelText: (widget.showSpeechToText && _speechToText.isListening) ? "Listening..." : widget.labelText ?? "", + label: Text( + (widget.showSpeechToText && _speechToText.isListening) ? "Listening..." : widget.labelText ?? "", + ), labelStyle: widget.labelStyle, hintStyle: widget.labelStyle, prefixIcon: widget.prefixIcon ?? diff --git a/lib/new_views/common_widgets/multiple_item_drop_down_menu.dart b/lib/new_views/common_widgets/multiple_item_drop_down_menu.dart new file mode 100644 index 00000000..d0df92fa --- /dev/null +++ b/lib/new_views/common_widgets/multiple_item_drop_down_menu.dart @@ -0,0 +1,252 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/new_views/common_widgets/app_loading_manager.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; +import 'package:test_sa/views/widgets/bottom_sheets/selection_bottom_sheet.dart'; +import 'package:test_sa/views/widgets/fullscreen_dialogs/multiple_selection_fullscreen_dialog.dart'; +import 'package:test_sa/views/widgets/fullscreen_dialogs/selection_fullscreen_dialog.dart'; + +import '../../models/base.dart'; +import '../app_style/app_color.dart'; + +class MultipleItemDropDownMenu extends StatefulWidget { + final BuildContext context; + final Function(List?)? onSelect; // Now accepts nullable values + final List initialValue; + final T? disableValue; + final bool enabled; + final bool showAsBottomSheet; + final bool showAsFullScreenDialog; + final List? staticData; + final String title; + final double? height; + final bool showShadow; + final bool showCancel; + final Color? backgroundColor; // Now nullable + final bool? loading; // Now nullable + final int? requestById; // Now nullable + + /// To use a static data (without calling API) + /// just send [NullableLoadingProvider] as generic data type and fill the [staticData] + const MultipleItemDropDownMenu({ + Key? key, + this.title = "", + required this.context, + this.onSelect, + this.initialValue = const [], + this.disableValue, + this.enabled = true, + this.height, + this.showAsBottomSheet = false, + this.showAsFullScreenDialog = true, + this.staticData, // Provide a default empty list + this.showShadow = true, + this.showCancel = false, + this.backgroundColor, + this.loading, + this.requestById, + }) : super(key: key); + + @override + State> createState() => _MultipleItemDropDownMenuState(); +} + +class _MultipleItemDropDownMenuState extends State> { + List _selectedItem = []; + X? provider; + + @override + void initState() { + if (X != NullableLoadingProvider) { + provider = Provider.of(widget.context); + } + + if (widget.initialValue.isNotEmpty) { + // Get the source list + final sourceList = (X == NullableLoadingProvider ? widget.staticData : provider?.items); + + if (sourceList != null && sourceList.isNotEmpty) { + // Collect matches based on identifier + final results = sourceList.where((element) => widget.initialValue!.any((init) => init.name == element.name)).toList(); + + if (results.isNotEmpty) { + _selectedItem + ..clear() + ..addAll(results.cast()); + + // Call onSelect if items changed + if (widget.onSelect != null) { + widget.onSelect!(_selectedItem); + } + } + } + } + + // if (widget.initialValue != null) { + // final result = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.where((element) => element.identifier == widget.initialValue?.identifier); + // if (result?.isNotEmpty ?? false) _selectedItem.add(result!.first as T); + // if (widget.onSelect != null && (widget.initialValue?.identifier ?? "") != (_selectedItem?.identifier ?? "")) { + // widget.onSelect!(_selectedItem); // Non-null assertion after null check + // } + // } + super.initState(); + } + + @override + void setState(VoidCallback fn) { + if (mounted) super.setState(fn); + } + + @override + void didUpdateWidget(covariant MultipleItemDropDownMenu oldWidget) { + if (widget.initialValue.isNotEmpty) { + // Get the source list + final sourceList = (X == NullableLoadingProvider ? widget.staticData : provider?.items); + if (sourceList != null && sourceList.isNotEmpty) { + // Collect matches based on identifier + + final results = sourceList.where((element) => widget.initialValue!.any((init) => init.name == element.name)).toList(); + if (results.isNotEmpty) { + _selectedItem + ..clear() + ..addAll(results.cast()); + + // Call onSelect if items changed + if (widget.onSelect != null) { + widget.onSelect!(_selectedItem); + } + } + } + } + + // if (widget.initialValue != null) { + // final result = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.where((element) => element.identifier == widget.initialValue?.identifier); + // + // if (result?.isNotEmpty ?? false) { + // _selectedItem.add(result!.first as T); + // } else { + // _selectedItem = []; + // } + // // if (widget.onSelect != null && (widget.initialValue?.identifier ?? "") != (_selectedItem?.identifier ?? "")) { + // // widget.onSelect!(_selectedItem); // Non-null assertion after null check + // // } + // } else { + // _selectedItem = []; + // } + super.didUpdateWidget(oldWidget); + } + + @override + Widget build(BuildContext context) { + final isEmpty = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.isEmpty ?? true; + return AppLoadingManager( + isLoading: widget.loading ?? ((X == NullableLoadingProvider) ? false : provider?.loading ?? false), + // Provide default value if null + isFailedLoading: (X == NullableLoadingProvider) ? false : provider?.items == null, + stateCode: (X == NullableLoadingProvider) ? 200 : provider?.stateCode, + onRefresh: () async { + if (X != NullableLoadingProvider) { + provider?.reset(); + await provider?.getData(id: widget.requestById); + } + }, + child: Container( + // height: widget.height ?? 60.toScreenHeight, + padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth, vertical: 8.toScreenHeight), + decoration: BoxDecoration( + color: context.isDark && (widget.enabled == false || isEmpty) + ? AppColor.neutral20 + : (widget.enabled == false || isEmpty) + ? AppColor.neutral40 + : widget.backgroundColor ?? AppColor.background(context), + borderRadius: BorderRadius.circular(10), + boxShadow: widget.showShadow ? [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)] : null, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + widget.title, + style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500), + ), + if (_selectedItem.isEmpty) + Text( + context.translation.select, + style: Theme.of(context).textTheme.bodyLarge, + ) + else + ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.only(top: 8), + separatorBuilder: (cxt, index) => const Divider( + thickness: 1, + color: Colors.white, + ), + itemBuilder: (cxt, index) => Text( + _selectedItem[index].name ?? "", // Null-aware operator for value.name + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.white10 : AppColor.black10), + ), + itemCount: _selectedItem.length, + ), + ], + ) + .onPress( + widget.enabled && widget.showAsFullScreenDialog + ? () { + openDialog(); + } + : null, + ) + .expanded, + if (widget.enabled) const PositionedDirectional(end: 0, child: Icon(Icons.keyboard_arrow_down_rounded)), + ], + ), + ), + ); + } + + void openDialog() async { + Widget child = MultipleSelectionFullScreenDialog( + // Specify generic type + items: ((X == NullableLoadingProvider) ? widget.staticData : provider?.items as List) ?? [], + // Provide default empty list if null + selectedItem: _selectedItem, + disableItem: widget.disableValue, + title: widget.title, + showCancel: widget.showCancel, + onSelect: (selectedT) { + setState(() { + _selectedItem = selectedT; + }); + widget.onSelect!(selectedT); + }, + builderString: (emp) => emp?.name ?? "", // Null-aware operator for emp.name + ); + pushRouter(child); + // final selectedT = await pushRouter(child); + // if (selectedT != null) { + // setState(() { + // _selectedItem = selectedT; + // }); + // widget.onSelect!(selectedT); // Non-null assertion after null check + // } + } + + Future pushRouter(Widget child) async { + // PageRoute pRoute = !Platform.isIOS ? CupertinoPageRoute(fullscreenDialog: true, builder: (context) => child) : MaterialPageRoute(fullscreenDialog: true, builder: (context) => child); + + return await Navigator.of(context).push(CupertinoPageRoute(fullscreenDialog: true, builder: (context) => child)); + } +} diff --git a/lib/new_views/common_widgets/single_item_drop_down_menu.dart b/lib/new_views/common_widgets/single_item_drop_down_menu.dart index c36af1a3..5c77205a 100644 --- a/lib/new_views/common_widgets/single_item_drop_down_menu.dart +++ b/lib/new_views/common_widgets/single_item_drop_down_menu.dart @@ -27,6 +27,7 @@ class SingleItemDropDownMenu exte final bool showCancel; final Color? backgroundColor; // Now nullable final bool? loading; // Now nullable + final int? requestById; /// To use a static data (without calling API) /// just send [NullableLoadingProvider] as generic data type and fill the [staticData] @@ -46,6 +47,7 @@ class SingleItemDropDownMenu exte this.showCancel = false, this.backgroundColor, this.loading, + this.requestById, }) : super(key: key); @override @@ -106,7 +108,7 @@ class _SingleItemDropDownMenuState( - // Specify return type - context: context, - isScrollControlled: true, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.circular(20), - ), - ), - clipBehavior: Clip.antiAliasWithSaveLayer, - builder: (BuildContext context) => SelectionBottomSheet( - // Specify generic type - items: ((X == NullableLoadingProvider) ? widget.staticData : provider?.items as List) ?? [], - // Provide default empty list if null - selectedItem: _selectedItem, - title: widget.title, - showCancel: widget.showCancel, - onSelect: (selectedT) { - setState(() { - _selectedItem = selectedT; - }); - widget.onSelect!(selectedT); - }, - builderString: (emp) => emp?.name ?? "", // Null-aware operator for emp.name - ), - ); - // if (selectedT != null) { - // setState(() { - // _selectedItem = selectedT; - // }); - // widget.onSelect!(selectedT); // Non-null assertion after null check - // } - } - : null) - : null), + ), ], - ), + ).onPress(widget.enabled && widget.showAsFullScreenDialog + ? () { + openDialog(); + } + : widget.enabled + ? (widget.showAsBottomSheet + ? () async { + final selectedT = await showModalBottomSheet( + // Specify return type + context: context, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(20), + ), + ), + clipBehavior: Clip.antiAliasWithSaveLayer, + builder: (BuildContext context) => SelectionBottomSheet( + // Specify generic type + items: ((X == NullableLoadingProvider) ? widget.staticData : provider?.items as List) ?? [], + // Provide default empty list if null + selectedItem: _selectedItem, + title: widget.title, + showCancel: widget.showCancel, + onSelect: (selectedT) { + setState(() { + _selectedItem = selectedT; + }); + widget.onSelect!(selectedT); + }, + builderString: (emp) => emp?.name ?? "", // Null-aware operator for emp.name + ), + ); + // if (selectedT != null) { + // setState(() { + // _selectedItem = selectedT; + // }); + // widget.onSelect!(selectedT); // Non-null assertion after null check + // } + } + : null) + : null), ], ), ), diff --git a/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart b/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart index edd2ba89..cca09e6e 100644 --- a/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart +++ b/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart @@ -3,21 +3,22 @@ import 'package:provider/provider.dart'; import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/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/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'; import 'package:test_sa/new_views/pages/gas_refill_request_form.dart'; import 'package:test_sa/views/pages/device_transfer/create__device_transfer_request.dart'; class CreateRequestTypeBottomSheet extends StatelessWidget { - final bool isEngineer; - - const CreateRequestTypeBottomSheet({super.key, required this.isEngineer}); + const CreateRequestTypeBottomSheet({super.key}); @override Widget build(BuildContext context) { - List requestList = CreateRequestModel.requestsList(context, isEngineer); + List requestList = CreateRequestModel.requestsList(context); return SafeArea( top: false, @@ -87,9 +88,12 @@ class CreateRequestModel { CreateRequestModel(this.title, this.icon, this.routeName); - static List requestsList(BuildContext context, bool isEngineer) { + static List requestsList(BuildContext context) { List list = []; - if (isEngineer) { + + if (context.userProvider.isAssessor) { + list.add(CreateRequestModel("TRAF".addTranslation, "add_icon", CreateTRAFRequestPage.id)); + } else if (context.userProvider.isEngineer) { if (Provider.of(context, listen: false).engineerCanCreateCM) { list.add(CreateRequestModel(context.translation.correctiveMaintenance, "add_icon", CreateNewRequest.id)); } @@ -101,6 +105,7 @@ class CreateRequestModel { list.add(CreateRequestModel(context.translation.transferAsset, "add_icon", CreateDeviceTransferRequest.id)); //TODO uncommit this to enable task. list.add(CreateRequestModel(context.translation.task, "add_icon", CreateTaskView.id)); + list.add(CreateRequestModel("TRAF".addTranslation, "add_icon", CreateTRAFRequestPage.id)); } return list; } diff --git a/lib/new_views/pages/land_page/land_page.dart b/lib/new_views/pages/land_page/land_page.dart index 0aebe82e..b28b6eb2 100644 --- a/lib/new_views/pages/land_page/land_page.dart +++ b/lib/new_views/pages/land_page/land_page.dart @@ -161,13 +161,12 @@ class _LandPageState extends State { : AppBottomNavigationBar( selectedIndex: currentPageIndex, onPressed: (index) { - bool isEngineer = _userProvider!.user!.type == UsersTypes.engineer; if (index == 2) { showModalBottomSheet( context: context, useSafeArea: true, backgroundColor: Colors.white, - builder: (context) => CreateRequestTypeBottomSheet(isEngineer: isEngineer), + builder: (context) => CreateRequestTypeBottomSheet(), ); } else if (index == 4) { showModalBottomSheet( diff --git a/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart b/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart index e8cfd8f9..9cfce9d4 100644 --- a/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart +++ b/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart @@ -61,7 +61,7 @@ class _AllRequestsFilterPageState extends State { ); search!.searchBySelectedValue = search?.requestNumber; } - final types = { + var types = { context.translation.correctiveMaintenance: 1, context.translation.gasRefill: 2, context.translation.transferAsset: 3, @@ -76,6 +76,14 @@ class _AllRequestsFilterPageState extends State { if (context.settingProvider.isUserFlowMedical && isEngineer) { types['Recall and Alert'] = 7; } + + if (!isEngineer) { + types['TRAF'] = 9; + } + if (context.userProvider.isAssessor) { + types = {"TRAF": 9}; + } + final statuses = { "All WO": 0, context.translation.open: 1, diff --git a/lib/new_views/pages/land_page/my_request/my_requests_page.dart b/lib/new_views/pages/land_page/my_request/my_requests_page.dart index 25245710..17525ab6 100644 --- a/lib/new_views/pages/land_page/my_request/my_requests_page.dart +++ b/lib/new_views/pages/land_page/my_request/my_requests_page.dart @@ -55,6 +55,16 @@ class _MyRequestsPageState extends State { if (Provider.of(context, listen: false).user!.type != UsersTypes.normal_user) { requestsList.add(Request(8, 'Inventory Session'.addTranslation)); } + if (context.userProvider.user!.type == UsersTypes.normal_user) { + requestsList.add(Request(9, 'TRAF')); + } + + if (context.userProvider.isAssessor) { + requestsList = [ + Request(null, context.translation.allWorkOrder), + Request(9, 'TRAF'), + ]; + } _provider = Provider.of(context, listen: false); _provider!.reset(); WidgetsBinding.instance.addPostFrameCallback((_) { diff --git a/lib/new_views/pages/land_page/requests/gas_refill_item_view.dart b/lib/new_views/pages/land_page/requests/gas_refill_item_view.dart index bad36d00..397ac41c 100644 --- a/lib/new_views/pages/land_page/requests/gas_refill_item_view.dart +++ b/lib/new_views/pages/land_page/requests/gas_refill_item_view.dart @@ -21,9 +21,7 @@ class GasRefillItemView extends StatelessWidget { @override Widget build(BuildContext context) { - if (requestData != null) { - return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -144,6 +142,6 @@ class GasRefillItemView extends StatelessWidget { }); } - return const SizedBox(); + return const SizedBox(); } } diff --git a/lib/new_views/pages/land_page/requests/request_paginated_listview.dart b/lib/new_views/pages/land_page/requests/request_paginated_listview.dart index 16ac1c20..e436e87d 100644 --- a/lib/new_views/pages/land_page/requests/request_paginated_listview.dart +++ b/lib/new_views/pages/land_page/requests/request_paginated_listview.dart @@ -3,6 +3,7 @@ import 'package:test_sa/extensions/int_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/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'; @@ -63,6 +64,8 @@ class RequestPaginatedListview extends StatelessWidget { return TaskRequestItemView(requestData: request); case 8: return InventorySessionItemView(requestData: request); + case 9: + return TrafRequestItemView(requestData: request); default: return Container( height: 100, diff --git a/lib/new_views/pages/land_page/widgets/request_item_view_list.dart b/lib/new_views/pages/land_page/widgets/request_item_view_list.dart index 5031a033..b84406cc 100644 --- a/lib/new_views/pages/land_page/widgets/request_item_view_list.dart +++ b/lib/new_views/pages/land_page/widgets/request_item_view_list.dart @@ -6,6 +6,7 @@ import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/all_requests_and_count_model.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/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'; @@ -43,8 +44,10 @@ class RequestItemViewList extends StatelessWidget { return TaskRequestItemView(requestDetails: list[index]); case 7: return TaskRequestItemView(requestDetails: list[index]); - case 8: + case 8: return InventorySessionItemView(requestDetails: list[index]); + case 9: + return TrafRequestItemView(requestDetails: list[index]); default: Container( height: 100, diff --git a/lib/providers/asset_transfer/asset_transfer_status_provider.dart b/lib/providers/asset_transfer/asset_transfer_status_provider.dart index a39928ed..a2ebf366 100644 --- a/lib/providers/asset_transfer/asset_transfer_status_provider.dart +++ b/lib/providers/asset_transfer/asset_transfer_status_provider.dart @@ -9,7 +9,7 @@ import '../loading_list_notifier.dart'; class AssetTransferStatusProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading ?? false) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/department_provider.dart b/lib/providers/department_provider.dart index 96a2d814..78041d0f 100644 --- a/lib/providers/department_provider.dart +++ b/lib/providers/department_provider.dart @@ -9,7 +9,7 @@ import 'loading_list_notifier.dart'; class DepartmentProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading ?? false) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/gas_request_providers/cylinder_size_provider.dart b/lib/providers/gas_request_providers/cylinder_size_provider.dart index 379d84d6..5e8d4409 100644 --- a/lib/providers/gas_request_providers/cylinder_size_provider.dart +++ b/lib/providers/gas_request_providers/cylinder_size_provider.dart @@ -9,7 +9,7 @@ import '../loading_list_notifier.dart'; class CylinderSizeProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading ?? false) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/gas_request_providers/cylinder_type_provider.dart b/lib/providers/gas_request_providers/cylinder_type_provider.dart index a4d9ba98..356af1d3 100644 --- a/lib/providers/gas_request_providers/cylinder_type_provider.dart +++ b/lib/providers/gas_request_providers/cylinder_type_provider.dart @@ -9,7 +9,7 @@ import '../loading_list_notifier.dart'; class CylinderTypesProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading ?? false) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/gas_request_providers/gas_status_provider.dart b/lib/providers/gas_request_providers/gas_status_provider.dart index f375b100..7057a503 100644 --- a/lib/providers/gas_request_providers/gas_status_provider.dart +++ b/lib/providers/gas_request_providers/gas_status_provider.dart @@ -9,7 +9,7 @@ import '../loading_list_notifier.dart'; class GasStatusProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading ?? false) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/gas_request_providers/gas_types_provider.dart b/lib/providers/gas_request_providers/gas_types_provider.dart index 7f539739..78d724f3 100644 --- a/lib/providers/gas_request_providers/gas_types_provider.dart +++ b/lib/providers/gas_request_providers/gas_types_provider.dart @@ -9,7 +9,7 @@ import '../../controllers/api_routes/urls.dart'; class GasTypesProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading ?? false) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/gas_request_providers/site_provider.dart b/lib/providers/gas_request_providers/site_provider.dart index 430a50e3..d497a88d 100644 --- a/lib/providers/gas_request_providers/site_provider.dart +++ b/lib/providers/gas_request_providers/site_provider.dart @@ -10,7 +10,7 @@ import '../../controllers/api_routes/urls.dart'; class SiteProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading ?? false) return -2; loading = true; notifyListeners(); @@ -35,7 +35,7 @@ class SiteProvider extends LoadingListNotifier { class MappedSiteProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading ?? false) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/loading_list_notifier.dart b/lib/providers/loading_list_notifier.dart index 2fee42b0..134f2a29 100644 --- a/lib/providers/loading_list_notifier.dart +++ b/lib/providers/loading_list_notifier.dart @@ -3,7 +3,7 @@ import 'package:test_sa/models/base.dart'; class NullableLoadingProvider extends LoadingListNotifier { @override - Future getData() { + Future getData({int? id}) { return Future.value(); } } @@ -28,5 +28,5 @@ abstract class LoadingListNotifier extends ChangeNotifier { stateCode = null; } - Future getData(); + Future getData({int? id}); } diff --git a/lib/providers/lookups/classification_lookup_provider.dart b/lib/providers/lookups/classification_lookup_provider.dart new file mode 100644 index 00000000..9e56dfa3 --- /dev/null +++ b/lib/providers/lookups/classification_lookup_provider.dart @@ -0,0 +1,33 @@ +import 'dart:convert'; + +import 'package:http/http.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; + +import '../../controllers/api_routes/api_manager.dart'; +import '../../controllers/api_routes/urls.dart'; +import '../../models/lookup.dart'; + +class ClassificationLookupProvider extends LoadingListNotifier { + @override + Future getData({int? id}) async { + if (loading == true) return -2; + loading = true; + notifyListeners(); + try { + Response response = await ApiManager.instance.get(URLs.getClassificationTypeLookup); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + List categoriesListJson = json.decode(response.body)["data"]; + items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList(); + } + loading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + loading = false; + stateCode = -1; + notifyListeners(); + return -1; + } + } +} diff --git a/lib/providers/lookups/department_lookup_provider.dart b/lib/providers/lookups/department_lookup_provider.dart new file mode 100644 index 00000000..fcd0fe8e --- /dev/null +++ b/lib/providers/lookups/department_lookup_provider.dart @@ -0,0 +1,34 @@ +import 'dart:convert'; + +import 'package:http/http.dart'; +import 'package:test_sa/models/new_models/traf_department.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; + +import '../../controllers/api_routes/api_manager.dart'; +import '../../controllers/api_routes/urls.dart'; +import '../../models/lookup.dart'; + +class DepartmentLookupProvider extends LoadingListNotifier { + @override + Future getData({int? id}) async { + if (loading == true) return -2; + loading = true; + notifyListeners(); + try { + Response response = await ApiManager.instance.get(URLs.getDepartmentBasedOnSite + "?customerId=$id"); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + List categoriesListJson = json.decode(response.body)["data"]; + items = categoriesListJson.map((item) => TrafDepartment.fromJson(item)).toList(); + } + loading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + loading = false; + stateCode = -1; + notifyListeners(); + return -1; + } + } +} diff --git a/lib/providers/lookups/recommendation_lookup_provider.dart b/lib/providers/lookups/recommendation_lookup_provider.dart new file mode 100644 index 00000000..e096656f --- /dev/null +++ b/lib/providers/lookups/recommendation_lookup_provider.dart @@ -0,0 +1,33 @@ +import 'dart:convert'; + +import 'package:http/http.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; + +import '../../controllers/api_routes/api_manager.dart'; +import '../../controllers/api_routes/urls.dart'; +import '../../models/lookup.dart'; + +class RecommendationLookupProvider extends LoadingListNotifier { + @override + Future getData({int? id}) async { + if (loading == true) return -2; + loading = true; + notifyListeners(); + try { + Response response = await ApiManager.instance.get(URLs.getRecommendationTypeLookup); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + List categoriesListJson = json.decode(response.body)["data"]; + items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList(); + } + loading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + loading = false; + stateCode = -1; + notifyListeners(); + return -1; + } + } +} diff --git a/lib/providers/lookups/request_type_lookup_provider.dart b/lib/providers/lookups/request_type_lookup_provider.dart new file mode 100644 index 00000000..ba709ccc --- /dev/null +++ b/lib/providers/lookups/request_type_lookup_provider.dart @@ -0,0 +1,33 @@ +import 'dart:convert'; + +import 'package:http/http.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; + +import '../../controllers/api_routes/api_manager.dart'; +import '../../controllers/api_routes/urls.dart'; +import '../../models/lookup.dart'; + +class RequestTypeLookupProvider extends LoadingListNotifier { + @override + Future getData({int? id}) async { + if (loading == true) return -2; + loading = true; + notifyListeners(); + try { + Response response = await ApiManager.instance.get(URLs.getTrafRequestTypeLookup); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + List categoriesListJson = json.decode(response.body)["data"]; + items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList(); + } + loading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + loading = false; + stateCode = -1; + notifyListeners(); + return -1; + } + } +} diff --git a/lib/providers/lookups/yes_no_lookup_provider.dart b/lib/providers/lookups/yes_no_lookup_provider.dart new file mode 100644 index 00000000..e3b87d66 --- /dev/null +++ b/lib/providers/lookups/yes_no_lookup_provider.dart @@ -0,0 +1,33 @@ +import 'dart:convert'; + +import 'package:http/http.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; + +import '../../controllers/api_routes/api_manager.dart'; +import '../../controllers/api_routes/urls.dart'; +import '../../models/lookup.dart'; + +class YesNoLookupProvider extends LoadingListNotifier { + @override + Future getData({int? id}) async { + if (loading == true) return -2; + loading = true; + notifyListeners(); + try { + Response response = await ApiManager.instance.get(URLs.getYesNoRequestTypeLookup); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + List categoriesListJson = json.decode(response.body)["data"]; + items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList(); + } + loading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + loading = false; + stateCode = -1; + notifyListeners(); + return -1; + } + } +} diff --git a/lib/providers/ppm_asset_availability_provider.dart b/lib/providers/ppm_asset_availability_provider.dart index 456ad99e..219647c6 100644 --- a/lib/providers/ppm_asset_availability_provider.dart +++ b/lib/providers/ppm_asset_availability_provider.dart @@ -9,7 +9,7 @@ import 'loading_list_notifier.dart'; class PpmAssetAvailabilityProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading ?? false) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/ppm_checklist_status_provider.dart b/lib/providers/ppm_checklist_status_provider.dart index d6b62a80..2744482e 100644 --- a/lib/providers/ppm_checklist_status_provider.dart +++ b/lib/providers/ppm_checklist_status_provider.dart @@ -9,7 +9,7 @@ import 'loading_list_notifier.dart'; class PpmChecklistStatusProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading ?? false) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/ppm_device_status_provider.dart b/lib/providers/ppm_device_status_provider.dart index 378ddcf1..ea49ade0 100644 --- a/lib/providers/ppm_device_status_provider.dart +++ b/lib/providers/ppm_device_status_provider.dart @@ -9,7 +9,7 @@ import 'loading_list_notifier.dart'; class PPMDeviceStatusProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading ?? false) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/ppm_electrical_safety_provider.dart b/lib/providers/ppm_electrical_safety_provider.dart index a681b927..c67dfbf5 100644 --- a/lib/providers/ppm_electrical_safety_provider.dart +++ b/lib/providers/ppm_electrical_safety_provider.dart @@ -9,7 +9,7 @@ import 'loading_list_notifier.dart'; class PpmElectricalSafetyProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading ?? false) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/ppm_service_provider.dart b/lib/providers/ppm_service_provider.dart index 0238c803..2e0a2d41 100644 --- a/lib/providers/ppm_service_provider.dart +++ b/lib/providers/ppm_service_provider.dart @@ -9,7 +9,7 @@ import 'loading_list_notifier.dart'; class PpmServiceProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading ?? false) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/ppm_task_status_provider.dart b/lib/providers/ppm_task_status_provider.dart index ed7a7943..2b1e86a8 100644 --- a/lib/providers/ppm_task_status_provider.dart +++ b/lib/providers/ppm_task_status_provider.dart @@ -9,7 +9,7 @@ import 'loading_list_notifier.dart'; class PpmTaskStatusProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading ?? false) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/ppm_visit_status_provider.dart b/lib/providers/ppm_visit_status_provider.dart index 19625daa..dc72c873 100644 --- a/lib/providers/ppm_visit_status_provider.dart +++ b/lib/providers/ppm_visit_status_provider.dart @@ -9,7 +9,7 @@ import 'loading_list_notifier.dart'; class PPMVisitStatusProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading ?? false) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/service_request_providers/commissioning_status_provider.dart b/lib/providers/service_request_providers/commissioning_status_provider.dart index 60e07ff9..bba116b8 100644 --- a/lib/providers/service_request_providers/commissioning_status_provider.dart +++ b/lib/providers/service_request_providers/commissioning_status_provider.dart @@ -9,7 +9,7 @@ import '../../models/lookup.dart'; class CommissioningStatusProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/service_request_providers/equipment_status_provider.dart b/lib/providers/service_request_providers/equipment_status_provider.dart index 7280aa58..5f2f9fad 100644 --- a/lib/providers/service_request_providers/equipment_status_provider.dart +++ b/lib/providers/service_request_providers/equipment_status_provider.dart @@ -9,7 +9,7 @@ import '../../models/lookup.dart'; class EquipmentStatusProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/service_request_providers/first_action_provider.dart b/lib/providers/service_request_providers/first_action_provider.dart index 32123215..4908e3b2 100644 --- a/lib/providers/service_request_providers/first_action_provider.dart +++ b/lib/providers/service_request_providers/first_action_provider.dart @@ -9,7 +9,7 @@ import '../../models/lookup.dart'; class FirstActionStatusProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/service_request_providers/last_situation_provider.dart b/lib/providers/service_request_providers/last_situation_provider.dart index f61a9d4f..56794e0f 100644 --- a/lib/providers/service_request_providers/last_situation_provider.dart +++ b/lib/providers/service_request_providers/last_situation_provider.dart @@ -9,7 +9,7 @@ import '../../models/lookup.dart'; class LastSituationProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/service_request_providers/loan_availability_provider.dart b/lib/providers/service_request_providers/loan_availability_provider.dart index 8118f46e..8d6d7f5d 100644 --- a/lib/providers/service_request_providers/loan_availability_provider.dart +++ b/lib/providers/service_request_providers/loan_availability_provider.dart @@ -9,7 +9,7 @@ import '../../models/lookup.dart'; class LoanAvailabilityProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; diff --git a/lib/providers/service_request_providers/priority_provider.dart b/lib/providers/service_request_providers/priority_provider.dart index 4850d1e9..87af53a2 100644 --- a/lib/providers/service_request_providers/priority_provider.dart +++ b/lib/providers/service_request_providers/priority_provider.dart @@ -9,7 +9,7 @@ import '../../models/lookup.dart'; class PriorityProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/service_request_providers/reject_reason_provider.dart b/lib/providers/service_request_providers/reject_reason_provider.dart index e7311c58..f8c6d880 100644 --- a/lib/providers/service_request_providers/reject_reason_provider.dart +++ b/lib/providers/service_request_providers/reject_reason_provider.dart @@ -10,7 +10,7 @@ import '../../models/lookup.dart'; class RejectReasonProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/service_request_providers/requested_through_provider.dart b/lib/providers/service_request_providers/requested_through_provider.dart index 72e481ff..de0ef414 100644 --- a/lib/providers/service_request_providers/requested_through_provider.dart +++ b/lib/providers/service_request_providers/requested_through_provider.dart @@ -9,7 +9,7 @@ import '../../models/lookup.dart'; class RequestedThroughProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/service_request_providers/type_of_request_provider.dart b/lib/providers/service_request_providers/type_of_request_provider.dart index ef9f306f..ba651ff9 100644 --- a/lib/providers/service_request_providers/type_of_request_provider.dart +++ b/lib/providers/service_request_providers/type_of_request_provider.dart @@ -9,7 +9,7 @@ import '../../models/lookup.dart'; class TypeOfRequestProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; diff --git a/lib/providers/task_request_provider/task_job_provider.dart b/lib/providers/task_request_provider/task_job_provider.dart index b66e10cf..8b9b74fc 100644 --- a/lib/providers/task_request_provider/task_job_provider.dart +++ b/lib/providers/task_request_provider/task_job_provider.dart @@ -11,7 +11,7 @@ import '../../controllers/api_routes/urls.dart'; class TaskTypeProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { print('get data called...'); if (loading == true) return -2; loading = true; @@ -39,7 +39,7 @@ class TaskTypeProvider extends LoadingListNotifier { } class TaskEvaluatorUserProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); @@ -65,7 +65,7 @@ class TaskEvaluatorUserProvider extends LoadingListNotifier { class TaskJobTypeOfAlertProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); @@ -90,7 +90,7 @@ class TaskJobTypeOfAlertProvider extends LoadingListNotifier { } class TaskJobRiskLevelProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); @@ -115,7 +115,7 @@ class TaskJobRiskLevelProvider extends LoadingListNotifier { } class TaskJobResourceProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); @@ -140,7 +140,7 @@ class TaskJobResourceProvider extends LoadingListNotifier { } class TaskJobActionNeededProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); @@ -165,7 +165,7 @@ class TaskJobActionNeededProvider extends LoadingListNotifier { } class TaskJobImpactStatusProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/work_order/activity_status_provider.dart b/lib/providers/work_order/activity_status_provider.dart index 21e0afa9..afa606e4 100644 --- a/lib/providers/work_order/activity_status_provider.dart +++ b/lib/providers/work_order/activity_status_provider.dart @@ -10,7 +10,7 @@ import '../../models/lookup.dart'; class ActivityStatusProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/work_order/need_visit_reminder_time_provider.dart b/lib/providers/work_order/need_visit_reminder_time_provider.dart index 2f82d41f..1247ee18 100644 --- a/lib/providers/work_order/need_visit_reminder_time_provider.dart +++ b/lib/providers/work_order/need_visit_reminder_time_provider.dart @@ -9,7 +9,7 @@ import '../../models/lookup.dart'; class NeedVisitReminderTimeProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/work_order/reason_provider.dart b/lib/providers/work_order/reason_provider.dart index d077fef2..a5ea7e6e 100644 --- a/lib/providers/work_order/reason_provider.dart +++ b/lib/providers/work_order/reason_provider.dart @@ -11,7 +11,7 @@ class ReasonProvider extends LoadingListNotifier { String? serviceRequestId; @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/work_order/retirement_type_provider.dart b/lib/providers/work_order/retirement_type_provider.dart index 436e9cc7..038f13a7 100644 --- a/lib/providers/work_order/retirement_type_provider.dart +++ b/lib/providers/work_order/retirement_type_provider.dart @@ -12,7 +12,7 @@ class RetirementTypeProvider extends LoadingListNotifier { String? serviceRequestId; @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; print('get data called...'); loading = true; diff --git a/lib/providers/work_order/service_type_provider.dart b/lib/providers/work_order/service_type_provider.dart index 0313f09e..1df3616c 100644 --- a/lib/providers/work_order/service_type_provider.dart +++ b/lib/providers/work_order/service_type_provider.dart @@ -9,7 +9,7 @@ import '../../models/lookup.dart'; class ServiceTypeProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/work_order/supplier_engineer_provider.dart b/lib/providers/work_order/supplier_engineer_provider.dart index e1bbf62e..490b990e 100644 --- a/lib/providers/work_order/supplier_engineer_provider.dart +++ b/lib/providers/work_order/supplier_engineer_provider.dart @@ -8,7 +8,7 @@ import 'package:test_sa/providers/loading_list_notifier.dart'; class SupplierEngineerProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/work_order/vendor_provider.dart b/lib/providers/work_order/vendor_provider.dart index 071189bf..52a2a77c 100644 --- a/lib/providers/work_order/vendor_provider.dart +++ b/lib/providers/work_order/vendor_provider.dart @@ -8,7 +8,7 @@ import 'package:test_sa/providers/loading_list_notifier.dart'; class VendorProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); diff --git a/lib/providers/work_order/wo_frame_provider.dart b/lib/providers/work_order/wo_frame_provider.dart index 0c92841a..cacebf4c 100644 --- a/lib/providers/work_order/wo_frame_provider.dart +++ b/lib/providers/work_order/wo_frame_provider.dart @@ -9,7 +9,7 @@ import '../../models/lookup.dart'; class WoFrameProvider extends LoadingListNotifier { @override - Future getData() async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); diff --git a/lib/views/widgets/bottom_sheets/muliple_selection_bottom_sheet.dart b/lib/views/widgets/bottom_sheets/muliple_selection_bottom_sheet.dart new file mode 100644 index 00000000..a9abe36e --- /dev/null +++ b/lib/views/widgets/bottom_sheets/muliple_selection_bottom_sheet.dart @@ -0,0 +1,172 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; + +typedef SelectionBuilderString = String Function(dynamic); + +class MultipleSelectionBottomSheet extends StatefulWidget { + final List? items; + final List selectedItem; // Now nullable + final String title; + final SelectionBuilderString builderString; + final bool showCancel; + final Function(List) onSelect; + + const MultipleSelectionBottomSheet({Key? key, this.items = const [], this.selectedItem = const [], this.title = "", required this.builderString, this.showCancel = false, required this.onSelect}) + : super(key: key); + + @override + _MultipleSelectionBottomSheetState createState() => _MultipleSelectionBottomSheetState(); +} + +class _MultipleSelectionBottomSheetState extends State> { + late List _selectedValue; // Now nullable + + String query = ""; + + List? get filteredList => widget.items?.where((element) => widget.builderString(element).toLowerCase().contains(query.toLowerCase())).toList(); + + @override + void initState() { + _selectedValue = widget.selectedItem; + super.initState(); + } + + FocusNode searchFocusNode = FocusNode(); + + @override + void dispose() { + searchFocusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + height: MediaQuery.of(context).size.height * .7, + color: Theme.of(context).scaffoldBackgroundColor, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + widget.title.heading5(context).expanded, + if (widget.showCancel) + AnimatedOpacity( + opacity: _selectedValue != null ? 1 : 0, + duration: const Duration(milliseconds: 250), + child: Container( + height: 30, + decoration: BoxDecoration(color: const Color(0xffF63939).withOpacity(.75), borderRadius: BorderRadius.circular(30)), + padding: const EdgeInsets.only(left: 8, right: 12, top: 4, bottom: 4), + alignment: Alignment.center, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.clear, color: Colors.white, size: 16), + 4.width, + const Text( + "Clear", + style: TextStyle(fontSize: 14, color: Colors.white), + ) + ], + ), + ).onPress(_selectedValue.isNotEmpty + ? () { + Navigator.pop(context); + widget.onSelect([]); + } + : null), + ), + ], + ).paddingOnly(top: 16, start: 16, end: 16, bottom: 0), + TextField( + onChanged: (queryString) { + query = queryString; + setState(() {}); + }, + style: const TextStyle(fontSize: 14), + focusNode: searchFocusNode, + decoration: InputDecoration( + hintText: 'Search by name', + labelText: 'Search', + labelStyle: TextStyle(color: AppColor.textColor(context)), + filled: true, + fillColor: AppColor.fieldBgColor(context), + hintStyle: const TextStyle(fontSize: 14), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: AppColor.blueStatus(context), width: 2.0), + borderRadius: const BorderRadius.all(Radius.circular(12.0)), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: AppColor.blueStatus(context), width: 1.0), + borderRadius: const BorderRadius.all(Radius.circular(12.0)), + ), + contentPadding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + ), + ).paddingOnly(top: 16, start: 16, end: 16, bottom: 16), + Expanded( + // Wrap ListView with Expanded + child: ListView.builder( + itemCount: filteredList?.length, + padding: EdgeInsets.zero, + itemBuilder: (cxt, index) => Theme( + data: Theme.of(context).copyWith( + radioTheme: RadioThemeData( + fillColor: MaterialStateColor.resolveWith((states) { + if (states.contains(MaterialState.selected)) { + return AppColor.textColor(context); // Active color + } + return Colors.grey; // Inactive color + }), + ), + ), + child: CheckboxListTile( + value: checkItContains(filteredList![index]), + dense: true, + // groupValue: _selectedValue, + activeColor: AppColor.textColor(context), + contentPadding: const EdgeInsets.only(left: 16, right: 16), + onChanged: (value) { + if (value == true) { + _selectedValue.add(filteredList![index]); + } else if (value == false) { + _selectedValue.remove(filteredList![index]); + } + searchFocusNode.unfocus(); + setState(() {}); + }, + title: Text( + widget.builderString(filteredList![index]).cleanupWhitespace.capitalizeFirstOfEach ?? "", + style: Theme.of(context).textTheme.bodyLarge, + ), + ), + ), + ), + ), + 8.height, + if (_selectedValue.isNotEmpty) + FooterActionButton.footerContainer( + context: context, + child: AppFilledButton( + label: context.translation.select, + maxWidth: true, + onPressed: () { + Navigator.pop(context); + widget.onSelect(_selectedValue); + }, + )), + ], + ), + ); + } + + bool? checkItContains(T) { + return widget.items?.contains(T); + } +} diff --git a/lib/views/widgets/fullscreen_dialogs/multiple_selection_fullscreen_dialog.dart b/lib/views/widgets/fullscreen_dialogs/multiple_selection_fullscreen_dialog.dart new file mode 100644 index 00000000..a4d4628b --- /dev/null +++ b/lib/views/widgets/fullscreen_dialogs/multiple_selection_fullscreen_dialog.dart @@ -0,0 +1,193 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/base.dart'; +import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; + +typedef SelectionBuilderString = String Function(dynamic); + +class MultipleSelectionFullScreenDialog extends StatefulWidget { + final List? items; + final List selectedItem; // Now nullable + final T? disableItem; // Now nullable + final String title; + final bool showCancel; + final SelectionBuilderString builderString; + final Function(List) onSelect; + + const MultipleSelectionFullScreenDialog( + {Key? key, this.items, this.selectedItem = const [], this.disableItem, this.title = "", required this.builderString, this.showCancel = false, required this.onSelect}) + : super(key: key); + + @override + _SelectionBottomSheetState createState() => _SelectionBottomSheetState(); +} + +class _SelectionBottomSheetState extends State> { + late List _selectedValue; // Now nullable + + String query = ""; + + List? get filteredList => widget.items?.where((element) => widget.builderString(element).toLowerCase().contains(query.toLowerCase())).toList(); + + @override + void initState() { + _selectedValue = widget.selectedItem; + super.initState(); + } + + FocusNode searchFocusNode = FocusNode(); + + @override + void dispose() { + searchFocusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: widget.title.heading5(context), + actions: [ + if (widget.showCancel) + AnimatedContainer( + height: 24, + duration: const Duration(milliseconds: 250), + margin: const EdgeInsets.only(right: 16), + decoration: BoxDecoration(color: _selectedValue.isNotEmpty ? const Color(0xffF63939).withOpacity(.75) : Colors.grey.withOpacity(.75), borderRadius: BorderRadius.circular(30)), + padding: const EdgeInsets.only(left: 4, right: 8, top: 4, bottom: 4), + alignment: Alignment.center, + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Icon(Icons.clear, color: Colors.white, size: 16), + 4.width, + const Text( + "Clear", + style: TextStyle(fontSize: 14, color: Colors.white, height: 1), + ) + ], + ).onPress(_selectedValue.isNotEmpty + ? () { + Navigator.pop(context); + widget.onSelect([]); + } + : null), + ), + ], + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SearchBar( + focusNode: searchFocusNode, + elevation: WidgetStateProperty.all(0), + backgroundColor: WidgetStateProperty.all(context.isDark ? AppColor.neutral120 : null), + leading: Icon(Icons.search, color: AppColor.iconColor(context)), + textStyle: WidgetStateProperty.all( + TextStyle(color: AppColor.textColor(context), fontSize: 16.0), + ), + hintStyle: WidgetStateProperty.all( + TextStyle(color: AppColor.textColor(context), fontSize: 14.0), + ), + hintText: 'Search by name', + onChanged: (queryString) { + query = queryString; + setState(() {}); + }, + ).paddingOnly(top: 16, start: 16, end: 16, bottom: 8), + // TextField( + // onChanged: (queryString) { + // query = queryString; + // setState(() {}); + // }, + // style: const TextStyle(fontSize: 14), + // focusNode: searchFocusNode, + // decoration: InputDecoration( + // hintText: 'Search by name', + // labelText: 'Search', + // hintStyle: const TextStyle(fontSize: 14), + // focusedBorder: OutlineInputBorder( + // borderSide: BorderSide(color: AppColor.blueStatus(context), width: 2.0), + // borderRadius: const BorderRadius.all(Radius.circular(12.0)), + // ), + // enabledBorder: OutlineInputBorder( + // borderSide: BorderSide(color: AppColor.blueStatus(context), width: 1.0), + // borderRadius: const BorderRadius.all(Radius.circular(12.0)), + // ), + // contentPadding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + // ), + // ), + 8.height, + Expanded( + child: ListView.builder( + itemCount: filteredList?.length, + padding: EdgeInsets.zero, + itemBuilder: (cxt, index) { + bool isDisabledItem = widget.disableItem != null && widget.disableItem?.identifier == filteredList![index].identifier; + return Theme( + data: Theme.of(context).copyWith( + radioTheme: RadioThemeData( + fillColor: MaterialStateColor.resolveWith((states) { + if (states.contains(MaterialState.selected)) { + return AppColor.iconColor(context); // Active color + } + return Colors.grey; // Inactive color + }), + ), + ), + child: CheckboxListTile( + value: checkItContains(filteredList![index]), + dense: true, + controlAffinity: ListTileControlAffinity.leading, + activeColor: AppColor.textColor(context), + contentPadding: const EdgeInsets.only(left: 16, right: 16), + onChanged: (value) { + // if (checkItContains(filteredList![index]) ?? false) { + // _selectedValue.remove(filteredList![index]); + // } else { + // _selectedValue.add(filteredList![index]); + // } + if (value == true) { + _selectedValue.add(filteredList![index]); + } else if (value == false) { + _selectedValue.remove(filteredList![index]); + } + searchFocusNode.unfocus(); + setState(() {}); + }, + title: Text( + widget.builderString(filteredList![index]).cleanupWhitespace.capitalizeFirstOfEach ?? "", + style: Theme.of(context).textTheme.bodyLarge, + ), + ), + ); + }), + ), + 8.height, + if (_selectedValue.isNotEmpty) + FooterActionButton.footerContainer( + context: context, + child: AppFilledButton( + label: context.translation.select, + maxWidth: true, + onPressed: () { + Navigator.pop(context); + widget.onSelect(_selectedValue); + }, + )), + ], + ), + ); + } + + bool? checkItContains(T) { + return _selectedValue.contains(T); + } +}