diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ab69b28..c347fd7 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ @@ -8,6 +9,16 @@ + + + + + + + + @@ -23,6 +34,19 @@ android:label="Al Habib Mohemm" android:extractNativeLibs="true" android:networkSecurityConfig="@xml/network_security_config"> + android:networkSecurityConfig="@xml/network_security_config" + android:roundIcon="@mipmap/ic_launcher_round"> + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/xcellent.svg b/assets/images/excellent.svg similarity index 100% rename from assets/images/xcellent.svg rename to assets/images/excellent.svg diff --git a/assets/images/bad.svg b/assets/images/fair.svg similarity index 100% rename from assets/images/bad.svg rename to assets/images/fair.svg diff --git a/assets/images/normal.svg b/assets/images/ok.svg similarity index 100% rename from assets/images/normal.svg rename to assets/images/ok.svg diff --git a/assets/json/demo_upcoming_marathon b/assets/json/demo_upcoming_marathon index 2fdabf0..f08cb22 100644 --- a/assets/json/demo_upcoming_marathon +++ b/assets/json/demo_upcoming_marathon @@ -16,7 +16,7 @@ "projects": { "id": "b1cd3fa3-bb27-422e-a4c1-08dac09254df", "nameEn": "Cloud Solutions", - "nameAr": "333شركة حلول السحابة للاتصالات وتقنية المعلومات", + "nameAr": "شركة حلول السحابة للاتصالات وتقنية المعلومات", "projectCode": "CS" }, "sponsors": [ @@ -42,5 +42,6 @@ "totalQuestions": 10, "cancelReason": null, "marathonBufferTime": 30, - "currentTime": "2022-12-28T08:03:24.3671803Z" + "currentTime": "2022-12-28T08:03:24.3671803Z", + "displayCorrectAnswer": true } diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 8ff0ea3..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": "بصمة", @@ -542,5 +542,6 @@ "uploadedDocuments": "المستندات التي تم تحميلها", "addAtLeastOneAttachment": "الرجاء إضافة مرفق واحد على الأقل.", "pleaseClickButtonToJoinMarathon": "الرجاء الضغط على الزر أدناه للانضمام إلى الماراثون", - "youCannotJoinTheMarathon": "لا يمكنك الانضمام إلى الماراثون لأنك تجاوزت الحد الزمني" + "youCannotJoinTheMarathon": "لا يمكنك الانضمام إلى الماراثون لأنك تجاوزت الحد الزمني", + "open": "يفتح" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 2449802..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", @@ -559,6 +559,7 @@ "userSearch": "User Search", "userName": "User Name", "userId": "UserID", + "open": "open", "addAtLeastOneAttachment": "Please add at least one attachment.", "pleaseClickButtonToJoinMarathon": "Press the button below to join the Marathon.", "youCannotJoinTheMarathon": "You cannot join the Marathon because you have exceeded the time limit." diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 96c28f1..9de1131 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -227,6 +227,7 @@ }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -241,6 +242,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -251,7 +253,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; C4CFBC4C5CAC00182015ACD5 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; diff --git a/lib/api/chat/chat_api_client.dart b/lib/api/chat/chat_api_client.dart index d077026..14b4e42 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": "80a3b01fc1ef2453eb4f1daa4fc31d8142d9cb67baf848e91350b607971fe2ba", }, ); diff --git a/lib/api/marathon/marathon_api_client.dart b/lib/api/marathon/marathon_api_client.dart index 428e586..d9021c6 100644 --- a/lib/api/marathon/marathon_api_client.dart +++ b/lib/api/marathon/marathon_api_client.dart @@ -11,6 +11,7 @@ import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/models/marathon/marathon_generic_model.dart'; import 'package:mohem_flutter_app/models/marathon/marathon_model.dart'; import 'package:mohem_flutter_app/models/marathon/question_model.dart'; +import 'package:mohem_flutter_app/models/marathon/tutorial_notification_model.dart'; import 'package:mohem_flutter_app/models/marathon/winner_model.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart'; import 'package:provider/provider.dart'; @@ -195,6 +196,24 @@ class MarathonApiClient { return null; } + Future getMarathonTutorial() async { + Response response = await ApiClient().getJsonForResponse( + // ApiConsts.marathonGetTutorial, + "https://marathoon.com/uatservice/api/tutorial/GetTutorialNotification", + token: AppState().getMarathonToken == null || AppState().getMarathonToken == "" ? await getMarathonToken() : AppState().getMarathonToken, + ); + try { + MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(jsonDecode(response.body)); + if (marathonModel.data is List) { + return TutorialNotificationModel.fromJson(marathonModel.data[0]); + } + } catch (ex) { + print(ex); + } + + return null; + } + // late HubConnection hubConnection; // Future buildHubConnection(BuildContext context, String prizeId) async { // HttpConnectionOptions httpOptions = HttpConnectionOptions(skipNegotiation: false, logMessageContent: true); 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..d2389ab 100644 --- a/lib/api/worklist/worklist_api_client.dart +++ b/lib/api/worklist/worklist_api_client.dart @@ -21,9 +21,11 @@ import 'package:mohem_flutter_app/models/get_quotation_analysis_list_model.dart' import 'package:mohem_flutter_app/models/get_stamp_ms_notification_body_list_model.dart'; import 'package:mohem_flutter_app/models/get_stamp_ns_notification_body_list_model.dart'; import 'package:mohem_flutter_app/models/get_user_item_type_list.dart'; +import 'package:mohem_flutter_app/models/itg_forms_models/itg_forms_attachments_model.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'; @@ -363,6 +365,21 @@ class WorkListApiClient { }, url, postParams); } + Future?> getITGFormAttachments(String requestType, taskId, itemId, String employeeNumber) async { + String url = "${ApiConsts.cocRest}ITGGetFormDetialsAttachment"; + Map postParams = { + "RequestType": requestType, + "TaskID": taskId, + "ItemIDStr": itemId, + "EmployeeNumber": employeeNumber, + }; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + ItgFormsModel responseData = ItgFormsModel.fromJson(json); + return responseData.itgFormAttachmentsList; + }, url, postParams); + } + Future rejectITGRequest(String requestType, taskId, itemId, String employeeNumber, String comments) async { String url = "${ApiConsts.cocRest}ITGRejectRequest"; Map postParams = { @@ -550,6 +567,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/classes/consts.dart b/lib/classes/consts.dart index d345415..a6eaad2 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -51,9 +51,9 @@ class ApiConsts { static String marathonQualifiersUrl = marathonBaseUrl + "winner/getWinner/"; static String marathonSelectedWinner = marathonBaseUrl + "winner/getSelectedWinner/"; static String marathonGetMarathonersCount = marathonBaseUrl + "Participant/GetRemainingParticipants"; + static String marathonGetTutorial = marathonBaseUrl + "tutorial/GetTutorialNotification"; //DummyCards for the UI - static CardContent dummyQuestion = const CardContent(); static int tabletMinLength = 500; } @@ -71,3 +71,5 @@ class SharedPrefsConsts { static String mohemmWifiPassword = "mohemmWifiPassword"; static String editItemForSale = "editItemForSale"; } + + diff --git a/lib/classes/decorations_helper.dart b/lib/classes/decorations_helper.dart index b313673..fd6d4f7 100644 --- a/lib/classes/decorations_helper.dart +++ b/lib/classes/decorations_helper.dart @@ -30,7 +30,6 @@ class MyDecorations { return getContainersDecoration(MyColors.greenColor); case QuestionsOptionStatus.wrong: return getContainersDecoration(MyColors.redColor); - case QuestionsOptionStatus.selected: return getContainersDecoration(MyColors.yellowColorII); case QuestionsOptionStatus.unSelected: diff --git a/lib/classes/file_process.dart b/lib/classes/file_process.dart index 6974702..0720c8d 100644 --- a/lib/classes/file_process.dart +++ b/lib/classes/file_process.dart @@ -2,8 +2,9 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:open_file/open_file.dart'; +import 'package:open_filex/open_filex.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:http/http.dart' as http; class FileProcess { static bool isFolderCreated = false; @@ -23,9 +24,9 @@ class FileProcess { } } - static void openFile(String fileName) { + static void openFile(String fileName, {bool isFullPath = false}) { String dir = directory!.path + "/$fileName.pdf"; - OpenFile.open(dir); + OpenFilex.open(isFullPath ? fileName : dir); } static Future downloadFile(String base64Content, String fileName) async { @@ -37,4 +38,24 @@ class FileProcess { await file.writeAsBytes(bytes); return file; } + + static Future downloadFileFromUrl(String url,String fileName) async { + await checkDocumentFolder(); + String filePath = '${directory!.path}/$fileName'; + + if (await File(filePath).exists()) { + await Future.delayed(const Duration(seconds: 1)); + return filePath; + } else { + var response = await http.get(Uri.parse(url)); + if (response.statusCode == 200) { + var bytes = response.bodyBytes; + File file = File(filePath); + await file.writeAsBytes(bytes); + return filePath; + } else { + throw Exception('Failed to download file'); + } + } + } } diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 07a927d..33559e2 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -27,6 +27,7 @@ import 'package:mohem_flutter_app/ui/login/verify_login_screen.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_intro_screen.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_screen.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_sponsor_video_screen.dart'; +import 'package:mohem_flutter_app/ui/marathon/marathon_tutorial_viewer_screen.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_waiting_screen.dart'; import 'package:mohem_flutter_app/ui/misc/request_submit_screen.dart'; import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart'; @@ -203,6 +204,7 @@ class AppRoutes { static const String marathonWinnerScreen = "/marathonWinnerScreen"; static const String marathonSponsorVideoScreen = "/marathonSponsorVideoScreen"; static const String marathonWaitingScreen = "/marathonWaitingScreen"; + static const String marathonTutorialScreen = "/marathonTutorialScreen"; static const String unsafeDeviceScreen = "/unsafeDeviceScreen"; static const String appUpdateScreen = "/appUpdateScreen"; @@ -319,6 +321,7 @@ class AppRoutes { marathonScreen: (BuildContext context) => MarathonScreen(), marathonSponsorVideoScreen: (BuildContext context) => const SponsorVideoScreen(), marathonWaitingScreen: (BuildContext context) => const MarathonWaitingScreen(), + marathonTutorialScreen: (BuildContext context) => const MarathonTutorialViewerScreen(), unsafeDeviceScreen: (BuildContext context) => const UnsafeDeviceScreen(), appUpdateScreen: (BuildContext context) => const AppUpdateScreen(), diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 3af55f0..91ded82 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -68,8 +68,9 @@ extension EmailValidator on String { style: TextStyle(fontSize: 10, fontStyle: fontStyle ?? FontStyle.normal, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.4), ); - Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isBold = false, int maxLine = 0}) => Text( + Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0}) => Text( this, + textAlign: isCenter ? TextAlign.center : null, maxLines: (maxLine > 0) ? maxLine : null, style: TextStyle( fontSize: 11, 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/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 7a3501f..a6fd43d 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -549,4 +549,5 @@ abstract class LocaleKeys { static const addAtLeastOneAttachment ='addAtLeastOneAttachment'; static const pleaseClickButtonToJoinMarathon ='pleaseClickButtonToJoinMarathon'; static const youCannotJoinTheMarathon ='youCannotJoinTheMarathon'; + static const open ='open'; } diff --git a/lib/models/dashboard/itg_forms_model.dart b/lib/models/dashboard/itg_forms_model.dart index ae67ef8..7109d4e 100644 --- a/lib/models/dashboard/itg_forms_model.dart +++ b/lib/models/dashboard/itg_forms_model.dart @@ -4,6 +4,7 @@ import 'dart:convert'; +import 'package:mohem_flutter_app/models/itg_forms_models/itg_forms_attachments_model.dart'; import 'package:mohem_flutter_app/models/itg_forms_models/itg_request_model.dart'; import 'package:mohem_flutter_app/models/itg_forms_models/request_type_model.dart'; @@ -84,7 +85,8 @@ class ItgFormsModel { dynamic successMsgN; dynamic vidaUpdatedResponse; ITGRequest? itgRequest; - dynamic itgFormAttachmentsList; + // dynamic itgFormAttachmentsList; + List? itgFormAttachmentsList; String? message; dynamic mohemmItgDepartmentSectionsList; dynamic mohemmItgProjectDepartmentsList; @@ -128,7 +130,11 @@ class ItgFormsModel { successMsgN: json["SuccessMsgN"], vidaUpdatedResponse: json["VidaUpdatedResponse"], itgRequest: json['ITGRequest'] != null ? ITGRequest.fromJson(json['ITGRequest']) : null, - itgFormAttachmentsList: json["Itg_FormAttachmentsList"], + itgFormAttachmentsList: + json["Itg_FormAttachmentsList"] == null ? [] : json["Itg_FormAttachmentsList"]!.map((v) => ITGFormsAttachmentsModel.fromJson(v)).toList(), + + // json["RequestType"] == null ? [] : json['RequestType']!.map((v) => RequestType.fromJson(v)).toList(), + message: json["Message"] == null ? null : json["Message"], mohemmItgDepartmentSectionsList: json["Mohemm_ITG_DepartmentSectionsList"], mohemmItgProjectDepartmentsList: json["Mohemm_ITG_ProjectDepartmentsList"], 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/itg/advertisement.dart b/lib/models/itg/advertisement.dart index ebcf704..fb172ad 100644 --- a/lib/models/itg/advertisement.dart +++ b/lib/models/itg/advertisement.dart @@ -94,8 +94,9 @@ class ViewAttachFileColl { dynamic referenceItemId; dynamic content; dynamic filePath; + dynamic languageId; - ViewAttachFileColl({this.attachmentId, this.fileName, this.contentType, this.attachFileStream, this.base64String, this.isActive, this.referenceItemId, this.content, this.filePath}); + ViewAttachFileColl({this.attachmentId, this.fileName, this.contentType, this.attachFileStream, this.base64String, this.isActive, this.referenceItemId, this.content, this.filePath, this.languageId}); ViewAttachFileColl.fromJson(Map json) { attachmentId = json['attachmentId']; @@ -107,6 +108,7 @@ class ViewAttachFileColl { referenceItemId = json['referenceItemId']; content = json['content']; filePath = json['filePath']; + languageId = json['languageId']; } Map toJson() { @@ -120,6 +122,7 @@ class ViewAttachFileColl { data['referenceItemId'] = this.referenceItemId; data['content'] = this.content; data['filePath'] = this.filePath; + data['languageId'] = this.languageId; return data; } } diff --git a/lib/models/itg/itg_response_model.dart b/lib/models/itg/itg_response_model.dart index dff7869..6a85cb1 100644 --- a/lib/models/itg/itg_response_model.dart +++ b/lib/models/itg/itg_response_model.dart @@ -45,22 +45,57 @@ class ItgResponseResult { this.data, this.errormsg, }); +// +// final dynamic totalItemsCount; +// final ItgResponseData? data; +// final dynamic errormsg; +// +// factory ItgResponseResult.fromJson(Map json) => ItgResponseResult( +// totalItemsCount: json["totalItemsCount"], +// data: json["data"] == null ? null : ItgResponseData.fromJson(json["data"]), +// errormsg: json["errormsg"], +// ); +// +// Map toJson() => { +// "totalItemsCount": totalItemsCount, +// "data": data == null ? null : data!.toJson(), +// "errormsg": errormsg, +// }; +// } + +// todo these lines, for ERM new change on UAT +dynamic totalItemsCount; +List? data; +dynamic errormsg; + +ItgResponseResult.fromJson(Map json) { + totalItemsCount = json['totalItemsCount']; + if (json['data'] != null) { + data = []; + if(json['data'] is List) { + json['data'].forEach((v) { + data!.add(ItgResponseData.fromJson(v)); + }); + } else { + data!.add(ItgResponseData.fromJson(json['data'])); + } + + } + errormsg = json['errormsg']; +} - final dynamic totalItemsCount; - final ItgResponseData? data; - final dynamic errormsg; - - factory ItgResponseResult.fromJson(Map json) => ItgResponseResult( - totalItemsCount: json["totalItemsCount"], - data: json["data"] == null ? null : ItgResponseData.fromJson(json["data"]), - errormsg: json["errormsg"], - ); - - Map toJson() => { - "totalItemsCount": totalItemsCount, - "data": data == null ? null : data!.toJson(), - "errormsg": errormsg, - }; +// +// factory ItgResponseResult.fromJson(Map json) => ItgResponseResult( +// totalItemsCount: json["totalItemsCount"], +// data: json["data"] == null ? null : ItgResponseData.fromJson(json["data"]), +// errormsg: json["errormsg"], +// ); + +Map toJson() => { + "totalItemsCount": totalItemsCount, + "data": data == null ? null : data!.map((v) => v.toJson()).toList(), + "errormsg": errormsg, + }; } class ItgResponseData { diff --git a/lib/models/itg/survey_model.dart b/lib/models/itg/survey_model.dart index f9ac415..501ce65 100644 --- a/lib/models/itg/survey_model.dart +++ b/lib/models/itg/survey_model.dart @@ -9,13 +9,18 @@ class SurveyModel { dynamic pageNo; dynamic languageId; - SurveyModel({this.surveyId, this.referenceNo, this.title, this.description, this.questions, this.isActive, this.pageSize, this.pageNo, this.languageId}); + String? descriptionAr; + String? titleAr; + + SurveyModel({this.surveyId, this.referenceNo, this.title,this.titleAr, this.description, this.descriptionAr, this.questions, this.isActive, this.pageSize, this.pageNo, this.languageId}); SurveyModel.fromJson(Map json) { surveyId = json['surveyId']; referenceNo = json['referenceNo']; title = json['title']; + titleAr = json['titleAr']; description = json['description']; + descriptionAr = json['descriptionAr']; if (json['questions'] != null) { questions = []; json['questions'].forEach((v) { @@ -33,7 +38,9 @@ class SurveyModel { data['surveyId'] = this.surveyId; data['referenceNo'] = this.referenceNo; data['title'] = this.title; + data['titleAr'] = this.titleAr; data['description'] = this.description; + data['descriptionAr'] = this.descriptionAr; if (this.questions != null) { data['questions'] = this.questions!.map((v) => v.toJson()).toList(); } @@ -48,6 +55,7 @@ class SurveyModel { class Questions { int? questionId; String? title; + String? titleAr; bool? isRequired; String? type; int? sequenceNo; @@ -59,11 +67,12 @@ class Questions { dynamic pageNo; dynamic languageId; - Questions({this.questionId, this.title, this.isRequired, this.type, this.sequenceNo, this.surveyId, this.options, this.rspPercentage, this.isActive, this.pageSize, this.pageNo, this.languageId}); + Questions({this.questionId, this.title, this.titleAr, this.isRequired, this.type, this.sequenceNo, this.surveyId, this.options, this.rspPercentage, this.isActive, this.pageSize, this.pageNo, this.languageId}); Questions.fromJson(Map json) { questionId = json['questionId']; title = json['title']; + titleAr = json['titleAr']; isRequired = json['isRequired']; type = json['type']; sequenceNo = json['sequenceNo']; @@ -85,6 +94,7 @@ class Questions { Map data = new Map(); data['questionId'] = this.questionId; data['title'] = this.title; + data['titleAr'] = this.titleAr; data['isRequired'] = this.isRequired; data['type'] = this.type; data['sequenceNo'] = this.sequenceNo; @@ -104,6 +114,7 @@ class Questions { class Options { int? optionId; String? title; + String? titleAr; bool? isCommentsRequired; int? sequenceNo; int? questionId; @@ -114,11 +125,12 @@ class Options { dynamic pageNo; dynamic languageId; - Options({this.optionId, this.title, this.isCommentsRequired, this.sequenceNo, this.questionId, this.rspPercentage, this.count, this.isActive, this.pageSize, this.pageNo, this.languageId}); + Options({this.optionId, this.title,this.titleAr, this.isCommentsRequired, this.sequenceNo, this.questionId, this.rspPercentage, this.count, this.isActive, this.pageSize, this.pageNo, this.languageId}); Options.fromJson(Map json) { optionId = json['optionId']; title = json['title']; + titleAr = json['titleAr']; isCommentsRequired = json['isCommentsRequired']; sequenceNo = json['sequenceNo']; questionId = json['questionId']; @@ -134,6 +146,7 @@ class Options { Map data = new Map(); data['optionId'] = this.optionId; data['title'] = this.title; + data['titleAr'] = this.titleAr; data['isCommentsRequired'] = this.isCommentsRequired; data['sequenceNo'] = this.sequenceNo; data['questionId'] = this.questionId; diff --git a/lib/models/itg_forms_models/itg_forms_attachments_model.dart b/lib/models/itg_forms_models/itg_forms_attachments_model.dart new file mode 100644 index 0000000..2f5d8b5 --- /dev/null +++ b/lib/models/itg_forms_models/itg_forms_attachments_model.dart @@ -0,0 +1,25 @@ +class ITGFormsAttachmentsModel { + String? fileBase64; + dynamic fileData; + String? fileName; + String? fileType; + + ITGFormsAttachmentsModel( + {this.fileBase64, this.fileData, this.fileName, this.fileType}); + + ITGFormsAttachmentsModel.fromJson(Map json) { + fileBase64 = json['fileBase64']; + fileData = json['fileData']; + fileName = json['fileName']; + fileType = json['fileType']; + } + + Map toJson() { + Map data = new Map(); + data['fileBase64'] = this.fileBase64; + data['fileData'] = this.fileData; + data['fileName'] = this.fileName; + data['fileType'] = this.fileType; + return data; + } +} 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/marathon/marathon_model.dart b/lib/models/marathon/marathon_model.dart index d86ffc7..934b73b 100644 --- a/lib/models/marathon/marathon_model.dart +++ b/lib/models/marathon/marathon_model.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + class MarathonDetailModel { String? id; String? titleEn; @@ -17,6 +19,7 @@ class MarathonDetailModel { List? questions; int? totalQuestions; int? marathonBufferTime; + bool? displayCorrectAnswer; MarathonDetailModel({ id, @@ -37,6 +40,7 @@ class MarathonDetailModel { questions, totalQuestions, marathonBufferTime, + displayCorrectAnswer, }); MarathonDetailModel.fromJson(Map json) { @@ -68,6 +72,7 @@ class MarathonDetailModel { } totalQuestions = json["totalQuestions"]; marathonBufferTime = json["marathonBufferTime"]; + displayCorrectAnswer = json["displayCorrectAnswer"]; } Map toJson() { diff --git a/lib/models/marathon/tutorial_notification_model.dart b/lib/models/marathon/tutorial_notification_model.dart new file mode 100644 index 0000000..ca1dd52 --- /dev/null +++ b/lib/models/marathon/tutorial_notification_model.dart @@ -0,0 +1,104 @@ +// class TutorialNotificationModel { +// List? data; +// bool? isSuccessful; +// String? message; +// int? statusCode; +// +// TutorialNotificationModel({this.data, this.isSuccessful, this.message, this.statusCode}); +// +// TutorialNotificationModel.fromJson(Map json) { +// if (json['data'] != null) { +// data = []; +// json['data'].forEach((v) { +// data!.add(new Data.fromJson(v)); +// }); +// } +// isSuccessful = json['isSuccessful']; +// message = json['message']; +// statusCode = json['statusCode']; +// } +// +// Map toJson() { +// final Map data = new Map(); +// if (this.data != null) { +// data['data'] = this.data!.map((v) => v.toJson()).toList(); +// } +// data['isSuccessful'] = this.isSuccessful; +// data['message'] = this.message; +// data['statusCode'] = this.statusCode; +// +// return data; +// } +// } + +class TutorialNotificationModel { + String? tutorialNotificationId; + String? tutorialName; + String? tutorialDescription; + String? startDate; + String? endDate; + String? fileName; + String? contentType; + String? filePath; + int? orderNo; + bool? isActive; + int? isStatus; + String? created; + String? createdBy; + String? modified; + String? modifiedBy; + + TutorialNotificationModel({this.tutorialNotificationId, + this.tutorialName, + this.tutorialDescription, + this.startDate, + this.endDate, + this.fileName, + this.contentType, + this.filePath, + this.orderNo, + this.isActive, + this.isStatus, + this.created, + this.createdBy, + this.modified, + this.modifiedBy}); + + TutorialNotificationModel.fromJson(Map json) { + tutorialNotificationId = json['tutorialNotificationId']; + tutorialName = json['tutorialName']; + tutorialDescription = json['tutorialDescription']; + startDate = json['startDate']; + endDate = json['endDate']; + fileName = json['fileName']; + contentType = json['contentType']; + filePath = json['filePath']; + orderNo = json['orderNo']; + isActive = json['isActive']; + isStatus = json['isStatus']; + created = json['created']; + createdBy = json['createdBy']; + modified = json['modified']; + modifiedBy = json['modifiedBy']; + } + + Map toJson() { + Map data = new Map(); + data['tutorialNotificationId'] = this.tutorialNotificationId; + data['tutorialName'] = this.tutorialName; + data['tutorialDescription'] = this.tutorialDescription; + data['startDate'] = this.startDate; + data['endDate'] = this.endDate; + data['fileName'] = this.fileName; + data['contentType'] = this.contentType; + data['filePath'] = this.filePath; + data['orderNo'] = this.orderNo; + data['isActive'] = this.isActive; + data['isStatus'] = this.isStatus; + data['created'] = this.created; + data['createdBy'] = this.createdBy; + data['modified'] = this.modified; + data['modifiedBy'] = this.modifiedBy; + return data; + } +} 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/provider/chat_provider_model.dart b/lib/provider/chat_provider_model.dart index 00545b2..0a37f8e 100644 --- a/lib/provider/chat_provider_model.dart +++ b/lib/provider/chat_provider_model.dart @@ -37,7 +37,7 @@ import 'package:mohem_flutter_app/models/my_team/get_employee_subordinates_list. import 'package:mohem_flutter_app/ui/chat/chat_detailed_screen.dart'; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:mohem_flutter_app/widgets/image_picker.dart'; -import 'package:open_file/open_file.dart'; +import 'package:open_filex/open_filex.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:signalr_netcore/hub_connection.dart'; @@ -1674,7 +1674,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { try { String path = await downChatMedia(encodedString, fileTypeName ?? ""); Utils.hideLoading(context); - OpenFile.open(path); + OpenFilex.open(path); } catch (e) { Utils.showToast("Cannot open file."); } diff --git a/lib/ui/attendance/monthly_attendance_screen.dart b/lib/ui/attendance/monthly_attendance_screen.dart index 1753c29..65f101b 100644 --- a/lib/ui/attendance/monthly_attendance_screen.dart +++ b/lib/ui/attendance/monthly_attendance_screen.dart @@ -304,10 +304,17 @@ class _MonthlyAttendanceScreenState extends State { if (details.date.month == formattedDate.month && details.date.year == formattedDate.year) { int val = details.date.day; //check day is off - if (getDayHoursTypeDetailsList.isNotEmpty) { - bool isDayIsOff = getDayHoursTypeDetailsList[val - 1].aTTENDEDFLAG == 'N' && getDayHoursTypeDetailsList[val - 1].dAYTYPE == 'OFF'; - bool isDayIsPresent = getDayHoursTypeDetailsList[val - 1].aTTENDEDFLAG == 'Y'; - bool isDayIsAbsent = getDayHoursTypeDetailsList[val - 1].aTTENDEDFLAG == 'N' && getDayHoursTypeDetailsList[val - 1].aBSENTFLAG == 'Y'; + // if (getDayHoursTypeDetailsList.isNotEmpty) { + // bool isDayIsOff = getDayHoursTypeDetailsList[val - 1].aTTENDEDFLAG == 'N' && getDayHoursTypeDetailsList[val - 1].dAYTYPE == 'OFF'; + // bool isDayIsPresent = getDayHoursTypeDetailsList[val - 1].aTTENDEDFLAG == 'Y'; + // bool isDayIsAbsent = getDayHoursTypeDetailsList[val - 1].aTTENDEDFLAG == 'N' && getDayHoursTypeDetailsList[val - 1].aBSENTFLAG == 'Y'; + List getDayHours = + getDayHoursTypeDetailsList.where((GetDayHoursTypeDetailsList element) => DateFormat("MM/dd/yyyy", "en_US").parse(element.sCHEDULEDATE!).day == details.date.day).toList(); + + if (getDayHours.isNotEmpty) { + bool isDayIsOff = getDayHoursTypeDetailsList[0].aTTENDEDFLAG == 'N' && getDayHoursTypeDetailsList[0].dAYTYPE == 'OFF'; + bool isDayIsPresent = getDayHoursTypeDetailsList[0].aTTENDEDFLAG == 'Y'; + bool isDayIsAbsent = getDayHoursTypeDetailsList[0].aTTENDEDFLAG == 'N' && getDayHoursTypeDetailsList[0].aBSENTFLAG == 'Y'; if (isDayIsOff) { return Container( diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index 3ab8741..a24a343 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -16,6 +16,8 @@ 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/itg/itg_main_response.dart'; +import 'package:mohem_flutter_app/models/itg/itg_response_model.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; import 'package:mohem_flutter_app/models/privilege_list_model.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; @@ -150,6 +152,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb data.fetchMenuEntries(); data.getCategoryOffersListAPI(context); marathonProvider.getMarathonDetailsFromApi(); + marathonProvider.getMarathonTutorial(); if (isFromInit) { checkERMChannel(); } @@ -157,39 +160,72 @@ class _DashboardScreenState extends State with WidgetsBindingOb _refreshController.refreshCompleted(); } + int ermIndex = 0; + + void handleErmChannel(List list) async { + try { + if (ermIndex == list.length) { + return; + } + Utils.showLoading(context); + ItgMainRes? response = await DashboardApiClient().getAdvertisementDetail(list[ermIndex].notificationMasterId ?? ""); + Utils.hideLoading(context); + if (response!.mohemmItgResponseItem!.result!.data != null) { + if (list[ermIndex].notificationType == "Survey") { + await Navigator.pushNamed(context, AppRoutes.survey, arguments: response.mohemmItgResponseItem!.result!.data!.first); + } else { + await Navigator.pushNamed(context, AppRoutes.advertisement, arguments: { + "masterId": list[ermIndex].notificationMasterId, + "advertisement": response.mohemmItgResponseItem!.result!.data!.first.advertisement, + }); + } + } + ermIndex++; + handleErmChannel(list); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + return; + } + } + void checkERMChannel() { - data.getITGNotification().then((val) { + data.getITGNotification().then((val) async { if (val!.result!.data != null) { - print("-------------------- Survey ----------------------------"); - if (val.result!.data!.notificationType == "Survey") { - DashboardApiClient().getAdvertisementDetail(val.result!.data!.notificationMasterId ?? "").then( - (value) { - if (value!.mohemmItgResponseItem!.statusCode == 200) { - if (value.mohemmItgResponseItem!.result!.data != null) { - // Navigator.pushNamed(context, AppRoutes.survey, arguments: val.result!.data); - Navigator.pushNamed(context, AppRoutes.survey, arguments: value.mohemmItgResponseItem!.result!.data); - // Navigator.pushNamed(context, AppRoutes.advertisement, arguments: { - // "masterId": val.result!.data!.notificationMasterId, - // "advertisement": value.mohemmItgResponseItem!.result!.data!.advertisement, - // }); - } - } - }, - ); - } else { - print("------------------------------------------- Ads --------------------"); - DashboardApiClient().getAdvertisementDetail(val.result!.data!.notificationMasterId ?? "").then( - (value) { - if (value!.mohemmItgResponseItem!.statusCode == 200) { - if (value.mohemmItgResponseItem!.result!.data != null) { - Navigator.pushNamed(context, AppRoutes.advertisement, arguments: { - "masterId": val.result!.data!.notificationMasterId, - "advertisement": value.mohemmItgResponseItem!.result!.data!.advertisement, - }); - } - } - }, - ); + ermIndex = 0; + val.result?.data?.removeWhere((element) => element.notificationType == "Announcement"); + handleErmChannel(val.result?.data ?? []); + { + // if (ERMvalue.notificationType == "Survey") { + // await DashboardApiClient().getAdvertisementDetail(val.result!.data!.first.notificationMasterId ?? "").then( + // (value) async { + // if (value!.mohemmItgResponseItem!.statusCode == 200) { + // if (value.mohemmItgResponseItem!.result!.data != null) { + // // Navigator.pushNamed(context, AppRoutes.survey, arguments: val.result!.data); + // await Navigator.pushNamed(context, AppRoutes.survey, arguments: value.mohemmItgResponseItem!.result!.data!.first); + // // Navigator.pushNamed(context, AppRoutes.advertisement, arguments: { + // // "masterId": val.result!.data!.notificationMasterId, + // // "advertisement": value.mohemmItgResponseItem!.result!.data!.advertisement, + // // }); + // } + // } + // }, + // ); + // } else { + // print("------------------------------------------- Ads --------------------"); + // await DashboardApiClient().getAdvertisementDetail(ERMvalue.notificationMasterId ?? "").then( + // (value) async { + // if (value!.mohemmItgResponseItem!.statusCode == 200) { + // if (value.mohemmItgResponseItem!.result!.data != null) { + // await Navigator.pushNamed(context, AppRoutes.advertisement, arguments: { + // "masterId": val.result!.data!.first.notificationMasterId, + // "advertisement": value.mohemmItgResponseItem!.result!.data!.first.advertisement, + // }); + // } + // } + // }, + // ); + // } } } }); @@ -493,7 +529,50 @@ class _DashboardScreenState extends State with WidgetsBindingOb mainAxisSize: MainAxisSize.min, children: [ ServicesWidget(), - context.watch().isLoading ? const MarathonBannerShimmer().paddingAll(20) : const MarathonBanner().paddingOnly(left: 21, right: 21, bottom: 21, top: 8), + context.watch().isLoading ? const MarathonBannerShimmer().paddingAll(20) : const MarathonBanner().paddingOnly(left: 21, right: 21, bottom: 8, top: 8), + // context.watch().isTutorialLoading + // ? const MarathonBannerShimmer().paddingAll(20) + // : Container( + // padding: EdgeInsets.only(bottom: 12, top: 12), + // margin: EdgeInsets.only(left: 21, right: 21, bottom: 21, top: 8), + // width: double.infinity, + // alignment: Alignment.center, + // decoration: BoxDecoration( + // color: MyColors.backgroundBlackColor, + // borderRadius: BorderRadius.circular(20), + // border: Border.all(color: MyColors.lightGreyEDColor, width: 1), + // ), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisSize: MainAxisSize.min, + // children: [ + // Text( + // "Tutorial:", + // style: TextStyle( + // fontSize: 11, + // fontStyle: FontStyle.italic, + // fontWeight: FontWeight.w600, + // color: MyColors.white.withOpacity(0.83), + // letterSpacing: -0.4, + // ), + // ), + // Text( + // context.read().tutorial?.tutorialName ?? "", + // overflow: TextOverflow.ellipsis, + // style: TextStyle( + // fontStyle: FontStyle.italic, + // fontSize: 19, + // fontWeight: FontWeight.bold, + // color: MyColors.white, + // height: 32 / 22, + // ), + // ), + // ], + // ), + // ).onPress(() { + // checkERMChannel(); + // // Navigator.pushNamed(context, AppRoutes.marathonTutorialScreen); + // }), ], ), ), diff --git a/lib/ui/landing/itg/its_add_screen_video_image.dart b/lib/ui/landing/itg/its_add_screen_video_image.dart index 8ce89d0..60bd4ba 100644 --- a/lib/ui/landing/itg/its_add_screen_video_image.dart +++ b/lib/ui/landing/itg/its_add_screen_video_image.dart @@ -35,7 +35,9 @@ class _ITGAdsScreenState extends State { bool isImage = false; bool isAudio = false; String ext = ''; - late File imageFile; + + // late File imageFile; + late String imageUrl; ads.Advertisement? advertisementData; dynamic data; String? masterID; @@ -47,12 +49,13 @@ class _ITGAdsScreenState extends State { late CountdownTimerController timerController; - void checkFileType() async { + void checkFileType() { String? rFile = advertisementData!.viewAttachFileColl!.first.base64String; String? rFileExt = advertisementData!.viewAttachFileColl!.first.fileName; ext = "." + rFileExt!.split(".").last.toLowerCase(); if (ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gif") { - await processImage(rFile!); + // await processImage(); + imageUrl = rFile!; isImage = true; } else { if (ext == ".aac") { @@ -62,11 +65,11 @@ class _ITGAdsScreenState extends State { _futureController = createVideoPlayer(rFile!); } - advertisementData?.actionButtonsColl!.forEach((element) { + // advertisementData?.actionButtonsColl!.forEach((element) { advertisementData?.actionButtonsColl!.removeWhere((element1) => element1.actionButtonId == advertisementData?.skipButtonId); - }); + //}); - setState(() {}); + // setState(() {}); } void changeTimer(bool isTimerEnd) { @@ -80,16 +83,17 @@ class _ITGAdsScreenState extends State { timerController.dispose(); } - Future processImage(String encodedBytes) async { - try { - Uint8List decodedBytes = base64Decode(encodedBytes.split("base64,").last); - Directory appDocumentsDirectory = await getApplicationDocumentsDirectory(); // 1 - imageFile = Io.File("${appDocumentsDirectory.path}/addImage$ext"); - imageFile.writeAsBytesSync(decodedBytes); - } catch (e) { - logger.d(e); - } - } + // + // Future processImage(String encodedBytes) async { + // try { + // Uint8List decodedBytes = base64Decode(encodedBytes.split("base64,").last); + // Directory appDocumentsDirectory = await getApplicationDocumentsDirectory(); // 1 + // imageFile = Io.File("${appDocumentsDirectory.path}/addImage$ext"); + // imageFile.writeAsBytesSync(decodedBytes); + // } catch (e) { + // logger.d(e); + // } + // } Future createVideoPlayer(String encodedBytes) async { try { @@ -176,13 +180,11 @@ class _ITGAdsScreenState extends State { itemBuilder: (context, index) { String? btnText = AppState().isArabic(context) ? advertisementData?.actionButtonsColl![index].btnTextAr : advertisementData?.actionButtonsColl![index].btnTextEn; return DefaultButton(btnText!, () async { - Navigator.pop(context); - DashboardApiClient() - .setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, advertisementData?.actionButtonsColl![index].actionValue) - .then((value) { - logger.d(value); - }); - }).paddingOnly(left: 60.0, right: 60.0, top: 8, bottom: 8); + Navigator.pop(context); + DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!, advertisementData?.actionButtonsColl![index].actionValue).then((value) { + logger.d(value); + }); + }).paddingOnly(left: 60.0, right: 60.0, top: 8, bottom: 8); }, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 1, @@ -266,7 +268,7 @@ class _ITGAdsScreenState extends State { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - Image.file(imageFile), + Image.network(imageUrl), 50.height, Container(padding: const EdgeInsets.all(16), decoration: Utils.containerRadius(MyColors.white, 10), child: const Icon(Icons.thumb_up, color: MyColors.gradiantEndColor)).onPress( () { diff --git a/lib/ui/landing/itg/survey_screen.dart b/lib/ui/landing/itg/survey_screen.dart index 59f3c14..3bbb27f 100644 --- a/lib/ui/landing/itg/survey_screen.dart +++ b/lib/ui/landing/itg/survey_screen.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; @@ -14,6 +15,8 @@ import 'package:mohem_flutter_app/models/itg/itg_response_model.dart'; import 'package:mohem_flutter_app/models/itg/survey_model.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; +import 'package:flutter/widgets.dart' as fw; + class SurveyScreen extends StatefulWidget { const SurveyScreen({Key? key}) : super(key: key); @@ -46,29 +49,32 @@ class _SurveyScreenState extends State { body: Column( children: [ Expanded( - child: ListView( - children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - 32.height, - itgResponseData?.survey?.title?.toText24() ?? const Text(""), - 8.height, - itgResponseData?.survey?.description?.toText16() ?? const Text(""), - ListView.builder( - padding: EdgeInsets.zero, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: itgResponseData?.survey?.questions?.length, - itemBuilder: (cxt, index) { - return answeredQuestions.isNotEmpty ? getSurveyWidget(itgResponseData?.survey?.questions![index], index) : Container(); - }, - ), - ], - ).paddingOnly(left: 21, right: 21), - ), - ], + child: Directionality( + textDirection: AppState().isArabic(context) ? fw.TextDirection.rtl : fw.TextDirection.ltr, + child: ListView( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + 32.height, + (AppState().isArabic(context) ? itgResponseData?.survey?.titleAr : itgResponseData?.survey?.title)?.toText24() ?? const Text(""), + 8.height, + (AppState().isArabic(context) ? itgResponseData?.survey?.descriptionAr : itgResponseData?.survey?.description)?.toText16() ?? const Text(""), + ListView.builder( + padding: EdgeInsets.zero, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: itgResponseData?.survey?.questions?.length, + itemBuilder: (cxt, index) { + return answeredQuestions.isNotEmpty ? getSurveyWidget(itgResponseData?.survey?.questions![index], index) : Container(); + }, + ), + ], + ).paddingOnly(left: 21, right: 21), + ), + ], + ), )), DefaultButton( LocaleKeys.submitSurvey.tr(), @@ -105,7 +111,7 @@ class _SurveyScreenState extends State { answeredQuestions.clear(); itgResponseData?.survey?.questions?.forEach((element) { if (element.type != "Stars") { - if(element.type == "Faces") { + if (element.type == "Faces") { _selectedIndex = element.options![0].optionId!; } answeredQuestions.add(element.options![0].optionId.toString()); @@ -122,15 +128,16 @@ class _SurveyScreenState extends State { return Column( children: [ 24.height, - question?.title?.toText18() ?? const Text(""), + (AppState().isArabic(context) ? question?.titleAr : question?.title)?.toText18() ?? const Text(""), 16.height, GridView.builder( padding: EdgeInsets.zero, - itemCount: question?.options?.length, + itemCount: question?.options?.length ?? 0, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) { - return radioOption(question?.options?[index].title ?? "", question?.options?[index].optionId.toString() ?? "", answeredQuestions[parentIndex], parentIndex); + return radioOption((AppState().isArabic(context) ? (question!.options![index].titleAr) : question!.options![index].title) ?? "", question?.options?[index].optionId.toString() ?? "", + answeredQuestions[parentIndex], parentIndex); }, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, @@ -145,7 +152,7 @@ class _SurveyScreenState extends State { mainAxisSize: MainAxisSize.min, children: [ 24.height, - question?.title?.toText18() ?? Text(""), + (AppState().isArabic(context) ? question?.titleAr : question?.title)?.toText18() ?? Text(""), 16.height, RatingBar.builder( initialRating: 3, @@ -171,7 +178,7 @@ class _SurveyScreenState extends State { mainAxisSize: MainAxisSize.min, children: [ 24.height, - question?.title?.toText18() ?? Text("asdasdasdasd"), + (AppState().isArabic(context) ? question?.titleAr : question?.title)?.toText18() ?? Text(""), 16.height, GridView( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 5, crossAxisSpacing: 7, mainAxisSpacing: 7), @@ -179,11 +186,13 @@ class _SurveyScreenState extends State { padding: const EdgeInsets.only(top: 0), shrinkWrap: true, children: [ - optionUI("poor.svg", question?.options?[0].optionId, parentIndex), - optionUI("bad.svg", question?.options?[1].optionId, parentIndex), - optionUI("normal.svg", question?.options?[2].optionId, parentIndex), - optionUI("good.svg", question?.options?[3].optionId, parentIndex), - optionUI("xcellent.svg", question?.options?[4].optionId, parentIndex), + for (int i = 0; i < (question?.options?.length ?? 0); i++) optionUI("${question?.options![i].title!.toLowerCase()}.svg", question?.options?[i].optionId, parentIndex), + + // optionUI("poor.svg", question?.options?[0].optionId, parentIndex), + // optionUI("fair.svg", question?.options?[1].optionId, parentIndex), + // optionUI("ok.svg", question?.options?[2].optionId, parentIndex), + // optionUI("good.svg", question?.options?[3].optionId, parentIndex), + // optionUI("excellent.svg", question?.options?[4].optionId, parentIndex), ], ), ], 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..70b7456 100644 --- a/lib/ui/login/new_password_screen.dart +++ b/lib/ui/login/new_password_screen.dart @@ -103,19 +103,19 @@ class _NewPasswordScreenState extends State { 8.height, passwordConstraintsUI(LocaleKeys.doNotAddRepeatingLetters.tr(), checkRepeatedChars(password.text)), // 8.height, - // passwordConstraintsUI(LocaleKeys.itShouldContainSpecialCharacter.tr(), checkRegEx(r'[!@#$%^&*(),.?":{}|<>]')), + // 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/marathon/marathon_provider.dart b/lib/ui/marathon/marathon_provider.dart index 544aad3..ce8476b 100644 --- a/lib/ui/marathon/marathon_provider.dart +++ b/lib/ui/marathon/marathon_provider.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:developer'; import 'package:appinio_swiper/appinio_swiper.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -13,6 +14,7 @@ import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/models/marathon/marathon_generic_model.dart'; import 'package:mohem_flutter_app/models/marathon/marathon_model.dart'; import 'package:mohem_flutter_app/models/marathon/question_model.dart'; +import 'package:mohem_flutter_app/models/marathon/tutorial_notification_model.dart'; import 'package:mohem_flutter_app/models/marathon/winner_model.dart'; import 'package:mohem_flutter_app/models/privilege_list_model.dart'; import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart'; @@ -414,13 +416,16 @@ class MarathonProvider extends ChangeNotifier { notifyListeners(); } - void updateCurrentQuestionOptionStatus(QuestionsOptionStatus status, int index) { + void updateCurrentQuestionOptionStatus({required QuestionsOptionStatus status, required int selectedOptIndex, required int correctOptionIndex}) { for (int i = 0; i < currentQuestion.questionOptions!.length; i++) { currentQuestion.questionOptions![i].optionStatus = QuestionsOptionStatus.unSelected; } - currentQuestion.questionOptions![index].optionStatus = status; - selectedOptionId = currentQuestion.questionOptions![index].id; - selectedOptionIndex = index; + if (status == QuestionsOptionStatus.wrong) { + currentQuestion.questionOptions![correctOptionIndex].optionStatus = QuestionsOptionStatus.correct; // if person's answer is wrong we have to show him the actual right answer + } + currentQuestion.questionOptions![selectedOptIndex].optionStatus = status; + selectedOptionId = currentQuestion.questionOptions![selectedOptIndex].id; + selectedOptionIndex = selectedOptIndex; notifyListeners(); } @@ -435,22 +440,30 @@ class MarathonProvider extends ChangeNotifier { } void getCorrectAnswerAndUpdateAnswerColor() { + log("correctOptionIndex"); + callCountThreshold = 1; + int correctOptionIndex = -1; + if (demoMarathonDetailModel.displayCorrectAnswer ?? false) { + correctOptionIndex = currentQuestion.questionOptions!.indexWhere((QuestionOptions element) => element.isCorrectOption ?? false); + log("correctOptionIndex: $correctOptionIndex"); + } + if (selectedOptionIndex != null) { scheduleMicrotask(() async { if (AppState().getIsDemoMarathon) { if (currentQuestion.questionOptions![selectedOptionIndex!].isCorrectOption!) { - updateCurrentQuestionOptionStatus(QuestionsOptionStatus.correct, selectedOptionIndex!); + updateCurrentQuestionOptionStatus(status: QuestionsOptionStatus.correct, selectedOptIndex: selectedOptionIndex!, correctOptionIndex: correctOptionIndex); } else { - updateCurrentQuestionOptionStatus(QuestionsOptionStatus.wrong, selectedOptionIndex!); + updateCurrentQuestionOptionStatus(status: QuestionsOptionStatus.wrong, selectedOptIndex: selectedOptionIndex!, correctOptionIndex: correctOptionIndex); } } else { await callSubmitOptionApi().then((bool value) async { updateIsUserOutOfGame = !value; if (value) { - updateCurrentQuestionOptionStatus(QuestionsOptionStatus.correct, selectedOptionIndex!); + updateCurrentQuestionOptionStatus(status: QuestionsOptionStatus.correct, selectedOptIndex: selectedOptionIndex!, correctOptionIndex: correctOptionIndex); } else { - updateCurrentQuestionOptionStatus(QuestionsOptionStatus.wrong, selectedOptionIndex!); + updateCurrentQuestionOptionStatus(status: QuestionsOptionStatus.wrong, selectedOptIndex: selectedOptionIndex!, correctOptionIndex: correctOptionIndex); } }); } @@ -551,7 +564,6 @@ class MarathonProvider extends ChangeNotifier { notifyListeners(); isPrivilegedWithMarathon = checkIfPrivilegedForMarathon(); demoMarathonDetailModel = await DemoMarathonRepo().getDemoMarathonDetails(); - if (isPrivilegedWithMarathon) { marathonDetailModel = await MarathonApiClient().getMarathonDetails(); updateTotalSecondsToWaitForMarathon = marathonDetailModel.marathonBufferTime ?? 30; @@ -594,4 +606,15 @@ class MarathonProvider extends ChangeNotifier { AppState().setIsDemoMarathon = true; await callNextQuestionApi(); } + + bool isTutorialLoading = false; + TutorialNotificationModel? tutorial; + + Future getMarathonTutorial() async { + isTutorialLoading = true; + notifyListeners(); + tutorial = await MarathonApiClient().getMarathonTutorial(); + isTutorialLoading = false; + notifyListeners(); + } } diff --git a/lib/ui/marathon/marathon_tutorial_viewer_screen.dart b/lib/ui/marathon/marathon_tutorial_viewer_screen.dart new file mode 100644 index 0000000..0cdc8d8 --- /dev/null +++ b/lib/ui/marathon/marathon_tutorial_viewer_screen.dart @@ -0,0 +1,241 @@ +import 'dart:convert'; +import 'dart:io' as Io; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_countdown_timer/index.dart'; +import 'package:lottie/lottie.dart'; +import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/file_process.dart'; +import 'package:mohem_flutter_app/classes/lottie_consts.dart'; +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/extensions/widget_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/itg/advertisement.dart' as ads; +import 'package:mohem_flutter_app/models/marathon/tutorial_notification_model.dart'; +import 'package:mohem_flutter_app/ui/chat/common.dart'; +import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart'; +import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; +import 'package:mohem_flutter_app/widgets/button/default_button.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:provider/provider.dart'; +import 'package:video_player/video_player.dart'; + +class MarathonTutorialViewerScreen extends StatefulWidget { + const MarathonTutorialViewerScreen({Key? key}) : super(key: key); + + @override + _MarathonTutorialViewerScreenState createState() => _MarathonTutorialViewerScreenState(); +} + +class _MarathonTutorialViewerScreenState extends State { + late Future _futureController; + VideoPlayerController? _controller; + bool skip = false; + + bool isVideo = false; + bool isImage = false; + bool isAudio = false; + bool isPdf = false; + + String link = ""; + + String ext = ''; + late File imageFile; + ads.Advertisement? advertisementData; + dynamic data; + String? masterID; + int videoDuration = 0; + + ValueNotifier videoLength = ValueNotifier(0); + + void checkFileTypes(String link) { + ext = "." + link.split(".").last.toLowerCase(); + if (ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gif") { + isImage = true; + } else if (ext == ".pdf") { + isPdf = true; + } else { + if (ext == ".aac") { + isAudio = true; + } + isVideo = true; + _futureController = createVideoPlayer(link); + } + + setState(() {}); + } + + Future processImage(String encodedBytes) async { + try { + Uint8List decodedBytes = base64Decode(encodedBytes.split("base64,").last); + Directory appDocumentsDirectory = await getApplicationDocumentsDirectory(); // 1 + imageFile = Io.File("${appDocumentsDirectory.path}/addImage$ext"); + imageFile.writeAsBytesSync(decodedBytes); + } catch (e) { + logger.d(e); + } + } + + Future createVideoPlayer(String link) async { + try { + VideoPlayerController controller = VideoPlayerController.networkUrl(Uri.parse(link)); + await controller.initialize(); + await controller.play(); + await controller.setVolume(1.0); + await controller.setLooping(false); + controller.addListener(() { + controller.position.then((value) { + videoLength.value = value!.inMilliseconds; + // if(controller.value.isCompleted) { + // videoLength.value = 0; + // } + }); + }); + return controller; + } catch (e) { + print(e); + return VideoPlayerController.network("https://apimohemmweb.cloudsolutions.com.sa/ErmAttachment/compressedvideo.mp4"); + } + } + + bool showControls = false; + + void showVideoControls() { + showControls = !showControls; + setState(() {}); + } + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + _controller?.dispose(); + super.dispose(); + } + + TutorialNotificationModel? tutorial; + + @override + Widget build(BuildContext context) { + if (tutorial == null) { + tutorial ??= context.read().tutorial; + link = tutorial!.filePath!; + checkFileTypes(link); + } + + return Scaffold( + backgroundColor: Colors.black, + appBar: AppBarWidget(context, title: tutorial!.fileName!, showHomeButton: false), + body: Stack( + children: [ + if (isVideo) + FutureBuilder( + future: _futureController, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done && snapshot.data != null) { + _controller = snapshot.data as VideoPlayerController; + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Center( + child: isAudio + ? Lottie.asset(MyLottieConsts.audioPlaybackLottie) + : AspectRatio( + aspectRatio: _controller!.value.aspectRatio, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + VideoPlayer(_controller!), + AnimatedContainer( + duration: const Duration(milliseconds: 250), + color: Colors.black.withOpacity(showControls ? .4 : .0), + child: AnimatedOpacity( + opacity: showControls ? 1 : 0, + duration: const Duration(milliseconds: 250), + child: Column( + children: [ + Icon(_controller!.value.isPlaying ? Icons.pause : Icons.play_arrow, color: Colors.white) + .onPress(() { + if (!showControls) { + showVideoControls(); + return; + } + + if (_controller!.value.isPlaying) { + _controller!.pause(); + } + // else if (_controller!.value.isCompleted) { + // videoLength.value = 0; + // _controller!.play(); + // } + else { + _controller!.play(); + } + setState(() {}); + }) + .center + .expanded, + ValueListenableBuilder( + valueListenable: videoLength, + builder: (context, val, child) { + return SeekBar( + duration: _controller!.value.duration, + position: Duration(milliseconds: val), + bufferedPosition: Duration(), + onChanged: (duration) { + _controller!.seekTo(duration); + }, + ); + }), + ], + ), + ), + ).onPress(() { + showVideoControls(); + }), + ], + ), + ), + ), + 30.height, + ], + ); + } else { + return const Center( + child: CircularProgressIndicator( + color: Colors.white, + ), + ); + } + }, + ), + if (isImage) Image.network(link).center, + if (isPdf) + DefaultButton(LocaleKeys.open.tr(), () async { + try { + Utils.showLoading(context); + await FileProcess.downloadFileFromUrl(link, tutorial!.fileName!).then((path) { + Utils.hideLoading(context); + FileProcess.openFile(path, isFullPath: true); + }); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + }).paddingAll(21).center + ], + ), + ); + } +} diff --git a/lib/ui/marathon/widgets/question_card.dart b/lib/ui/marathon/widgets/question_card.dart index 8aa76bd..0965c9d 100644 --- a/lib/ui/marathon/widgets/question_card.dart +++ b/lib/ui/marathon/widgets/question_card.dart @@ -133,7 +133,7 @@ class AnswerContent extends StatelessWidget { if (provider.totalCurrentQuestionTime - provider.currentGapTime <= 0) { null; } else { - provider.updateCurrentQuestionOptionStatus(QuestionsOptionStatus.selected, index); + provider.updateCurrentQuestionOptionStatus(status: QuestionsOptionStatus.selected, selectedOptIndex: index, correctOptionIndex: -1); // } }, ); diff --git a/lib/ui/screens/mowadhafhi/request_details.dart b/lib/ui/screens/mowadhafhi/request_details.dart index aa22bdd..a104f51 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_filex/open_filex.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 OpenFilex.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/item_history_screen.dart b/lib/ui/work_list/item_history_screen.dart index e88dcba..7490d38 100644 --- a/lib/ui/work_list/item_history_screen.dart +++ b/lib/ui/work_list/item_history_screen.dart @@ -22,7 +22,7 @@ import 'package:mohem_flutter_app/models/get_quotation_analysis_list_model.dart' import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/circular_avatar.dart'; import 'package:mohem_flutter_app/widgets/item_detail_view_widget.dart'; -import 'package:open_file/open_file.dart'; +import 'package:open_filex/open_filex.dart'; import 'package:path_provider/path_provider.dart'; class ItemHistoryScreenParams { @@ -278,7 +278,7 @@ class _ItemHistoryScreenState extends State { ).objectContainerView().onPress(() async { try { String path = await _createFileFromString(getAttachmentList[index].fILEDATA ?? "", getAttachmentList[index].fILECONTENTTYPE ?? ""); - OpenFile.open(path); + OpenFilex.open(path); } catch (ex) { Utils.showToast("Cannot open file."); } diff --git a/lib/ui/work_list/itg_detail_screen.dart b/lib/ui/work_list/itg_detail_screen.dart index 5d48c53..546d320 100644 --- a/lib/ui/work_list/itg_detail_screen.dart +++ b/lib/ui/work_list/itg_detail_screen.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:lottie/lottie.dart'; import 'package:mohem_flutter_app/api/worklist/worklist_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; @@ -12,10 +13,12 @@ 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/itg_forms_models/allowed_actions_model.dart'; +import 'package:mohem_flutter_app/models/itg_forms_models/itg_forms_attachments_model.dart'; import 'package:mohem_flutter_app/models/itg_forms_models/itg_request_model.dart'; import 'package:mohem_flutter_app/models/itg_forms_models/request_detail_model.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/ui/work_list/itg_fragments/approval_level_fragment.dart'; +import 'package:mohem_flutter_app/ui/work_list/itg_fragments/attachments_fragment.dart'; import 'package:mohem_flutter_app/ui/work_list/itg_fragments/request_detail_fragment.dart'; import 'package:mohem_flutter_app/ui/work_list/sheets/delegate_sheet.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; @@ -44,9 +47,12 @@ class _ItgDetailScreenState extends State { bool isRejectAvailable = false; List allowedActionList = []; + List itgFormAttachmentsList = []; late DashboardProviderModel providerData; + bool isAttachmentLoaded = false; + @override void initState() { providerData = Provider.of(context, listen: false); @@ -87,6 +93,7 @@ class _ItgDetailScreenState extends State { requestDetails = AppState().requestAllList![AppState().itgWorkListIndex!]; // ModalRoute.of(context)!.settings.arguments as WorkListResponseModel; providerData.itgFormsModel!.totalCount = providerData.itgFormsModel!.totalCount! - 1; getItgData(); + getItgRequestAttachments(); } } @@ -140,6 +147,7 @@ class _ItgDetailScreenState extends State { myTab(LocaleKeys.requestDetails.tr(), 0), myTab(LocaleKeys.approvalLevel.tr(), 1), myTab(LocaleKeys.requesterDetails.tr(), 2), + myTab(LocaleKeys.attachments.tr(), 3), ], ), ), @@ -157,6 +165,11 @@ class _ItgDetailScreenState extends State { voidCallback: reloadITG, ), RequestDetailFragment(fields: itgRequest?.fieldGoups?[0].fields ?? []), + isAttachmentLoaded + ? itgFormAttachmentsList.isEmpty + ? Utils.getNoDataWidget(context) + : ITGAttachmentsFragment(itgFormAttachmentsList) + : showLoadingAnimation(), ], ).expanded, if (isApproveAvailable || isRejectAvailable || isCloseAvailable) @@ -405,7 +418,7 @@ class _ItgDetailScreenState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ - title.toText12(color: isSelected ? Colors.white : Colors.white.withOpacity(.74), isCenter: true), + title.toText11(color: isSelected ? Colors.white : Colors.white.withOpacity(.74), isCenter: true), 4.height, Container( height: 8, @@ -478,7 +491,7 @@ class _ItgDetailScreenState extends State { ); } - void performAnswerAction(String requestType, taskId, itemId, String employeeNumber, String comments) async { + void performAnswerAction(String requestType, taskId, itemId, String employeeNumber, String comments) async { try { Utils.showLoading(context); ITGRequest? itgRequest = await WorkListApiClient().answerITGRequest(requestType, taskId, itemId, employeeNumber, "", comments); @@ -502,7 +515,7 @@ class _ItgDetailScreenState extends State { } } - void performRejectAction(String requestType, taskId, itemId, String employeeNumber, String comments) async { + void performRejectAction(String requestType, taskId, itemId, String employeeNumber, String comments) async { try { Utils.showLoading(context); ITGRequest? itgRequest = await WorkListApiClient().rejectITGRequest(requestType, taskId, itemId, employeeNumber, comments); @@ -526,7 +539,7 @@ class _ItgDetailScreenState extends State { } } - void performApproveAction(String requestType, taskId, itemId, String employeeNumber, String comments) async { + void performApproveAction(String requestType, taskId, itemId, String employeeNumber, String comments) async { try { Utils.showLoading(context); ITGRequest? itgRequest = await WorkListApiClient().approveITGRequest(requestType, taskId, itemId, employeeNumber, comments); @@ -576,6 +589,35 @@ class _ItgDetailScreenState extends State { } } + Widget showLoadingAnimation() { + return Lottie.asset( + 'assets/lottie/loading.json', + repeat: true, + reverse: false, + ); + } + + void getItgRequestAttachments() async { + try { + // Utils.showLoading(context); + itgFormAttachmentsList = + (await WorkListApiClient().getITGFormAttachments(requestDetails!.requestType!, requestDetails!.iD, requestDetails!.itemID, AppState().memberInformationList?.eMPLOYEENUMBER ?? ""))!; + // allowedActionList = itgRequest?.allowedActions ?? []; + // if (allowedActionList.isNotEmpty) { + // isCloseAvailable = allowedActionList.any((element) => element.action == "CLOSE"); + // isApproveAvailable = itgRequest!.allowedActions!.any((element) => element.action == "Approve"); + // isRejectAvailable = itgRequest!.allowedActions!.any((element) => element.action == "Reject"); + // } + // Utils.hideLoading(context); + setState(() { + isAttachmentLoaded = true; + }); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } + void reloadITG() { animationIndex = animationIndex + 1; AppState().requestAllList!.removeAt(AppState().itgWorkListIndex!); diff --git a/lib/ui/work_list/itg_fragments/attachments_fragment.dart b/lib/ui/work_list/itg_fragments/attachments_fragment.dart new file mode 100644 index 0000000..d31e897 --- /dev/null +++ b/lib/ui/work_list/itg_fragments/attachments_fragment.dart @@ -0,0 +1,78 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +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/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/models/itg_forms_models/itg_forms_attachments_model.dart'; +import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart'; +import 'package:open_filex/open_filex.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class ITGAttachmentsFragment extends StatelessWidget { + List itgFormAttachmentsList; + + ITGAttachmentsFragment(this.itgFormAttachmentsList); + + @override + Widget build(BuildContext context) { + return ListView.separated( + itemCount: itgFormAttachmentsList.length, + itemBuilder: (context, index) { + return Row( + children: [ + SvgPicture.asset(determineFileIcon(itgFormAttachmentsList[index].fileType ?? "")), + 12.width, + (itgFormAttachmentsList[index].fileName ?? "").toText16().expanded, + ], + ).objectContainerView().onPress(() async { + try { + String path = await _createFileFromString(itgFormAttachmentsList[index].fileBase64 ?? "", itgFormAttachmentsList[index].fileType ?? ""); + OpenFilex.open(path); + } catch (ex) { + Utils.showToast("Cannot open file."); + } + }); + }, + separatorBuilder: (BuildContext context, int index) => 12.height, + ).paddingAll(21); + } + + String determineFileIcon(String fileContentType) { + String icon = ""; + switch (fileContentType) { + case "pdf": + icon = "assets/images/pdf.svg"; + break; + case "xls": + icon = "assets/images/xls.svg"; + break; + case "xlsx": + icon = "assets/images/xls.svg"; + break; + case "png": + icon = "assets/images/png.svg"; + break; + case "jpg": + icon = "assets/images/jpg.svg"; + break; + case "jpeg": + icon = "assets/images/jpg.svg"; + break; + } + return icon; + } + + 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; + } +} 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/attachments_fragment.dart b/lib/ui/work_list/worklist_fragments/attachments_fragment.dart index bc1bde6..e1381ac 100644 --- a/lib/ui/work_list/worklist_fragments/attachments_fragment.dart +++ b/lib/ui/work_list/worklist_fragments/attachments_fragment.dart @@ -9,8 +9,10 @@ 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/models/get_attachement_list_model.dart'; -import 'package:open_file/open_file.dart'; +import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart'; +import 'package:open_filex/open_filex.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; class AttachmentsFragment extends StatelessWidget { final List getAttachmentList; @@ -31,8 +33,9 @@ class AttachmentsFragment extends StatelessWidget { ).objectContainerView().onPress(() async { try { String path = await _createFileFromString(getAttachmentList[index].fILEDATA ?? "", getAttachmentList[index].fILECONTENTTYPE ?? ""); - OpenFile.open(path); - } catch (ex) {Utils.showToast("Cannot open file."); + OpenFilex.open(path); + } catch (ex) { + Utils.showToast("Cannot open file."); } }); }, 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..38fcdef 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,61 @@ 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) async { + if (!isGranted) { + Permission.storage.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); + }, + ), + ); + } + }); + } else { + 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); + } + }); }, ), ); diff --git a/pubspec.yaml b/pubspec.yaml index 8df025d..bd6a839 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -59,11 +59,12 @@ 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 - open_file: ^3.2.1 + month_picker_dialog_2: ^0.5.5 +# open_file: ^3.2.1 + open_filex: ^4.4.0 wifi_iot: ^0.3.18 flutter_html: ^3.0.0-alpha.6 # flutter_barcode_scanner: ^2.0.0 @@ -87,7 +88,7 @@ dependencies: signalr_netcore: ^1.3.3 logging: ^1.0.1 swipe_to: ^1.0.2 - flutter_webrtc: ^0.9.16 + flutter_webrtc: ^0.9.17 camera: ^0.10.3 flutter_local_notifications: ^10.0.0 #firebase_analytics: any @@ -99,7 +100,7 @@ dependencies: #Encryption flutter_des: ^2.1.0 - video_player: ^2.5.1 + video_player: 2.7.0 just_audio: ^0.9.30 # safe_device: ^1.1.2 flutter_layout_grid: ^2.0.1