diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 2fd8082..5e9dbb7 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -26,6 +26,7 @@ abstract class ApiClient { bool isAllowAny, bool isExternal, bool isRCService, + bool isPaymentServices, bool bypassConnectionCheck, }); @@ -97,6 +98,7 @@ class ApiClientImp implements ApiClient { bool isAllowAny = false, bool isExternal = false, bool isRCService = false, + bool isPaymentServices = false, bool bypassConnectionCheck = false, }) async { String url; @@ -204,7 +206,12 @@ class ApiClientImp implements ApiClient { } else { var parsed = json.decode(utf8.decode(response.bodyBytes)); if (isAllowAny) { - onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage']); + if (isPaymentServices) { + onSuccess(parsed, statusCode, messageStatus: 1, errorMessage: ""); + } else { + onSuccess(parsed, statusCode, + messageStatus: parsed.contains('MessageStatus') ? parsed['MessageStatus'] : 1, errorMessage: parsed.contains('ErrorEndUserMessage') ? parsed['ErrorEndUserMessage'] : ""); + } } else { if (parsed['Response_Message'] != null) { onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index 0afa53c..0edec4e 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -7,6 +7,8 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_repo. import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart'; import 'package:hmg_patient_app_new/features/common/common_repo.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/models/habib_wallet_repo.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/models/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_repo.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/lab_repo.dart'; @@ -85,6 +87,7 @@ class AppDependencies { getIt.registerLazySingleton(() => InsuranceRepoImp(loggerService: getIt(), apiClient: getIt())); getIt.registerLazySingleton(() => PayfortRepoImp(loggerService: getIt(), apiClient: getIt())); getIt.registerLazySingleton(() => LocalAuthService(loggerService: getIt(), localAuth: getIt())); + getIt.registerLazySingleton(() => HabibWalletRepoImp(loggerService: getIt(), apiClient: getIt())); // ViewModels // Global/shared VMs → LazySingleton @@ -125,24 +128,24 @@ class AppDependencies { ); getIt.registerLazySingleton( - () => PayfortViewModel( + () => PayfortViewModel( payfortRepo: getIt(), errorHandlerService: getIt(), ), ); - getIt.registerLazySingleton( - () => AuthenticationViewModel( - authenticationRepo: getIt(), - cacheService: getIt(), - navigationService: getIt(), - dialogService: getIt(), - appState: getIt(), + getIt.registerLazySingleton( + () => HabibWalletViewModel( + habibWalletRepo: getIt(), errorHandlerService: getIt(), - localAuthService: getIt() ), ); + getIt.registerLazySingleton( + () => AuthenticationViewModel( + authenticationRepo: getIt(), cacheService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), localAuthService: getIt()), + ); + // Screen-specific VMs → Factory // getIt.registerFactory( // () => BookAppointmentsViewModel( diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index ebb7037..a330879 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -568,9 +568,10 @@ class Utils { ); } - static Widget getPaymentAmountWithSymbol(Widget paymentAmountWidget, Color iconColor, double iconSize, {bool isSaudiCurrency = true}) { + static Widget getPaymentAmountWithSymbol(Widget paymentAmountWidget, Color iconColor, double iconSize, {bool isSaudiCurrency = true, bool isExpanded = true}) { return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: isExpanded ? MainAxisAlignment.spaceBetween : MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, children: [ appState.isArabic() ? Container() diff --git a/lib/extensions/widget_extensions.dart b/lib/extensions/widget_extensions.dart index f576d0c..5724b91 100644 --- a/lib/extensions/widget_extensions.dart +++ b/lib/extensions/widget_extensions.dart @@ -39,6 +39,8 @@ extension WidgetExtensions on Widget { child: this, ); + + //TODO: @Sikander to make shimmer widget width dynamic Widget toShimmer2({bool isShow = true, double radius = 20}) => isShow ? Shimmer.fromColors( baseColor: const Color(0xffe8eff0), diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index 0add5a0..e2ed026 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -525,7 +525,7 @@ class AuthenticationViewModel extends ChangeNotifier { if (!_appState.isAuthenticated) { loginTypeEnum = (_appState.deviceTypeID == 1 ? LoginTypeEnum.face : LoginTypeEnum.fingerprint); print(loginTypeEnum); - checkActivationCode(otpTypeEnum: OTPTypeEnum.faceIDFingerprint, activationCode: null, onWrongActivationCode: (String? message) {}); + await checkActivationCode(otpTypeEnum: OTPTypeEnum.faceIDFingerprint, activationCode: null, onWrongActivationCode: (String? message) {}); insertPatientIMEIData((_appState.deviceTypeID == 1 ? LoginTypeEnum.face.toInt : LoginTypeEnum.fingerprint.toInt)); } else { // authenticated = true; diff --git a/lib/features/habib_wallet/models/habib_wallet_repo.dart b/lib/features/habib_wallet/models/habib_wallet_repo.dart new file mode 100644 index 0000000..5f47f6c --- /dev/null +++ b/lib/features/habib_wallet/models/habib_wallet_repo.dart @@ -0,0 +1,58 @@ +import 'package:dartz/dartz.dart'; +import 'package:hmg_patient_app_new/core/api/api_client.dart'; +import 'package:hmg_patient_app_new/core/api_consts.dart'; +import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; +import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; + +abstract class HabibWalletRepo { + Future>> getPatientBalanceAmount(); +} + +class HabibWalletRepoImp implements HabibWalletRepo { + final ApiClient apiClient; + final LoggerService loggerService; + + HabibWalletRepoImp({required this.loggerService, required this.apiClient}); + + @override + Future>> getPatientBalanceAmount() async { + Map mapDevice = {}; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + GET_PATIENT_AdVANCE_BALANCE_AMOUNT, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + // final list = response['ListPLO']; + // if (list == null || list.isEmpty) { + // throw Exception("lab list is empty"); + // } + + // final labOrders = list.map((item) => PatientLabOrdersResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response["TotalAdvanceBalanceAmount"], + ); + } 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/habib_wallet/models/habib_wallet_view_model.dart b/lib/features/habib_wallet/models/habib_wallet_view_model.dart new file mode 100644 index 0000000..64c6ddf --- /dev/null +++ b/lib/features/habib_wallet/models/habib_wallet_view_model.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/models/habib_wallet_repo.dart'; +import 'package:hmg_patient_app_new/services/error_handler_service.dart'; + +class HabibWalletViewModel extends ChangeNotifier { + bool isWalletAmountLoading = false; + num habibWalletAmount = 0; + + HabibWalletRepo habibWalletRepo; + ErrorHandlerService errorHandlerService; + + HabibWalletViewModel({required this.habibWalletRepo, required this.errorHandlerService}); + + initHabibWalletProvider() { + isWalletAmountLoading = true; + habibWalletAmount = 0; + notifyListeners(); + } + + Future getPatientBalanceAmount({Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await habibWalletRepo.getPatientBalanceAmount(); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + habibWalletAmount = apiResponse.data!; + isWalletAmountLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } +} diff --git a/lib/features/lab/lab_repo.dart b/lib/features/lab/lab_repo.dart index ede2ec8..f205494 100644 --- a/lib/features/lab/lab_repo.dart +++ b/lib/features/lab/lab_repo.dart @@ -7,7 +7,7 @@ import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_ import 'package:hmg_patient_app_new/services/logger_service.dart'; abstract class LabRepo { - Future>>> getPatientLabOrders({required String patientId}); + Future>>> getPatientLabOrders(); } class LabRepoImp implements LabRepo { @@ -17,7 +17,7 @@ class LabRepoImp implements LabRepo { LabRepoImp({required this.loggerService, required this.apiClient}); @override - Future>>> getPatientLabOrders({required String patientId}) async { + Future>>> getPatientLabOrders() async { Map mapDevice = {}; try { diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart index 52f470d..2e481f4 100644 --- a/lib/features/lab/lab_view_model.dart +++ b/lib/features/lab/lab_view_model.dart @@ -29,7 +29,7 @@ class LabViewModel extends ChangeNotifier { } Future getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await labRepo.getPatientLabOrders(patientId: "1231755"); + final result = await labRepo.getPatientLabOrders(); result.fold( (failure) async => await errorHandlerService.handleError(failure: failure), diff --git a/lib/features/payfort/payfort_repo.dart b/lib/features/payfort/payfort_repo.dart index 565422b..9f836d1 100644 --- a/lib/features/payfort/payfort_repo.dart +++ b/lib/features/payfort/payfort_repo.dart @@ -53,7 +53,7 @@ class PayfortRepoImp implements PayfortRepo { } catch (e) { failure = DataParsingFailure(e.toString()); } - }, isAllowAny: true); + }, isAllowAny: true, isPaymentServices: true); if (failure != null) return Left(failure!); if (apiResponse == null) return Left(ServerFailure("Unknown error")); return Right(apiResponse!); @@ -80,7 +80,7 @@ class PayfortRepoImp implements PayfortRepo { } catch (e) { failure = DataParsingFailure(e.toString()); } - }, isAllowAny: true); + }, isAllowAny: true, isPaymentServices: true); if (failure != null) return Left(failure!); if (apiResponse == null) return Left(ServerFailure("Unknown error")); return Right(apiResponse!); @@ -109,7 +109,7 @@ class PayfortRepoImp implements PayfortRepo { } catch (e) { failure = DataParsingFailure(e.toString()); } - }, isAllowAny: true, isExternal: true); + }, isAllowAny: true, isExternal: true, isPaymentServices: true); if (failure != null) return Left(failure!); if (apiResponse == null) return Left(ServerFailure("Unknown error")); return Right(apiResponse!); @@ -139,7 +139,7 @@ class PayfortRepoImp implements PayfortRepo { } catch (e) { failure = DataParsingFailure(e.toString()); } - }, isAllowAny: true); + }, isAllowAny: true, isPaymentServices: true); if (failure != null) return Left(failure!); if (apiResponse == null) return Left(ServerFailure("Unknown error")); return Right(apiResponse!); diff --git a/lib/main.dart b/lib/main.dart index 70a2158..2739a6b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,6 +9,7 @@ import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/models/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; @@ -109,6 +110,12 @@ void main() async { errorHandlerService: getIt(), ), ), + ChangeNotifierProvider( + create: (_) => HabibWalletViewModel( + habibWalletRepo: getIt(), + errorHandlerService: getIt(), + ), + ), ChangeNotifierProvider( create: (_) => AuthenticationViewModel( authenticationRepo: getIt(), diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index 7f2ea9d..8430f05 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -55,6 +55,7 @@ class _AppointmentPaymentPageState extends State { void initState() { scheduleMicrotask(() { payfortViewModel.initPayfortViewModel(); + payfortViewModel.setIsApplePayConfigurationLoading(false); myAppointmentsViewModel.getPatientShareAppointment( widget.patientAppointmentHistoryResponseModel.projectID, widget.patientAppointmentHistoryResponseModel.clinicID, @@ -283,7 +284,7 @@ class _AppointmentPaymentPageState extends State { height: 80.h, fit: BoxFit.contain, ).paddingSymmetrical(24.h, 0.h).onPress(() { - payfortVM.setIsApplePayConfigurationLoading(true); + // payfortVM.setIsApplePayConfigurationLoading(true); startApplePay(); }), SizedBox(height: 12.h), @@ -344,62 +345,74 @@ class _AppointmentPaymentPageState extends State { // updateTamaraRequestStatus("Failed", "00", Utils.getAppointmentTransID(appo.projectID, appo.clinicID, appo.appointmentNo), tamaraOrderID, num.parse(selectedInstallments), appo); // } } else { - showCommonBottomSheet(context, - child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false); - await payfortViewModel.checkPaymentStatus( - transactionID: transID, - onSuccess: (apiResponse) async { - print(apiResponse.data); - if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") { - await myAppointmentsViewModel.createAdvancePayment( - paymentMethodName: selectedPaymentMethod, - projectID: widget.patientAppointmentHistoryResponseModel.projectID, - clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, - appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), - payedAmount: payfortViewModel.payfortCheckPaymentStatusResponseModel!.amount!, - paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!, - patientID: "4767477", - patientType: 1, - onSuccess: (value) async { - print(value); - await myAppointmentsViewModel.addAdvanceNumberRequest( - advanceNumber: Utils.isVidaPlusProject(appState, widget.patientAppointmentHistoryResponseModel.projectID) - ? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString() - : value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(), - paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!, - appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), - onSuccess: (value) async { - if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) { - //TODO: Implement LiveCare Check-In API Call - } else { - await myAppointmentsViewModel.generateAppointmentQR( - clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, - projectID: widget.patientAppointmentHistoryResponseModel.projectID, - appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), - isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!, - onSuccess: (apiResponse) { - Future.delayed(Duration(milliseconds: 500), () { - Navigator.of(context).pop(); - Navigator.pushAndRemoveUntil( - context, - FadePage( - page: LandingNavigation(), - ), - (r) => false); - Navigator.of(context).push( - FadePage(page: MyAppointmentsPage()), - ); - }); - }); - } - }); - }); - } else {} - }); + checkPaymentStatus(); // checkPaymentStatus(appo); } } + void checkPaymentStatus() async { + showCommonBottomSheet(context, + child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false); + await payfortViewModel.checkPaymentStatus( + transactionID: transID, + onSuccess: (apiResponse) async { + print(apiResponse.data); + if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") { + await myAppointmentsViewModel.createAdvancePayment( + paymentMethodName: selectedPaymentMethod, + projectID: widget.patientAppointmentHistoryResponseModel.projectID, + clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, + appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), + payedAmount: payfortViewModel.payfortCheckPaymentStatusResponseModel!.amount!, + paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!, + patientID: "4767477", + patientType: 1, + onSuccess: (value) async { + print(value); + await myAppointmentsViewModel.addAdvanceNumberRequest( + advanceNumber: Utils.isVidaPlusProject(appState, widget.patientAppointmentHistoryResponseModel.projectID) + ? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString() + : value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(), + paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!, + appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), + onSuccess: (value) async { + if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) { + //TODO: Implement LiveCare Check-In API Call + } else { + await myAppointmentsViewModel.generateAppointmentQR( + clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, + projectID: widget.patientAppointmentHistoryResponseModel.projectID, + appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), + isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!, + onSuccess: (apiResponse) { + Future.delayed(Duration(milliseconds: 500), () { + Navigator.of(context).pop(); + Navigator.pushAndRemoveUntil( + context, + FadePage( + page: LandingNavigation(), + ), + (r) => false); + Navigator.of(context).push( + FadePage(page: MyAppointmentsPage()), + ); + }); + }); + } + }); + }); + } else { + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } + }); + } + openPaymentURL(String paymentMethod) { browser = MyInAppBrowser(onExitCallback: onBrowserExit, onLoadStartCallback: onBrowserLoadStart, context: context); transID = Utils.getAppointmentTransID( @@ -433,6 +446,8 @@ class _AppointmentPaymentPageState extends State { } startApplePay() async { + showCommonBottomSheet(context, + child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false); transID = Utils.getAppointmentTransID( widget.patientAppointmentHistoryResponseModel.projectID, widget.patientAppointmentHistoryResponseModel.clinicID, @@ -447,8 +462,7 @@ class _AppointmentPaymentPageState extends State { applePayInsertRequest.clientRequestID = transID; applePayInsertRequest.clinicID = widget.patientAppointmentHistoryResponseModel.clinicID; - //TODO: Need to pass dynamic params to the payment request instead of static values - applePayInsertRequest.currency = "SAR"; + applePayInsertRequest.currency = appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED"; applePayInsertRequest.customerEmail = "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com"; applePayInsertRequest.customerID = appState.getAuthenticatedUser()!.patientId.toString(); applePayInsertRequest.customerName = "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}"; @@ -460,8 +474,8 @@ class _AppointmentPaymentPageState extends State { applePayInsertRequest.serviceID = ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum().toString(); applePayInsertRequest.channelID = 3; applePayInsertRequest.patientID = appState.getAuthenticatedUser()!.patientId.toString(); - applePayInsertRequest.patientTypeID = 1; - applePayInsertRequest.patientOutSA = 0; + applePayInsertRequest.patientTypeID = appState.getAuthenticatedUser()!.patientType; + applePayInsertRequest.patientOutSA = appState.getAuthenticatedUser()!.outSa; applePayInsertRequest.appointmentDate = widget.patientAppointmentHistoryResponseModel.appointmentDate; applePayInsertRequest.appointmentNo = widget.patientAppointmentHistoryResponseModel.appointmentNo; applePayInsertRequest.orderDescription = "Appointment Payment"; @@ -472,7 +486,7 @@ class _AppointmentPaymentPageState extends State { applePayInsertRequest.isSchedule = widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? "1" : "0"; applePayInsertRequest.language = appState.isArabic() ? 'ar' : 'en'; applePayInsertRequest.languageID = appState.isArabic() ? 1 : 2; - applePayInsertRequest.userName = 3628599; + applePayInsertRequest.userName = appState.getAuthenticatedUser()!.patientId; applePayInsertRequest.responseContinueURL = "http://hmg.com/Documents/success.html"; applePayInsertRequest.backClickUrl = "http://hmg.com/Documents/success.html"; applePayInsertRequest.paymentOption = "ApplePay"; @@ -499,12 +513,22 @@ class _AppointmentPaymentPageState extends State { merchantIdentifier: payfortViewModel.payfortProjectDetailsRespModel!.merchantIdentifier, applePayAccessCode: payfortViewModel.payfortProjectDetailsRespModel!.accessCode, applePayShaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel!.shaRequest, - currency: "SAR", + currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED", onFailed: (failureResult) async { log("failureResult: ${failureResult.message.toString()}"); + Navigator.of(context).pop(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: failureResult.message.toString()), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); }, onSucceeded: (successResult) async { + Navigator.of(context).pop(); log("successResult: ${successResult.responseMessage.toString()}"); + checkPaymentStatus(); }, // projectId: appo.projectID, // serviceTypeEnum: ServiceTypeEnum.appointmentPayment, diff --git a/lib/presentation/habib_wallet/habib_wallet_page.dart b/lib/presentation/habib_wallet/habib_wallet_page.dart new file mode 100644 index 0000000..92d62ec --- /dev/null +++ b/lib/presentation/habib_wallet/habib_wallet_page.dart @@ -0,0 +1,111 @@ +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/app_state.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/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/models/habib_wallet_view_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_page.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/transitions/fade_page.dart'; +import 'package:provider/provider.dart'; + +class HabibWalletPage extends StatefulWidget { + const HabibWalletPage({super.key}); + + @override + State createState() => _HabibWalletState(); +} + +class _HabibWalletState extends State { + @override + Widget build(BuildContext context) { + AppState _appState = getIt.get(); + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + appBar: AppBar( + title: LocaleKeys.myWallet.tr(context: context).toText18(), + backgroundColor: AppColors.bgScaffoldColor, + ), + body: Padding( + padding: EdgeInsets.all(24.h), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.myWallet.tr(context: context).toText24(isBold: true), + SizedBox(height: 24.h), + Container( + width: double.infinity, + height: 180.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.blackBgColor, + borderRadius: 24, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "${_appState.getAuthenticatedUser()!.firstName!} ${_appState.getAuthenticatedUser()!.lastName!}".toText19(isBold: true, color: AppColors.whiteColor), + "MRN: ${_appState.getAuthenticatedUser()!.patientId!}".toText14(weight: FontWeight.w500, color: AppColors.greyTextColor), + ], + ), + Utils.buildSvgWithAssets(icon: AppAssets.habiblogo, width: 24.h, height: 24.h), + ], + ), + Spacer(), + LocaleKeys.balanceAmount.tr(context: context).toText14(weight: FontWeight.w500, color: AppColors.whiteColor), + Consumer(builder: (context, habibWalletVM, child) { + return Utils.getPaymentAmountWithSymbol(habibWalletVM.habibWalletAmount.toString().toText32(isBold: true, color: AppColors.whiteColor), AppColors.whiteColor, 13.h, + isExpanded: false) + .toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.h); + }), + ], + ), + ), + ), + SizedBox(height: 16.h), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CustomButton( + icon: AppAssets.recharge_icon, + iconSize: 21.h, + text: "Recharge".needTranslation, + onPressed: () { + Navigator.of(context).push( + FadePage( + page: RechargeWalletPage(), + ), + ); + }, + backgroundColor: AppColors.infoColor, + borderColor: AppColors.infoColor, + textColor: AppColors.whiteColor, + fontSize: 14, + fontWeight: FontWeight.bold, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/presentation/habib_wallet/recharge_wallet_page.dart b/lib/presentation/habib_wallet/recharge_wallet_page.dart new file mode 100644 index 0000000..0d8f16c --- /dev/null +++ b/lib/presentation/habib_wallet/recharge_wallet_page.dart @@ -0,0 +1,105 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.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/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/input_widget.dart'; + +class RechargeWalletPage extends StatefulWidget { + const RechargeWalletPage({super.key}); + + @override + State createState() => _RechargeWalletPageState(); +} + +class _RechargeWalletPageState extends State { + + FocusNode textFocusNode = FocusNode(); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + appBar: AppBar( + title: LocaleKeys.createAdvancedPayment.tr(context: context).toText18(), + backgroundColor: AppColors.bgScaffoldColor, + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.all(24.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.createAdvancedPayment.tr(context: context).toText20(isBold: true), + SizedBox(height: 24.h), + Container( + height: 135.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.h, + hasShadow: false, + side: BorderSide(color: AppColors.textColor, width: 2.h), + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Enter an amount".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + Spacer(), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Utils.getPaymentAmountWithSymbol( + SizedBox( + width: 150.h, + child: TextInputWidget( + labelText: "", + hintText: "", + isEnable: true, + prefix: null, + isAllowRadius: true, + isBorderAllowed: false, + isAllowLeadingIcon: true, + autoFocus: true, + fontSize: 40, + padding: EdgeInsets.symmetric(horizontal: 8.h, vertical: 0.h), + focusNode: textFocusNode, + isWalletAmountInput: true, + // leadingIcon: AppAssets.student_card, + ), + ), + AppColors.textColor, + 13.h, + isExpanded: false), + const Spacer(), + "SAR".needTranslation.toText20(color: AppColors.greyTextColor, weight: FontWeight.w500), + ], + ), + ], + ), + ), + ), + ], + ), + ), + )), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.h, + hasShadow: false, + ), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 55afffe..bf8dcc6 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; @@ -10,6 +12,7 @@ import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; 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/authentication/authentication_view_model.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/models/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/authentication/quick_login.dart'; import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart'; @@ -35,22 +38,32 @@ class LandingPage extends StatefulWidget { class _LandingPageState extends State { late final AuthenticationViewModel authVM; + late final HabibWalletViewModel habibWalletVM; + + late AppState appState; @override void initState() { authVM = context.read(); + habibWalletVM = context.read(); authVM.savePushTokenToAppState(); if (mounted) { authVM.checkLastLoginStatus(() { showQuickLogin(context, false); }); } + scheduleMicrotask(() { + if (appState.isAuthenticated) { + habibWalletVM.initHabibWalletProvider(); + habibWalletVM.getPatientBalanceAmount(); + } + }); super.initState(); } @override Widget build(BuildContext context) { - AppState appState = getIt.get(); + appState = getIt.get(); NavigationService navigationService = getIt.get(); return Scaffold( diff --git a/lib/presentation/home/widgets/habib_wallet_card.dart b/lib/presentation/home/widgets/habib_wallet_card.dart index 80855dd..815337d 100644 --- a/lib/presentation/home/widgets/habib_wallet_card.dart +++ b/lib/presentation/home/widgets/habib_wallet_card.dart @@ -4,8 +4,12 @@ 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/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/habib_wallet/models/habib_wallet_view_model.dart'; +import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.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/transitions/fade_page.dart'; +import 'package:provider/provider.dart'; class HabibWalletCard extends StatelessWidget { const HabibWalletCard({super.key}); @@ -69,19 +73,21 @@ class HabibWalletCard extends StatelessWidget { ], ), SizedBox(height: 4.h), - Row( - children: [ - Utils.buildSvgWithAssets( - icon: AppAssets.saudi_riyal_icon, - iconColor: AppColors.dividerColor, - width: 24.h, - height: 24.h, - fit: BoxFit.contain, - ), - SizedBox(width: 8.h), - "200.18".toText32(isBold: true), - ], - ), + Consumer(builder: (context, habibWalletVM, child) { + return Row( + children: [ + Utils.buildSvgWithAssets( + icon: AppAssets.saudi_riyal_icon, + iconColor: AppColors.dividerColor, + width: 24.h, + height: 24.h, + fit: BoxFit.contain, + ), + SizedBox(width: 8.h), + habibWalletVM.habibWalletAmount.toString().toText32(isBold: true).toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.h), + ], + ); + }), Padding( padding: EdgeInsets.symmetric(horizontal: 50.h), child: Row( @@ -90,7 +96,13 @@ class HabibWalletCard extends StatelessWidget { SizedBox(width: 2.h), Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), ], - ), + ).onPress(() { + Navigator.of(context).push( + FadePage( + page: HabibWalletPage(), + ), + ); + }), ), SizedBox(height: 16.h), Row( @@ -103,7 +115,7 @@ class HabibWalletCard extends StatelessWidget { CustomButton( icon: AppAssets.recharge_icon, iconSize: 18.h, - text: "Recharge", + text: "Recharge".needTranslation, onPressed: () {}, backgroundColor: AppColors.infoColor, borderColor: AppColors.infoColor, diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart index 4fcf6f3..44e1327 100644 --- a/lib/widgets/input_widget.dart +++ b/lib/widgets/input_widget.dart @@ -5,9 +5,11 @@ import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/enums.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/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dart'; +import 'package:keyboard_actions/keyboard_actions.dart'; import '../core/dependencies.dart'; @@ -36,6 +38,8 @@ class TextInputWidget extends StatelessWidget { final String? errorMessage; Function(CountryEnum)? onCountryChange; final SelectionTypeEnum? selectionType; + final num? fontSize; + final bool? isWalletAmountInput; // final List countryList; // final Function(Country)? onCountryChange; @@ -63,10 +67,37 @@ class TextInputWidget extends StatelessWidget { this.errorMessage, this.onCountryChange, this.selectionType, + this.fontSize = 14, + this.isWalletAmountInput = false, // this.countryList = const [], // this.onCountryChange, }); + final FocusNode _focusNode = FocusNode(); + + KeyboardActionsConfig get _keyboardActionsConfig { + return KeyboardActionsConfig( + keyboardActionsPlatform: KeyboardActionsPlatform.ALL, + keyboardBarColor: const Color(0xFFCAD1D9), //Apple keyboard color + actions: [ + KeyboardActionsItem( + focusNode: focusNode ?? _focusNode, + toolbarButtons: [ + (node) { + return GestureDetector( + onTap: () => node.unfocus(), + child: Container( + padding: const EdgeInsets.all(12.0), + child: "Done".toText16(weight: FontWeight.w500, color: AppColors.infoColor), + ), + ); + } + ], + ), + ], + ); + } + @override Widget build(BuildContext context) { final errorColor = AppColors.primaryRedColor; @@ -184,40 +215,46 @@ class TextInputWidget extends StatelessWidget { } Widget _buildTextField(BuildContext context) { - return TextField( - enabled: isEnable, - scrollPadding: EdgeInsets.zero, - keyboardType: keyboardType, - controller: controller, - readOnly: isReadOnly, - textAlignVertical: TextAlignVertical.top, - textAlign: TextAlign.left, - textDirection: TextDirection.ltr, - onChanged: onChange, - focusNode: focusNode, - autofocus: autoFocus, - style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, color: AppColors.textColor, letterSpacing: -0.2), - decoration: InputDecoration( - isDense: true, - hintText: hintText, - hintStyle: TextStyle(fontSize: 14.fSize, height: 21 / 16, fontWeight: FontWeight.w500, color: Color(0xff898A8D), letterSpacing: -0.2), - prefixIconConstraints: BoxConstraints(minWidth: 45.h), - prefixIcon: prefix == null - ? null - : Text( - "+" + prefix!, - style: TextStyle( - fontSize: 14.fSize, - height: 21 / 14, - fontWeight: FontWeight.w500, - color: Color(0xff2E303A), - letterSpacing: -0.2, + return KeyboardActions( + config: _keyboardActionsConfig, + disableScroll: true, + child: TextField( + enabled: isEnable, + scrollPadding: EdgeInsets.zero, + keyboardType: keyboardType, + controller: controller, + readOnly: isReadOnly, + textAlignVertical: TextAlignVertical.top, + textAlign: TextAlign.left, + textDirection: TextDirection.ltr, + onChanged: onChange, + focusNode: focusNode ?? _focusNode, + autofocus: autoFocus, + textInputAction: TextInputAction.done, + cursorHeight: isWalletAmountInput! ? 40.h : 18.h, + style: TextStyle(fontSize: fontSize!.fSize, height: isWalletAmountInput! ? 1 / 4 : 21 / 14, fontWeight: FontWeight.w500, color: AppColors.textColor, letterSpacing: -0.2), + decoration: InputDecoration( + isDense: true, + hintText: hintText, + hintStyle: TextStyle(fontSize: 14.fSize, height: 21 / 16, fontWeight: FontWeight.w500, color: Color(0xff898A8D), letterSpacing: -0.2), + prefixIconConstraints: BoxConstraints(minWidth: 45.h), + prefixIcon: prefix == null + ? null + : Text( + "+" + prefix!, + style: TextStyle( + fontSize: 14.fSize, + height: 21 / 14, + fontWeight: FontWeight.w500, + color: Color(0xff2E303A), + letterSpacing: -0.2, + ), ), - ), - contentPadding: EdgeInsets.zero, - border: InputBorder.none, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, + contentPadding: EdgeInsets.zero, + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + ), ), ); } diff --git a/pubspec.yaml b/pubspec.yaml index afbac11..36326f4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -75,6 +75,7 @@ dependencies: network_info_plus: ^6.1.4 flutter_nfc_kit: ^3.6.0 barcode_scan2: ^4.5.1 + keyboard_actions: ^4.2.0 dev_dependencies: flutter_test: