From d6c3fc277876e6f26f0e1ccd07b52e8a53c9bbed Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 9 May 2024 14:51:13 +0300 Subject: [PATCH] head nurse role added, pending asset service requests added & AssetGroup header added. --- lib/controllers/api_routes/api_manager.dart | 2 + lib/controllers/api_routes/urls.dart | 1 + .../api/service_requests_provider.dart | 15 ++++ .../providers/settings/app_settings.dart | 1 + .../providers/settings/setting_provider.dart | 34 ++++++++ .../pending_service_request_model.dart | 44 +++++++++++ lib/models/user.dart | 37 ++++++++- .../common_widgets/app_filled_button.dart | 17 +++- .../requests/create_service_request_page.dart | 57 +++++++++++--- .../pending_request_bottom_sheet.dart | 78 +++++++++++++++++++ 10 files changed, 271 insertions(+), 15 deletions(-) create mode 100644 lib/models/service_request/pending_service_request_model.dart create mode 100644 lib/views/widgets/bottom_sheets/pending_request_bottom_sheet.dart diff --git a/lib/controllers/api_routes/api_manager.dart b/lib/controllers/api_routes/api_manager.dart index 8a57940..e846492 100644 --- a/lib/controllers/api_routes/api_manager.dart +++ b/lib/controllers/api_routes/api_manager.dart @@ -20,11 +20,13 @@ class ApiManager { Map get _headers => { 'Content-Type': 'application/json', if (user != null) 'Authorization': 'Bearer ${user.token}', + if (assetGroup != null) 'AssetGroup': assetGroup.id.toString(), }; static ApiManager instance = ApiManager._(); User user; + AssetGroup assetGroup; Future get( String url, { diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 3a95524..2136a92 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -61,6 +61,7 @@ class URLs { static get getSystemNotifications => "$_baseUrl/SystemNotification/GetSystemNotifications"; // get static get getRecentNotifications => "$_baseUrl/return/user/recent/notification"; // get static get createRequest => "$_baseUrl/CallRequest/AddCallRequest"; // get + static get CheckIfAssetHasAnotherServiceRequest => "$_baseUrl/CallRequest/CheckIfAssetHasAnotherServiceRequest"; // get static get createReport => "$_baseUrl/handle/create/report/issue"; // get static get updateRequestDate => "$_baseUrl/CallRequest/UpdateCallRequest"; // get diff --git a/lib/controllers/providers/api/service_requests_provider.dart b/lib/controllers/providers/api/service_requests_provider.dart index b9c4523..edef014 100644 --- a/lib/controllers/providers/api/service_requests_provider.dart +++ b/lib/controllers/providers/api/service_requests_provider.dart @@ -9,8 +9,10 @@ import 'package:test_sa/controllers/api_routes/api_manager.dart'; import 'package:test_sa/controllers/api_routes/http_status_manger.dart'; import 'package:test_sa/controllers/api_routes/urls.dart'; import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/models/issue.dart'; import 'package:test_sa/models/lookup.dart'; +import 'package:test_sa/models/service_request/pending_service_request_model.dart'; import 'package:test_sa/models/service_request/service_report.dart'; import 'package:test_sa/models/service_request/service_request.dart'; import 'package:test_sa/models/service_request/service_request_search.dart'; @@ -232,6 +234,19 @@ class ServiceRequestsProvider extends ChangeNotifier { } } + Future checkAssetPendingRequest(int assetId) async { + Response response; + try { + response = await await ApiManager.instance.get(URLs.CheckIfAssetHasAnotherServiceRequest + "?assetId=$assetId"); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) {} + return PendingAssetServiceRequest.fromJson(json.decode(response.body)["data"]); + } catch (error) { + "Please Try Again\n$error".addTranslation.showToast; + return null; + } + } + Future updateRequest({@required User user, @required ServiceRequest request}) async { isLoading = true; notifyListeners(); diff --git a/lib/controllers/providers/settings/app_settings.dart b/lib/controllers/providers/settings/app_settings.dart index c22e178..8b83c0c 100644 --- a/lib/controllers/providers/settings/app_settings.dart +++ b/lib/controllers/providers/settings/app_settings.dart @@ -1,5 +1,6 @@ class ASettings { static final String user = "user"; + static final String assetGroup = "asset_group"; static final String host = "host"; static final String language = "language"; static final String theme = "theme"; diff --git a/lib/controllers/providers/settings/setting_provider.dart b/lib/controllers/providers/settings/setting_provider.dart index 0a71687..b87ff49 100644 --- a/lib/controllers/providers/settings/setting_provider.dart +++ b/lib/controllers/providers/settings/setting_provider.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.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/user.dart'; @@ -12,7 +13,9 @@ class SettingProvider extends ChangeNotifier { resetSettings() async { SharedPreferences prefs = await SharedPreferences.getInstance(); user = null; + _assetGroup = null; prefs.remove(ASettings.user); + prefs.remove(ASettings.assetGroup); notifyListeners(); } @@ -27,8 +30,30 @@ class SettingProvider extends ChangeNotifier { prefs.setString(ASettings.user, json.encode(user.toJson())); this.user = user; notifyListeners(); + selectAssetGroup(user); } + void selectAssetGroup(User user) { + if (user.assetGroups.length == 1) { + _assetGroup = user.assetGroups.first; + } else { + int fmIndex = user.assetGroups.indexWhere((element) => element.id == 1); + if (fmIndex < 0) { + _assetGroup = user.assetGroups.first; + } else { + _assetGroup = user.assetGroups[fmIndex]; + } + } + ApiManager.instance.assetGroup = _assetGroup; + } + + // Future setAssetGroup(AssetGroup assetGroup) async { + // SharedPreferences prefs = await SharedPreferences.getInstance(); + // prefs.setString(ASettings.assetGroup, json.encode(assetGroup.toJson())); + // _assetGroup = assetGroup; + // notifyListeners(); + // } + String _host; String get host => _host; @@ -41,9 +66,12 @@ class SettingProvider extends ChangeNotifier { } String _language; + AssetGroup _assetGroup; String get language => _language; + AssetGroup get assetGroup => _assetGroup; + Future setLanguage(String currentLanguage) async { SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.setString(ASettings.language, currentLanguage); @@ -103,8 +131,14 @@ class SettingProvider extends ChangeNotifier { if (prefs.containsKey(ASettings.user)) { String userJson = prefs.getString(ASettings.user); user = User.fromJson(json.decode(userJson)); + selectAssetGroup(user); } + // if (prefs.containsKey(ASettings.assetGroup)) { // + // String assetJson = prefs.getString(ASettings.assetGroup); + // _assetGroup = AssetGroup.fromJson(json.decode(assetJson)); + // } + if (prefs.containsKey(ASettings.host)) { _host = prefs.getString(ASettings.host); } else { diff --git a/lib/models/service_request/pending_service_request_model.dart b/lib/models/service_request/pending_service_request_model.dart new file mode 100644 index 0000000..3385529 --- /dev/null +++ b/lib/models/service_request/pending_service_request_model.dart @@ -0,0 +1,44 @@ +class PendingAssetServiceRequest { + String headerMessage; + List
details; + + PendingAssetServiceRequest({this.headerMessage, this.details}); + + PendingAssetServiceRequest.fromJson(Map json) { + headerMessage = json['headerMessage']; + details =
[]; + if (json['details'] != null) { + json['details'].forEach((v) { + details.add(new Details.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + data['headerMessage'] = this.headerMessage; + if (this.details != null) { + data['details'] = this.details.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class Details { + int id; + String message; + + Details({this.id, this.message}); + + Details.fromJson(Map json) { + id = json['id']; + message = json['message']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['message'] = this.message; + return data; + } +} diff --git a/lib/models/user.dart b/lib/models/user.dart index 777276e..eb9bcdb 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -33,6 +33,7 @@ class User { dynamic lockoutEnd; bool lockoutEnabled; int accessFailedCount; + List assetGroups; User({ this.clientId, @@ -82,7 +83,9 @@ class User { switch (userRoles?.first?.value) { case "R-6": return UsersTypes.engineer; - case "R-7": + case "R-7": // Nurse Role + return UsersTypes.normal_user; + case "R-33": // Head Nurse Role return UsersTypes.normal_user; default: return null; @@ -127,6 +130,9 @@ class User { if (userRoles != null) { map['userRoles'] = userRoles.map((v) => v.toJson()).toList(); } + if (assetGroups != null) { + map['assetGroups'] = assetGroups.map((v) => v.toJson()).toList(); + } map['tokenlife'] = tokenlife; map['isAuthenticated'] = isAuthenticated; map['hasError'] = hasError; @@ -177,6 +183,13 @@ class User { userRoles.add(UserRoles.fromJson(v)); }); } + + if (json['assetGroups'] != null) { + assetGroups = []; + json['assetGroups'].forEach((v) { + assetGroups.add(AssetGroup.fromJson(v)); + }); + } tokenlife = json['tokenlife']; isAuthenticated = json['isAuthenticated']; hasError = json['hasError']; @@ -235,3 +248,25 @@ class UserRoles { return map; } } + +class AssetGroup { + int id; + String name; + String code; + + AssetGroup({this.id, this.name, this.code}); + + AssetGroup.fromJson(Map json) { + id = json['id']; + name = json['name']; + code = json['code']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['name'] = this.name; + data['code'] = this.code; + return data; + } +} diff --git a/lib/new_views/common_widgets/app_filled_button.dart b/lib/new_views/common_widgets/app_filled_button.dart index 4736ad1..a9d46b5 100644 --- a/lib/new_views/common_widgets/app_filled_button.dart +++ b/lib/new_views/common_widgets/app_filled_button.dart @@ -10,12 +10,14 @@ class AppFilledButton extends StatelessWidget { final bool maxWidth, loading; final Color buttonColor; final Color textColor; + final bool showBorder; const AppFilledButton({ @required this.onPressed, @required this.label, this.maxWidth = false, this.loading = false, + this.showBorder = false, this.buttonColor, this.textColor, Key key, @@ -27,12 +29,19 @@ class AppFilledButton extends StatelessWidget { height: 56.toScreenHeight, width: maxWidth ? double.infinity : null, alignment: Alignment.center, - decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: buttonColor ?? AppColor.blueStatus(context)), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: buttonColor ?? AppColor.blueStatus(context), + border: showBorder ? Border.all(color: textColor ?? AppColor.background(context)) : null, + ), child: loading ? SizedBox( - width: 40, - height: 40, - child: CircularProgressIndicator(color: textColor ?? AppColor.background(context)), + width: 24, + height: 24, + child: CircularProgressIndicator( + color: textColor ?? AppColor.background(context), + strokeWidth: 2, + ), ) : label.heading6(context).custom(color: textColor ?? AppColor.background(context)), ).onPress(onPressed); diff --git a/lib/views/pages/user/requests/create_service_request_page.dart b/lib/views/pages/user/requests/create_service_request_page.dart index 40a255e..5d94ebe 100644 --- a/lib/views/pages/user/requests/create_service_request_page.dart +++ b/lib/views/pages/user/requests/create_service_request_page.dart @@ -10,9 +10,11 @@ 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/service_request/pending_service_request_model.dart'; import 'package:test_sa/models/service_request/service_request.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; import 'package:test_sa/providers/service_request_providers/requested_through_provider.dart'; +import 'package:test_sa/views/widgets/bottom_sheets/pending_request_bottom_sheet.dart'; import 'package:test_sa/views/widgets/equipment/pick_asset.dart'; import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; import 'package:test_sa/views/widgets/loaders/loading_manager.dart'; @@ -163,16 +165,17 @@ class CreateServiceRequestPageState extends State { ); }), 8.height, - Consumer(builder: (context, snapshot, _){ + Consumer(builder: (context, snapshot, _) { return SingleItemDropDownMenu( - context: context, - title: context.translation.requestType, - enabled: false, + context: context, + title: context.translation.requestType, + enabled: false, initialValue: snapshot.items?.firstWhere((element) => element.value == 1, orElse: () => null), - // onSelect: (value) { - // _serviceRequest.type = value; - // }, - );}), + // onSelect: (value) { + // _serviceRequest.type = value; + // }, + ); + }), 8.height, MultiFilesPicker(label: context.translation.attachImage, files: _deviceImages), ((_serviceRequest.devicePhotos?.isNotEmpty ?? false) ? 16 : 8).height, @@ -206,7 +209,8 @@ class CreateServiceRequestPageState extends State { ), ).expanded, AppFilledButton( - onPressed: _submit, + onPressed: checkPendingRequest ? null : _submit, + loading: checkPendingRequest, label: context.translation.submitRequest, ), ], @@ -217,11 +221,44 @@ class CreateServiceRequestPageState extends State { ); } + bool checkPendingRequest = false; + + Future checkAssetForPendingServiceRequest(int assetId) async { + checkPendingRequest = true; + setState(() {}); + + PendingAssetServiceRequest pendingAssetServiceRequest = await _serviceRequestsProvider.checkAssetPendingRequest(assetId); + + checkPendingRequest = false; + setState(() {}); + + if (pendingAssetServiceRequest.details.isEmpty) return true; + + bool submit = (await showModalBottomSheet( + context: context, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(20), + ), + ), + clipBehavior: Clip.antiAliasWithSaveLayer, + builder: (BuildContext context) => PendingRequestBottomSheet(pendingAssetServiceRequest), + )) as bool; + return submit ?? false; + } + Future _submit() async { _serviceRequest?.requestedThrough = Provider.of(context, listen: false).items?.firstWhere((element) => element.value == 3, orElse: () => null); - _serviceRequest?.type=Provider.of(context, listen: false).items?.firstWhere((element) => element.value == 1, orElse: () => null); + _serviceRequest?.type = Provider.of(context, listen: false).items?.firstWhere((element) => element.value == 1, orElse: () => null); if (_formKey.currentState.validate() && await _serviceRequest.validateNewRequest(context)) { _formKey.currentState.save(); + + bool canSubmitRequest = await checkAssetForPendingServiceRequest(_serviceRequest.device.id); + if (!canSubmitRequest) { + return; + } + _serviceRequest.devicePhotos = _deviceImages.map((e) => _isLocalUrl(e.path) ? "${e.path.split("/").last}|${base64Encode(e.readAsBytesSync())}" : e.path).toList(); if (_serviceRequest.audio != null) { if (_isLocalUrl(_serviceRequest.audio)) { diff --git a/lib/views/widgets/bottom_sheets/pending_request_bottom_sheet.dart b/lib/views/widgets/bottom_sheets/pending_request_bottom_sheet.dart new file mode 100644 index 0000000..21e9fdb --- /dev/null +++ b/lib/views/widgets/bottom_sheets/pending_request_bottom_sheet.dart @@ -0,0 +1,78 @@ +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/service_request/pending_service_request_model.dart'; +import 'package:test_sa/models/service_request/service_request.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/views/pages/user/requests/service_request_details.dart'; + +class PendingRequestBottomSheet extends StatelessWidget { + PendingAssetServiceRequest pendingAssetServiceRequest; + + PendingRequestBottomSheet(this.pendingAssetServiceRequest, {Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + height: MediaQuery.of(context).size.height * .6, + padding: const EdgeInsets.all(21), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + pendingAssetServiceRequest.headerMessage.heading5(context), + "${pendingAssetServiceRequest.details.length} found".bodyText(context), + 8.height, + ListView.separated( + itemCount: pendingAssetServiceRequest.details.length, + padding: EdgeInsets.only(top: 8), + separatorBuilder: (cxt, index) => 12.height, + itemBuilder: (cxt, index) => Container( + padding: EdgeInsets.symmetric(vertical: 16, horizontal: 8), + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.circular(16), + ), + child: Row( + children: [ + Text( + pendingAssetServiceRequest.details[index].message.cleanupWhitespace?.capitalizeFirstOfEach ?? "", + style: Theme.of(context).textTheme.bodyLarge, + ).expanded, + Icon(Icons.arrow_forward_ios, size: 16) + ], + ), + ).onPress(() { + Navigator.of(context).push(MaterialPageRoute( + builder: (_) => ServiceRequestDetailsPage( + serviceRequest: ServiceRequest(id: pendingAssetServiceRequest.details[index].id.toString()), + ))); + })).expanded, + 8.height, + Row( + children: [ + AppFilledButton( + label: context.translation.cancel, + textColor: AppColor.blueStatus(context), + buttonColor: AppColor.background(context), + maxWidth: true, + showBorder: true, + onPressed: () { + Navigator.pop(context, false); + }).expanded, + 16.width, + AppFilledButton( + label: "Continue", + maxWidth: true, + onPressed: () { + Navigator.pop(context, true); + }).expanded, + ], + ), + ], + ), + ); + } +}