diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 3f5e424..15b0c54 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ @@ -8,6 +9,16 @@ + + + + + + + + @@ -24,6 +35,17 @@ android:extractNativeLibs="true" android:networkSecurityConfig="@xml/network_security_config" android:roundIcon="@mipmap/ic_launcher_round"> + + + + + + + + + + \ No newline at end of file diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 7ac0e9f..8f127f2 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -88,7 +88,7 @@ "atLeastOneNumeric": "رقم واحد على الأقل", "minimum8Characters": "8 أحرف على الأقل", "doNotAddRepeatingLetters": "لا تقم بإضافة أحرف متكررة", - "itShouldContainSpecialCharacter": "يجب أن يحتوي على طابع خاص", + "itShouldContainSpecialCharacter": "يجب لا يحتوي على أحرف خاصة", "confirmPasswordMustMatch": "يجب أن يتطابق تأكيد كلمة المرور", "sms": "رسالة قصيرة", "fingerPrint": "بصمة", diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index b679368..eec20d4 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -85,7 +85,7 @@ "atLeastOneNumeric": "At least one numeric", "minimum8Characters": "Minimum 8 characters", "doNotAddRepeatingLetters": "Do not add repeating letters", - "itShouldContainSpecialCharacter": "It should contain special character", + "itShouldContainSpecialCharacter": "It should not contain special characters", "confirmPasswordMustMatch": "Confirm password must match", "sms": "SMS", "fingerPrint": "Fingerprint", diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 073c15f..e734dc5 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -376,7 +376,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.7.2; + MARKETING_VERSION = 3.7.8; PRODUCT_BUNDLE_IDENTIFIER = com.cloudsolutions.mohemm; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -514,7 +514,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.7.2; + MARKETING_VERSION = 3.7.8; PRODUCT_BUNDLE_IDENTIFIER = com.cloudsolutions.mohemm; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -544,7 +544,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.7.2; + MARKETING_VERSION = 3.7.8; PRODUCT_BUNDLE_IDENTIFIER = com.cloudsolutions.mohemm; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/lib/api/chat/chat_api_client.dart b/lib/api/chat/chat_api_client.dart index d077026..daafa92 100644 --- a/lib/api/chat/chat_api_client.dart +++ b/lib/api/chat/chat_api_client.dart @@ -29,6 +29,7 @@ class ChatApiClient { Future getUserLoginToken() async { user.UserAutoLoginModel userLoginResponse = user.UserAutoLoginModel(); + String? deviceToken = AppState().getIsHuawei ? AppState().getHuaweiPushToken : AppState().getDeviceToken; Response response = await ApiClient().postJsonForResponse( "${ApiConsts.chatLoginTokenUrl}externaluserlogin", { @@ -38,6 +39,7 @@ class ChatApiClient { "platform": Platform.isIOS ? "ios" : "android", "deviceToken": AppState().getIsHuawei ? AppState().getHuaweiPushToken : AppState().getDeviceToken, "isHuaweiDevice": AppState().getIsHuawei, + "voipToken": "", }, ); diff --git a/lib/api/mowadhafhi/mowadhafhi_api_client.dart b/lib/api/mowadhafhi/mowadhafhi_api_client.dart index e5ecc4b..d724c67 100644 --- a/lib/api/mowadhafhi/mowadhafhi_api_client.dart +++ b/lib/api/mowadhafhi/mowadhafhi_api_client.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:mohem_flutter_app/api/api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/consts.dart'; @@ -10,6 +12,7 @@ import 'package:mohem_flutter_app/models/mowadhafhi/get_ticket_details.dart'; import 'package:mohem_flutter_app/models/mowadhafhi/get_ticket_transactions.dart'; import 'package:mohem_flutter_app/models/mowadhafhi/get_ticket_types.dart'; import 'package:mohem_flutter_app/models/mowadhafhi/get_tickets_list.dart'; +import 'package:mohem_flutter_app/models/mowadhafhi/get_transaction_attachment_model.dart'; class MowadhafhiApiClient { static final MowadhafhiApiClient _instance = MowadhafhiApiClient._internal(); @@ -51,6 +54,18 @@ class MowadhafhiApiClient { }, url, postParams); } + Future getTransactionAttachments(int? attachmentID) async { + String url = "${ApiConsts.cocRest}Mohemm_ITG_GetTicketAttachment"; + Map postParams = {"EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER, "ItgAttachmentId": attachmentID}; + + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + GenericResponseModel? responseData = GenericResponseModel.fromJson(json); + var jsonDecodedData = jsonDecode(responseData.mohemmITGResponseItem!); + return GetTransactionAttachmentModel.fromJson(jsonDecodedData); + }, url, postParams); + } + Future> getTicketTypes() async { String url = "${ApiConsts.cocRest}Mohemm_ITG_GetTicketTypes"; Map postParams = {"EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER}; diff --git a/lib/api/worklist/worklist_api_client.dart b/lib/api/worklist/worklist_api_client.dart index 29805ec..611bcf4 100644 --- a/lib/api/worklist/worklist_api_client.dart +++ b/lib/api/worklist/worklist_api_client.dart @@ -24,6 +24,7 @@ import 'package:mohem_flutter_app/models/get_user_item_type_list.dart'; import 'package:mohem_flutter_app/models/itg_forms_models/itg_request_model.dart'; import 'package:mohem_flutter_app/models/member_information_list_model.dart'; import 'package:mohem_flutter_app/models/notification_get_respond_attributes_list_model.dart'; +import 'package:mohem_flutter_app/models/termination/termination_notification_body.dart'; import 'package:mohem_flutter_app/models/update_user_item_type_list.dart'; import 'package:mohem_flutter_app/models/worklist/GetRFCEmployeeList.dart'; import 'package:mohem_flutter_app/models/worklist/get_favorite_replacements_model.dart'; @@ -550,6 +551,22 @@ class WorkListApiClient { }, url, postParams); } + Future?> getTerminationNotificationBodyList(int? notificationId) async { + String url = "${ApiConsts.erpRest}GET_TERM_NOTIFICATION_BODY"; + Map postParams = { + "P_NOTIFICATION_ID": notificationId, + "P_PAGE_LIMIT": 100, + "P_PAGE_NUM": 1, + }; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + GenericResponseModel responseData = GenericResponseModel.fromJson(json); + return responseData.getTermNotificationBodyList; + }, url, postParams); + } + + + Future?> getFavoriteReplacementWithoutImage() async { String url = "${ApiConsts.erpRest}Mohemm_GetFavoriteReplacementsWithoutImage"; Map postParams = {}; diff --git a/lib/app_state/app_state.dart b/lib/app_state/app_state.dart index 24d8edf..51469c2 100644 --- a/lib/app_state/app_state.dart +++ b/lib/app_state/app_state.dart @@ -90,7 +90,7 @@ class AppState { String get getHuaweiPushToken => _huaweiPushToken; - final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 6.3, mobileType: Platform.isAndroid ? "android" : "ios"); + final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 5.7, mobileType: Platform.isAndroid ? "android" : "ios"); void setPostParamsInitConfig() { isAuthenticated = false; diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index a433421..af972f6 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -104,7 +104,7 @@ class CodegenLoader extends AssetLoader { "atLeastOneNumeric": "رقم واحد على الأقل", "minimum8Characters": "8 أحرف على الأقل", "doNotAddRepeatingLetters": "لا تقم بإضافة أحرف متكررة", - "itShouldContainSpecialCharacter": "يجب أن يحتوي على طابع خاص", + "itShouldContainSpecialCharacter": "يجب لا يحتوي على أحرف خاصة", "confirmPasswordMustMatch": "يجب أن يتطابق تأكيد كلمة المرور", "sms": "رسالة قصيرة", "fingerPrint": "بصمة", @@ -639,7 +639,7 @@ class CodegenLoader extends AssetLoader { "atLeastOneNumeric": "At least one numeric", "minimum8Characters": "Minimum 8 characters", "doNotAddRepeatingLetters": "Do not add repeating letters", - "itShouldContainSpecialCharacter": "It should contain special character", + "itShouldContainSpecialCharacter": "It should not contain special character", "confirmPasswordMustMatch": "Confirm password must match", "sms": "SMS", "fingerPrint": "Fingerprint", diff --git a/lib/models/generic_response_model.dart b/lib/models/generic_response_model.dart index 5188b2c..893b5b9 100644 --- a/lib/models/generic_response_model.dart +++ b/lib/models/generic_response_model.dart @@ -92,6 +92,7 @@ import 'package:mohem_flutter_app/models/submit_term_transaction_list_model.dart import 'package:mohem_flutter_app/models/subordinates_on_leaves_model.dart'; import 'package:mohem_flutter_app/models/termination/get_term_cols_structure_list_model.dart'; import 'package:mohem_flutter_app/models/termination/get_term_dff_structure_list_model.dart'; +import 'package:mohem_flutter_app/models/termination/termination_notification_body.dart'; import 'package:mohem_flutter_app/models/update_item_type_success_list.dart'; import 'package:mohem_flutter_app/models/update_user_item_type_list.dart'; import 'package:mohem_flutter_app/models/vacation_rule/create_vacation_rule_list_model.dart'; @@ -223,7 +224,6 @@ class GenericResponseModel { List? getOrganizationsSalariesList; List? getPaymentInformationList; List? getPayslipList; - // List? getPendingReqDetailsList; // List? getPendingReqFunctionsList; List? getPerformanceAppraisalList; @@ -254,7 +254,7 @@ class GenericResponseModel { List? getSwipesList; List? getTermColsStructureList; List? getTermDffStructureList; - List? getTermNotificationBodyList; + List? getTermNotificationBodyList; List? getTimeCardSummaryList; List? getTicketsByEmployeeList; List? getTicketDetailsByEmployee; @@ -680,6 +680,7 @@ class GenericResponseModel { successMsg = json['SuccessMsg']; successMsgN = json['SuccessMsgN']; vidaUpdatedResponse = json['VidaUpdatedResponse']; + if (json['AddAttSuccessList'] != null) { addAttSuccessList = []; json['AddAttSuccessList'].forEach((v) { @@ -693,6 +694,14 @@ class GenericResponseModel { businessCardPrivilege = json['BusinessCardPrivilege']; calculateAbsenceDuration = json['CalculateAbsenceDuration'] != null ? new CalculateAbsenceDuration.fromJson(json['CalculateAbsenceDuration']) : null; cancelHRTransactionLIst = json['CancelHRTransactionLIst'] != null ? new CancelHRTransactionLIst.fromJson(json['CancelHRTransactionLIst']) : null; + + if (json['GetTermNotificationBodyList'] != null) { + getTermNotificationBodyList = []; + json['GetTermNotificationBodyList'].forEach((v) { + getTermNotificationBodyList!.add(TerminationNotificationBody.fromJson(v)); + }); + } + chatEmployeeLoginList = json['Chat_EmployeeLoginList']; companyBadge = json['CompanyBadge']; companyImage = json['CompanyImage']; @@ -1090,7 +1099,7 @@ class GenericResponseModel { }); } - getTermNotificationBodyList = json['GetTermNotificationBodyList']; + if (json['GetTimeCardSummaryList'] != null) { getTimeCardSummaryList = []; @@ -1612,6 +1621,9 @@ class GenericResponseModel { data['GetNotificationReassignModeList'] = getNotificationReassignModeList!.map((v) => v.toJson()).toList(); } + if(getTermNotificationBodyList !=null){ + data['GetTermNotificationBodyList'] = getTermNotificationBodyList!.map((v) => v.toJson()).toList(); + } data['GetObjectValuesList'] = this.getObjectValuesList; data['GetOpenMissingSwipesList'] = this.getOpenMissingSwipesList; data['GetOpenNotificationsList'] = this.getOpenNotificationsList; @@ -1688,7 +1700,6 @@ class GenericResponseModel { data['GetTermDffStructureList'] = this.getTermDffStructureList!.map((v) => v.toJson()).toList(); } - data['GetTermNotificationBodyList'] = this.getTermNotificationBodyList; if (this.getTimeCardSummaryList != null) { data['GetTimeCardSummaryList'] = this.getTimeCardSummaryList!.map((v) => v.toJson()).toList(); } diff --git a/lib/models/leave_balance/calculate_absence_duration_model.dart b/lib/models/leave_balance/calculate_absence_duration_model.dart index 0f0a48b..cf32a0e 100644 --- a/lib/models/leave_balance/calculate_absence_duration_model.dart +++ b/lib/models/leave_balance/calculate_absence_duration_model.dart @@ -1,6 +1,6 @@ class CalculateAbsenceDuration { - num? pABSENCEDAYS; - num? pABSENCEHOURS; + double? pABSENCEDAYS; + double? pABSENCEHOURS; String? pRETURNMSG; String? pRETURNSTATUS; diff --git a/lib/models/mowadhafhi/get_ticket_transactions.dart b/lib/models/mowadhafhi/get_ticket_transactions.dart index be793ca..3cb0ce1 100644 --- a/lib/models/mowadhafhi/get_ticket_transactions.dart +++ b/lib/models/mowadhafhi/get_ticket_transactions.dart @@ -1,24 +1,24 @@ class GetTicketTransactions { String? actionBy; String? actionDate; + List? attachments; String? comments; String? statusDisplayText; String? statusName; String? ticketId; int? ticketTransactionId; - GetTicketTransactions( - {this.actionBy, - this.actionDate, - this.comments, - this.statusDisplayText, - this.statusName, - this.ticketId, - this.ticketTransactionId}); + GetTicketTransactions({this.actionBy, this.actionDate, this.attachments, this.comments, this.statusDisplayText, this.statusName, this.ticketId, this.ticketTransactionId}); GetTicketTransactions.fromJson(Map json) { actionBy = json['actionBy']; actionDate = json['actionDate']; + if (json['attachments'] != null) { + attachments = []; + json['attachments'].forEach((v) { + attachments!.add(new Attachments.fromJson(v)); + }); + } comments = json['comments']; statusDisplayText = json['statusDisplayText']; statusName = json['statusName']; @@ -27,9 +27,12 @@ class GetTicketTransactions { } Map toJson() { - Map data = new Map(); + Map data = Map(); data['actionBy'] = this.actionBy; data['actionDate'] = this.actionDate; + if (this.attachments != null) { + data['attachments'] = this.attachments!.map((v) => v.toJson()).toList(); + } data['comments'] = this.comments; data['statusDisplayText'] = this.statusDisplayText; data['statusName'] = this.statusName; @@ -38,3 +41,22 @@ class GetTicketTransactions { return data; } } + +class Attachments { + int? attachmentId; + String? fileName; + + Attachments({this.attachmentId, this.fileName}); + + Attachments.fromJson(Map json) { + attachmentId = json['attachmentId']; + fileName = json['fileName']; + } + + Map toJson() { + Map data = Map(); + data['attachmentId'] = this.attachmentId; + data['fileName'] = this.fileName; + return data; + } +} diff --git a/lib/models/mowadhafhi/get_transaction_attachment_model.dart b/lib/models/mowadhafhi/get_transaction_attachment_model.dart new file mode 100644 index 0000000..1234946 --- /dev/null +++ b/lib/models/mowadhafhi/get_transaction_attachment_model.dart @@ -0,0 +1,52 @@ +class GetTransactionAttachmentModel { + int? attachmentId; + String? fileName; + String? contentType; + dynamic attachFileStream; + String? base64String; + dynamic isActive; + dynamic referenceItemId; + dynamic content; + dynamic filePath; + int? languageId; + + GetTransactionAttachmentModel( + {this.attachmentId, + this.fileName, + this.contentType, + this.attachFileStream, + this.base64String, + this.isActive, + this.referenceItemId, + this.content, + this.filePath, + this.languageId}); + + GetTransactionAttachmentModel.fromJson(Map json) { + attachmentId = json['attachmentId']; + fileName = json['fileName']; + contentType = json['contentType']; + attachFileStream = json['attachFileStream']; + base64String = json['base64String']; + isActive = json['isActive']; + referenceItemId = json['referenceItemId']; + content = json['content']; + filePath = json['filePath']; + languageId = json['languageId']; + } + + Map toJson() { + Map data = Map(); + data['attachmentId'] = this.attachmentId; + data['fileName'] = this.fileName; + data['contentType'] = this.contentType; + data['attachFileStream'] = this.attachFileStream; + data['base64String'] = this.base64String; + data['isActive'] = this.isActive; + data['referenceItemId'] = this.referenceItemId; + data['content'] = this.content; + data['filePath'] = this.filePath; + data['languageId'] = this.languageId; + return data; + } +} diff --git a/lib/models/termination/termination_notification_body.dart b/lib/models/termination/termination_notification_body.dart new file mode 100644 index 0000000..2c2c709 --- /dev/null +++ b/lib/models/termination/termination_notification_body.dart @@ -0,0 +1,18 @@ +class TerminationNotificationBody { + String? sEGMENTPROMPT; + String? sEGMENTVALUEDSP; + + TerminationNotificationBody({this.sEGMENTPROMPT, this.sEGMENTVALUEDSP}); + + TerminationNotificationBody.fromJson(Map json) { + sEGMENTPROMPT = json['SEGMENT_PROMPT']; + sEGMENTVALUEDSP = json['SEGMENT_VALUE_DSP']; + } + + Map toJson() { + Map data = new Map(); + data['SEGMENT_PROMPT'] = this.sEGMENTPROMPT; + data['SEGMENT_VALUE_DSP'] = this.sEGMENTVALUEDSP; + return data; + } +} diff --git a/lib/ui/leave_balance/add_leave_balance_screen.dart b/lib/ui/leave_balance/add_leave_balance_screen.dart index a16e81d..830ca85 100644 --- a/lib/ui/leave_balance/add_leave_balance_screen.dart +++ b/lib/ui/leave_balance/add_leave_balance_screen.dart @@ -40,7 +40,7 @@ class _AddLeaveBalanceScreenState extends State { GetAbsenceAttendanceTypesList? selectedAbsenceType; DateTime? startDateTime; DateTime? endDateTime; - int? totalDays; + double? totalDays; String comment = ""; ReplacementList? selectedReplacementEmployee; String? selectedEmp; @@ -90,7 +90,7 @@ class _AddLeaveBalanceScreenState extends State { Utils.showLoading(context); CalculateAbsenceDuration duration = await LeaveBalanceApiClient() .calculateAbsenceDuration(selectedAbsenceType!.aBSENCEATTENDANCETYPEID!, Utils.getMonthNamedFormat(startDateTime!), Utils.getMonthNamedFormat(endDateTime!), -999, empID: selectedEmp); - totalDays = duration.pABSENCEDAYS?.toInt(); + totalDays = duration.pABSENCEDAYS?.toDouble(); Utils.hideLoading(context); setState(() {}); } catch (ex) { @@ -232,7 +232,7 @@ class _AddLeaveBalanceScreenState extends State { isInputTypeNum: true, isEnable: false, onChange: (input) { - totalDays = int.parse(input); + totalDays = double.tryParse(input); }, ), 12.height, diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index 94724fc..c85ecb7 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -17,11 +17,14 @@ import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/notifications.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; +import 'package:mohem_flutter_app/dialogs/otp_dialog.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/basic_member_information_model.dart'; import 'package:mohem_flutter_app/models/check_mobile_app_version_model.dart'; +import 'package:mohem_flutter_app/models/generic_response_model.dart'; import 'package:mohem_flutter_app/models/get_mobile_login_info_list_model.dart'; import 'package:mohem_flutter_app/models/member_information_list_model.dart'; import 'package:mohem_flutter_app/models/member_login_list_model.dart'; @@ -59,6 +62,9 @@ class _LoginScreenState extends State { bool isOnExternalStorage = false; bool isDevelopmentModeEnable = false; + BasicMemberInformationModel? _basicMemberInformation; + GenericResponseModel? genericResponseModel; + // late HmsApiAvailability hmsApiAvailability; @override @@ -199,7 +205,8 @@ class _LoginScreenState extends State { onTap: () async { if (msg.toLowerCase().contains("password has expired")) { Navigator.pop(context); - await Navigator.pushNamed(context, AppRoutes.newPassword, arguments: username.text); + // await Navigator.pushNamed(context, AppRoutes.newPassword, arguments: username.text); + performForgotPassword(); } else { Navigator.pop(context); } @@ -209,6 +216,53 @@ class _LoginScreenState extends State { } } + void performForgotPassword() async { + // if (username.text.isEmpty) { + // return; + // } + Utils.showLoading(context); + try { + _basicMemberInformation = await LoginApiClient().getBasicUserInformation("CS", username.text); + genericResponseModel = await LoginApiClient().sendPublicActivationCode(_basicMemberInformation?.pMOBILENUMBER, username.text); + Utils.hideLoading(context); + OtpDialog( + context, + 1, + int.tryParse(_basicMemberInformation?.pMOBILENUMBER ?? ""), + (value, TextEditingController _pinPutController) async { + Utils.showLoading(context); + try { + GenericResponseModel? genericResponseModel = await LoginApiClient().checkPublicActivationCode(value, username.text); + if (genericResponseModel?.errorMessage != null) { + Utils.showToast(genericResponseModel?.errorMessage ?? ""); + return; + } + Utils.hideLoading(context); + await Navigator.pushNamed(context, AppRoutes.newPassword, arguments: username.text); + Navigator.pop(context); + // Navigator.pop(context); + } catch (ex) { + print(ex); + _pinPutController.clear(); + otpFieldClear.value = ""; + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + }, + () => { + Navigator.pop(context), + }, + onResendCode: () { + performForgotPassword(); + }, + ).displayDialog(context); + } catch (ex) { + print(ex); + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } + @override Widget build(BuildContext context) { if (isAppOpenBySystem == null) { @@ -277,9 +331,12 @@ class _LoginScreenState extends State { 9.height, Align( alignment: Alignment.centerRight, - child: LocaleKeys.forgotPassword.tr().toText12(isUnderLine: true, color: MyColors.textMixColor).onPress(() { - Navigator.pushNamed(context, AppRoutes.forgotPassword); - }), + child: LocaleKeys.forgotPassword.tr().toText12(isUnderLine: true, color: MyColors.textMixColor).onPress( + () { + // Navigator.pushNamed(context, AppRoutes.forgotPassword); + performForgotPassword(); + }, + ), ), 20.height, // DefaultButton( diff --git a/lib/ui/login/new_password_screen.dart b/lib/ui/login/new_password_screen.dart index 9efee64..11ba74b 100644 --- a/lib/ui/login/new_password_screen.dart +++ b/lib/ui/login/new_password_screen.dart @@ -102,20 +102,20 @@ class _NewPasswordScreenState extends State { passwordConstraintsUI(LocaleKeys.minimum8Characters.tr(), password.text.length >= 8), 8.height, passwordConstraintsUI(LocaleKeys.doNotAddRepeatingLetters.tr(), checkRepeatedChars(password.text)), - // 8.height, - // passwordConstraintsUI(LocaleKeys.itShouldContainSpecialCharacter.tr(), checkRegEx(r'[!@#$%^&*(),.?":{}|<>]')), + 8.height, + passwordConstraintsUI(LocaleKeys.itShouldContainSpecialCharacter.tr(), checkRegEx(r'^[a-zA-Z0-9]+$')), 8.height, passwordConstraintsUI(LocaleKeys.confirmPasswordMustMatch.tr(), password.text.isNotEmpty && password.text == confirmPassword.text), ], ).expanded, DefaultButton( - LocaleKeys.update.tr(), - (!isPasswordCompliant(password.text, 8)) - ? null - : () async { - setNewPassword(); - }) - .insideContainer + LocaleKeys.update.tr(), + (!isPasswordCompliant(password.text, 8)) + ? null + : () async { + setNewPassword(); + }, + ).insideContainer ], ), ); diff --git a/lib/ui/screens/mowadhafhi/request_details.dart b/lib/ui/screens/mowadhafhi/request_details.dart index aa22bdd..352e195 100644 --- a/lib/ui/screens/mowadhafhi/request_details.dart +++ b/lib/ui/screens/mowadhafhi/request_details.dart @@ -1,3 +1,8 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:io' as Io; +import 'dart:typed_data'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/mowadhafhi/mowadhafhi_api_client.dart'; @@ -6,9 +11,16 @@ import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/mowadhafhi/get_ticket_details.dart'; import 'package:mohem_flutter_app/models/mowadhafhi/get_ticket_transactions.dart'; +import 'package:mohem_flutter_app/models/mowadhafhi/get_transaction_attachment_model.dart'; +import 'package:mohem_flutter_app/ui/screens/mowadhafhi/view_transaction_attachment.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; +import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart'; +import 'package:open_file/open_file.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; class MowadhafhiRequestDetails extends StatefulWidget { const MowadhafhiRequestDetails({Key? key}) : super(key: key); @@ -21,6 +33,9 @@ class _RequestDetailsState extends State { String? itgTicketID; List getTicketsByEmployeeList = []; List getTicketTransactionsList = []; + GetTransactionAttachmentModel? getTransactionAttachmentModel; + + late File imageFile; @override void initState() { @@ -43,7 +58,7 @@ class _RequestDetailsState extends State { backgroundColor: Colors.white, appBar: AppBarWidget( context, - title: LocaleKeys.mowadhafhiRequest.tr(), + title: LocaleKeys.mowadhafhiRequest.tr(), ), body: SingleChildScrollView( child: getTicketsByEmployeeList.length != 0 @@ -76,16 +91,16 @@ class _RequestDetailsState extends State { ], ), 8.height, - LocaleKeys.ticketReference.tr().toText12(color: MyColors.grey98Color), + LocaleKeys.ticketReference.tr().toText12(color: MyColors.grey98Color), getTicketsByEmployeeList![0].ticketReferenceNo!.toText14(color: MyColors.grey57Color), 8.height, - LocaleKeys.section.tr().toText12(color: MyColors.grey98Color), + LocaleKeys.section.tr().toText12(color: MyColors.grey98Color), getTicketsByEmployeeList![0].sectionName!.toText14(color: MyColors.grey57Color), 8.height, - LocaleKeys.topic.tr().toText12(color: MyColors.grey98Color), + LocaleKeys.topic.tr().toText12(color: MyColors.grey98Color), getTicketsByEmployeeList![0].topicName!.toText14(color: MyColors.grey57Color), 8.height, - LocaleKeys.description.tr().toText12(color: MyColors.grey98Color), + LocaleKeys.description.tr().toText12(color: MyColors.grey98Color), getTicketsByEmployeeList![0].description!.toText14(color: MyColors.grey57Color), ], ), @@ -128,15 +143,31 @@ class _RequestDetailsState extends State { Row( children: [ LocaleKeys.actionBy.tr().toText14(color: MyColors.grey57Color), + ": ".toText14(), getTicketTransactionsList![index].actionBy!.toText14(color: MyColors.grey57Color), ], ), + Row( + children: [ + LocaleKeys.actions.tr().toText14(color: MyColors.grey57Color), + ": ".toText14(), + getTicketTransactionsList![index].statusDisplayText!.toText14(color: MyColors.grey57Color), + ], + ), getTicketTransactionsList![index].comments!.toText14(color: MyColors.grey98Color), 12.height, + if (getTicketTransactionsList[index].attachments != null) + InkWell( + onTap: () { + print(getTicketTransactionsList[index].attachments![0].attachmentId); + getTransactionAttachment(getTicketTransactionsList[index].attachments![0].attachmentId!); + }, + child: LocaleKeys.attachments.tr().toText14(color: MyColors.gradiantEndColor, isUnderLine: true), + ), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - getTicketTransactionsList![0].actionDate!.split(" ")[0].toText12(color: MyColors.grey70Color), + getTicketTransactionsList![index].actionDate!.split(" ")[0].toText12(color: MyColors.grey70Color), ], ), ], @@ -212,4 +243,58 @@ class _RequestDetailsState extends State { Utils.handleException(ex, context, null); } } + + void getTransactionAttachment(int attachmentID) async { + try { + Utils.showLoading(context); + getTransactionAttachmentModel = await MowadhafhiApiClient().getTransactionAttachments(attachmentID); + Utils.hideLoading(context); + handleTransactionAttachment(); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } + + Future _createFileFromString(String encodedStr, String ext) async { + Uint8List bytes = base64.decode(encodedStr); + String dir = (await getApplicationDocumentsDirectory()).path; + File file = File("$dir/" + DateTime.now().millisecondsSinceEpoch.toString() + "." + ext); + await file.writeAsBytes(bytes); + return file.path; + } + + void handleTransactionAttachment() async { + Permission.storage.isGranted.then((isGranted) { + if (!isGranted) { + Permission.manageExternalStorage.request().then((granted) async { + if (granted == PermissionStatus.granted) { + String ext = ''; + String? rFile = getTransactionAttachmentModel!.base64String; + String? rFileExt = getTransactionAttachmentModel!.fileName; + + ext = rFileExt!.split(".").last.toLowerCase(); + try { + String path = await _createFileFromString(rFile!.split("base64,").last ?? "", ext ?? ""); + await OpenFile.open(path).catchError((err) { + print(err); + }); + } catch (ex) { + Utils.showToast("Cannot open file."); + } + } else { + showDialog( + context: context, + builder: (BuildContext cxt) => ConfirmDialog( + message: "You need to give storage permission to view files.", + onTap: () { + Navigator.pop(context); + }, + ), + ); + } + }); + } + }); + } } diff --git a/lib/ui/screens/mowadhafhi/view_transaction_attachment.dart b/lib/ui/screens/mowadhafhi/view_transaction_attachment.dart new file mode 100644 index 0000000..e6ef8bc --- /dev/null +++ b/lib/ui/screens/mowadhafhi/view_transaction_attachment.dart @@ -0,0 +1,34 @@ +import 'dart:io'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; + +class ViewTransactionAttachment extends StatelessWidget { + final File imageFile; + + const ViewTransactionAttachment({Key? key, required this.imageFile}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBarWidget( + context, + title: LocaleKeys.mowadhafhiRequest.tr(), + ), + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.file(imageFile), + 50.height, + ], + ), + ), + ); + } +} diff --git a/lib/ui/screens/my_documents/my_documents_fragment.dart b/lib/ui/screens/my_documents/my_documents_fragment.dart index 6bc46f7..5a9c88d 100644 --- a/lib/ui/screens/my_documents/my_documents_fragment.dart +++ b/lib/ui/screens/my_documents/my_documents_fragment.dart @@ -79,7 +79,7 @@ class _MyDocumentsFragmentState extends State { Navigator.pushNamed( context, AppRoutes.addDynamicInput, - arguments: DynamicListViewParams(documentfilteredList[index].dOCUMENTTYPE!, documentfilteredList[index].fUNCTIONNAME!, + arguments: DynamicListViewParams(documentfilteredList[index].dOCUMENTTYPENAME!, documentfilteredList[index].fUNCTIONNAME!, selectedEmp: AppState().getUserName, isAttachmentMandatory: true), ); }); diff --git a/lib/ui/work_list/worklist_detail_screen.dart b/lib/ui/work_list/worklist_detail_screen.dart index 0a4f8cc..c80f1b4 100644 --- a/lib/ui/work_list/worklist_detail_screen.dart +++ b/lib/ui/work_list/worklist_detail_screen.dart @@ -44,6 +44,8 @@ import 'package:mohem_flutter_app/widgets/bottom_sheet.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/dialogs/accept_reject_input_dialog.dart'; import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart'; +import 'package:mohem_flutter_app/models/termination/termination_notification_body.dart'; + class WorkListDetailScreen extends StatefulWidget { WorkListDetailScreen({Key? key}) : super(key: key); @@ -80,6 +82,8 @@ class _WorkListDetailScreenState extends State { List? getAbsenceCollectionNotificationBodyList = []; GetContactNotificationBodyList? getContactNotificationBodyList; List? getAddressNotificationBodyList = []; + List? getTerminationNotificationBodyList = []; + GenericResponseModel? getBasicNTFBody; GenericResponseModel? getICBody; @@ -121,6 +125,7 @@ class _WorkListDetailScreenState extends State { getItemCreationNtfBody?.itemCreationHeader!.clear(); getPhonesNotificationBodyList!.clear(); getBasicDetNtfBodyList!.clear(); + getTerminationNotificationBodyList!.clear(); getAbsenceCollectionNotificationBodyList!.clear(); getContactNotificationBodyList = null; getAddressNotificationBodyList!.clear(); @@ -147,6 +152,8 @@ class _WorkListDetailScreenState extends State { getContactNotificationBody(); } else if (workListData!.rEQUESTTYPE == "ADDRESS") { getAddressNotificationBody(); + } else if(workListData!.rEQUESTTYPE =='TERMINATION'){ + getTerminationNotificationBody(); } } if (workListData!.iTEMTYPE == "STAMP") { @@ -257,6 +264,7 @@ class _WorkListDetailScreenState extends State { getAbsenceCollectionNotificationBodyList: getAbsenceCollectionNotificationBodyList, getContactNotificationBodyList: getContactNotificationBodyList, getPrNotificationBodyList: getPrNotificationBody, + getTerminationNotificationBodyList:getTerminationNotificationBodyList ), (workListData!.iTEMTYPE == "HRSSA" || workListData!.iTEMTYPE == "STAMP") ? DetailFragment(workListData, memberInformationListModel) @@ -854,6 +862,25 @@ class _WorkListDetailScreenState extends State { Utils.handleException(ex, context, null); } } + void getTerminationNotificationBody() async { + try { + if (apiCallCount == 0) + apiCallCount++; + getTerminationNotificationBodyList = await WorkListApiClient().getTerminationNotificationBodyList(workListData!.nOTIFICATIONID); + Utils.hideLoading(context); + apiCallCount--; + if (apiCallCount == 0) { + + setState(() {}); + } + } catch (ex) { + apiCallCount--; + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } + + void getStampNotificationBody() async { try { diff --git a/lib/ui/work_list/worklist_fragments/actions_fragment.dart b/lib/ui/work_list/worklist_fragments/actions_fragment.dart index 1d88129..10d027a 100644 --- a/lib/ui/work_list/worklist_fragments/actions_fragment.dart +++ b/lib/ui/work_list/worklist_fragments/actions_fragment.dart @@ -167,10 +167,14 @@ class ActionsFragment extends StatelessWidget { Duration duration = DateTime.now().difference(dateTimeFrom); return "Action duration: " + DateUtil.formatDuration(duration); } else { - DateTime dateTimeTo = DateUtil.convertSimpleStringDateToDate(actionHistoryList[index].nOTIFICATIONDATE!); - DateTime dateTimeFrom = DateUtil.convertSimpleStringDateToDate(actionHistoryList[index + 1].nOTIFICATIONDATE!); - Duration duration = dateTimeTo.difference(dateTimeFrom); - return "Action duration: " + DateUtil.formatDuration(duration); + if (actionHistoryList[index + 1].nOTIFICATIONDATE!.isEmpty) { + return ""; + } else { + DateTime dateTimeTo = DateUtil.convertSimpleStringDateToDate(actionHistoryList[index].nOTIFICATIONDATE!); + DateTime dateTimeFrom = DateUtil.convertSimpleStringDateToDate(actionHistoryList[index + 1].nOTIFICATIONDATE!); + Duration duration = dateTimeTo.difference(dateTimeFrom); + return "Action duration: " + DateUtil.formatDuration(duration); + } } } diff --git a/lib/ui/work_list/worklist_fragments/info_fragments.dart b/lib/ui/work_list/worklist_fragments/info_fragments.dart index 5edb6f1..7e1cd1c 100644 --- a/lib/ui/work_list/worklist_fragments/info_fragments.dart +++ b/lib/ui/work_list/worklist_fragments/info_fragments.dart @@ -20,6 +20,8 @@ import 'package:mohem_flutter_app/models/worklist/hr/get_phones_notification_bod import 'package:mohem_flutter_app/models/worklist_response_model.dart'; import 'package:mohem_flutter_app/widgets/item_detail_view_widget.dart'; +import 'package:mohem_flutter_app/models/termination/termination_notification_body.dart'; + class InfoFragment extends StatelessWidget { WorkListResponseModel? workListData; List poHeaderList; @@ -33,7 +35,7 @@ class InfoFragment extends StatelessWidget { GetContactNotificationBodyList? getContactNotificationBodyList; GetPrNotificationBodyList? getPrNotificationBodyList; List? getAddressNotificationBodyList = []; - + List? getTerminationNotificationBodyList =[]; InfoFragment( {this.workListData, this.poHeaderList = const [], @@ -46,7 +48,9 @@ class InfoFragment extends StatelessWidget { this.getAbsenceCollectionNotificationBodyList, this.getContactNotificationBodyList, this.getPrNotificationBodyList, - this.getAddressNotificationBodyList}); + this.getAddressNotificationBodyList, + this.getTerminationNotificationBodyList + }); double itemHeight = 0; double itemWidth = 0; @@ -93,6 +97,7 @@ class InfoFragment extends StatelessWidget { if (getAbsenceCollectionNotificationBodyList?.isNotEmpty ?? false) getAbsenceCollectionNotificationBodyListWidget(getAbsenceCollectionNotificationBodyList ?? []), if (getContactNotificationBodyList != null) getContactNotificationBodyListWidget(getContactNotificationBodyList ?? GetContactNotificationBodyList()).objectContainerView(), if (getAddressNotificationBodyList?.isNotEmpty ?? false) getAddressNotificationBodyListWidget(getAddressNotificationBodyList!), + if (getTerminationNotificationBodyList?.isNotEmpty ?? false) getTerminationNotificationBodyListWidget(getTerminationNotificationBodyList!), ]; return Container( width: double.infinity, @@ -499,6 +504,39 @@ class InfoFragment extends StatelessWidget { ).objectContainerView(); } + + Widget getTerminationNotificationBodyListWidget(List getterminationNotificationBodyList) { + bool isOdd = false; + try { + if (getterminationNotificationBodyList.length % 2 != 0) { + isOdd = true; + getterminationNotificationBodyList.add(TerminationNotificationBody(sEGMENTPROMPT: "--", sEGMENTVALUEDSP: "--")); + } + } catch (e) {} + + return GridView.builder( + itemCount: getterminationNotificationBodyList!.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) => ItemDetailViewGridItem( + index, + getterminationNotificationBodyList[index].sEGMENTPROMPT, + getterminationNotificationBodyList[index].sEGMENTVALUEDSP, + isNeedToShowEmptyDivider: (getterminationNotificationBodyList.length == index + 1) + ? isOdd + ? true + : false + : false, + ), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: (itemWidth / itemHeight), + ), + ).objectContainerView(); + } + + + List getPRHeaderValues() { List pRHeaders = []; getPrNotificationBodyList!.pRHeader!.forEach((element) { diff --git a/lib/widgets/image_picker.dart b/lib/widgets/image_picker.dart index ef04f84..19b1e00 100644 --- a/lib/widgets/image_picker.dart +++ b/lib/widgets/image_picker.dart @@ -8,6 +8,8 @@ import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/widgets/bottom_sheet.dart'; import 'package:mohem_flutter_app/widgets/bottom_sheets/attachment_options.dart'; +import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart'; +import 'package:permission_handler/permission_handler.dart'; class ImageOptions { static void showImageOptionsNew(BuildContext context, bool showFilesOption, Function(String, File) image) { @@ -43,24 +45,42 @@ class ImageOptions { } }, onFilesTap: () async { - FilePickerResult? result = await FilePicker.platform.pickFiles( - type: FileType.custom, - allowedExtensions: [ - 'jpg', - 'jpeg ', - 'pdf', - 'txt', - 'docx', - 'doc', - 'pptx', - 'xlsx', - 'png', - 'rar', - 'zip', - ], - ); - List files = result!.paths.map((path) => File(path!)).toList(); - image(result.files.first.path.toString(), files.first); + Permission.storage.isGranted.then((isGranted) { + if (!isGranted) { + Permission.manageExternalStorage.request().then((granted) async { + if (granted == PermissionStatus.granted) { + FilePickerResult? result = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: [ + 'jpg', + 'jpeg ', + 'pdf', + 'txt', + 'docx', + 'doc', + 'pptx', + 'xlsx', + 'png', + 'rar', + 'zip', + ], + ); + List files = result!.paths.map((path) => File(path!)).toList(); + image(result.files.first.path.toString(), files.first); + } else { + showDialog( + context: context, + builder: (BuildContext cxt) => ConfirmDialog( + message: "You need to give storage permission to upload files.", + onTap: () { + Navigator.pop(context); + }, + ), + ); + } + }); + } + }); }, ), ); diff --git a/pubspec.yaml b/pubspec.yaml index e3b15bd..82ae41b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html #version: 3.3.01+300040 -version: 3.7.7+1 +version: 3.3.6+300046 environment: sdk: ">=2.16.0 <3.0.0" @@ -60,7 +60,7 @@ dependencies: # android_id: ^0.1.3+1 platform_device_id: ^1.0.1 image_picker: ^0.8.5+3 - file_picker: ^4.6.1 + file_picker: 5.2.5 geolocator: ^9.0.2 month_year_picker: ^0.2.0+1 month_picker_dialog_2: 0.5.5 @@ -124,6 +124,8 @@ dependencies: #todo its for temporary purpose, later will remove this. dotted_border: ^2.0.0+3 +# saf: ^1.0.3+4 + dependency_overrides: firebase_core_platform_interface: 4.5.1