diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..e69de29b diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro index 94ad242d..b2ea0470 100644 --- a/android/app/proguard-rules.pro +++ b/android/app/proguard-rules.pro @@ -77,4 +77,12 @@ -keep class com.peng.pennavmap.db.** { *; } -keep class com.hiennv.flutter_callkit_incoming.** { *; } --keepattributes Signature, Annotation, InnerClasses, EnclosingMethod \ No newline at end of file +-keepattributes Signature, Annotation, InnerClasses, EnclosingMethod + +-keep class com.peng.pennavmap.** { *; } +-keep class com.peng.bus.** { *; } +# EventBus subscribers (reflection-based) +-keepclassmembers class * { + @com.peng.bus.PISubscribe ; +} +-keep class com.peng.pennavmap.models.bus.BackButtonEventBusData { *; } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 0fb36b85..ba1f830e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -41,6 +41,11 @@ android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" tools:node="remove" /> + + + + + diff --git a/assets/images/svg/appointment_parking_icon.svg b/assets/images/svg/appointment_parking_icon.svg new file mode 100644 index 00000000..0a1c6daa --- /dev/null +++ b/assets/images/svg/appointment_parking_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 27502377..c2d4decf 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -230,6 +230,17 @@ "companyName": "اسم الشركة:", "receiptOn": "الإيصال على:", "expiryDate": "تاريخ الانتهاء:", + "myCards": "بطاقاتي", + "savedCards": "البطاقات المحفوظة", + "addNewCard": "إضافة بطاقة جديدة", + "noSavedCards": "لا توجد بطاقات محفوظة", + "cardEndingWith": "البطاقة المنتهية بـ", + "expiry": "الصلاحية", + "defaultCard": "افتراضي", + "deleteCard": "حذف البطاقة؟", + "deleteCardConfirmation": "هل أنت متأكد من حذف هذه البطاقة المنتهية بـ", + "cardDeletedSuccessfully": "تم حذف البطاقة بنجاح", + "addCardComingSoon": "وظيفة إضافة البطاقة قريباً", "expiryPoints": "منتهي الصلاحية", "expiryOn": "ينتهي في:", "procedureName": "اسم الإجراء:", @@ -1893,6 +1904,8 @@ "transactions": "المعاملات", "pharmacy": "الصيدلية", "visitsOrders": "الزيارات/الطلبات", + "earned": "حصل", + "getParkingQR": "الحصول على رمز الاستجابة السريعة لوقوف السيارات", "cannotOpenThisFile": "لا يمكن فتح هذا الملف", "refundDetails": "تفاصيل الاسترداد", "requestNo": "رقم الطلب.", @@ -1900,5 +1913,8 @@ "advanceNumber": "رقم الدفعة المقدمة", "amountOnly": "المبلغ", "invoiceHistory": "سجل الفواتير", + "refund": "استرداد", + "invoiceHistory": "سجل الفواتير", + "thisInvoiceIsNotEligibleForRefund": "هذه الفاتورة غير مؤهلة للاسترداد", "refund": "استرداد" } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index cc8c3769..70293941 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -227,6 +227,17 @@ "companyName": "Company Name:", "receiptOn": "Receipt on:", "expiryDate": "Expiry Date:", + "myCards": "My Cards", + "savedCards": "Saved Cards", + "addNewCard": "Add New Card", + "noSavedCards": "No saved cards", + "cardEndingWith": "Card ending with", + "expiry": "Expiry", + "defaultCard": "Default", + "deleteCard": "Delete Card?", + "deleteCardConfirmation": "Are you sure you want to delete this card ending in", + "cardDeletedSuccessfully": "Card deleted successfully", + "addCardComingSoon": "Add card functionality coming soon", "expiryPoints": "Expired", "expiryOn": "Expiry on:", "procedureName": "Procedure Name:", @@ -1884,6 +1895,8 @@ "transactions": "Transactions", "pharmacy": "Pharmacy", "visitsOrders": "Visits/Orders", + "earned": "Earned", + "getParkingQR": "Get Parking QR", "cannotOpenThisFile": "Cannot open this file", "thisInvoiceIsNotEligibleForRefund": "This invoice is not eligible for refund", "refundDetails": "Refund Details", diff --git a/ios/Podfile b/ios/Podfile index 4c8bed13..a74c7fb2 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '14.0' +platform :ios, '15.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -54,6 +54,7 @@ post_install do |installer| 'PERMISSION_EVENTS_FULL_ACCESS=1', ## dart: PermissionGroup.reminders 'PERMISSION_REMINDERS=1', + 'PERMISSION_PHOTOS=1', ## dart: PermissionGroup.notification 'PERMISSION_NOTIFICATIONS=1', ] diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 31bbb556..3d10df4e 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -4,6 +4,7 @@ import 'dart:developer'; import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/api/http_client_manager.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart'; @@ -243,10 +244,13 @@ class ApiClientImp implements ApiClient { // Handle body encoding based on isBodyPlainText flag final dynamic requestBody = isBodyPlainText ? body : json.encode(body); - debugPrint("uri: ${Uri.parse(url.trim())}"); - var requestBodyJSON = json.encode(body); - // debugPrint("body: $requestBodyJSON", wrapWidth: 2048); - log("body: $requestBodyJSON"); + + if (kDebugMode) { + debugPrint("uri: ${Uri.parse(url.trim())}"); + var requestBodyJSON = json.encode(body); + debugPrint("body: $requestBodyJSON", wrapWidth: 2048); + // log("body: $requestBodyJSON"); + } http.Response response; try { diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index aa2a5945..80f552a3 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -290,7 +290,7 @@ class ApiConsts { static String googleCloudStorageENTranslationFileBaseURL = "https://storage.googleapis.com/hmg-patientapp-translations"; // ************ static values for Api **************** - static final double appVersionID = 21.2; + static final double appVersionID = 21.3; // static final double appVersionID = 50.7; static final int appChannelId = 3; @@ -1007,6 +1007,8 @@ const DOWNLOAD_INVOICE_PDF = 'Services/Notifications.svc/REST/DownloadInvoiceRep const DOWNLOAD_PHARMACY_INVOICE_PDF = 'Services/Notifications.svc/REST/DownloadInvoiceReport'; +const GET_APPOINTMENT_PARKING_QR = 'Services/outps.svc/rest/pms_getParkingTicketByAppointmentNo'; + class ApiKeyConstants { static final String googleMapsApiKey = 'AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng'; } diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index 37f9c4fd..2e27d633 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -256,6 +256,7 @@ class AppAssets { static const String back_top_nav_icon = '$svgBasePath/back_top_nav_icon.svg'; static const String bluetooth = '$svgBasePath/bluetooth.svg'; static const String calendar_filled_icon = '$svgBasePath/calendar_filled_icon.svg'; + static const String appointment_parking_icon = '$svgBasePath/appointment_parking_icon.svg'; //smartwatch static const String watchActivity = '$svgBasePath/watch_activity.svg'; diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart index 9a4826b0..aae2d03b 100644 --- a/lib/core/app_state.dart +++ b/lib/core/app_state.dart @@ -48,6 +48,11 @@ class AppState { bool isPaytabsEnabled = false; + void setIsPaytabsEnabled(bool value) { + isPaytabsEnabled = value; + // isPaytabsEnabled = false; + } + int? _superUserID; bool isChildLoggedIn = false; bool isGMSAvailable = true; diff --git a/lib/core/utils/date_util.dart b/lib/core/utils/date_util.dart index 5aa48b07..eab4c4f1 100644 --- a/lib/core/utils/date_util.dart +++ b/lib/core/utils/date_util.dart @@ -18,7 +18,7 @@ class DateUtil { final endIndex = date.indexOf(end, startIndex + start.length); return DateTime.fromMillisecondsSinceEpoch(int.parse( date.substring(startIndex + start.length, endIndex), - )); + ), isUtc: true).add(Duration(hours: 3)); } static DateTime convertStringToDateSaudiTimezone(String date, int projectId) { diff --git a/lib/core/utils/push_notification_handler.dart b/lib/core/utils/push_notification_handler.dart index 4b7250ba..e179a1ab 100644 --- a/lib/core/utils/push_notification_handler.dart +++ b/lib/core/utils/push_notification_handler.dart @@ -49,10 +49,10 @@ _incomingCall(Map data) async { log('the value of the _incomingCall remote message is $data'); // Check if there's already a call in progress to prevent duplicates - if (_isCallInProgress && _currentCallId != null) { - log('⚠️ Call already in progress (ID: $_currentCallId), ignoring duplicate notification'); - return; - } + // if (_isCallInProgress && _currentCallId != null) { + // log('⚠️ Call already in progress (ID: $_currentCallId), ignoring duplicate notification'); + // return; + // } String roomID = data['session_id'] ?? ''; String callTypeID = data['AppointmentNo'] ?? ''; diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index ef83cb9f..35532d69 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -771,6 +771,7 @@ class Utils { double height = 24, BoxFit fit = BoxFit.cover, bool applyThemeColor = true, + String? errorAsset, }) { final Color? resolvedColor = iconColor ?? (applyThemeColor && AppColors.isDarkMode ? AppColors.textColor : null); return SvgPicture.asset( @@ -779,6 +780,15 @@ class Utils { width: width, height: height, fit: fit, + errorBuilder: errorAsset != null + ? (context, error, stackTrace) => SvgPicture.asset( + errorAsset, + colorFilter: resolvedColor != null ? ColorFilter.mode(isDisabled ? resolvedColor.withOpacity(0.5) : resolvedColor, BlendMode.srcIn) : null, + width: width, + height: height, + fit: fit, + ) + : null, ); } @@ -1144,4 +1154,30 @@ class Utils { } return result; } + + /// Normalize Arabic text for search by handling Hamza variations and removing diacritics + /// This makes searching more flexible for Arabic text + static String normalizeArabicText(String text) { + if (text.isEmpty) return text; + + String normalized = text; + + // Normalize Hamza variations to a single character 'ا' + normalized = normalized.replaceAll('أ', 'ا'); // Hamza on Alif + normalized = normalized.replaceAll('إ', 'ا'); // Hamza below Alif + normalized = normalized.replaceAll('آ', 'ا'); // Madda on Alif + normalized = normalized.replaceAll('ء', 'ا'); // Hamza alone + normalized = normalized.replaceAll('ؤ', 'و'); // Hamza on Waw + normalized = normalized.replaceAll('ئ', 'ي'); // Hamza on Ya + + // Normalize Alif Maqsura to Ya + normalized = normalized.replaceAll('ى', 'ي'); + + // Remove Arabic diacritics (Tashkeel) + normalized = normalized.replaceAll(RegExp(r'[\u064B-\u065F]'), ''); // Fatha, Damma, Kasra, etc. + normalized = normalized.replaceAll(RegExp(r'[\u0670]'), ''); // Superscript Alif + normalized = normalized.replaceAll(RegExp(r'[\u0640]'), ''); // Tatweel + + return normalized; + } } diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index c0e55a4d..7393a88f 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -1221,6 +1221,7 @@ class AuthenticationViewModel extends ChangeNotifier { _appState.setAuthenticatedUser(activation.list!.first); _appState.setPrivilegeModelList(activation.list!.first.listPrivilege!); _appState.setUserBloodGroup = activation.patientBlodType ?? "N/A"; + _appState.setIsPaytabsEnabled(Utils.havePrivilege(120)); // Refresh privileges after user switching to ensure correct privileges are loaded if (isSwitchUser) { @@ -1409,15 +1410,14 @@ class AuthenticationViewModel extends ChangeNotifier { checkLastLoginStatus(Function() onSuccess) async { Future.delayed(Duration(seconds: 1), () async { if (cacheService.getBool(key: CacheConst.quickLoginEnabled) == null) { - if (_appState.getSelectDeviceByImeiRespModelElement != null && - (_appState.getSelectDeviceByImeiRespModelElement!.logInType == 1 || _appState.getSelectDeviceByImeiRespModelElement!.logInType == 4)) { + final deviceElement = _appState.getSelectDeviceByImeiRespModelElement; + if (deviceElement != null && (deviceElement.logInType == 1 || deviceElement.logInType == 4)) { phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0") ? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "") : _appState.getAuthenticatedUser()!.mobileNumber)!; nationalIdController.text = _appState.getAuthenticatedUser()!.patientIdentificationNo!; onSuccess(); - } else if ((loginTypeEnum == LoginTypeEnum.sms || loginTypeEnum == LoginTypeEnum.whatsapp && _appState.getSelectDeviceByImeiRespModelElement == null) && - _appState.getAuthenticatedUser() != null) { + } else if ((loginTypeEnum == LoginTypeEnum.sms || loginTypeEnum == LoginTypeEnum.whatsapp && deviceElement == null) && _appState.getAuthenticatedUser() != null) { phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0") ? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "") : _appState.getAuthenticatedUser()!.mobileNumber)!; @@ -1668,14 +1668,26 @@ class AuthenticationViewModel extends ChangeNotifier { patientId: _appState.getSelectDeviceByImeiRespModelElement!.patientId!, patientType: _appState.getSelectDeviceByImeiRespModelElement!.patientType, patientOutSa: _appState.getSelectDeviceByImeiRespModelElement!.outSa == true ? 1 : 0, + projectOutSa: _appState.getSelectDeviceByImeiRespModelElement!.outSa, loginType: loginType, languageId: _appState.getLanguageID(), latitude: _appState.userLat, longitude: _appState.userLong, mobileNo: _appState.getSelectDeviceByImeiRespModelElement!.mobile!, patientMobileNumber: int.parse(_appState.getSelectDeviceByImeiRespModelElement!.mobile!), - nationalId: _appState.getSelectDeviceByImeiRespModelElement!.identificationNo) + nationalId: _appState.getSelectDeviceByImeiRespModelElement!.identificationNo, + zipCode: _appState.getSelectDeviceByImeiRespModelElement!.isOther == true + ? "0" + : _appState.getSelectDeviceByImeiRespModelElement!.outSa == true + ? CountryEnum.unitedArabEmirates.countryCode + : CountryEnum.saudiArabia.countryCode, + isRegister: false, + logInTokenId: "", + searchType: 2, + patientIdentificationId: "0", + otpSendType: loginType) .toJson()); + resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async { if (apiResponse.messageStatus == 1) { dynamic deviceInfo = apiResponse.data['List_MobileLoginInfo']; @@ -1752,6 +1764,7 @@ class AuthenticationViewModel extends ChangeNotifier { projectDetailListModel.add(ProjectDetailListModel.fromJson(v)); }); _appState.setProjectsDetailList(projectDetailListModel); + _appState.setIsPaytabsEnabled(Utils.havePrivilege(120)); } }, ); diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index e6cfa6c2..200b51de 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -372,7 +372,15 @@ class BookAppointmentsViewModel extends ChangeNotifier { _filteredClinicsList = List.from(clinicsList); showSortFilterButtons = false; } else { - _filteredClinicsList = clinicsList.where((clinic) => clinic.clinicDescription?.toLowerCase().contains(query!.toLowerCase()) ?? false).toList(); + // Normalize the search query for better Arabic text matching + String normalizedQuery = Utils.normalizeArabicText(query.toLowerCase()); + + _filteredClinicsList = clinicsList.where((clinic) { + String clinicName = clinic.clinicDescription?.toLowerCase() ?? ''; + String normalizedClinicName = Utils.normalizeArabicText(clinicName); + return normalizedClinicName.contains(normalizedQuery); + }).toList(); + showSortFilterButtons = query.length >= 3; } notifyListeners(); @@ -735,7 +743,9 @@ class BookAppointmentsViewModel extends ChangeNotifier { result.fold( (failure) async { - onError?.call(selectedClinic.clinicID == 23 ? failure.message : LocaleKeys.noDoctorFound.tr()); + if (onError != null) { + onError.call(selectedClinic.clinicID == 23 ? failure.message : LocaleKeys.noDoctorFound.tr()); + } }, (apiResponse) async { if (apiResponse.messageStatus == 2) { diff --git a/lib/features/emergency_services/emergency_services_view_model.dart b/lib/features/emergency_services/emergency_services_view_model.dart index 83d2a564..d581d0f5 100644 --- a/lib/features/emergency_services/emergency_services_view_model.dart +++ b/lib/features/emergency_services/emergency_services_view_model.dart @@ -117,6 +117,13 @@ class EmergencyServicesViewModel extends ChangeNotifier { bool historyLoading = false; OrderDislpay currentlyDisplayedOrder = OrderDislpay.ALL; + bool isAdvanceERBalanceNeedToBeLoaded = true; + + setIsAdvanceERBalanceNeedToBeLoaded(bool value) { + isAdvanceERBalanceNeedToBeLoaded = value; + notifyListeners(); + } + setSelectedRRTProcedure(RRTProceduresResponseModel procedure) { selectedRRTProcedure = procedure; notifyListeners(); @@ -331,6 +338,8 @@ class EmergencyServicesViewModel extends ChangeNotifier { } Future checkPatientERAdvanceBalance({Function(dynamic)? onSuccess, Function(String)? onError}) async { + if (!isAdvanceERBalanceNeedToBeLoaded) return; + final result = await emergencyServicesRepo.checkPatientERAdvanceBalance(); result.fold( @@ -338,6 +347,7 @@ class EmergencyServicesViewModel extends ChangeNotifier { (failure) { patientHasAdvanceERBalance = false; isERBookAppointment = true; + isAdvanceERBalanceNeedToBeLoaded = true; if (onSuccess != null) { onSuccess(failure.message); } @@ -347,9 +357,11 @@ class EmergencyServicesViewModel extends ChangeNotifier { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); patientHasAdvanceERBalance = false; isERBookAppointment = true; + isAdvanceERBalanceNeedToBeLoaded = true; } else if (apiResponse.messageStatus == 1) { patientHasAdvanceERBalance = apiResponse.data; isERBookAppointment = !patientHasAdvanceERBalance; + isAdvanceERBalanceNeedToBeLoaded = false; notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); diff --git a/lib/features/habib_wallet/habib_wallet_view_model.dart b/lib/features/habib_wallet/habib_wallet_view_model.dart index 00ba55cc..76886045 100644 --- a/lib/features/habib_wallet/habib_wallet_view_model.dart +++ b/lib/features/habib_wallet/habib_wallet_view_model.dart @@ -30,6 +30,7 @@ class HabibWalletViewModel extends ChangeNotifier { num habibWalletAmount = 0; num walletRechargeAmount = 0; String notesText = ""; + bool isWalletAmountToBeLoaded = true; bool isBottomSheetContentLoading = false; @@ -324,10 +325,12 @@ class HabibWalletViewModel extends ChangeNotifier { } initHabibWalletProvider() { - isWalletAmountLoading = true; + if (isWalletAmountToBeLoaded) { + isWalletAmountLoading = true; + habibWalletAmount = 0; + isLakumAccountInfoLoading = true; + } isBottomSheetContentLoading = false; - isLakumAccountInfoLoading = true; - habibWalletAmount = 0; walletRechargeAmount = 0; selectedRechargeType = 1; advancePaymentHospitals.clear(); @@ -392,6 +395,11 @@ class HabibWalletViewModel extends ChangeNotifier { notifyListeners(); } + setIsWalletAmountToBeLoaded(bool value) { + isWalletAmountToBeLoaded = value; + notifyListeners(); + } + String getSelectedRechargeTypeValue() { switch (selectedRechargeType) { case 1: @@ -406,6 +414,9 @@ class HabibWalletViewModel extends ChangeNotifier { } Future getPatientBalanceAmount({Function(dynamic)? onSuccess, Function(String)? onError}) async { + if (!isWalletAmountToBeLoaded) { + return; + } isWalletAmountLoading = true; notifyListeners(); @@ -425,6 +436,7 @@ class HabibWalletViewModel extends ChangeNotifier { habibWalletBalanceList.removeWhere((element) => element.patientAdvanceBalanceAmount == 0); isWalletAmountLoading = false; + isWalletAmountToBeLoaded = false; notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); @@ -462,12 +474,7 @@ class HabibWalletViewModel extends ChangeNotifier { Function(dynamic)? onSuccess, Function(String)? onError}) async { final result = await habibWalletRepo.HISCreateAdvancePayment( - paymentMethodName: paymentMethodName, - paidAmount: paidAmount, - paymentReference: paymentReference, - patientID: patientID, - projectID: projectID, - depositorName: depositorName); + paymentMethodName: paymentMethodName, paidAmount: paidAmount, paymentReference: paymentReference, patientID: patientID, projectID: projectID, depositorName: depositorName); result.fold( // (failure) async => await errorHandlerService.handleError(failure: failure), @@ -489,8 +496,7 @@ class HabibWalletViewModel extends ChangeNotifier { ); } - Future addAdvanceNumberRequest( - {required String advanceNumber, required String paymentReference, Function(dynamic)? onSuccess, Function(String)? onError}) async { + Future addAdvanceNumberRequest({required String advanceNumber, required String paymentReference, Function(dynamic)? onSuccess, Function(String)? onError}) async { final result = await habibWalletRepo.addAdvanceNumberRequest(advanceNumber: advanceNumber, paymentReference: paymentReference); result.fold( @@ -534,12 +540,15 @@ class HabibWalletViewModel extends ChangeNotifier { } Future getLakumAccountInformation({Function(dynamic)? onSuccess, Function(String)? onError}) async { + if (!isWalletAmountToBeLoaded) { + return; + } + isLakumAccountInfoLoading = true; lakumAccountInfo = LakumInquiryInformationResponseModel(); notifyListeners(); - final result = await habibWalletRepo.getLakumAccountInformation( - identificationNumber: getIt.get().getAuthenticatedUser()!.patientIdentificationNo ?? ""); + final result = await habibWalletRepo.getLakumAccountInformation(identificationNumber: getIt.get().getAuthenticatedUser()!.patientIdentificationNo ?? ""); result.fold( (failure) async => await errorHandlerService.handleError(failure: failure), diff --git a/lib/features/habib_wallet/models/lakum_inquiry_information_response_model.dart b/lib/features/habib_wallet/models/lakum_inquiry_information_response_model.dart index 621434fe..1e49ef61 100644 --- a/lib/features/habib_wallet/models/lakum_inquiry_information_response_model.dart +++ b/lib/features/habib_wallet/models/lakum_inquiry_information_response_model.dart @@ -99,7 +99,7 @@ class LakumInquiryInformationResponseModel { memberName = json['MemberName']; memberUniversalId = json['MemberUniversalId']; mobileNumber = json['MobileNumber']; - pointsBalance = json['PointsBalance']; + pointsBalance = json['PointsBalance'] ?? 0; pointsBalanceAmount = json['PointsBalanceAmount']; pointsWillBeExpired = json['PointsWillBeExpired']; prefLang = json['PrefLang']; diff --git a/lib/features/my_appointments/models/resp_models/appointment_parking_QR_response_model.dart b/lib/features/my_appointments/models/resp_models/appointment_parking_QR_response_model.dart new file mode 100644 index 00000000..a0a2c4d9 --- /dev/null +++ b/lib/features/my_appointments/models/resp_models/appointment_parking_QR_response_model.dart @@ -0,0 +1,84 @@ +class AppointmentParkingQRResponseModel { + String? admissionDate; + int? admissionNo; + String? appointmentDate; + int? appointmentNo; + int? createdBy; + String? createdOn; + String? gUID; + String? invoiceDate; + int? invoiceNo; + bool? isActive; + bool? isOutInPatient; + int? isPatient; + bool? isUsed; + int? patientID; + int? projectID; + dynamic ticketBase64; + int? ticketID; + String? ticketURL; + + AppointmentParkingQRResponseModel( + {this.admissionDate, + this.admissionNo, + this.appointmentDate, + this.appointmentNo, + this.createdBy, + this.createdOn, + this.gUID, + this.invoiceDate, + this.invoiceNo, + this.isActive, + this.isOutInPatient, + this.isPatient, + this.isUsed, + this.patientID, + this.projectID, + this.ticketBase64, + this.ticketID, + this.ticketURL}); + + AppointmentParkingQRResponseModel.fromJson(Map json) { + admissionDate = json['AdmissionDate']; + admissionNo = json['AdmissionNo']; + appointmentDate = json['AppointmentDate']; + appointmentNo = json['AppointmentNo']; + createdBy = json['CreatedBy']; + createdOn = json['CreatedOn']; + gUID = json['GUID']; + invoiceDate = json['InvoiceDate']; + invoiceNo = json['InvoiceNo']; + isActive = json['IsActive']; + isOutInPatient = json['IsOutInPatient']; + isPatient = json['IsPatient']; + isUsed = json['IsUsed']; + patientID = json['PatientID']; + projectID = json['ProjectID']; + ticketBase64 = json['TicketBase64'].cast(); + ticketID = json['TicketID']; + ticketURL = json['TicketURL']; + } + + Map toJson() { + final Map data = new Map(); + data['AdmissionDate'] = this.admissionDate; + data['AdmissionNo'] = this.admissionNo; + data['AppointmentDate'] = this.appointmentDate; + data['AppointmentNo'] = this.appointmentNo; + data['CreatedBy'] = this.createdBy; + data['CreatedOn'] = this.createdOn; + data['GUID'] = this.gUID; + data['InvoiceDate'] = this.invoiceDate; + data['InvoiceNo'] = this.invoiceNo; + data['IsActive'] = this.isActive; + data['IsOutInPatient'] = this.isOutInPatient; + data['IsPatient'] = this.isPatient; + data['IsUsed'] = this.isUsed; + data['PatientID'] = this.patientID; + data['ProjectID'] = this.projectID; + data['TicketBase64'] = this.ticketBase64; + data['TicketID'] = this.ticketID; + data['TicketURL'] = this.ticketURL; + return data; + } +} diff --git a/lib/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart b/lib/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart index 420c0482..d9a71d9f 100644 --- a/lib/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart +++ b/lib/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart @@ -76,6 +76,7 @@ class PatientAppointmentHistoryResponseModel { num? patientTaxAmount; String? doctorNationalityFlagURL; bool? isClinicReBookingAllowed; + bool? isParkingAvailable; PatientAppointmentHistoryResponseModel({ this.setupID, @@ -154,6 +155,7 @@ class PatientAppointmentHistoryResponseModel { this.patientTaxAmount, this.doctorNationalityFlagURL, this.isClinicReBookingAllowed, + this.isParkingAvailable, }); PatientAppointmentHistoryResponseModel.fromJson(Map json) { @@ -244,6 +246,7 @@ class PatientAppointmentHistoryResponseModel { patientTaxAmount = json['PatientTaxAmount']; doctorNationalityFlagURL = json['DoctorNationalityFlagURL']; isClinicReBookingAllowed = json['IsClinicReBookingAllowed']; + isParkingAvailable = json['IsParkingAvailable']; } Map toJson() { @@ -317,6 +320,7 @@ class PatientAppointmentHistoryResponseModel { data['SMSButtonVisable'] = this.sMSButtonVisable; data['ServiceID'] = this.serviceID; data['IsClinicReBookingAllowed'] = this.isClinicReBookingAllowed; + data['IsParkingAvailable'] = this.isParkingAvailable; return data; } } diff --git a/lib/features/my_appointments/my_appointments_repo.dart b/lib/features/my_appointments/my_appointments_repo.dart index 553491e1..d7a025fc 100644 --- a/lib/features/my_appointments/my_appointments_repo.dart +++ b/lib/features/my_appointments/my_appointments_repo.dart @@ -9,6 +9,7 @@ import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_favorite_doctors_list.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/appointment_parking_QR_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/appointment_rated_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/rate_appointment_resp_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ask_doctor_request_type_response_model.dart'; @@ -92,6 +93,8 @@ abstract class MyAppointmentsRepo { }); Future>>> getAppointmentInvoice(num appointmentNum); + + Future>> getParkingQR(num appointmentNum, int projectID); } class MyAppointmentsRepoImp implements MyAppointmentsRepo { @@ -1183,4 +1186,40 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>> getParkingQR(num appointmentNum, int projectID) async { + Map mapDevice = {"AppointmentNo": appointmentNum, "ProjectID": projectID}; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + GET_APPOINTMENT_PARKING_QR, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + AppointmentParkingQRResponseModel appointmentParkingQRResponseModel = AppointmentParkingQRResponseModel.fromJson(response['ParkingTicketsForAppointment'][0]); + + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: appointmentParkingQRResponseModel, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index 0ad21586..5aae8211 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -10,6 +10,7 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_favorite_doctors_list.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/appointemnet_filters.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/reminder_type.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/appointment_parking_QR_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/appointment_rated_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ask_doctor_request_type_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart'; @@ -100,6 +101,8 @@ class MyAppointmentsViewModel extends ChangeNotifier { AppointmentRatedResponseModel? appointmentRatedResponseModel; bool isAppointmentRatedResponseLoading = false; + AppointmentParkingQRResponseModel? appointmentParkingQRResponseModel; + MyAppointmentsViewModel({required this.myAppointmentsRepo, required this.errorHandlerService, required this.appState}); void onTabChange(int index) { @@ -722,6 +725,25 @@ class MyAppointmentsViewModel extends ChangeNotifier { ); } + Future getParkingQR(num appointmentNum, int projectID, {Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await myAppointmentsRepo.getParkingQR(appointmentNum, projectID); + + result.fold( + (failure) async { + if (onError != null) { + onError(failure.message); + } + }, + (apiResponse) { + appointmentParkingQRResponseModel = apiResponse.data!; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + }, + ); + } + // Method to force refresh favorite doctors list void refreshFavouriteDoctors() { isFavouriteDoctorsDataFetched = false; diff --git a/lib/features/paytabs/models/paytabs_transaction_response_model.dart b/lib/features/paytabs/models/paytabs_transaction_response_model.dart index 57d23349..85760132 100644 --- a/lib/features/paytabs/models/paytabs_transaction_response_model.dart +++ b/lib/features/paytabs/models/paytabs_transaction_response_model.dart @@ -1,5 +1,5 @@ class PaytabsTransactionResponseModel { - int? tranTotal; + dynamic tranTotal; String? transactionReference; String? cartCurrency; String? cartDescription; @@ -13,10 +13,10 @@ class PaytabsTransactionResponseModel { bool? isAuthorized; String? trace; dynamic cartAmount; - int? merchantId; - int? profileId; + dynamic merchantId; + dynamic profileId; bool? isProcessed; - int? serviceId; + dynamic serviceId; PaymentInfo? paymentInfo; bool? isSuccess; @@ -98,8 +98,8 @@ class PaytabsTransactionResponseModel { class PaymentInfo { String? cardScheme; String? cardType; - int? expiryMonth; - int? expiryYear; + dynamic expiryMonth; + dynamic expiryYear; String? paymentDescription; String? paymentMethod; diff --git a/lib/features/paytabs/paytabs_view_model.dart b/lib/features/paytabs/paytabs_view_model.dart index 0e0273e6..8615ca22 100644 --- a/lib/features/paytabs/paytabs_view_model.dart +++ b/lib/features/paytabs/paytabs_view_model.dart @@ -3,6 +3,7 @@ import 'package:flutter_paytabs_bridge/BaseBillingShippingInfo.dart'; import 'package:flutter_paytabs_bridge/IOSThemeConfiguration.dart'; import 'package:flutter_paytabs_bridge/PaymentSdkConfigurationDetails.dart'; import 'package:flutter_paytabs_bridge/PaymentSdkLocale.dart'; +import 'package:flutter_paytabs_bridge/PaymentSdkTokenFormat.dart'; import 'package:flutter_paytabs_bridge/PaymentSdkTokeniseType.dart'; import 'package:flutter_paytabs_bridge/PaymentSdkTransactionType.dart'; import 'package:flutter_paytabs_bridge/flutter_paytabs_bridge.dart'; @@ -21,7 +22,7 @@ class PayTabsViewModel extends ChangeNotifier { required this.appState, }); - setPaymentConfiguration(String paymentDescription, num amount) { + setPaymentConfiguration(String paymentDescription, num amount, {bool isAddCard = false}) { String cartID = DateTime.now().millisecondsSinceEpoch.toString(); paymentConfiguration = PaymentSdkConfigurationDetails( profileId: PaymentSdkDefaultConfig.profileID, @@ -62,7 +63,8 @@ class PayTabsViewModel extends ChangeNotifier { "12626"), alternativePaymentMethods: [], linkBillingNameWithCardHolderName: true, - simplifyApplePayValidation: true); + simplifyApplePayValidation: true, + ); paymentConfiguration.iOSThemeConfigurations = IOSThemeConfigurations( // logoImage: "assets/images/png/hmg_logo.png", @@ -84,17 +86,24 @@ class PayTabsViewModel extends ChangeNotifier { placeholderColorDark: "2E3039", buttonColor: "ED1C2B", buttonColorDark: "ED1C2B", - // buttonFont: "Poppins", - ); - paymentConfiguration.tokeniseType = PaymentSdkTokeniseType.MERCHANT_MANDATORY; + inputsCornerRadius: 12 + // buttonFont: "Poppins", + ); + paymentConfiguration.tokeniseType = PaymentSdkTokeniseType.USER_OPTIONAL_DEFAULT_ON; + paymentConfiguration.tokenFormat = PaymentSdkTokenFormat.Hex32Format; notifyListeners(); } - startApplePayPayment({Function(PaytabsTransactionResponseModel)? onSuccess, Function(String)? onError}) { - FlutterPaytabsBridge.startApplePayPayment(paymentConfiguration, (event) { + startApplePayPayment({Function(PaytabsTransactionResponseModel)? onSuccess, Function(String)? onError}) async { + bool _callbackFired = false; // prevent duplicate callback invocations + + await FlutterPaytabsBridge.startApplePayPayment(paymentConfiguration, (event) { + if (_callbackFired) return; // ignore any subsequent callback calls + final transactionDetails = event["data"] as Map?; if (event["status"] == "success") { + _callbackFired = true; // Cast Map to Map final data = Map.from(event["data"] as Map); paytabsTransactionResponseModel = PaytabsTransactionResponseModel.fromJson(data); @@ -110,11 +119,13 @@ class PayTabsViewModel extends ChangeNotifier { onError!(errorMessage); } } else if (event["status"] == "error") { + _callbackFired = true; final errorMessage = event["message"] ?? "An error occurred"; debugPrint("Error occurred in transaction: $errorMessage"); debugPrint("Full error event: $event"); onError!(errorMessage); } else if (event["status"] == "event") { + // Intermediate SDK events – do NOT mark _callbackFired, just log final eventMessage = event["message"] ?? "Event occurred"; debugPrint("Event occurred: $eventMessage"); } @@ -122,9 +133,14 @@ class PayTabsViewModel extends ChangeNotifier { } startCardPayment({Function(PaytabsTransactionResponseModel)? onSuccess, Function(String)? onError}) { + bool _callbackFired = false; // prevent duplicate callback invocations + FlutterPaytabsBridge.startCardPayment(paymentConfiguration, (event) { + if (_callbackFired) return; // ignore any subsequent callback calls + final transactionDetails = event["data"] as Map?; if (event["status"] == "success") { + _callbackFired = true; // Cast Map to Map final data = Map.from(event["data"] as Map); paytabsTransactionResponseModel = PaytabsTransactionResponseModel.fromJson(data); @@ -140,11 +156,13 @@ class PayTabsViewModel extends ChangeNotifier { onError!(errorMessage); } } else if (event["status"] == "error") { + _callbackFired = true; final errorMessage = event["message"] ?? "An error occurred"; debugPrint("Error occurred in transaction: $errorMessage"); debugPrint("Full error event: $event"); onError!(errorMessage); } else if (event["status"] == "event") { + // Intermediate SDK events – do NOT mark _callbackFired, just log final eventMessage = event["message"] ?? "Event occurred"; debugPrint("Event occurred: $eventMessage"); } diff --git a/lib/features/prescriptions/prescriptions_view_model.dart b/lib/features/prescriptions/prescriptions_view_model.dart index 475368f0..bfb83a2c 100644 --- a/lib/features/prescriptions/prescriptions_view_model.dart +++ b/lib/features/prescriptions/prescriptions_view_model.dart @@ -323,7 +323,7 @@ class PrescriptionsViewModel extends ChangeNotifier { isGmsAvailable: getIt.get().isGMSAvailable, ), direction: AxisDirection.down), - ); + ) ?? false; print("Location Selected: $result"); if (result) { LocationViewModel locationViewModel = getIt.get(); diff --git a/lib/features/todo_section/todo_section_view_model.dart b/lib/features/todo_section/todo_section_view_model.dart index 131774d6..7d1fb88a 100644 --- a/lib/features/todo_section/todo_section_view_model.dart +++ b/lib/features/todo_section/todo_section_view_model.dart @@ -13,13 +13,13 @@ class TodoSectionViewModel extends ChangeNotifier { String? notificationsCount = "0"; initializeTodoSectionViewModel() async { - // if (isAncillaryOrdersNeedReloading) { - patientAncillaryOrdersList.clear(); - isAncillaryOrdersLoading = true; + if (isAncillaryOrdersNeedReloading) { + patientAncillaryOrdersList.clear(); + isAncillaryOrdersLoading = true; isAncillaryDetailsProceduresLoading = true; notificationsCount = "0"; getPatientOnlineAncillaryOrderList(); - // } + } getPatientDashboard(); } @@ -67,9 +67,9 @@ class TodoSectionViewModel extends ChangeNotifier { } Future getPatientOnlineAncillaryOrderList({Function(dynamic)? onSuccess, Function(String)? onError}) async { - // if (!isAncillaryOrdersNeedReloading) { - // return; - // } + if (!isAncillaryOrdersNeedReloading) { + return; + } patientAncillaryOrdersList.clear(); isAncillaryOrdersLoading = true; diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index b66dcab9..b18de6db 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -231,6 +231,17 @@ abstract class LocaleKeys { static const companyName = 'companyName'; static const receiptOn = 'receiptOn'; static const expiryDate = 'expiryDate'; + static const myCards = 'myCards'; + static const savedCards = 'savedCards'; + static const addNewCard = 'addNewCard'; + static const noSavedCards = 'noSavedCards'; + static const cardEndingWith = 'cardEndingWith'; + static const expiry = 'expiry'; + static const defaultCard = 'defaultCard'; + static const deleteCard = 'deleteCard'; + static const deleteCardConfirmation = 'deleteCardConfirmation'; + static const cardDeletedSuccessfully = 'cardDeletedSuccessfully'; + static const addCardComingSoon = 'addCardComingSoon'; static const expiryPoints = 'expiryPoints'; static const expiryOn = 'expiryOn'; static const procedureName = 'procedureName'; @@ -1877,6 +1888,8 @@ abstract class LocaleKeys { static const transactions = 'transactions'; static const pharmacy = 'pharmacy'; static const visitsOrders = 'visitsOrders'; + static const earned = 'earned'; + static const getParkingQR = 'getParkingQR'; static const cannotOpenThisFile = 'cannotOpenThisFile'; static const refundDetails = 'refundDetails'; static const requestNo = 'requestNo'; @@ -1885,5 +1898,4 @@ abstract class LocaleKeys { static const amountOnly = 'amountOnly'; static const invoiceHistory = 'invoiceHistory'; static const refund = 'refund'; - } diff --git a/lib/main.dart b/lib/main.dart index ae7f7036..0119cd1c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -96,7 +96,7 @@ Future callAppStateInitializations() async { PlatformDispatcher.instance.onError = (error, stack) { if (!kDebugMode) { FirebaseCrashlytics.instance.recordError(error, stack, - fatal: true, + fatal: false, printDetails: true, reason: "${appState.isAuthenticated ? "Authenticated User ID: ${appState.getAuthenticatedUser()!.patientId} - ${appState.getAuthenticatedUser()!.mobileNumber}" : "Unauthenticated User"} - Uncaught asynchronous error"); diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index 2ec8f568..ddf9ec86 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -874,6 +874,35 @@ class _AppointmentDetailsPageState extends State { } }) : SizedBox.shrink(), + // (widget.patientAppointmentHistoryResponseModel.isParkingAvailable ?? false) + // ? MedicalFileCard( + // label: LocaleKeys.getParkingQR.tr(context: context), + // textColor: AppColors.blackColor, + // backgroundColor: AppColors.whiteColor, + // svgIcon: AppAssets.appointment_parking_icon, + // isLargeText: true, + // iconSize: 36.w, + // ).onPress(() { + // LoaderBottomSheet.showLoader(loadingText: LocaleKeys.loadingText.tr(context: context)); + // myAppointmentsViewModel.getParkingQR( + // widget.patientAppointmentHistoryResponseModel.appointmentNo, + // widget.patientAppointmentHistoryResponseModel.projectID, + // onSuccess: (val) { + // LoaderBottomSheet.hideLoader(); + // }, + // onError: (err) { + // LoaderBottomSheet.hideLoader(); + // showCommonBottomSheetWithoutHeight( + // context, + // child: Utils.getErrorWidget(loadingText: err), + // callBackFunc: () {}, + // isFullScreen: false, + // isCloseButtonVisible: true, + // ); + // }, + // ); + // }) + // : SizedBox.shrink(), ], ); }), diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index d909b73a..bae7514c 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -795,6 +795,7 @@ class _AppointmentPaymentPageState extends State { //TODO: Need to pass dynamic params to the Apple Pay instead of static values await payfortViewModel.applePayRequestInsert(applePayInsertRequest: applePayInsertRequest).then((value) { if (appState.isPaytabsEnabled) { + selectedPaymentMethod = "VISA"; LoaderBottomSheet.hideLoader(); paytabsViewModel.setPaymentConfiguration( "Appointment Payment", diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index e17d52dd..03c12c3b 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -639,7 +639,9 @@ class _AppointmentCardState extends State { ), ); } else { - if (!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) { + if (!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) && + widget.patientAppointmentHistoryResponseModel.projectID != null && + widget.patientAppointmentHistoryResponseModel.clinicID != null) { widget.bookAppointmentsViewModel.getAppointmentNearestGate( projectID: widget.patientAppointmentHistoryResponseModel.projectID, clinicID: widget.patientAppointmentHistoryResponseModel.clinicID); } diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart index ac346d10..299c3db9 100644 --- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart +++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart @@ -98,21 +98,24 @@ class AppointmentDoctorCard extends StatelessWidget { children: [ Row( children: [ - patientAppointmentHistoryResponseModel.doctorNameObj!.toText16( - isBold: true, - isEnglishOnly: !Utils.isArabicText(patientAppointmentHistoryResponseModel.doctorNameObj ?? ""), - textOverflow: TextOverflow.ellipsis, + Expanded( + child: patientAppointmentHistoryResponseModel.doctorNameObj!.toText16( + isBold: true, + isEnglishOnly: !Utils.isArabicText(patientAppointmentHistoryResponseModel.doctorNameObj ?? ""), + textOverflow: TextOverflow.ellipsis, + maxlines: 2 + ), ), SizedBox(width: 12.w), - (patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null && - patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty) - ? Image.network( - patientAppointmentHistoryResponseModel.doctorNationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png", - width: 20.h, - height: 15.h, - fit: BoxFit.cover, - ) - : SizedBox.shrink(), + // (patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null && + // patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty) + // ? Image.network( + // patientAppointmentHistoryResponseModel.doctorNationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png", + // width: 20.h, + // height: 15.h, + // fit: BoxFit.cover, + // ) + // : SizedBox.shrink(), ], ), SizedBox(height: 8.h), @@ -213,7 +216,7 @@ class AppointmentDoctorCard extends StatelessWidget { AppointmentType.isArrived(patientAppointmentHistoryResponseModel), ), ), - if (timerWidget != null) timerWidget ?? SizedBox() + if (!AppointmentType.isArrived(patientAppointmentHistoryResponseModel) && timerWidget != null) timerWidget ?? SizedBox() ], ), ), diff --git a/lib/presentation/ask_doctor/doctor_response_page.dart b/lib/presentation/ask_doctor/doctor_response_page.dart index 1aed8cfb..922d2fdb 100644 --- a/lib/presentation/ask_doctor/doctor_response_page.dart +++ b/lib/presentation/ask_doctor/doctor_response_page.dart @@ -133,7 +133,7 @@ class _DoctorResponsePageState extends State { ).onPress(() { showCommonBottomSheetWithoutHeight( context, - title: askDoctorVM.doctorResponsesList[index].requestTypeDescription!, + title: askDoctorVM.doctorResponsesList[index].requestTypeDescription ?? "", child: DoctorResponseTransactions(doctorResponseModel: askDoctorVM.doctorResponsesList[index],), callBackFunc: () {}, isFullScreen: false, diff --git a/lib/presentation/authentication/register.dart b/lib/presentation/authentication/register.dart index c69b1d2b..e77038a5 100644 --- a/lib/presentation/authentication/register.dart +++ b/lib/presentation/authentication/register.dart @@ -52,7 +52,7 @@ class _RegisterNew extends State { // Clear errors when leaving register screen // Use post frame callback to avoid calling notifyListeners during dispose WidgetsBinding.instance.addPostFrameCallback((_) { - final authVm = context.read(); + final authVm = getIt.get(); authVm.clearNationalIdError(); authVm.clearDobError(); authVm.clearPhoneNumberError(); diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart index 57e17608..e2bb0c17 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart @@ -632,6 +632,7 @@ class _ImmediateLiveCarePaymentPageState extends State paymentReference: transactionData.transactionReference!, appointmentNo: "0", onSuccess: (val) { + emergencyServicesViewModel.setIsAdvanceERBalanceNeedToBeLoaded(true); LoaderBottomSheet.hideLoader(); if (emergencyServicesViewModel.isERBookAppointment) { showCommonBottomSheetWithoutHeight( @@ -228,6 +229,7 @@ class _ErOnlineCheckinPaymentPageState extends State paymentReference: transactionData.transactionReference!, appointmentNo: "0", onSuccess: (val) { + emergencyServicesViewModel.setIsAdvanceERBalanceNeedToBeLoaded(true); LoaderBottomSheet.hideLoader(); if (emergencyServicesViewModel.isERBookAppointment) { showCommonBottomSheetWithoutHeight( @@ -492,6 +494,7 @@ class _ErOnlineCheckinPaymentPageState extends State //TODO: Need to pass dynamic params to the Apple Pay instead of static values await payfortViewModel.applePayRequestInsert(applePayInsertRequest: applePayInsertRequest).then((value) { if (appState.isPaytabsEnabled) { + selectedPaymentMethod = "VISA"; LoaderBottomSheet.hideLoader(); paytabsViewModel.setPaymentConfiguration( "ER Online Check-In Payment", @@ -509,6 +512,7 @@ class _ErOnlineCheckinPaymentPageState extends State paymentReference: transactionData.transactionReference!, appointmentNo: "0", onSuccess: (val) { + emergencyServicesViewModel.setIsAdvanceERBalanceNeedToBeLoaded(true); LoaderBottomSheet.hideLoader(); if (emergencyServicesViewModel.isERBookAppointment) { showCommonBottomSheetWithoutHeight( @@ -589,6 +593,7 @@ class _ErOnlineCheckinPaymentPageState extends State paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!, appointmentNo: "0", onSuccess: (val) { + emergencyServicesViewModel.setIsAdvanceERBalanceNeedToBeLoaded(true); LoaderBottomSheet.hideLoader(); if (emergencyServicesViewModel.isERBookAppointment) { showCommonBottomSheetWithoutHeight( diff --git a/lib/presentation/habib_wallet/habib_wallet_page.dart b/lib/presentation/habib_wallet/habib_wallet_page.dart index 4f056dad..c4ee9356 100644 --- a/lib/presentation/habib_wallet/habib_wallet_page.dart +++ b/lib/presentation/habib_wallet/habib_wallet_page.dart @@ -136,23 +136,23 @@ class _HabibWalletState extends State { }, ), ), - SizedBox(width: 8.w), - Flexible( - child: CustomButton( - height: 40.h, - icon: AppAssets.refundIcon, - iconSize: 24.w, - backgroundColor: AppColors.successColor, - textColor: Colors.white, - text: LocaleKeys.withdraw.tr(context: context), - borderWidth: 0.w, - fontWeight: FontWeight.w600, - borderColor: Colors.transparent, - padding: EdgeInsets.fromLTRB(4, 0, 12, 0), - fontSize: 14.f, - onPressed: () => Navigator.of(context).push(CustomPageRoute(page: WithdrawRequestCreatePage())), - ), - ), + // SizedBox(width: 8.w), + // Flexible( + // child: CustomButton( + // height: 40.h, + // icon: AppAssets.refundIcon, + // iconSize: 24.w, + // backgroundColor: AppColors.successColor, + // textColor: Colors.white, + // text: LocaleKeys.withdraw.tr(context: context), + // borderWidth: 0.w, + // fontWeight: FontWeight.w600, + // borderColor: Colors.transparent, + // padding: EdgeInsets.fromLTRB(4, 0, 12, 0), + // fontSize: 14.f, + // onPressed: () => Navigator.of(context).push(CustomPageRoute(page: WithdrawRequestCreatePage())), + // ), + // ), ], ), SizedBox(height: 24.h), diff --git a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart index 1363025e..dd3c5ff8 100644 --- a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart +++ b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart @@ -127,6 +127,7 @@ class _WalletPaymentConfirmPageState extends State { LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight(getIt.get().navigatorKey.currentContext!, child: Utils.getSuccessWidget(loadingText: "Payment Successful!"), callBackFunc: () { + habibWalletVM.setIsWalletAmountToBeLoaded(true); habibWalletVM.initHabibWalletProvider(); habibWalletVM.getPatientBalanceAmount(); Navigator.of(getIt.get().navigatorKey.currentContext!).pop(); @@ -228,6 +229,7 @@ class _WalletPaymentConfirmPageState extends State { LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight(getIt.get().navigatorKey.currentContext!, child: Utils.getSuccessWidget(loadingText: "Payment Successful!"), callBackFunc: () { + habibWalletVM.setIsWalletAmountToBeLoaded(true); habibWalletVM.initHabibWalletProvider(); habibWalletVM.getPatientBalanceAmount(); Navigator.of(getIt.get().navigatorKey.currentContext!).pop(); @@ -389,6 +391,7 @@ class _WalletPaymentConfirmPageState extends State { //TODO: Need to pass dynamic params to the Apple Pay instead of static values await payfortViewModel.applePayRequestInsert(applePayInsertRequest: applePayInsertRequest).then((value) { if(appState.isPaytabsEnabled) { + selectedPaymentMethod = "VISA"; LoaderBottomSheet.hideLoader(); paytabsViewModel.setPaymentConfiguration("Advance Payment", habibWalletVM.walletRechargeAmount); paytabsViewModel.startApplePayPayment(onSuccess: (PaytabsTransactionResponseModel transactionData) async { @@ -410,8 +413,9 @@ class _WalletPaymentConfirmPageState extends State { LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight(getIt.get().navigatorKey.currentContext!, child: Utils.getSuccessWidget(loadingText: "Payment Successful!"), callBackFunc: () { - habibWalletVM.initHabibWalletProvider(); - habibWalletVM.getPatientBalanceAmount(); + habibWalletVM.setIsWalletAmountToBeLoaded(true); + habibWalletVM.initHabibWalletProvider(); + habibWalletVM.getPatientBalanceAmount(); Navigator.of(getIt.get().navigatorKey.currentContext!).pop(); Navigator.of(getIt.get().navigatorKey.currentContext!).pop(); }, isFullScreen: false, isCloseButtonVisible: true, isAutoDismiss: true); @@ -451,7 +455,7 @@ class _WalletPaymentConfirmPageState extends State { customerName: "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}", // customerEmail: projectViewModel.authenticatedUserObject.user.emailAddress, customerEmail: "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com", - orderDescription: "Appointment Payment", + orderDescription: "Advance Payment", orderAmount: double.parse(habibWalletVM.walletRechargeAmount.toString()), merchantReference: transID, merchantIdentifier: payfortViewModel.payfortProjectDetailsRespModel!.merchantIdentifier, @@ -502,6 +506,7 @@ class _WalletPaymentConfirmPageState extends State { LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight(getIt.get().navigatorKey.currentContext!, child: Utils.getSuccessWidget(loadingText: "Payment Successful!"), callBackFunc: () { + habibWalletVM.setIsWalletAmountToBeLoaded(true); habibWalletVM.initHabibWalletProvider(); habibWalletVM.getPatientBalanceAmount(); Navigator.of(getIt.get().navigatorKey.currentContext!).pop(); diff --git a/lib/presentation/home/lakum_wallet_details.dart b/lib/presentation/home/lakum_wallet_details.dart index 79b71ec0..d2ef15f4 100644 --- a/lib/presentation/home/lakum_wallet_details.dart +++ b/lib/presentation/home/lakum_wallet_details.dart @@ -304,7 +304,7 @@ class LakumWalletDetails extends StatelessWidget { Widget _buildTransactionItem(PointsDetails transaction) { // Determine if points were gained or used - bool isGained = transaction.operationType?.toLowerCase() == 'gain' || transaction.operationType?.toLowerCase() == 'credit' || (transaction.points ?? 0) > 0; + bool isGained = transaction.subTransactionType == 1 || transaction.operationType?.toLowerCase() == 'cr' || (transaction.points ?? 0) > 0; Color transactionColor = isGained ? AppColors.habibPharmacyColor : AppColors.primaryRedColor; String pointsText = "${isGained ? '+' : '-'}${transaction.points?.abs() ?? 0}"; @@ -345,7 +345,7 @@ class LakumWalletDetails extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - (transaction.subTransactionTypeDescription ?? transaction.operationType ?? "Transaction").toText14( + (isGained ? LocaleKeys.earned.tr() : LocaleKeys.consumed.tr()).toText14( isBold: true, maxlines: 2, ), diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 89f738b1..40028315 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -59,6 +59,7 @@ import 'package:hmg_patient_app_new/presentation/todo_section/todo_page.dart'; import 'package:hmg_patient_app_new/presentation/todo_section/widgets/ancillary_orders_list.dart'; import 'package:hmg_patient_app_new/routes/app_routes.dart'; import 'package:hmg_patient_app_new/services/cache_service.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; @@ -156,9 +157,13 @@ class _LandingPageState extends State { appointmentRatingViewModel.getLastRatingAppointment( onSuccess: (response) { if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) { + final lastAppointment = appointmentRatingViewModel.appointmentRatedList.last; + final appointmentNo = lastAppointment.appointmentNo; + final projectID = lastAppointment.projectID; + if (appointmentNo == null || projectID == null) return; appointmentRatingViewModel.getAppointmentDetails( - appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, - appointmentRatingViewModel.appointmentRatedList.last.projectID!, + appointmentNo, + projectID, onSuccess: ((response) { appointmentRatingViewModel.setClinicOrDoctor(false); appointmentRatingViewModel.setTitle(LocaleKeys.rateDoctor.tr(context: context)); @@ -208,11 +213,31 @@ class _LandingPageState extends State { habibWalletVM.getLakumAccountInformation(); // Refresh Ancillary Orders + todoSectionViewModel.setIsAncillaryOrdersNeedReloading(true); todoSectionViewModel.initializeTodoSectionViewModel(); // Refresh Immediate LiveCare Data immediateLiveCareViewModel.initImmediateLiveCare(); immediateLiveCareViewModel.getPatientLiveCareHistory(); + + // appointmentRatingViewModel.getLastRatingAppointment( + // onSuccess: (response) { + // if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) { + // appointmentRatingViewModel.getAppointmentDetails( + // appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, + // appointmentRatingViewModel.appointmentRatedList.last.projectID!, + // onSuccess: ((response) { + // appointmentRatingViewModel.setClinicOrDoctor(false); + // appointmentRatingViewModel.setTitle(LocaleKeys.rateDoctor.tr(context: context)); + // appointmentRatingViewModel.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context)); + // openLastRating(); + // appState.setRatedVisible(true); + // }), + // ); + // } + // }, + // ); + } }, child: SingleChildScrollView( @@ -1218,7 +1243,7 @@ class _LandingPageState extends State { }, backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20), textColor: AppColors.alertColor, - borderColor: AppColors.infoBannerBgColor, + borderColor: AppColors.warningColorYellow.withValues(alpha: 0.01), fontSize: 12.f, fontWeight: FontWeight.w600, borderRadius: 12.r, @@ -1377,7 +1402,7 @@ class _LandingPageState extends State { void showQuickLogin(BuildContext context) { showCommonBottomSheetWithoutHeight( - context, + getIt().navigatorKey.currentContext!, // title: "", isCloseButtonVisible: false, child: StatefulBuilder( diff --git a/lib/presentation/insurance/insurance_approval_details_page.dart b/lib/presentation/insurance/insurance_approval_details_page.dart index c80767eb..e3b684bf 100644 --- a/lib/presentation/insurance/insurance_approval_details_page.dart +++ b/lib/presentation/insurance/insurance_approval_details_page.dart @@ -152,7 +152,7 @@ class InsuranceApprovalDetailsPage extends StatelessWidget { child: insuranceApprovalResponseModel.apporvalDetails != null ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.approvalDetails.toText16(isBold: true), + LocaleKeys.approvalDetails.tr().toText16(isBold: true), ListView.separated( padding: EdgeInsets.only(top: 16.h), shrinkWrap: true, diff --git a/lib/presentation/my_invoices/widgets/invoice_list_card.dart b/lib/presentation/my_invoices/widgets/invoice_list_card.dart index 3f8b635e..1e9aefd8 100644 --- a/lib/presentation/my_invoices/widgets/invoice_list_card.dart +++ b/lib/presentation/my_invoices/widgets/invoice_list_card.dart @@ -211,28 +211,28 @@ class InvoiceListCard extends StatelessWidget { iconSize: 14.h, ), ), - if (getInvoicesListResponseModel.isAllowRefund == true) ...[ - SizedBox(width: 8.w), - Expanded( - child: CustomButton( - text: LocaleKeys.requestRefund.tr(context: context), - onPressed: () { - _handleRefundInvoiceTap(context); - }, - icon: AppAssets.refundIcon, - iconColor: AppColors.caloriesCalculatorColor, - backgroundColor: AppColors.blueColor.withValues(alpha: 0.14), - borderColor: AppColors.primaryRedColor.withValues(alpha: 0.01), - textColor: AppColors.caloriesCalculatorColor, - fontSize: 14.f, - fontWeight: FontWeight.w600, - borderRadius: 12.r, - padding: EdgeInsets.symmetric(horizontal: 10.w), - height: 40.h, - iconSize: 14.h, - ), - ), - ] + // if (getInvoicesListResponseModel.isAllowRefund == true) ...[ + // SizedBox(width: 8.w), + // Expanded( + // child: CustomButton( + // text: LocaleKeys.requestRefund.tr(context: context), + // onPressed: () { + // _handleRefundInvoiceTap(context); + // }, + // icon: AppAssets.refundIcon, + // iconColor: AppColors.caloriesCalculatorColor, + // backgroundColor: AppColors.blueColor.withValues(alpha: 0.14), + // borderColor: AppColors.primaryRedColor.withValues(alpha: 0.01), + // textColor: AppColors.caloriesCalculatorColor, + // fontSize: 14.f, + // fontWeight: FontWeight.w600, + // borderRadius: 12.r, + // padding: EdgeInsets.symmetric(horizontal: 10.w), + // height: 40.h, + // iconSize: 14.h, + // ), + // ), + // ] ], ), ], diff --git a/lib/presentation/profile_settings/widgets/preferred_language_widget.dart b/lib/presentation/profile_settings/widgets/preferred_language_widget.dart index 32dcc57b..d0f85090 100644 --- a/lib/presentation/profile_settings/widgets/preferred_language_widget.dart +++ b/lib/presentation/profile_settings/widgets/preferred_language_widget.dart @@ -1,5 +1,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; @@ -7,6 +8,7 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; @@ -70,7 +72,7 @@ class _PreferredLanguageWidgetState extends State { LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight(context, title: LocaleKeys.success.tr(context: context), child: Utils.getSuccessWidget(loadingText: LocaleKeys.success.tr()), callBackFunc: () async { - Navigator.of(context).pop(); + Navigator.of(GetIt.instance().navigatorKey.currentContext!).pop(); profileSettingsViewModel.getProfileSettings(); }, isFullScreen: false, isAutoDismiss: true); }, diff --git a/lib/presentation/profile_settings/widgets/profile_picture_widget.dart b/lib/presentation/profile_settings/widgets/profile_picture_widget.dart index 06cf3a8f..b5aa89ee 100644 --- a/lib/presentation/profile_settings/widgets/profile_picture_widget.dart +++ b/lib/presentation/profile_settings/widgets/profile_picture_widget.dart @@ -1,4 +1,3 @@ - import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; diff --git a/lib/presentation/profile_settings/widgets/update_email_widget.dart b/lib/presentation/profile_settings/widgets/update_email_widget.dart index 4f13726a..55f848a6 100644 --- a/lib/presentation/profile_settings/widgets/update_email_widget.dart +++ b/lib/presentation/profile_settings/widgets/update_email_widget.dart @@ -1,11 +1,13 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; @@ -130,7 +132,7 @@ class _UpdateEmailDialogState extends State { profileSettingsViewModel!.clearEmailError(); showCommonBottomSheetWithoutHeight(context, title: LocaleKeys.success.tr(context: context), child: Utils.getSuccessWidget(loadingText: LocaleKeys.success.tr()), callBackFunc: () async { - Navigator.of(context).pop(); + Navigator.of(getIt().navigatorKey.currentContext!).pop(); profileSettingsViewModel!.getProfileSettings(); }, isFullScreen: false, isAutoDismiss: true); }, diff --git a/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart b/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart index e1faadca..f1a34565 100644 --- a/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart +++ b/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart @@ -130,9 +130,11 @@ class _UpdateEmergencyContactDialogState extends State().navigatorKey.currentContext; + if (navContext == null) return; + showCommonBottomSheetWithoutHeight(navContext, title: LocaleKeys.success.tr(context: navContext), child: Utils.getSuccessWidget(loadingText: LocaleKeys.success.tr()), callBackFunc: () async { - Navigator.of(getIt.get().navigatorKey.currentContext!).pop(); + Navigator.of(navContext).pop(); profileSettingsViewModel!.getProfileSettings(); }, isFullScreen: false, isAutoDismiss: true); }, diff --git a/lib/presentation/symptoms_checker/triage_screen.dart b/lib/presentation/symptoms_checker/triage_screen.dart index 1d5b18ef..12b17353 100644 --- a/lib/presentation/symptoms_checker/triage_screen.dart +++ b/lib/presentation/symptoms_checker/triage_screen.dart @@ -80,7 +80,8 @@ class _TriagePageState extends State { // Case 2: Should stop flag is true OR // Case 3: Probability >= 70% OR // Case 4: 30 or more questions answered - if (viewModel.shouldStopTriage || highestProbability >= 70.0 || viewModel.triageQuestionCount >= 30) { + // if (viewModel.shouldStopTriage || highestProbability >= 70.0 || viewModel.triageQuestionCount >= 30) { + if (highestProbability >= 70.0 || viewModel.triageQuestionCount >= 30 || viewModel.shouldStopTriage) { // Navigate to results/possible conditions screen context.navigateWithName(AppRoutes.possibleConditionsPage); return; diff --git a/lib/presentation/todo_section/ancillary_order_payment_page.dart b/lib/presentation/todo_section/ancillary_order_payment_page.dart index 24ed9fc5..e48adeeb 100644 --- a/lib/presentation/todo_section/ancillary_order_payment_page.dart +++ b/lib/presentation/todo_section/ancillary_order_payment_page.dart @@ -632,6 +632,7 @@ class _AncillaryOrderPaymentPageState extends State { } // Only proceed with Apple Pay if insert was successful if(appState.isPaytabsEnabled) { + selectedPaymentMethod = "VISA"; LoaderBottomSheet.hideLoader(); paytabsViewModel.setPaymentConfiguration( "Ancillary Orders Payment", diff --git a/lib/presentation/todo_section/ancillary_procedures_details_page.dart b/lib/presentation/todo_section/ancillary_procedures_details_page.dart index 41428a6e..65e1bdaf 100644 --- a/lib/presentation/todo_section/ancillary_procedures_details_page.dart +++ b/lib/presentation/todo_section/ancillary_procedures_details_page.dart @@ -66,6 +66,7 @@ class _AncillaryOrderDetailsListState extends State { } void _autoSelectEligibleProcedures() { + if (!mounted) return; selectedProcedures.clear(); if (todoSectionViewModel.patientAncillaryOrderProceduresList.isNotEmpty) { final procedures = todoSectionViewModel.patientAncillaryOrderProceduresList[0].ancillaryOrderProcDetailsList; @@ -77,7 +78,7 @@ class _AncillaryOrderDetailsListState extends State { } } } - setState(() {}); + if (mounted) setState(() {}); } bool _isProcedureDisabled(AncillaryOrderProcDetail procedure) { diff --git a/lib/splashPage.dart b/lib/splashPage.dart index 22b9f979..a93f75a1 100644 --- a/lib/splashPage.dart +++ b/lib/splashPage.dart @@ -1,5 +1,7 @@ import 'dart:async'; +import 'package:clarity_flutter/clarity_flutter.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_callkit_incoming/entities/call_event.dart'; import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart'; @@ -55,7 +57,13 @@ class _SplashScreenState extends State { await notificationService.initialize(onNotificationClick: (payload) { // Handle notification click here }); + ZoomService().initializeZoomSDK(); + + // if (!kDebugMode) { + // _initializeClarity(); + // } + if (isAppOpenedFromCall) { navigateToTeleConsult(); } else { @@ -76,6 +84,15 @@ class _SplashScreenState extends State { // zoom.initSdk(initConfig); } + void _initializeClarity() { + final config = ClarityConfig( + projectId: "x0qgorlez4", // You can find it on the Settings page of Clarity dashboard. + logLevel: LogLevel.Verbose, // Optional: Set the log level (Verbose, Debug, Info, Warning, Error, None) + ); + + Clarity.initialize(context, config); + } + navigateToTeleConsult() async { String roomID = await Utils.getStringFromPrefs(CacheConst.zoomRoomID); String callTypeID = await Utils.getStringFromPrefs(CacheConst.callTypeID); diff --git a/lib/widgets/image_picker.dart b/lib/widgets/image_picker.dart index e849cc96..6ab2b439 100644 --- a/lib/widgets/image_picker.dart +++ b/lib/widgets/image_picker.dart @@ -95,7 +95,8 @@ class ImageOptions { 'zip', ], ); - List files = result!.paths.map((path) => File(path!)).toList(); + if (result == null) return; + 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 768726b9..40ee9510 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -105,6 +105,7 @@ dependencies: in_app_review: ^2.0.11 flutter_paytabs_bridge: ^2.7.13 + clarity_flutter: 1.9.0 dev_dependencies: flutter_test: