diff --git a/assets/images/svg/small_livecare_icon.svg b/assets/images/svg/small_livecare_icon.svg new file mode 100644 index 0000000..a496914 --- /dev/null +++ b/assets/images/svg/small_livecare_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/walkin_appointment_icon.svg b/assets/images/svg/walkin_appointment_icon.svg new file mode 100644 index 0000000..ceafb38 --- /dev/null +++ b/assets/images/svg/walkin_appointment_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index 83fb842..f7ca57d 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -133,6 +133,8 @@ class AppAssets { static const String minus = '$svgBasePath/minus.svg'; static const String home_lab_result_icon = '$svgBasePath/home_lab_result_icon.svg'; static const String visa_mastercard_icon = '$svgBasePath/visa_mastercard.svg'; + static const String small_livecare_icon = '$svgBasePath/small_livecare_icon.svg'; + static const String walkin_appointment_icon = '$svgBasePath/walkin_appointment_icon.svg'; //bottom navigation// static const String homeBottom = '$svgBasePath/home_bottom.svg'; diff --git a/lib/features/book_appointments/book_appointments_repo.dart b/lib/features/book_appointments/book_appointments_repo.dart index c3cc9cf..5e99585 100644 --- a/lib/features/book_appointments/book_appointments_repo.dart +++ b/lib/features/book_appointments/book_appointments_repo.dart @@ -53,6 +53,19 @@ abstract class BookAppointmentsRepo { Future>> getLiveCareDoctorFreeSlots(int clinicID, int serviceID, int projectID, int doctorId, bool isBookingForLiveCare, {Function(dynamic)? onSuccess, Function(String)? onError}); + + Future>> insertSpecificAppointmentForLiveCare( + {required int docID, + required int clinicID, + required int projectID, + required String selectedTime, + required String selectedDate, + required int initialSlotDuration, + required int genderID, + required int userAge, + required int serviceID, + Function(dynamic)? onSuccess, + Function(String)? onError}); } class BookAppointmentsRepoImp implements BookAppointmentsRepo { @@ -580,4 +593,68 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future> insertSpecificAppointmentForLiveCare( + {required int docID, + required int clinicID, + required int projectID, + required String selectedTime, + required String selectedDate, + required int initialSlotDuration, + required int genderID, + required int userAge, + required int serviceID, + Function(dynamic)? onSuccess, + Function(String)? onError}) async { + Map mapDevice = { + "IsForLiveCare": true, + "ProjectID": projectID, + "ClinicID": clinicID, + "DoctorID": docID, + "ServiceID": serviceID, + "StartTime": selectedTime, + "SelectedTime": selectedTime, + "EndTime": selectedTime, + "InitialSlotDuration": initialSlotDuration, + "StrAppointmentDate": selectedDate, + "IsVirtual": false, + "BookedBy": 102, + "VisitType": 1, + "VisitFor": 1, + "GenderID": genderID, + "Age": userAge + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + INSERT_LIVECARE_SCHEDULE_APPOINTMENT, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final appointmentNo = response['AppointmentNo']; + + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: response["ErrorEndUserMessage"], + data: response, + ); + } 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/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index e8d02b9..c42a20d 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -414,18 +414,9 @@ class BookAppointmentsViewModel extends ChangeNotifier { ); } - //TODO: Handle the cases for LiveCare Schedule, Dental & Laser Clinics + //TODO: Handle the cases for Dental & Laser Clinics Future insertSpecificAppointment( - { - // required int docID, - // required int clinicID, - // required int projectID, - // required String selectedTime, - // required String selectedDate, - // required int initialSlotDuration, - // required int genderID, - // required int userAge, - String? procedureID, + {String? procedureID, num? testTypeEnum, num? testProcedureEnum, int? invoiceNumber, @@ -527,6 +518,102 @@ class BookAppointmentsViewModel extends ChangeNotifier { ); } + Future insertSpecificAppointmentForLiveCare({Function(dynamic p1)? onSuccess, Function(dynamic p1)? onError}) async { + _appState = getIt(); + final result = await bookAppointmentsRepo.insertSpecificAppointmentForLiveCare( + docID: selectedDoctor.doctorID!, + clinicID: selectedDoctor.clinicID!, + projectID: selectedDoctor.projectID!, + serviceID: selectedLiveCareClinic.serviceID!, + selectedDate: selectedAppointmentDate, + selectedTime: selectedAppointmentTime, + initialSlotDuration: initialSlotDuration, + genderID: _appState.getAuthenticatedUser()!.gender!, + userAge: _appState.getAuthenticatedUser()!.age!, + onError: onError); + + result.fold( + (failure) async { + print(failure); + }, + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // onError!(apiResponse); + LoadingUtils.hideFullScreenLoader(); + showCommonBottomSheetWithoutHeight( + title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), + navigationService.navigatorKey.currentContext!, + child: Utils.getWarningWidget( + loadingText: apiResponse.data["ErrorEndUserMessage"], + isShowActionButtons: true, + onCancelTap: () { + navigationService.pop(); + }, + onConfirmTap: () async { + navigationService.pop(); + PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel( + appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'], + clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'], + projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'], + endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'], + startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'], + doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'], + isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'], + originalClinicID: 0, + originalProjectID: 0, + appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'], + ); + + showCommonBottomSheet(navigationService.navigatorKey.currentContext!, + child: Utils.getLoadingWidget(loadingText: "Cancelling your previous appointment....".needTranslation), + callBackFunc: (str) {}, + title: "", + height: ResponsiveExtension.screenHeight * 0.3, + isCloseButtonVisible: false, + isDismissible: false, + isFullScreen: false); + await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async { + navigationService.pop(); + Future.delayed(Duration(milliseconds: 50)).then((value) async {}); + LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation); + await insertSpecificAppointment( + onError: (err) {}, + onSuccess: (apiResp) async { + LoadingUtils.hideFullScreenLoader(); + await Future.delayed(Duration(milliseconds: 50)).then((value) async { + LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); + await Future.delayed(Duration(milliseconds: 4000)).then((value) { + LoadingUtils.hideFullScreenLoader(); + Navigator.pushAndRemoveUntil( + navigationService.navigatorKey.currentContext!, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }); + }); + }); + }); + }), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } else if (apiResponse.messageStatus == 1) { + if (apiResponse.data == null || apiResponse.data!.isEmpty) { + onError!("No free slots available".tr()); + return; + } + + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + Future getRegionMappedProjectList() async { isRegionListLoading = true; notifyListeners(); diff --git a/lib/features/my_appointments/my_appointments_repo.dart b/lib/features/my_appointments/my_appointments_repo.dart index e4745cb..99f7c7d 100644 --- a/lib/features/my_appointments/my_appointments_repo.dart +++ b/lib/features/my_appointments/my_appointments_repo.dart @@ -1,10 +1,13 @@ +import 'dart:io'; + 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/cache_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/features/my_appointments/models/resp_models/hospital_model.dart' - show HospitalsModel; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; @@ -12,7 +15,8 @@ import 'package:hmg_patient_app_new/services/logger_service.dart'; abstract class MyAppointmentsRepo { Future>>> getPatientAppointments({required bool isActiveAppointment, required bool isArrivedAppointments}); - Future>> getPatientShareAppointment({required int projectID, required int clinicID, required String appointmentNo}); + Future>> getPatientShareAppointment( + {required int projectID, required int clinicID, required String appointmentNo, required bool isLiveCareAppointment}); Future>> createAdvancePayment( {required String paymentMethodName, @@ -39,7 +43,7 @@ abstract class MyAppointmentsRepo { Future>>> getPatientDoctorsList(); - + Future>> insertLiveCareVIDARequest({required clientRequestID, required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}); } class MyAppointmentsRepoImp implements MyAppointmentsRepo { @@ -99,14 +103,15 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { } @override - Future>> getPatientShareAppointment({required int projectID, required int clinicID, required String appointmentNo}) async { - Map mapRequest = {"ProjectID": projectID, "ClinicID": clinicID, "AppointmentNo": appointmentNo, "IsActiveAppointment": true}; + Future>> getPatientShareAppointment( + {required int projectID, required int clinicID, required String appointmentNo, required bool isLiveCareAppointment}) async { + Map mapRequest = {"ProjectID": projectID, "ClinicID": clinicID, "AppointmentNo": appointmentNo, "IsActiveAppointment": true, "IsForLiveCare": isLiveCareAppointment}; try { GenericApiModel? apiResponse; Failure? failure; await apiClient.post( - GET_PATIENT_SHARE, + isLiveCareAppointment ? GET_PATIENT_SHARE_LIVECARE : GET_PATIENT_SHARE, body: mapRequest, onFailure: (error, statusCode, {messageStatus, failureType}) { failure = failureType; @@ -495,4 +500,50 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future> insertLiveCareVIDARequest({required clientRequestID, required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel}) async { + Map requestBody = { + "AppointmentNo": patientAppointmentHistoryResponseModel.appointmentNo, + "AppointmentDate": patientAppointmentHistoryResponseModel.appointmentDate, + "ClientRequestID": clientRequestID, + "ClinicID": patientAppointmentHistoryResponseModel.clinicID, + "ProjectID": patientAppointmentHistoryResponseModel.projectID, + "ServiceID": patientAppointmentHistoryResponseModel.serviceID, + "AcceptedBy": patientAppointmentHistoryResponseModel.doctorID, + "IsFlutter": true, + "DeviceToken": await Utils.getStringFromPrefs(CacheConst.pushToken), + "VoipToken": "", // TODO: Add VoIP Token functionality + "IsVoip": Platform.isIOS ? true : false + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + INSERT_VIDA_REQUEST, + body: requestBody, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + } 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 55fafc4..1b884f7 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -79,7 +79,6 @@ class MyAppointmentsViewModel extends ChangeNotifier { } Future getPatientAppointments(bool isActiveAppointment, bool isArrivedAppointments, {Function(dynamic)? onSuccess, Function(String)? onError}) async { - patientAppointmentsHistoryList.clear(); patientUpcomingAppointmentsHistoryList.clear(); patientArrivedAppointmentsHistoryList.clear(); @@ -127,8 +126,8 @@ class MyAppointmentsViewModel extends ChangeNotifier { print('All Appointments: ${patientAppointmentsHistoryList.length}'); } - Future getPatientShareAppointment(int projectID, int clinicID, String appointmentNo, {Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await myAppointmentsRepo.getPatientShareAppointment(projectID: projectID, clinicID: clinicID, appointmentNo: appointmentNo); + Future getPatientShareAppointment(int projectID, int clinicID, String appointmentNo, bool isLiveCareAppointment, {Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await myAppointmentsRepo.getPatientShareAppointment(projectID: projectID, clinicID: clinicID, appointmentNo: appointmentNo, isLiveCareAppointment: isLiveCareAppointment); result.fold( (failure) async => await errorHandlerService.handleError(failure: failure), @@ -305,4 +304,24 @@ class MyAppointmentsViewModel extends ChangeNotifier { }, ); } + + Future insertLiveCareVIDARequest( + {required clientRequestID, required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await myAppointmentsRepo.insertLiveCareVIDARequest(clientRequestID: clientRequestID, patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + onError!(apiResponse.errorMessage!); + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } } diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index 7a98b9d..f474104 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -81,29 +81,6 @@ class _AppointmentDetailsPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // "Appointment Details".needTranslation.toText20(isBold: true), - // if (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) - // CustomButton( - // text: "Report".needTranslation, - // onPressed: () {}, - // backgroundColor: AppColors.secondaryLightRedColor, - // borderColor: AppColors.secondaryLightRedColor, - // textColor: AppColors.primaryRedColor, - // fontSize: 14, - // fontWeight: FontWeight.w500, - // borderRadius: 12, - // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - // height: 40.h, - // iconSize: 16.h, - // icon: AppAssets.report_icon, - // iconColor: AppColors.primaryRedColor, - // ) - // ], - // ), - // SizedBox(height: 24.h), AppointmentDoctorCard( patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, onAskDoctorTap: () {}, @@ -162,42 +139,60 @@ class _AppointmentDetailsPageState extends State { ? "Not Confirmed".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500) : "Confirmed".needTranslation.toText12(color: AppColors.successColor, fontWeight: FontWeight.w500)), SizedBox(height: 16.h), - Stack( - children: [ - ClipRRect( - clipBehavior: Clip.hardEdge, - borderRadius: BorderRadius.circular(24), - child: Image.network( - "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng", - fit: BoxFit.contain, - ), - ), - Positioned( - bottom: 0, - child: SizedBox( - width: MediaQuery.of(context).size.width * 0.785, - child: CustomButton( - text: "Get Directions".needTranslation, - onPressed: () { - MapsLauncher.launchCoordinates(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!), - double.parse(widget.patientAppointmentHistoryResponseModel.longitude!), widget.patientAppointmentHistoryResponseModel.projectName); - }, - backgroundColor: AppColors.textColor.withOpacity(0.8), - borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01), - textColor: AppColors.whiteColor, - fontSize: 14, - fontWeight: FontWeight.w500, - borderRadius: 12.h, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - icon: AppAssets.directions_icon, - iconColor: AppColors.whiteColor, - iconSize: 13.h, - ).paddingAll(12.h), + widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false + ? Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 58.h, height: 58.h), + SizedBox(width: 18.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "LiveCare Appointment".toText18(color: AppColors.textColor, isBold: true), + "The doctor will call you once the appointment time approaches." + .needTranslation + .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + ], + ), + ), + ], + ) + : Stack( + children: [ + ClipRRect( + clipBehavior: Clip.hardEdge, + borderRadius: BorderRadius.circular(24), + child: Image.network( + "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng", + fit: BoxFit.contain, + ), + ), + Positioned( + bottom: 0, + child: SizedBox( + width: MediaQuery.of(context).size.width * 0.785, + child: CustomButton( + text: "Get Directions".needTranslation, + onPressed: () { + MapsLauncher.launchCoordinates(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!), + double.parse(widget.patientAppointmentHistoryResponseModel.longitude!), widget.patientAppointmentHistoryResponseModel.projectName); + }, + backgroundColor: AppColors.textColor.withOpacity(0.8), + borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01), + textColor: AppColors.whiteColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12.h, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + icon: AppAssets.directions_icon, + iconColor: AppColors.whiteColor, + iconSize: 13.h, + ).paddingAll(12.h), + ), + ), + ], ), - ), - ], - ), ], ), ), diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index 77d5d03..28355dc 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -27,9 +27,8 @@ 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'; import 'package:hmg_patient_app_new/widgets/in_app_browser/InAppBrowser.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; -import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; -import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; import 'package:smooth_corner/smooth_corner.dart'; @@ -61,6 +60,7 @@ class _AppointmentPaymentPageState extends State { widget.patientAppointmentHistoryResponseModel.projectID, widget.patientAppointmentHistoryResponseModel.clinicID, widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), + widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false, ); }); super.initState(); @@ -361,8 +361,7 @@ class _AppointmentPaymentPageState extends State { } void checkPaymentStatus() async { - showCommonBottomSheet(context, - child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false); + LoaderBottomSheet.showLoader(); await payfortViewModel.checkPaymentStatus( transactionID: transID, onSuccess: (apiResponse) async { @@ -388,6 +387,21 @@ class _AppointmentPaymentPageState extends State { onSuccess: (value) async { if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) { //TODO: Implement LiveCare Check-In API Call + await myAppointmentsViewModel.insertLiveCareVIDARequest( + clientRequestID: transID, + patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, + onSuccess: (apiResponse) { + Future.delayed(Duration(milliseconds: 500), () { + LoaderBottomSheet.hideLoader(); + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }); + }, + onError: (error) {}); } else { await myAppointmentsViewModel.generateAppointmentQR( clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, @@ -396,16 +410,16 @@ class _AppointmentPaymentPageState extends State { isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!, onSuccess: (apiResponse) { Future.delayed(Duration(milliseconds: 500), () { - Navigator.of(context).pop(); + LoaderBottomSheet.hideLoader(); Navigator.pushAndRemoveUntil( context, CustomPageRoute( page: LandingNavigation(), ), (r) => false); - Navigator.of(context).push( - CustomPageRoute(page: MyAppointmentsPage()), - ); + // Navigator.of(context).push( + // CustomPageRoute(page: MyAppointmentsPage()), + // ); }); }); } diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index c345099..152436a 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -18,6 +18,7 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; +import 'package:smooth_corner/smooth_corner.dart'; class AppointmentCard extends StatefulWidget { AppointmentCard({super.key, required this.patientAppointmentHistoryResponseModel, required this.myAppointmentsViewModel, this.isLoading = false, this.isFromHomePage = false}); @@ -63,44 +64,38 @@ class _AppointmentCardState extends State { spacing: 6.h, runSpacing: 6.h, children: [ - Row( - mainAxisSize: MainAxisSize.min, - children: [ - CustomButton( - text: widget.isLoading - ? "OutPatient" - : appState.isArabic() - ? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN! - : widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!, - onPressed: () {}, - backgroundColor: AppColors.primaryRedColor.withOpacity(0.1), - borderColor: AppColors.primaryRedColor.withOpacity(0.0), - textColor: AppColors.primaryRedColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 30.h, - ), - ], - ), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - CustomButton( - text: widget.isLoading ? "Booked" : AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!), - onPressed: () {}, - backgroundColor: AppColors.successColor.withOpacity(0.1), - borderColor: AppColors.successColor.withOpacity(0.0), - textColor: AppColors.successColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 8, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 30.h, - ), - ], - ), + AppCustomChipWidget( + icon: widget.isLoading + ? AppAssets.walkin_appointment_icon + : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppAssets.walkin_appointment_icon : AppAssets.small_livecare_icon), + iconColor: widget.isLoading + ? AppColors.textColor + : !widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! + ? AppColors.textColor + : AppColors.whiteColor, + labelText: widget.isLoading + ? "Walk In" + : widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! + ? LocaleKeys.livecare.tr(context: context) + : "Walk In".needTranslation, + backgroundColor: + widget.isLoading ? AppColors.greyColor : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor), + textColor: widget.isLoading ? AppColors.textColor : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor), + ).toShimmer2(isShow: widget.isLoading), + AppCustomChipWidget( + labelText: widget.isLoading + ? "OutPatient" + : appState.isArabic() + ? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN! + : widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!, + backgroundColor: AppColors.primaryRedColor.withOpacity(0.1), + textColor: AppColors.primaryRedColor, + ).toShimmer2(isShow: widget.isLoading), + AppCustomChipWidget( + labelText: widget.isLoading ? "Booked" : AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!), + backgroundColor: AppColors.successColor.withOpacity(0.1), + textColor: AppColors.successColor, + ).toShimmer2(isShow: widget.isLoading), ], ).toShimmer2(isShow: widget.isLoading), ), diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart index da00d88..b35305f 100644 --- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart +++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart @@ -59,6 +59,13 @@ class AppointmentDoctorCard extends StatelessWidget { icon: AppAssets.doctor_calendar_icon, labelText: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}, ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}"), + AppCustomChipWidget( + icon: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppAssets.walkin_appointment_icon : AppAssets.small_livecare_icon, + iconColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor, + labelText: patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? LocaleKeys.livecare.tr(context: context) : "Walk In".needTranslation, + backgroundColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor, + textColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor, + ), AppCustomChipWidget(icon: AppAssets.rating_icon, iconColor: AppColors.ratingColorYellow, labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"), ], ), @@ -113,29 +120,8 @@ class AppointmentDoctorCard extends StatelessWidget { iconSize: 16.h, ); } else { - return Row( - children: [ - Expanded( - child: CustomButton( - text: LocaleKeys.reschedule.tr(), - onPressed: () { - onRescheduleTap(); - }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - fontSize: 14, - fontWeight: FontWeight.w500, - borderRadius: 12.h, - height: 40.h, - icon: AppAssets.appointment_calendar_icon, - iconColor: AppColors.primaryRedColor, - iconSize: 16.h, - ), - ), - SizedBox(width: 16.h), - Expanded( - child: CustomButton( + return patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false + ? CustomButton( text: LocaleKeys.cancel.tr(), onPressed: () { onCancelTap(); @@ -150,10 +136,48 @@ class AppointmentDoctorCard extends StatelessWidget { icon: AppAssets.cancel, iconColor: AppColors.whiteColor, iconSize: 16.h, - ), - ), - ], - ); + ) + : Row( + children: [ + Expanded( + child: CustomButton( + text: LocaleKeys.reschedule.tr(), + onPressed: () { + onRescheduleTap(); + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12.h, + height: 40.h, + icon: AppAssets.appointment_calendar_icon, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, + ), + ), + SizedBox(width: 16.h), + Expanded( + child: CustomButton( + text: LocaleKeys.cancel.tr(), + onPressed: () { + onCancelTap(); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12.h, + height: 40.h, + icon: AppAssets.cancel, + iconColor: AppColors.whiteColor, + iconSize: 16.h, + ), + ), + ], + ); } } } diff --git a/lib/presentation/book_appointment/doctor_profile_page.dart b/lib/presentation/book_appointment/doctor_profile_page.dart index 8640c79..943bf59 100644 --- a/lib/presentation/book_appointment/doctor_profile_page.dart +++ b/lib/presentation/book_appointment/doctor_profile_page.dart @@ -115,29 +115,53 @@ class DoctorProfilePage extends StatelessWidget { text: "View available appointments".needTranslation, onPressed: () async { LoaderBottomSheet.showLoader(); - await bookAppointmentsViewModel.getDoctorFreeSlots( - isBookingForLiveCare: false, - onSuccess: (dynamic respData) async { - LoaderBottomSheet.hideLoader(); - showCommonBottomSheetWithoutHeight( - title: "Pick a Date".needTranslation, - context, - child: AppointmentCalendar(), - isFullScreen: false, - isCloseButtonVisible: true, - callBackFunc: () {}, - ); - }, - onError: (err) { - LoaderBottomSheet.hideLoader(); - showCommonBottomSheetWithoutHeight( - context, - child: Utils.getErrorWidget(loadingText: err), - callBackFunc: () {}, - isFullScreen: false, - isCloseButtonVisible: true, - ); - }); + bookAppointmentsViewModel.isLiveCareSchedule + ? await bookAppointmentsViewModel.getLiveCareDoctorFreeSlots( + isBookingForLiveCare: true, + onSuccess: (dynamic respData) async { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + title: "Pick a Date".needTranslation, + context, + child: AppointmentCalendar(), + isFullScreen: false, + isCloseButtonVisible: true, + callBackFunc: () {}, + ); + }, + onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: err), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }) + : await bookAppointmentsViewModel.getDoctorFreeSlots( + isBookingForLiveCare: false, + onSuccess: (dynamic respData) async { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + title: "Pick a Date".needTranslation, + context, + child: AppointmentCalendar(), + isFullScreen: false, + isCloseButtonVisible: true, + callBackFunc: () {}, + ); + }, + onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: err), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }); }, backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor, diff --git a/lib/presentation/book_appointment/review_appointment_page.dart b/lib/presentation/book_appointment/review_appointment_page.dart index d3804d8..1439270 100644 --- a/lib/presentation/book_appointment/review_appointment_page.dart +++ b/lib/presentation/book_appointment/review_appointment_page.dart @@ -204,34 +204,46 @@ class _ReviewAppointmentPageState extends State { void initiateBookAppointment() async { LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation); - await bookAppointmentsViewModel.insertSpecificAppointment(onError: (err) { - print(err.data["ErrorEndUserMessage"]); - LoadingUtils.hideFullScreenLoader(); - }, onSuccess: (apiResp) async { - LoadingUtils.hideFullScreenLoader(); - await Future.delayed(Duration(milliseconds: 50)).then((value) async { - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); - await Future.delayed(Duration(milliseconds: 4000)).then((value) { - LoadingUtils.hideFullScreenLoader(); - Navigator.pushAndRemoveUntil( - context, - CustomPageRoute( - page: LandingNavigation(), - ), - (r) => false); + if (bookAppointmentsViewModel.isLiveCareSchedule) { + await bookAppointmentsViewModel.insertSpecificAppointmentForLiveCare(onError: (err) { + print(err.data["ErrorEndUserMessage"]); + LoadingUtils.hideFullScreenLoader(); + }, onSuccess: (apiResp) async { + LoadingUtils.hideFullScreenLoader(); + await Future.delayed(Duration(milliseconds: 50)).then((value) async { + LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); + await Future.delayed(Duration(milliseconds: 4000)).then((value) { + LoadingUtils.hideFullScreenLoader(); + bookAppointmentsViewModel.setIsLiveCareSchedule(false); + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }); }); }); - }); - - // await Future.delayed(Duration(milliseconds: 4000)).then((value) async { - // LoadingUtils.hideFullScreenLoader(); - - // await Future.delayed(Duration(milliseconds: 50)).then((value) async { - // LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); - // await Future.delayed(Duration(milliseconds: 4000)).then((value) { - // LoadingUtils.hideFullScreenLoader(); - // }); - // }); - // }); + } else { + await bookAppointmentsViewModel.insertSpecificAppointment(onError: (err) { + print(err.data["ErrorEndUserMessage"]); + LoadingUtils.hideFullScreenLoader(); + }, onSuccess: (apiResp) async { + LoadingUtils.hideFullScreenLoader(); + await Future.delayed(Duration(milliseconds: 50)).then((value) async { + LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); + await Future.delayed(Duration(milliseconds: 4000)).then((value) { + LoadingUtils.hideFullScreenLoader(); + bookAppointmentsViewModel.setIsLiveCareSchedule(false); + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }); + }); + }); + } } } diff --git a/lib/widgets/chip/app_custom_chip_widget.dart b/lib/widgets/chip/app_custom_chip_widget.dart index a6e1817..a4db172 100644 --- a/lib/widgets/chip/app_custom_chip_widget.dart +++ b/lib/widgets/chip/app_custom_chip_widget.dart @@ -48,63 +48,46 @@ class AppCustomChipWidget extends StatelessWidget { padding: EdgeInsets.all(0.0), shape: SmoothRectangleBorder( side: BorderSide( - width: 0.0, + width: 10.0, color: Colors.transparent, // Crucially, set color to transparent style: BorderStyle.none, ), - borderRadius: BorderRadius.circular(10.0), // Apply a border radius of 16.0 + borderRadius: BorderRadius.circular(8.0), // Apply a border radius of 16.0 ), ), child: icon.isNotEmpty ? Chip( - avatar: icon.isNotEmpty - ? Utils.buildSvgWithAssets( - icon: icon, - width: iconSize.h, - height: iconSize.h, - iconColor: iconHasColor ? iconColor : null) - : SizedBox.shrink(), - label: richText ?? - labelText!.toText10( - weight: FontWeight.w500, - letterSpacing: -0.64, - color: textColor), + avatar: icon.isNotEmpty ? Utils.buildSvgWithAssets(icon: icon, width: iconSize.h, height: iconSize.h, iconColor: iconHasColor ? iconColor : null) : SizedBox.shrink(), + label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: -0.64, color: textColor), // padding: EdgeInsets.all(0.0), padding: padding, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - labelPadding: EdgeInsetsDirectional.only( - start: -4.h, - end: deleteIcon?.isNotEmpty == true ? 2.h : 8.h), + labelPadding: EdgeInsetsDirectional.only(start: -4.h, end: deleteIcon?.isNotEmpty == true ? 2.h : 8.h), backgroundColor: backgroundColor, - shape: shape, + shape: shape ?? + SmoothRectangleBorder( + borderRadius: BorderRadius.circular(8 ?? 0), + smoothness: 10, + side: BorderSide(color: AppColors.transparent, width: 1.5), + ), deleteIcon: deleteIcon?.isNotEmpty == true - ? Utils.buildSvgWithAssets( - icon: deleteIcon!, - width: deleteIconSize!.width!.h, - height: deleteIconSize!.height.h, - iconColor: deleteIconHasColor ? deleteIconColor : null) + ? Utils.buildSvgWithAssets(icon: deleteIcon!, width: deleteIconSize!.width!.h, height: deleteIconSize!.height.h, iconColor: deleteIconHasColor ? deleteIconColor : null) : null, onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, ) : Chip( materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - label: richText ?? - labelText!.toText10( - weight: FontWeight.w500, - letterSpacing: -0.64, - color: textColor), + label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: -0.64, color: textColor), padding: EdgeInsets.all(0.0), backgroundColor: backgroundColor, - shape: shape, - labelPadding: EdgeInsetsDirectional.only( - start: 8.h, - end: deleteIcon?.isNotEmpty == true ? -2.h : 8.h), + shape: shape ?? SmoothRectangleBorder( + borderRadius: BorderRadius.circular(8 ?? 0), + smoothness: 10, + side: BorderSide(color: AppColors.transparent, width: 1.5), + ), + labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: deleteIcon?.isNotEmpty == true ? -2.h : 8.h), deleteIcon: deleteIcon?.isNotEmpty == true - ? Utils.buildSvgWithAssets( - icon: deleteIcon!, - width: deleteIconSize!.width.h, - height: deleteIconSize!.height.h, - iconColor: deleteIconHasColor ? deleteIconColor : null) + ? Utils.buildSvgWithAssets(icon: deleteIcon!, width: deleteIconSize!.width.h, height: deleteIconSize!.height.h, iconColor: deleteIconHasColor ? deleteIconColor : null) : null, onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, ),