diff --git a/android/agconnect-services.json b/android/agconnect-services.json deleted file mode 100644 index 91aad2c..0000000 --- a/android/agconnect-services.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "agcgw":{ - "backurl":"connect-drcn.hispace.hicloud.com", - "url":"connect-drcn.dbankcloud.cn", - "websocketbackurl":"connect-ws-drcn.hispace.dbankcloud.com", - "websocketurl":"connect-ws-drcn.hispace.dbankcloud.cn" - }, - "agcgw_all":{ - "CN":"connect-drcn.dbankcloud.cn", - "CN_back":"connect-drcn.hispace.hicloud.com", - "DE":"connect-dre.dbankcloud.cn", - "DE_back":"connect-dre.hispace.hicloud.com", - "RU":"connect-drru.dbankcloud.cn", - "RU_back":"connect-drru.hispace.hicloud.com", - "SG":"connect-dra.dbankcloud.cn", - "SG_back":"connect-dra.hispace.hicloud.com" - }, - "client":{ - "cp_id":"2640966000002322881", - "product_id":"736430079244816567", - "client_id":"563735388191982656", - "client_secret":"650C7C799812AFFD53A10C7756CF05FB9F215A7E49032ABA8EBF3E14B77535CF", - "project_id":"736430079244816567", - "app_id":"102857389", - "api_key":"CgB6e3x9DJzMgRCmnT6dyUEkp6UsIfddb6l3w0ZEXzeiRMHEFi3400Z5fJ5qaHneU0OrAI/JRpk+DMGVs3QpUxlI", - "package_name":"com.ejada.hmg" - }, - "oauth_client":{ - "client_id":"102857389", - "client_type":1 - }, - "app_info":{ - "app_id":"102857389", - "package_name":"com.ejada.hmg" - }, - "service":{ - "analytics":{ - "collector_url":"datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn", - "collector_url_ru":"datacollector-drru.dt.hicloud.com,datacollector-drru.dt.dbankcloud.cn", - "collector_url_sg":"datacollector-dra.dt.hicloud.com,datacollector-dra.dt.dbankcloud.cn", - "collector_url_de":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn", - "collector_url_cn":"datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn", - "resource_id":"p1", - "channel_id":"" - }, - "search":{ - "url":"https://search-drcn.cloud.huawei.com" - }, - "cloudstorage":{ - "storage_url":"https://agc-storage-drcn.platform.dbankcloud.cn" - }, - "ml":{ - "mlservice_url":"ml-api-drcn.ai.dbankcloud.com,ml-api-drcn.ai.dbankcloud.cn" - } - }, - "region":"CN", - "configuration_version":"3.0", - "appInfos":[ - { - "package_name":"com.ejada.hmg", - "client":{ - "app_id":"102857389" - }, - "app_info":{ - "package_name":"com.ejada.hmg", - "app_id":"102857389" - }, - "oauth_client":{ - "client_type":1, - "client_id":"102857389" - } - } - ] -} diff --git a/android/app/key b/android/app/key new file mode 100644 index 0000000..15b9f5d Binary files /dev/null and b/android/app/key differ diff --git a/android/app/keystores/keystore b/android/app/keystores/keystore new file mode 100644 index 0000000..e69de29 diff --git a/android/app/libs/PenNavUI.aar b/android/app/libs/PenNavUI.aar new file mode 100644 index 0000000..d423bc1 Binary files /dev/null and b/android/app/libs/PenNavUI.aar differ diff --git a/android/app/libs/Penguin.aar b/android/app/libs/Penguin.aar new file mode 100644 index 0000000..5c789c6 Binary files /dev/null and b/android/app/libs/Penguin.aar differ diff --git a/android/app/libs/PenguinRenderer.aar b/android/app/libs/PenguinRenderer.aar new file mode 100644 index 0000000..b657ac6 Binary files /dev/null and b/android/app/libs/PenguinRenderer.aar differ diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index b8bd69e..6a1c47e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -248,9 +248,6 @@ - + + + + + + + + Gradle Configuration Cache + + + +
+ +
+ Loading... +
+ + + + + + diff --git a/android/key b/android/key new file mode 100644 index 0000000..15b9f5d Binary files /dev/null and b/android/key differ diff --git a/assets/images/svg/ambulance.svg b/assets/images/svg/ambulance.svg new file mode 100644 index 0000000..a73cfdb --- /dev/null +++ b/assets/images/svg/ambulance.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/pin_location.svg b/assets/images/svg/pin_location.svg new file mode 100644 index 0000000..1a8012d --- /dev/null +++ b/assets/images/svg/pin_location.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 99f28f2..2eab03a 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -455,6 +455,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = 3A359E86ZF; ENABLE_BITCODE = NO; @@ -467,6 +469,7 @@ MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = "com.HMG.HMG-Smartphone"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; @@ -638,6 +641,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = 3A359E86ZF; ENABLE_BITCODE = NO; @@ -650,6 +655,7 @@ MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = "com.HMG.HMG-Smartphone"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -664,6 +670,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = 3A359E86ZF; ENABLE_BITCODE = NO; @@ -676,6 +684,7 @@ MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = "com.HMG.HMG-Smartphone"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; @@ -693,7 +702,7 @@ 331C808A294A63A400263BE5 /* Profile */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Debug; }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; @@ -703,7 +712,7 @@ 249021D3217E4FDB00AE95B9 /* Profile */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Debug; }; 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { isa = XCConfigurationList; @@ -713,7 +722,7 @@ 249021D4217E4FDB00AE95B9 /* Profile */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Debug; }; /* End XCConfigurationList section */ }; diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 69204f3..b276dd7 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -2,13 +2,14 @@ import Flutter import UIKit import FirebaseCore import FirebaseMessaging - +import GoogleMaps @main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { + GMSServices.provideAPIKey("AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng") FirebaseApp.configure() GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index bdaba66..982e1ca 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -44,9 +44,9 @@ var PHARMACY_REDIRECT_URL = 'https://bit.ly/AlhabibPharmacy'; // var PHARMACY_BASE_URL = 'https://mdlaboratories.com/exacartapitest/api/'; // RC API URL -var RC_BASE_URL = 'https://rc.hmg.com/'; +// var RC_BASE_URL = 'https://rc.hmg.com/'; -// var RC_BASE_URL = 'https://rc.hmg.com/test/'; +var RC_BASE_URL = 'https://rc.hmg.com/uat/'; // var RC_BASE_URL = 'https://ms.hmg.com/rc/'; @@ -719,7 +719,7 @@ var GET_PRESCRIPTION_INSTRUCTIONS_PDF = 'Services/ChatBot_Service.svc/REST/Chatb class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index 61cf299..e8215ba 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -25,6 +25,7 @@ class AppAssets { static const String download = '$svgBasePath/download.svg'; static const String language = '$svgBasePath/language.svg'; static const String location = '$svgBasePath/location.svg'; + static const String pin_location = '$svgBasePath/pin_location.svg'; static const String whatsapp = '$svgBasePath/whatsapp.svg'; static const String card_user = '$svgBasePath/card_user.svg'; static const String habiblogo = '$svgBasePath/habiblogo.svg'; @@ -156,6 +157,7 @@ class AppAssets { static const String activeCheck = '$svgBasePath/active-check.svg'; static const String deleteIcon = '$svgBasePath/delete_icon.svg'; static const String call_ambulance_icon = '$svgBasePath/call_ambulance_icon.svg'; + static const String ambulance = '$svgBasePath/ambulance.svg'; static const String nearest_er_icon = '$svgBasePath/nearest_er_icon.svg'; static const String rrt_icon = '$svgBasePath/rrt_icon.svg'; static const String waiting_time_clock = '$svgBasePath/waiting_time_clock.svg'; diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart index cbeb4d4..c8659a7 100644 --- a/lib/core/app_state.dart +++ b/lib/core/app_state.dart @@ -56,12 +56,12 @@ class AppState { } FutureOr checkIfGMSAvailableOrNot() async { + isGMSAvailable = (await GmsCheck().checkGmsAvailability()) ?? true; if(Platform.isIOS) { - isGMSAvailable = false; + isGMSAvailable = true; isAndroid = false; return; } - isGMSAvailable = (await GmsCheck().checkGmsAvailability()) ?? true; } AuthenticatedUser? getAuthenticatedUser({bool isFamily = false}) { diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index a1eb471..37d0cc4 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -21,6 +21,8 @@ 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'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; +import 'package:hmg_patient_app_new/features/location/location_repo.dart'; +import 'package:hmg_patient_app_new/features/location/location_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_repo.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart'; @@ -108,6 +110,8 @@ class AppDependencies { getIt.registerLazySingleton(() => ImmediateLiveCareRepoImp(loggerService: getIt(), apiClient: getIt())); getIt.registerLazySingleton(() => EmergencyServicesRepoImp(loggerService: getIt(), apiClient: getIt())); getIt.registerLazySingleton(() => TodoSectionRepoImp(loggerService: getIt(), apiClient: getIt())); + getIt.registerLazySingleton( + () => LocationRepoImpl(apiClient: getIt())); getIt.registerLazySingleton(() => ContactUsRepoImp(loggerService: getIt(), apiClient: getIt())); // ViewModels @@ -216,6 +220,15 @@ class AppDependencies { emergencyServicesRepo: getIt(), appState: getIt(), errorHandlerService: getIt(), + appointmentRepo: getIt(), + dialogService: getIt() + ), + ); + + getIt.registerLazySingleton( + () => LocationViewModel( + locationRepo: getIt(), + errorHandlerService: getIt(), ), ); diff --git a/lib/core/utils/date_util.dart b/lib/core/utils/date_util.dart index 2fbbd09..d58aef6 100644 --- a/lib/core/utils/date_util.dart +++ b/lib/core/utils/date_util.dart @@ -6,7 +6,11 @@ class DateUtil { /// convert String To Date function /// [date] String we want to convert static DateTime convertStringToDate(String? date) { - if (date != null) { + print("the date is $date"); + + if (date == null) return DateTime.now(); + if (date.isEmpty) return DateTime.now(); + const start = "/Date("; const end = "+0300)"; final startIndex = date.indexOf(start); @@ -14,9 +18,7 @@ class DateUtil { return DateTime.fromMillisecondsSinceEpoch(int.parse( date.substring(startIndex + start.length, endIndex), )); - } else { - return DateTime.now(); - } + } static DateTime convertStringToDateSaudiTimezone(String date, int projectId) { diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index 6c8523d..6f1dcc6 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -102,6 +102,12 @@ class Utils { ? getMonthArabic(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString() : getMonth(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString(); } + static String getDayMonthYearDateFormatted(DateTime? dateTime) { + if(dateTime == null ) return ""; + return appState.isArabic() + ? "${dateTime.day.toString()} ${getMonthArabic(dateTime.month)}, ${dateTime.year.toString()}" + : "${dateTime.day.toString()} ${getMonth(dateTime.month)}, ${dateTime.year.toString()}"; + } /// get month by /// [month] convert month number in to month name @@ -749,10 +755,14 @@ class Utils { static Widget getPaymentAmountWithSymbol2(num habibWalletAmount, {double iconSize = 14, - Color iconColor = AppColors.textColor, + double? fontSize, + double? letterSpacing, + FontWeight? fontWeight, + Color iconColor = AppColors.textColor, Color textColor = AppColors.blackColor, bool isSaudiCurrency = true, - bool isExpanded = true}) { + bool isExpanded = true, + }) { return RichText( maxLines: 1, text: TextSpan( @@ -763,8 +773,13 @@ class Utils { child: Utils.buildSvgWithAssets(icon: AppAssets.saudi_riyal_icon, width: iconSize.h, height: iconSize.h, iconColor: iconColor), ), TextSpan( - text: " $habibWalletAmount", - style: TextStyle(color: textColor, fontSize: 32.f, letterSpacing: -4, fontWeight: FontWeight.w600, height: 1), + text: NumberFormat.currency(locale: 'en_US', symbol: " ", decimalDigits: 0).format(habibWalletAmount), + style: TextStyle( + color: textColor, + fontSize: fontSize ?? 32.f, + letterSpacing: letterSpacing??-4, + fontWeight: fontWeight ?? FontWeight.w600, + height: 1), ), ], ), @@ -838,4 +853,7 @@ class Utils { } return isHavePrivilege; } + + + } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 041e8b7..1a6d1cc 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -279,11 +279,11 @@ extension EmailValidator on String { height: 1, color: color ?? AppColors.blackColor, fontSize: 22.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), ); - Widget toText24({Color? color, bool isBold = false, bool isCenter = false, FontWeight? fontWeight}) => Text( + Widget toText24({Color? color, bool isBold = false, bool isCenter = false, FontWeight? fontWeight, double? letterSpacing}) => Text( this, textAlign: isCenter ? TextAlign.center : null, style: TextStyle( - height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.f, letterSpacing: letterSpacing??-1, fontWeight: isBold ? FontWeight.bold : fontWeight??FontWeight.normal), ); Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false, FontWeight? weight, double? letterSpacing}) => Text( diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index d95b316..3260ea5 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -245,6 +245,7 @@ class AuthenticationViewModel extends ChangeNotifier { } Future _handleNewImeiRegistration() async { + await selectDeviceImei(onSuccess: (dynamic respData) async { try { if (respData != null) { diff --git a/lib/features/emergency_services/emergency_services_repo.dart b/lib/features/emergency_services/emergency_services_repo.dart index 3b70cbb..c54c3d1 100644 --- a/lib/features/emergency_services/emergency_services_repo.dart +++ b/lib/features/emergency_services/emergency_services_repo.dart @@ -7,12 +7,16 @@ 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/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart'; -import 'package:hmg_patient_app_new/features/emergency_services/model/resp_model/EROnlineCheckInPaymentDetailsResponse.dart'; -import 'package:hmg_patient_app_new/features/emergency_services/model/resp_model/ProjectAvgERWaitingTime.dart'; -import 'package:hmg_patient_app_new/features/emergency_services/models/resp_models/rrt_procedures_response_model.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/request_model/PatientER_RC.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/AmbulanceRequestOrdersModel.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/EROnlineCheckInPaymentDetailsResponse.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/ProjectAvgERWaitingTime.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/rrt_procedures_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; +import 'models/resp_model/PatientERTransportationMethod.dart'; + abstract class EmergencyServicesRepo { Future>>> getRRTProcedures(); @@ -37,6 +41,16 @@ abstract class EmergencyServicesRepo { Future>> getProjectIDFromNFC({required String nfcCode}); Future>> autoGenerateInvoiceERClinic({required int projectID}); + + Future>>> getTransportationMethods({int? id}); + + + Future>> submitAmbulanceRequest(PatientER_RC request); + + Future>>> getTransportationOrders({int? id}); + + Future>> cancelOrder(int? iD, int patientId); + } class EmergencyServicesRepoImp implements EmergencyServicesRepo { @@ -121,27 +135,28 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo { } @override - Future> checkPatientERAdvanceBalance() async { - Map mapDevice = {"ClinicID": 10}; + Future>>> getTransportationMethods({int? id}) async { + Map mapDevice = {}; try { - GenericApiModel? apiResponse; + GenericApiModel>? apiResponse; Failure? failure; - await apiClient.post( - CHECK_PATIENT_ER_ADVANCE_BALANCE, - body: mapDevice, + await apiClient.get( + "$GET_ALL_TRANSPORTATIONS_RC?patientID=$id", + isRCService: true, onFailure: (error, statusCode, {messageStatus, failureType}) { failure = failureType; }, onSuccess: (response, statusCode, {messageStatus, errorMessage}) { try { - final bool patientHasERBalance = response['BalanceAmount'] > 0; - print(patientHasERBalance); - apiResponse = GenericApiModel( + final list = response['response']['transportationservices']; + final proceduresList = list.map((item) => PatientERTransportationMethod.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( messageStatus: messageStatus, statusCode: statusCode, errorMessage: null, - data: patientHasERBalance, + data: proceduresList, ); } catch (e) { failure = DataParsingFailure(e.toString()); @@ -194,6 +209,151 @@ class EmergencyServicesRepoImp implements EmergencyServicesRepo { } } + @override + Future>> submitAmbulanceRequest(PatientER_RC request) async { + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + body: request.toJson(), + INSERT_TRANSPORTATION_ORDER_RC, + isRCService: true, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: true, + ); + } 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())); + } + } + + @override + Future>>> getTransportationOrders({int? id}) async { + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + body: {}, + "$GET_ALL_TRANSPORTATIONS_ORDERS?patientID=$id", + isRCService: true, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['response']; + final proceduresList = list.map((item) => AmbulanceRequestOrdersModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: proceduresList, + ); + } 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())); + } + } + + @override + Future>> cancelOrder(int? iD, int patientID) async { + try { + GenericApiModel? apiResponse; + + Map request = {}; + request['Id'] = iD; + request['StatusId'] = 6; + request['ClickButton'] = 14; + request['PatientID'] = patientID; + Failure? failure; + await apiClient.post( + body: request, + CANCEL_AMBULANCE_REQUEST, + isRCService: true, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: true, + ); + } 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())); + } + } + + @override + Future> checkPatientERAdvanceBalance() async { + Map mapDevice = {"ClinicID": 10}; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + CHECK_PATIENT_ER_ADVANCE_BALANCE, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final bool patientHasERBalance = response['BalanceAmount'] > 0; + print(patientHasERBalance); + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: patientHasERBalance, + ); + } 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())); + } + } + + @override Future>> checkPatientERPaymentInformation({int? projectID}) async { Map mapDevice = {"ClinicID": 10, "ProjectID": projectID ?? 0}; diff --git a/lib/features/emergency_services/emergency_services_view_model.dart b/lib/features/emergency_services/emergency_services_view_model.dart index ead18f5..9526f16 100644 --- a/lib/features/emergency_services/emergency_services_view_model.dart +++ b/lib/features/emergency_services/emergency_services_view_model.dart @@ -4,32 +4,70 @@ import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart' as GMSMapServices; 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/location_util.dart'; +import 'package:hmg_patient_app_new/core/utils/date_util.dart'; +import 'package:hmg_patient_app_new/core/utils/doctor_response_mapper.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; +import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart'; import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_repo.dart'; -import 'package:hmg_patient_app_new/features/emergency_services/model/resp_model/EROnlineCheckInPaymentDetailsResponse.dart'; -import 'package:hmg_patient_app_new/features/emergency_services/model/resp_model/ProjectAvgERWaitingTime.dart'; -import 'package:hmg_patient_app_new/features/emergency_services/models/resp_models/rrt_procedures_response_model.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/EROnlineCheckInPaymentDetailsResponse.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/AmbulanceCallingPlace.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/request_model/PatientER_RC.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/AmbulanceRequestOrdersModel.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/PatientERTransportationMethod.dart' + show PatientERTransportationMethod; +import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/ProjectAvgERWaitingTime.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/rrt_procedures_response_model.dart'; +import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart' show PlaceDetails; +import 'package:hmg_patient_app_new/features/location/PlacePrediction.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart'; +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/my_appointments_repo.dart'; +import 'package:hmg_patient_app_new/presentation/authentication/login.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/call_ambulance_page.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_details_page.dart'; +import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/requesting_services_page.dart'; +import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/tracking_screen.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/nearest_er_page.dart'; +import 'package:hmg_patient_app_new/routes/app_routes.dart' show AppRoutes; +import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/model/BottomSheetType.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:hmg_patient_app_new/widgets/order_tracking/order_tracking_state.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:huawei_map/huawei_map.dart' as HMSCameraServices; import 'package:url_launcher/url_launcher.dart'; +import '../location/GeocodeResponse.dart'; +import 'models/ambulance_direction.dart'; + class EmergencyServicesViewModel extends ChangeNotifier { EmergencyServicesRepo emergencyServicesRepo; + MyAppointmentsRepo appointmentRepo; ErrorHandlerService errorHandlerService; + DialogService dialogService; + + Completer? gmsController; + Completer? hmsController; final NavigationService navServices; final LocationUtils? locationUtils; final AppState appState; + TextEditingController searchController = TextEditingController(); + + //loading variables bool isERListLoading = false; + bool isTransportationOptionsLoading = false; + List nearestERList = []; List nearestERFilteredList = []; @@ -44,6 +82,14 @@ class EmergencyServicesViewModel extends ChangeNotifier { int hmgCount = 0; int hmcCount = 0; bool pickupFromInsideTheLocation = true; + List? appointments; + List? orders = []; + + //ambulance selection data section + List transportationOptions = []; + PatientERTransportationMethod? selectedTransportOption; + AmbulanceCallingPlace callingPlace = AmbulanceCallingPlace.FROM_HOSPITAL; + AmbulanceDirection ambulanceDirection = AmbulanceDirection.ONE_WAY; late RRTProceduresResponseModel selectedRRTProcedure; @@ -58,23 +104,34 @@ class EmergencyServicesViewModel extends ChangeNotifier { notifyListeners(); } + get isGMSAvailable + { + return appState.isGMSAvailable; + } + EmergencyServicesViewModel({ required this.emergencyServicesRepo, required this.errorHandlerService, required this.navServices, required this.locationUtils, required this.appState, + required this.appointmentRepo, + required this.dialogService, }); - bool get isArabic => appState.isArabic(); - - get isGMSAvailable => appState.isGMSAvailable; - void setIsERBookAppointment(bool value) { isERBookAppointment = value; notifyListeners(); } + bool get isArabic => appState.isArabic(); + + bool haveAnAppointment = false; + + PatientAppointmentHistoryResponseModel? appointment; + + bool isMyAppointmentsLoading = false; + Future getRRTProcedures({Function(dynamic)? onSuccess, Function(String)? onError}) async { RRTProceduresList.clear(); notifyListeners(); @@ -186,84 +243,33 @@ class EmergencyServicesViewModel extends ChangeNotifier { //todo_section handle the camera moved position for HMS devices } - FutureOr getProjects() async { - // if (hospitalList.isNotEmpty) return; - var response = await emergencyServicesRepo.getProjectList(); - - response.fold( - (failure) async {}, - (apiResponse) async { - List? data = apiResponse.data; - if (data == null) return; - hospitalList = data; - hmgHospitalList = data.where((e) => e.isHMC == false).toList(); - hmcHospitalList = data.where((e) => e.isHMC == true).toList(); - hmgCount = hmgHospitalList?.length ?? 0; - hmcCount = hmcHospitalList?.length ?? 0; - notifyListeners(); - }, - ); - } - - setSelectedFacility(FacilitySelection selection) { - selectedFacility = selection; - notifyListeners(); - } - - searchHospitals(String query) { - if (query.isEmpty) { - getDisplayList(); - return; - } - List? sourceList; - switch (selectedFacility) { - case FacilitySelection.ALL: - sourceList = hospitalList; - break; - case FacilitySelection.HMG: - sourceList = hmgHospitalList; - break; - case FacilitySelection.HMC: - sourceList = hmcHospitalList; - break; - } - displayList = sourceList?.where((hospital) => hospital.name != null && hospital.name!.toLowerCase().contains(query.toLowerCase())).toList(); - notifyListeners(); - } - - getDisplayList() { - switch (selectedFacility) { - case FacilitySelection.ALL: - displayList = hospitalList; - break; - case FacilitySelection.HMG: - displayList = hmgHospitalList; - break; - case FacilitySelection.HMC: - displayList = hmcHospitalList; - break; - } - notifyListeners(); - } - - void setSelectedHospital(HospitalsModel? hospital) { - selectedHospital = hospital; - notifyListeners(); - } - - String? getSelectedHospitalName() { - return selectedHospital?.getName(isArabic); - } - void navigateTOAmbulancePage() { - locationUtils!.getLocation( - isShowConfirmDialog: true, - onSuccess: (position) { - updateBottomSheetState(BottomSheetType.FIXED); - navServices.push( - CustomPageRoute(page: CallAmbulancePage(), direction: AxisDirection.down), - ); - }); + placeValueInController(); + getProjects(); + flushData(); + selectedFacility = FacilitySelection.ALL; + + print("the app state is ${appState.isAuthenticated}"); + if (appState.isAuthenticated) { + locationUtils!.getLocation( + isShowConfirmDialog: true, + onSuccess: (position) { + updateBottomSheetState(BottomSheetType.FIXED); + navServices.push( + CustomPageRoute( + page: CallAmbulancePage(), direction: AxisDirection.down), + ); + }); + } else { + dialogService.showErrorBottomSheet( + message: "You Need To Login First To Continue".needTranslation, + onOkPressed: () { + navServices.pop(); + navServices.pushAndReplace( + AppRoutes.loginScreen + ); + }); + } } void navigateToEROnlineCheckIn() { @@ -279,27 +285,33 @@ class EmergencyServicesViewModel extends ChangeNotifier { } void updateBottomSheetState(BottomSheetType sheetType) { + if (sheetType == BottomSheetType.EXPANDED && selectedHospital == null) { + dialogService.showErrorBottomSheet(message: "Kindly Select Hospital".needTranslation); + return; + } bottomSheetType = sheetType; + if (sheetType == BottomSheetType.EXPANDED) { + clearAppointmentData(); + } notifyListeners(); } void setIsGMSAvailable(bool value) { notifyListeners(); } - Future checkPatientERAdvanceBalance({Function(dynamic)? onSuccess, Function(String)? onError}) async { final result = await emergencyServicesRepo.checkPatientERAdvanceBalance(); result.fold( // (failure) async => await errorHandlerService.handleError(failure: failure), - (failure) { + (failure) { patientHasAdvanceERBalance = false; isERBookAppointment = true; if (onSuccess != null) { onSuccess(failure.message); } }, - (apiResponse) { + (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); patientHasAdvanceERBalance = false; @@ -320,12 +332,12 @@ class EmergencyServicesViewModel extends ChangeNotifier { final result = await emergencyServicesRepo.checkPatientERPaymentInformation(projectID: selectedHospital!.iD); result.fold( - (failure) { + (failure) { if (onError != null) { onError(failure.message); } }, - (apiResponse) { + (apiResponse) { if (apiResponse.messageStatus == 2) { } else if (apiResponse.messageStatus == 1) { erOnlineCheckInPaymentDetailsResponse = apiResponse.data!; @@ -347,12 +359,12 @@ class EmergencyServicesViewModel extends ChangeNotifier { paymentReference: paymentReference); result.fold( - (failure) { + (failure) { if (onError != null) { onError(failure.message); } }, - (apiResponse) { + (apiResponse) { if (apiResponse.messageStatus == 2) { } else if (apiResponse.messageStatus == 1) { // erOnlineCheckInPaymentDetailsResponse = apiResponse.data!; @@ -370,8 +382,8 @@ class EmergencyServicesViewModel extends ChangeNotifier { final result = await emergencyServicesRepo.addAdvanceNumberRequest(advanceNumber: advanceNumber, paymentReference: paymentReference, appointmentNo: appointmentNo); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), - (apiResponse) { + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); } else if (apiResponse.messageStatus == 1) { @@ -389,12 +401,12 @@ class EmergencyServicesViewModel extends ChangeNotifier { result.fold( // (failure) async => await errorHandlerService.handleError(failure: failure), - (failure) { + (failure) { if (onError != null) { onError(failure.message); } }, - (apiResponse) { + (apiResponse) { if (apiResponse.messageStatus == 2) { if (onError != null) { onError(apiResponse.errorMessage!); @@ -414,12 +426,12 @@ class EmergencyServicesViewModel extends ChangeNotifier { result.fold( // (failure) async => await errorHandlerService.handleError(failure: failure), - (failure) { + (failure) { if (onError != null) { onError(failure.message); } }, - (apiResponse) { + (apiResponse) { if (apiResponse.messageStatus == 2) { if (onError != null) { onError(apiResponse.data["InvoiceResponse"]["Message"]); @@ -434,4 +446,401 @@ class EmergencyServicesViewModel extends ChangeNotifier { }, ); } + + Future getTransportationOption() async { + //handle the cache if the data is present then dont fetch it in the authenticated lifecycle + + print("the app state is ${appState.isAuthenticated}"); + if (appState.isAuthenticated == false) { + dialogService.showErrorBottomSheet( + message: "You Need To Login First To Continue".needTranslation, + onOkPressed: () { + navServices.pop(); + print("inside the ok button"); + getIt().onLoginPressed(); + }); + return; + } + + if (transportationOptions.isNotEmpty) return; + + int? id = appState.getAuthenticatedUser()?.patientId; + LoaderBottomSheet.showLoader( + loadingText: "Getting Ambulance Transport Option".needTranslation); + + notifyListeners(); + var response = await emergencyServicesRepo.getTransportationMethods(id: id); + + response.fold( + (failure) async { + LoaderBottomSheet.hideLoader(); + }, + (apiResponse) { + transportationOptions = apiResponse.data!; + LoaderBottomSheet.hideLoader(); + notifyListeners(); + }, + ); + } + + Future getTransportationMethods() async { + int? id = appState.getAuthenticatedUser()?.patientId; + LoaderBottomSheet.showLoader( + loadingText: "Getting Ambulance Transport Option".needTranslation); + + notifyListeners(); + var response = await emergencyServicesRepo.getTransportationMethods(id: id); + + response.fold( + (failure) async { + LoaderBottomSheet.hideLoader(); + }, + (apiResponse) { + transportationOptions = apiResponse.data!; + LoaderBottomSheet.hideLoader(); + notifyListeners(); + }, + ); + } + + void setTransportationOption(PatientERTransportationMethod item) { + selectedTransportOption = item; + } + + void updateCallingPlace(AmbulanceCallingPlace? value) { + callingPlace = value!; + notifyListeners(); + } + + void updateDirection(AmbulanceDirection? value) { + ambulanceDirection = value!; + notifyListeners(); + } + + void placeValueInController() { + if (isGMSAvailable) { + gmsController = Completer(); + } else { + hmsController = Completer(); + } + } + + void moveController(Location location) { + if (isGMSAvailable) { + gmsController?.future.then((controller) { + controller.animateCamera( + GMSMapServices.CameraUpdate.newCameraPosition( + GMSMapServices.CameraPosition( + target: GMSMapServices.LatLng(location.lat, location.lng), + zoom: 18, + ), + ), + ); + }); + } else { + hmsController?.future.then((controller) { + controller.animateCamera( + HMSCameraServices.CameraUpdate.newCameraPosition( + HMSCameraServices.CameraPosition( + target: HMSCameraServices.LatLng(location.lat, location.lng), + zoom: 18, + ), + ), + ); + }); + } + } + + void moveToCurrentLocation() { + moveController(Location(lat: appState.userLat, lng: appState.userLong)); + } + + FutureOr getProjects() async { + // if (hospitalList.isNotEmpty) return; + var response = await emergencyServicesRepo.getProjectList(); + + response.fold( + (failure) async {}, + (apiResponse) async { + List? data = apiResponse.data; + if (data == null) return; + hospitalList = data; + hmgHospitalList = data.where((e) => e.isHMC == false).toList(); + hmcHospitalList = data.where((e) => e.isHMC == true).toList(); + hmgCount = hmgHospitalList?.length ?? 0; + hmcCount = hmcHospitalList?.length ?? 0; + getDisplayList(); + notifyListeners(); + }, + ); + } + + setSelectedFacility(FacilitySelection selection) { + selectedFacility = selection; + notifyListeners(); + } + + searchHospitals(String query) { + if (query.isEmpty) { + getDisplayList(); + return; + } + List? sourceList; + switch (selectedFacility) { + case FacilitySelection.ALL: + sourceList = hospitalList; + break; + case FacilitySelection.HMG: + sourceList = hmgHospitalList; + break; + case FacilitySelection.HMC: + sourceList = hmcHospitalList; + break; + } + displayList = sourceList + ?.where((hospital) => + hospital.name != null && + hospital.name!.toLowerCase().contains(query.toLowerCase())) + .toList(); + notifyListeners(); + } + + getDisplayList() { + switch (selectedFacility) { + case FacilitySelection.ALL: + displayList = hospitalList; + break; + case FacilitySelection.HMG: + displayList = hmgHospitalList; + break; + case FacilitySelection.HMC: + displayList = hmcHospitalList; + break; + } + notifyListeners(); + } + + + void setSelectedHospital(HospitalsModel? hospital) { + selectedHospital = hospital; + notifyListeners(); + } + + String? getSelectedHospitalName() { + return selectedHospital?.getName(isArabic); + } + + void updateThePickupPlaceFromLocation(bool value) { + pickupFromInsideTheLocation = value; + notifyListeners(); + } + + void flushData() { + selectedHospital = null; + displayList = hospitalList; + selectedFacility = FacilitySelection.ALL; + pickupFromInsideTheLocation = false; + haveAnAppointment = false; + appointment = null; + notifyListeners(); + } + + void clearAppointmentData() { + haveAnAppointment = false; + appointment = null; + notifyListeners(); + } + + void flushPickupInformation() { + ambulanceDirection = AmbulanceDirection.ONE_WAY; + callingPlace = AmbulanceCallingPlace.FROM_HOSPITAL; + notifyListeners(); + } + + num getTotalPrice() { + print("the ambulance direction is $ambulanceDirection"); + switch (ambulanceDirection) { + case AmbulanceDirection.ONE_WAY: + return selectedTransportOption?.priceTotal ?? 0; + case AmbulanceDirection.TWO_WAY: + return (selectedTransportOption?.priceTotal ?? 0) * 2; + } + } + + Future updateAppointment(bool value) async { + + if (value) { + await getAppointments(); + } else { + clearAppointmentData(); + } + if(appointments?.isNotEmpty == true) { + haveAnAppointment = value; + } + notifyListeners(); + } + + Future getAppointments() async { + LoaderBottomSheet.showLoader(loadingText: "Fetching Appointment".needTranslation); + var result = await appointmentRepo.getPatientAppointments(isActiveAppointment: true, isArrivedAppointments: false); + LoaderBottomSheet.hideLoader(); + + result.fold((failure) { + haveAnAppointment = false; + notifyListeners(); + errorHandlerService.handleError(failure: failure); + }, (response) { + appointments = response.data; + if (appointments?.isEmpty == true) { + haveAnAppointment = false; + dialogService.showErrorBottomSheet(message: "Unable to Fetch Appointment"); + } + notifyListeners(); + }); + } + + void setSelectedAppointment(PatientAppointmentHistoryResponseModel selectedAppointment) { + appointment = selectedAppointment; + notifyListeners(); + } + + void checkHasAppointment() { + if (appointment == null) { + haveAnAppointment = false; + } + notifyListeners(); + } + + FutureOr submitAmbulanceRequest(GeocodeResult? result, PlaceDetails? place, PlacePrediction? placePrediction) async { + PatientER_RC request = createRequest(result, place, placePrediction); + navServices.push(CustomPageRoute(page: RequestingServicesPage())); + + var response = await emergencyServicesRepo.submitAmbulanceRequest(request); + response.fold((failure) { + navServices.pushAndRemoveUntil( + CustomPageRoute( + page: TrackingScreen( + state: OrderTrackingState.failed, + )), + ModalRoute.withName("/EmergencyServicesPage")); + }, (success) { + getTransportationOrders(shouldNavigateToTrackingScreen: true); + }); + } + + Future getTransportationOrders({bool shouldNavigateToTrackingScreen = false, bool showLoader = false}) async { + if(shouldNavigateToTrackingScreen == false && showLoader ) { + LoaderBottomSheet.showLoader(loadingText: "Fetching Orders"); + } + int? id = appState.getAuthenticatedUser()?.patientId; + + var response = await emergencyServicesRepo.getTransportationOrders(id: id); + if(shouldNavigateToTrackingScreen == false && showLoader ) { + LoaderBottomSheet.hideLoader();} + response.fold( + (failure) async { + if (shouldNavigateToTrackingScreen) { + navServices.pushAndRemoveUntil(CustomPageRoute(page: TrackingScreen(state: OrderTrackingState.waitingForCall)), ModalRoute.withName("/EmergencyServicesPage")); + } + }, + (apiResponse) { + if (shouldNavigateToTrackingScreen) { + navServices.pushAndRemoveUntil( + CustomPageRoute( + page: TrackingScreen( + state: OrderTrackingState.waitingForCall, + order: apiResponse.data?.firstWhere((element) => element.statusId == 1), + )), + ModalRoute.withName("/EmergencyServicesPage")); + } + + orders = apiResponse.data; + notifyListeners(); + }, + ); + } + + PatientER_RC createRequest(GeocodeResult? result, PlaceDetails? place, PlacePrediction? placePrediction) { + PatientER_RC request = PatientER_RC(); + TransportationDetails transportationDetails = TransportationDetails(); + transportationDetails.requesterIsOutSA = false; + transportationDetails.pickupSpot = pickupFromInsideTheLocation ? 1 : 0; + + transportationDetails.tripType = ambulanceDirection == AmbulanceDirection.ONE_WAY ? 0 : 1; + transportationDetails.ambulate = (transportationOptions.indexOf(selectedTransportOption!) + 1); + transportationDetails.transportationType = selectedTransportOption?.iD.toString() ?? ""; + request.patientERTransportationMethod = selectedTransportOption; + transportationDetails.pickupUrgency = 1; + switch (callingPlace) { + case AmbulanceCallingPlace.FROM_HOSPITAL: + transportationDetails.dropoffLatitude = selectedHospital!.latitude; + transportationDetails.dropoffLongitude = selectedHospital!.longitude; + transportationDetails.dropoffLocationName = selectedHospital!.name; + transportationDetails.pickupLatitude = ((result?.geometry.location.lat) ?? place?.lat)?.toStringAsFixed(6); + transportationDetails.pickupLongitude = ((result?.geometry.location.lng) ?? place?.lng)?.toStringAsFixed(6); + transportationDetails.pickupLocationName = result?.formattedAddress ?? placePrediction?.description; + transportationDetails.direction = 1; + break; + case AmbulanceCallingPlace.TO_HOSPITAL: + transportationDetails.pickupLatitude = selectedHospital!.latitude; + transportationDetails.pickupLongitude = selectedHospital!.longitude; + transportationDetails.pickupLocationName = selectedHospital!.name; + transportationDetails.dropoffLatitude = ((result?.geometry.location.lat) ?? place?.lat)?.toStringAsFixed(6); + transportationDetails.dropoffLongitude = ((result?.geometry.location.lng) ?? place?.lng)?.toStringAsFixed(6); + transportationDetails.dropoffLocationName = result?.formattedAddress ?? placePrediction?.description; + transportationDetails.direction = 0; + break; + } + AuthenticatedUser? user = appState.getAuthenticatedUser(); + if (user == null) throw Exception("Authentication Required to Continue"); + + request.patientIdentificationID = user.patientIdentificationNo; + request.projectID = user.projectId; + request.patientID = user.patientId; + transportationDetails.requesterIsOutSA = false; + transportationDetails.requesterMobileNo = user.mobileNumber; + transportationDetails.requesterFileNo = user.patientId; + transportationDetails.pickupDateTime = DateUtil.convertDateToStringLocation(DateTime.now()); + if (haveAnAppointment) { + transportationDetails.appointmentNo = appointment!.appointmentNo.toString(); + transportationDetails.appointmentClinicName = appointment!.clinicName; + transportationDetails.appointmentDoctorName = appointment!.doctorNameObj; + transportationDetails.appointmentBranch = appointment!.projectName; + transportationDetails.appointmentTime = appointment!.appointmentDate; + transportationDetails.haveAppointment = 1; + transportationDetails.dropoffLatitude = appointment!.latitude; + transportationDetails.dropoffLongitude = appointment!.longitude; + transportationDetails.dropoffLocationName = appointment!.projectName; + } + + transportationDetails.pickupSpot = pickupFromInsideTheLocation ? 1 : 0; + transportationDetails.ambulate = 3; + transportationDetails.ambulateTitle = "none"; + transportationDetails.notes = ""; + request.transportationDetails = transportationDetails; + + return request; + } + + void clearSearchText() { + searchController.clear(); + } + + Future cancelOrder(AmbulanceRequestOrdersModel? order, {bool shouldPop = false}) async { + dialogService.showCommonBottomSheetWithoutH( + message: "Do you want to cancel the order".needTranslation, + onOkPressed: () async { + navServices.pop(); + LoaderBottomSheet.showLoader(loadingText: "Cancelling Order".needTranslation); + var response = await emergencyServicesRepo.cancelOrder(order?.iD, appState.getAuthenticatedUser()?.patientId ?? 0); + LoaderBottomSheet.hideLoader(); + response.fold((failure) => errorHandlerService.handleError(failure: failure), (success) { + getTransportationOrders(); + if (shouldPop) navServices.pop(); + }); + }, + onCancelPressed: () { + navServices.pop(); + }); + } } diff --git a/lib/features/emergency_services/models/AmbulanceCallingPlace.dart b/lib/features/emergency_services/models/AmbulanceCallingPlace.dart new file mode 100644 index 0000000..cf648f7 --- /dev/null +++ b/lib/features/emergency_services/models/AmbulanceCallingPlace.dart @@ -0,0 +1,4 @@ +enum AmbulanceCallingPlace{ + FROM_HOSPITAL, + TO_HOSPITAL +} \ No newline at end of file diff --git a/lib/features/emergency_services/models/ambulance_direction.dart b/lib/features/emergency_services/models/ambulance_direction.dart new file mode 100644 index 0000000..d301921 --- /dev/null +++ b/lib/features/emergency_services/models/ambulance_direction.dart @@ -0,0 +1,3 @@ +enum AmbulanceDirection{ + ONE_WAY, TWO_WAY +} \ No newline at end of file diff --git a/lib/features/emergency_services/models/request_model/PatientER_RC.dart b/lib/features/emergency_services/models/request_model/PatientER_RC.dart new file mode 100644 index 0000000..9c3bbdc --- /dev/null +++ b/lib/features/emergency_services/models/request_model/PatientER_RC.dart @@ -0,0 +1,205 @@ + +import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/PatientERTransportationMethod.dart' show PatientERTransportationMethod; + +class PatientER_RC { + double? versionID; + int? channel; + int? languageID; + String? iPAdress; + String? generalid; + bool? patientOutSA; + String? sessionID; + bool? isDentalAllowedBackend; + int? deviceTypeID; + int? patientID; + String? tokenID; + int? patientTypeID; + int? patientType; + int? orderServiceID; + String? patientIdentificationID; + dynamic patientOutSa; + int? projectID; + int? lineItemNo; + TransportationDetails? transportationDetails; + PatientERTransportationMethod? patientERTransportationMethod; + + PatientER_RC( + {this.versionID, + this.channel, + this.languageID, + this.iPAdress, + this.generalid, + this.patientOutSA, + this.sessionID, + this.isDentalAllowedBackend, + this.deviceTypeID, + this.patientID, + this.tokenID, + this.patientTypeID, + this.patientType, + this.orderServiceID, + this.patientIdentificationID, + this.patientOutSa, + this.projectID, + this.lineItemNo, + this.transportationDetails }); + + PatientER_RC.fromJson(Map json) { + versionID = json['VersionID']; + channel = json['Channel']; + languageID = json['LanguageID']; + iPAdress = json['IPAdress']; + generalid = json['generalid']; + patientOutSA = json['PatientOutSA']; + sessionID = json['SessionID']; + isDentalAllowedBackend = json['isDentalAllowedBackend']; + deviceTypeID = json['DeviceTypeID']; + patientID = json['PatientID']; + tokenID = json['TokenID']; + patientTypeID = json['PatientTypeID']; + patientType = json['PatientType']; + orderServiceID = json['OrderServiceID']; + patientIdentificationID = json['PatientIdentificationID']; + patientOutSa = json['patientOutSa']; + projectID = json['projectID']; + lineItemNo = json['lineItemNo']; + transportationDetails = json['transportationDetails'] != null + ? new TransportationDetails.fromJson(json['transportationDetails']) + : null; + } + + Map toJson() { + final Map data = new Map(); + data['VersionID'] = this.versionID; + data['Channel'] = this.channel; + data['LanguageID'] = this.languageID; + data['IPAdress'] = this.iPAdress; + data['generalid'] = this.generalid; + data['PatientOutSA'] = this.patientOutSA; + data['SessionID'] = this.sessionID; + data['isDentalAllowedBackend'] = this.isDentalAllowedBackend; + data['DeviceTypeID'] = this.deviceTypeID; + data['PatientID'] = this.patientID; + data['TokenID'] = this.tokenID; + data['PatientTypeID'] = this.patientTypeID; + data['PatientType'] = this.patientType; + data['OrderServiceID'] = this.orderServiceID; + data['PatientIdentificationID'] = this.patientIdentificationID; + data['patientOutSa'] = this.patientOutSa; + data['projectID'] = this.projectID; + data['lineItemNo'] = this.lineItemNo; + if (this.transportationDetails != null) { + data['transportationDetails'] = this.transportationDetails!.toJson(); + } + return data; + } +} + +class TransportationDetails { + int? direction; + int? haveAppointment; + int? tripType; + int? pickupUrgency; + int? pickupSpot; + String? pickupDateTime; + String? transportationType; + int? ambulate; + String? ambulateTitle; + String? notes; + int? requesterFileNo; + String? requesterMobileNo; + bool? requesterIsOutSA; + String? pickupLocationName; + String? dropoffLocationName; + String? pickupLatitude; + String? pickupLongitude; + String? dropoffLatitude; + String? dropoffLongitude; + String? appointmentNo; + String? appointmentClinicName; + String? appointmentDoctorName; + String? appointmentBranch; + String? appointmentTime; + + TransportationDetails( + {this.direction, + this.haveAppointment = 0, + this.tripType, + this.pickupUrgency, + this.pickupSpot, + this.pickupDateTime, + this.transportationType, + this.ambulate, + this.ambulateTitle, + this.notes, + this.requesterFileNo, + this.requesterMobileNo, + this.requesterIsOutSA, + this.pickupLocationName, + this.dropoffLocationName, + this.pickupLatitude, + this.pickupLongitude, + this.dropoffLatitude, + this.dropoffLongitude, + this.appointmentNo = "0", + this.appointmentClinicName = "", + this.appointmentDoctorName = "", + this.appointmentBranch = "", + this.appointmentTime = ""}); + + TransportationDetails.fromJson(Map json) { + direction = json['direction']; + haveAppointment = json['haveAppointment']; + tripType = json['tripType']; + pickupUrgency = json['pickupUrgency']; + pickupSpot = json['pickupSpot']; + pickupDateTime = json['pickupDateTime']; + transportationType = json['transportationType']; + ambulate = json['ambulate']; + ambulateTitle = ""; + notes = json['notes']; + requesterFileNo = json['requesterFileNo']; + requesterMobileNo = json['requesterMobileNo']; + requesterIsOutSA = json['requesterIsOutSA']; + pickupLocationName = json['pickupLocationName']; + dropoffLocationName = json['dropoffLocationName']; + pickupLatitude = json['pickup_Latitude']; + pickupLongitude = json['pickup_Longitude']; + dropoffLatitude = json['dropoff_Latitude']; + dropoffLongitude = json['dropoff_Longitude']; + appointmentNo = json['appointmentNo']; + appointmentClinicName = json['appointmentClinicName']; + appointmentDoctorName = json['appointmentDoctorName']; + appointmentBranch = json['appointmentBranch']; + appointmentTime = json['appointmentTime']; + } + + Map toJson() { + final Map data = new Map(); + data['direction'] = this.direction; + data['haveAppointment'] = this.haveAppointment; + data['tripType'] = this.tripType; + data['pickupUrgency'] = this.pickupUrgency; + data['pickupSpot'] = this.pickupSpot; + data['pickupDateTime'] = this.pickupDateTime; + data['transportationType'] = this.transportationType; + data['ambulate'] = this.ambulate; + data['ambulateTitle'] = this.ambulateTitle; + data['notes'] = this.notes; + data['requesterFileNo'] = this.requesterFileNo; + data['requesterMobileNo'] = this.requesterMobileNo; + data['requesterIsOutSA'] = this.requesterIsOutSA; + data['pickupLocationName'] = this.pickupLocationName; + data['dropoffLocationName'] = this.dropoffLocationName; + data['pickup_Latitude'] = this.pickupLatitude; + data['pickup_Longitude'] = this.pickupLongitude; + data['dropoff_Latitude'] = this.dropoffLatitude; + data['dropoff_Longitude'] = this.dropoffLongitude; + data['appointmentNo'] = this.appointmentNo; + data['appointmentClinicName'] = this.appointmentClinicName; + data['appointmentDoctorName'] = this.appointmentDoctorName; + data['appointmentBranch'] = this.appointmentBranch; + data['appointmentTime'] = this.appointmentTime; + return data; + } +} diff --git a/lib/features/emergency_services/models/resp_model/AmbulanceRequestOrdersModel.dart b/lib/features/emergency_services/models/resp_model/AmbulanceRequestOrdersModel.dart new file mode 100644 index 0000000..6993183 --- /dev/null +++ b/lib/features/emergency_services/models/resp_model/AmbulanceRequestOrdersModel.dart @@ -0,0 +1,346 @@ +class AmbulanceRequestOrdersModel { + String? statusText; + num? paymentStatus; + dynamic clientRequestid; + dynamic paymentStatusText; + dynamic projectName; + String? nearestProjectName; + num? paymentAmount; + WFOrder? wFOrder; + String? serviceText; + bool? isSentForApproval; + num? exaCartOrderId; + String? exaCartGUID; + bool? isTimer; + num? timeSeconds; + num? totalPendingSeconds; + num? timeMinute; + num? timeHour; + num? timeTotalSeconds; + num? timeTotalMinute; + num? timeTotalHour; + dynamic approvalStatus; + bool? isActive; + num? clickButton; + dynamic orderHistory; + String? pickupLocation; + String? dropOffLocation; + String? clinicName; + String? doctorName; + String? branch; + String? time; + String? notes; + int? iD; + num? patientId; + num? patientOutSa; + bool? isOutPatient; + num? projectId; + num? nearestProjectId; + dynamic longitude; + dynamic latitude; + dynamic appointmentNo; + dynamic dischargeId; + int? statusId; + num? serviceId; + num? channel; + Orderpayment? orderpayment; + dynamic wforder; + dynamic orderapprovalobj; + String? created; + dynamic createdBy; + dynamic modified; + dynamic modifiedBy; + bool? isDeleted; + + AmbulanceRequestOrdersModel( + {this.statusText, + this.paymentStatus, + this.clientRequestid, + this.paymentStatusText, + this.projectName, + this.nearestProjectName, + this.paymentAmount, + this.wFOrder, + this.serviceText, + this.isSentForApproval, + this.exaCartOrderId, + this.exaCartGUID, + this.isTimer, + this.timeSeconds, + this.totalPendingSeconds, + this.timeMinute, + this.timeHour, + this.timeTotalSeconds, + this.timeTotalMinute, + this.timeTotalHour, + this.approvalStatus, + this.isActive, + this.clickButton, + this.orderHistory, + this.pickupLocation, + this.dropOffLocation, + this.clinicName, + this.doctorName, + this.branch, + this.time, + this.notes, + this.iD, + this.patientId, + this.patientOutSa, + this.isOutPatient, + this.projectId, + this.nearestProjectId, + this.longitude, + this.latitude, + this.appointmentNo, + this.dischargeId, + this.statusId, + this.serviceId, + this.channel, + this.orderpayment, + this.wforder, + this.orderapprovalobj, + this.created, + this.createdBy, + this.modified, + this.modifiedBy, + this.isDeleted}); + + AmbulanceRequestOrdersModel.fromJson(Map json) { + statusText = json['StatusText']; + paymentStatus = json['PaymentStatus']; + clientRequestid = json['ClientRequestid']; + paymentStatusText = json['PaymentStatusText']; + projectName = json['ProjectName']; + nearestProjectName = json['NearestProjectName']; + paymentAmount = json['PaymentAmount']; + wFOrder = json['WF_order'] != null + ? new WFOrder.fromJson(json['WF_order']) + : null; + serviceText = json['ServiceText']; + isSentForApproval = json['isSentForApproval']; + exaCartOrderId = json['ExaCart_OrderId']; + exaCartGUID = json['ExaCart_GUID']; + isTimer = json['isTimer']; + timeSeconds = json['TimeSeconds']; + totalPendingSeconds = json['TotalPendingSeconds']; + timeMinute = json['TimeMinute']; + timeHour = json['TimeHour']; + timeTotalSeconds = json['TimeTotalSeconds']; + timeTotalMinute = json['TimeTotalMinute']; + timeTotalHour = json['TimeTotalHour']; + approvalStatus = json['ApprovalStatus']; + isActive = json['isActive']; + clickButton = json['ClickButton']; + orderHistory = json['OrderHistory']; + pickupLocation = json['PickupLocation']; + dropOffLocation = json['DropOffLocation']; + clinicName = json['clinicName']; + doctorName = json['DoctorName']; + branch = json['Branch']; + time = json['Time']; + notes = json['Notes']; + iD = json['ID']; + patientId = json['PatientId']; + patientOutSa = json['PatientOutSa']; + isOutPatient = json['IsOutPatient']; + projectId = json['ProjectId']; + nearestProjectId = json['NearestProjectId']; + longitude = json['Longitude']; + latitude = json['Latitude']; + appointmentNo = json['AppointmentNo']; + dischargeId = json['DischargeId']; + statusId = json['StatusId']; + serviceId = json['ServiceId']; + channel = json['Channel']; + orderpayment = json['orderpayment'] != null + ? new Orderpayment.fromJson(json['orderpayment']) + : null; + wforder = json['wforder']; + orderapprovalobj = json['orderapprovalobj']; + created = json['Created']; + createdBy = json['CreatedBy']; + modified = json['Modified']; + modifiedBy = json['ModifiedBy']; + isDeleted = json['IsDeleted']; + } + + Map toJson() { + final Map data = new Map(); + data['StatusText'] = this.statusText; + data['PaymentStatus'] = this.paymentStatus; + data['ClientRequestid'] = this.clientRequestid; + data['PaymentStatusText'] = this.paymentStatusText; + data['ProjectName'] = this.projectName; + data['NearestProjectName'] = this.nearestProjectName; + data['PaymentAmount'] = this.paymentAmount; + if (this.wFOrder != null) { + data['WF_order'] = this.wFOrder!.toJson(); + } + data['ServiceText'] = this.serviceText; + data['isSentForApproval'] = this.isSentForApproval; + data['ExaCart_OrderId'] = this.exaCartOrderId; + data['ExaCart_GUID'] = this.exaCartGUID; + data['isTimer'] = this.isTimer; + data['TimeSeconds'] = this.timeSeconds; + data['TotalPendingSeconds'] = this.totalPendingSeconds; + data['TimeMinute'] = this.timeMinute; + data['TimeHour'] = this.timeHour; + data['TimeTotalSeconds'] = this.timeTotalSeconds; + data['TimeTotalMinute'] = this.timeTotalMinute; + data['TimeTotalHour'] = this.timeTotalHour; + data['ApprovalStatus'] = this.approvalStatus; + data['isActive'] = this.isActive; + data['ClickButton'] = this.clickButton; + data['OrderHistory'] = this.orderHistory; + data['PickupLocation'] = this.pickupLocation; + data['DropOffLocation'] = this.dropOffLocation; + data['clinicName'] = this.clinicName; + data['DoctorName'] = this.doctorName; + data['Branch'] = this.branch; + data['Time'] = this.time; + data['Notes'] = this.notes; + data['ID'] = this.iD; + data['PatientId'] = this.patientId; + data['PatientOutSa'] = this.patientOutSa; + data['IsOutPatient'] = this.isOutPatient; + data['ProjectId'] = this.projectId; + data['NearestProjectId'] = this.nearestProjectId; + data['Longitude'] = this.longitude; + data['Latitude'] = this.latitude; + data['AppointmentNo'] = this.appointmentNo; + data['DischargeId'] = this.dischargeId; + data['StatusId'] = this.statusId; + data['ServiceId'] = this.serviceId; + data['Channel'] = this.channel; + if (this.orderpayment != null) { + data['orderpayment'] = this.orderpayment!.toJson(); + } + data['wforder'] = this.wforder; + data['orderapprovalobj'] = this.orderapprovalobj; + data['Created'] = this.created; + data['CreatedBy'] = this.createdBy; + data['Modified'] = this.modified; + data['ModifiedBy'] = this.modifiedBy; + data['IsDeleted'] = this.isDeleted; + return data; + } +} + +class WFOrder { + dynamic wfButtonsDTO; + num? iD; + num? orderId; + num? previousStep; + num? nextStep; + num? serviceId; + dynamic order; + String? created; + dynamic createdBy; + dynamic modified; + dynamic modifiedBy; + bool? isDeleted; + + WFOrder( + {this.wfButtonsDTO, + this.iD, + this.orderId, + this.previousStep, + this.nextStep, + this.serviceId, + this.order, + this.created, + this.createdBy, + this.modified, + this.modifiedBy, + this.isDeleted}); + + WFOrder.fromJson(Map json) { + wfButtonsDTO = json['wf_ButtonsDTO']; + iD = json['ID']; + orderId = json['OrderId']; + previousStep = json['PreviousStep']; + nextStep = json['NextStep']; + serviceId = json['ServiceId']; + order = json['Order']; + created = json['Created']; + createdBy = json['CreatedBy']; + modified = json['Modified']; + modifiedBy = json['ModifiedBy']; + isDeleted = json['IsDeleted']; + } + + Map toJson() { + final Map data = new Map(); + data['wf_ButtonsDTO'] = this.wfButtonsDTO; + data['ID'] = this.iD; + data['OrderId'] = this.orderId; + data['PreviousStep'] = this.previousStep; + data['NextStep'] = this.nextStep; + data['ServiceId'] = this.serviceId; + data['Order'] = this.order; + data['Created'] = this.created; + data['CreatedBy'] = this.createdBy; + data['Modified'] = this.modified; + data['ModifiedBy'] = this.modifiedBy; + data['IsDeleted'] = this.isDeleted; + return data; + } +} + +class Orderpayment { + num? iD; + num? orderId; + dynamic clientRequestId; + num? totalAmount; + num? paymentStatus; + dynamic order; + String? created; + dynamic createdBy; + dynamic modified; + dynamic modifiedBy; + bool? isDeleted; + + Orderpayment( + {this.iD, + this.orderId, + this.clientRequestId, + this.totalAmount, + this.paymentStatus, + this.order, + this.created, + this.createdBy, + this.modified, + this.modifiedBy, + this.isDeleted}); + + Orderpayment.fromJson(Map json) { + iD = json['ID']; + orderId = json['OrderId']; + clientRequestId = json['ClientRequestId']; + totalAmount = json['TotalAmount']; + paymentStatus = json['PaymentStatus']; + order = json['Order']; + created = json['Created']; + createdBy = json['CreatedBy']; + modified = json['Modified']; + modifiedBy = json['ModifiedBy']; + isDeleted = json['IsDeleted']; + } + + Map toJson() { + final Map data = new Map(); + data['ID'] = this.iD; + data['OrderId'] = this.orderId; + data['ClientRequestId'] = this.clientRequestId; + data['TotalAmount'] = this.totalAmount; + data['PaymentStatus'] = this.paymentStatus; + data['Order'] = this.order; + data['Created'] = this.created; + data['CreatedBy'] = this.createdBy; + data['Modified'] = this.modified; + data['ModifiedBy'] = this.modifiedBy; + data['IsDeleted'] = this.isDeleted; + return data; + } +} diff --git a/lib/features/emergency_services/model/resp_model/EROnlineCheckInPaymentDetailsResponse.dart b/lib/features/emergency_services/models/resp_model/EROnlineCheckInPaymentDetailsResponse.dart similarity index 100% rename from lib/features/emergency_services/model/resp_model/EROnlineCheckInPaymentDetailsResponse.dart rename to lib/features/emergency_services/models/resp_model/EROnlineCheckInPaymentDetailsResponse.dart diff --git a/lib/features/emergency_services/models/resp_model/PatientERTransportationMethod.dart b/lib/features/emergency_services/models/resp_model/PatientERTransportationMethod.dart new file mode 100644 index 0000000..e4e6c7d --- /dev/null +++ b/lib/features/emergency_services/models/resp_model/PatientERTransportationMethod.dart @@ -0,0 +1,56 @@ +class PatientERTransportationMethod { + int? iD; + String? serviceID; + int? orderServiceID; + String? text; + String? textN; + dynamic price; + dynamic priceVAT; + dynamic priceTotal; + bool? isEnabled; + int? orderId; + int? quantity; + + PatientERTransportationMethod( + {this.iD, + this.serviceID, + this.orderServiceID, + this.text, + this.textN, + this.price, + this.priceVAT, + this.priceTotal, + this.isEnabled, + this.orderId, + this.quantity}); + + PatientERTransportationMethod.fromJson(Map json) { + iD = json['ID']; + serviceID = json['ServiceID']; + orderServiceID = json['OrderServiceID']; + text = json['Text']; + textN = json['TextN']; + price = json['Price']; + priceVAT = json['PriceVAT']; + priceTotal = json['PriceTotal']; + isEnabled = json['IsEnabled']; + orderId = json['OrderId']; + quantity = json['Quantity']; + } + + Map toJson() { + final Map data = new Map(); + data['ID'] = this.iD; + data['ServiceID'] = this.serviceID; + data['OrderServiceID'] = this.orderServiceID; + data['Text'] = this.text; + data['TextN'] = this.textN; + data['Price'] = this.price; + data['PriceVAT'] = this.priceVAT; + data['PriceTotal'] = this.priceTotal; + data['IsEnabled'] = this.isEnabled; + data['OrderId'] = this.orderId; + data['Quantity'] = this.quantity; + return data; + } +} \ No newline at end of file diff --git a/lib/features/emergency_services/model/resp_model/ProjectAvgERWaitingTime.dart b/lib/features/emergency_services/models/resp_model/ProjectAvgERWaitingTime.dart similarity index 100% rename from lib/features/emergency_services/model/resp_model/ProjectAvgERWaitingTime.dart rename to lib/features/emergency_services/models/resp_model/ProjectAvgERWaitingTime.dart diff --git a/lib/features/emergency_services/models/resp_models/rrt_procedures_response_model.dart b/lib/features/emergency_services/models/resp_model/rrt_procedures_response_model.dart similarity index 100% rename from lib/features/emergency_services/models/resp_models/rrt_procedures_response_model.dart rename to lib/features/emergency_services/models/resp_model/rrt_procedures_response_model.dart diff --git a/lib/features/location/GeocodeResponse.dart b/lib/features/location/GeocodeResponse.dart new file mode 100644 index 0000000..3e911aa --- /dev/null +++ b/lib/features/location/GeocodeResponse.dart @@ -0,0 +1,80 @@ +class GeocodeResponse { + final List results; + final String status; + + GeocodeResponse({ + required this.results, + required this.status, + }); + + factory GeocodeResponse.fromJson(Map json) { + final resultsList = (json['results'] as List? ?? []) + .map((e) => GeocodeResult.fromJson(e as Map)) + .toList(); + + return GeocodeResponse( + results: resultsList, + status: json['status'] ?? '', + ); + } + @override + String toString() { + return 'GeocodeResponse(status: $status, results: [${results.map((r) => r.toString()).join(', ')}])'; + } +} + +class GeocodeResult { + final String formattedAddress; + final Geometry geometry; + final String placeId; + + GeocodeResult({ + required this.formattedAddress, + required this.geometry, + required this.placeId, + }); + + factory GeocodeResult.fromJson(Map json) { + return GeocodeResult( + formattedAddress: json['formatted_address'] ?? '', + geometry: Geometry.fromJson(json['geometry']), + placeId: json['place_id'] ?? '', + ); + } + @override + String toString() { + return 'GeocodeResult(formattedAddress: $formattedAddress, placeId: $placeId, geometry: ${geometry.toString()})'; + } +} + +class Geometry { + final Location location; + + Geometry({required this.location}); + + factory Geometry.fromJson(Map json) { + return Geometry( + location: Location.fromJson(json['location']), + ); + } + + @override + String toString() => 'Geometry(location: ${location.toString()})'; +} + +class Location { + final double lat; + final double lng; + + Location({required this.lat, required this.lng}); + + factory Location.fromJson(Map json) { + return Location( + lat: (json['lat'] as num).toDouble(), + lng: (json['lng'] as num).toDouble(), + ); + } + + @override + String toString() => 'Location(lat: $lat, lng: $lng)'; +} \ No newline at end of file diff --git a/lib/features/location/PlaceDetails.dart b/lib/features/location/PlaceDetails.dart new file mode 100644 index 0000000..82846ea --- /dev/null +++ b/lib/features/location/PlaceDetails.dart @@ -0,0 +1,14 @@ +class PlaceDetails { + final double lat; + final double lng; + + PlaceDetails({required this.lat, required this.lng}); + + factory PlaceDetails.fromJson(Map json) { + final loc = json['result']['geometry']['location']; + return PlaceDetails( + lat: (loc['lat'] as num).toDouble(), + lng: (loc['lng'] as num).toDouble(), + ); + } +} \ No newline at end of file diff --git a/lib/features/location/PlacePrediction.dart b/lib/features/location/PlacePrediction.dart new file mode 100644 index 0000000..ea68153 --- /dev/null +++ b/lib/features/location/PlacePrediction.dart @@ -0,0 +1,11 @@ +class PlacePrediction { + final String description; + final String placeID; + PlacePrediction({required this.description, required this.placeID}); + factory PlacePrediction.fromJson(Map json) { + return PlacePrediction( + description: json['description'] ?? '', + placeID: json['place_id'] ?? '', + ); + } +} \ No newline at end of file diff --git a/lib/features/location/location_repo.dart b/lib/features/location/location_repo.dart new file mode 100644 index 0000000..46c97ac --- /dev/null +++ b/lib/features/location/location_repo.dart @@ -0,0 +1,153 @@ +import 'package:dartz/dartz.dart'; +import 'package:hmg_patient_app_new/core/api/api_client.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/location/GeocodeResponse.dart'; +import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart'; + +import '../../core/api_consts.dart'; +import 'PlacePrediction.dart'; + +abstract class LocationRepo { + Future>>> + getPlacePredictionsAsInput(String input); + + Future>> + getPlaceDetailsOfSelectedPrediction(String placeId); + + Future>> + getGeoCodeFromLatLng(double lat, double lng); +} + +class LocationRepoImpl implements LocationRepo { + final ApiClient apiClient; + + LocationRepoImpl({required this.apiClient}); + + @override + Future>>> getPlacePredictionsAsInput( + String input) async { + final url = Uri.parse( + 'https://maps.googleapis.com/maps/api/place/autocomplete/json?input=$input®ion=SA&key=$GOOGLE_API_KEY', + ); + + GenericApiModel>? apiResponse; + Failure? failure; + + try { + await apiClient.get( + url.toString(), + isExternal: true, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['predictions']; + final predictionsList = list + .map((item) => + PlacePrediction.fromJson(item as Map)) + .toList() + .cast(); + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: predictionsList, + ); + } 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())); + } + } + + @override + Future>> getGeoCodeFromLatLng(double lat, double lng) async { + final url = Uri.parse( + 'https://maps.googleapis.com/maps/api/geocode/json?latlng=$lat,$lng&key=$GOOGLE_API_KEY', + ); + + + GenericApiModel? apiResponse; + Failure? failure; + + try { + await apiClient.get( + url.toString(), + isExternal: true, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final predictionsList = GeocodeResponse.fromJson(response); + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: predictionsList, + ); + } 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())); + } + } + + @override + Future>> getPlaceDetailsOfSelectedPrediction(String placeId) async { + + + final url = Uri.parse( + 'https://maps.googleapis.com/maps/api/place/details/json' + '?place_id=$placeId&fields=geometry&key=$GOOGLE_API_KEY', + ); + + GenericApiModel? apiResponse; + Failure? failure; + + try { + await apiClient.get( + url.toString(), + isExternal: true, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final predictionsList = PlaceDetails.fromJson(response); + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: predictionsList, + ); + } 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/location/location_view_model.dart b/lib/features/location/location_view_model.dart new file mode 100644 index 0000000..e2e0cc5 --- /dev/null +++ b/lib/features/location/location_view_model.dart @@ -0,0 +1,116 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart' show ChangeNotifier; +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/src/types/camera.dart'; +import 'package:hmg_patient_app_new/features/location/GeocodeResponse.dart'; +import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart'; +import 'package:hmg_patient_app_new/features/location/location_repo.dart'; +import 'package:hmg_patient_app_new/services/error_handler_service.dart'; +import 'package:huawei_map/huawei_map.dart' as HMSCameraServices; +import 'package:google_maps_flutter/google_maps_flutter.dart' as GMSMapServices; + + + +import 'PlacePrediction.dart'; + +class LocationViewModel extends ChangeNotifier { + final LocationRepo locationRepo; + final ErrorHandlerService errorHandlerService; + + LocationViewModel({required this.locationRepo, required this.errorHandlerService}); + + List predictions = []; + PlacePrediction? selectedPrediction; + bool isPredictionLoading = false; + GeocodeResponse? geocodeResponse; + PlaceDetails? placeDetails; + + Location? mapCapturedLocation; + + FutureOr getPlacesPrediction(String input) async { + predictions = []; + isPredictionLoading= true; + final result = await locationRepo.getPlacePredictionsAsInput(input); + result.fold( + (failure) { + errorHandlerService.handleError(failure: failure); + }, + (apiModel) { + predictions = apiModel.data??[]; + }, + ); + isPredictionLoading = false; + notifyListeners(); + } + + FutureOr getPlaceEncodedData(double? lat, double? lng) async { + geocodeResponse = null; + final result = await locationRepo.getGeoCodeFromLatLng(lat!, lng!); + result.fold( + (failure) { + errorHandlerService.handleError(failure: failure); + }, + (apiModel) { + print("Geocode Response: ${apiModel.data}"); + geocodeResponse = apiModel.data; + }, + ); + notifyListeners(); + } + + FutureOr getPlaceDetails(String placeID) async { + placeDetails = null; + final result = await locationRepo.getPlaceDetailsOfSelectedPrediction(placeID); + result.fold( + (failure) { + errorHandlerService.handleError(failure: failure); + }, + (apiModel) { + placeDetails = apiModel.data; + }, + ); + notifyListeners(); + } + + handleGMSMapCameraMoved(GMSMapServices.CameraPosition value) { + mapCapturedLocation = Location(lat: value.target.latitude, lng: value.target.longitude); + + } + + handleHMSMapCameraMoved(HMSCameraServices.CameraPosition value) { + mapCapturedLocation = Location(lat: value.target.lat, lng: value.target.lng); + } + + handleOnCameraIdle(){ + if(mapCapturedLocation != null) { + getPlaceEncodedData(mapCapturedLocation!.lat, mapCapturedLocation!.lng); + } + } + + void updateSearchQuery(String? value) { + if(value == null || value.isEmpty){ + predictions = []; + return; + } + + getPlacesPrediction(value); + } + + void flushSearchPredictions() { + predictions = []; + mapCapturedLocation= null; + placeDetails= null; + geocodeResponse= null; + selectedPrediction= null; + + notifyListeners(); + } + + FutureOr selectPlacePrediction(PlacePrediction placePrediction) async{ + selectedPrediction= placePrediction; + await getPlaceDetails(placePrediction.placeID); + } + + +} \ No newline at end of file diff --git a/lib/features/my_appointments/models/resp_models/hospital_model.dart b/lib/features/my_appointments/models/resp_models/hospital_model.dart index 342fcea..9a211d0 100644 --- a/lib/features/my_appointments/models/resp_models/hospital_model.dart +++ b/lib/features/my_appointments/models/resp_models/hospital_model.dart @@ -101,4 +101,6 @@ class HospitalsModel { data['UsingInDoctorApp'] = this.usingInDoctorApp; return data; } + + } diff --git a/lib/main.dart b/lib/main.dart index 984cd26..20c4ece 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,6 +18,7 @@ import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_liveca import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/history/lab_history_viewmodel.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; +import 'package:hmg_patient_app_new/features/location/location_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; @@ -131,6 +132,8 @@ void main() async { ), ChangeNotifierProvider( create: (_) => getIt.get(), + ),ChangeNotifierProvider( + create: (_) => getIt.get(), ), ChangeNotifierProvider( create: (_) => getIt.get(), diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart index 4db3e2b..8d0f4e7 100644 --- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart +++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart @@ -13,18 +13,18 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; class AppointmentDoctorCard extends StatelessWidget { - const AppointmentDoctorCard({ - super.key, - required this.patientAppointmentHistoryResponseModel, - required this.onRescheduleTap, - required this.onCancelTap, - required this.onAskDoctorTap, - }); + const AppointmentDoctorCard( + {super.key, + required this.patientAppointmentHistoryResponseModel, + required this.onRescheduleTap, + required this.onCancelTap, + required this.onAskDoctorTap, this.renderWidgetForERDisplay = false}); final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; final VoidCallback onRescheduleTap; final VoidCallback onCancelTap; final VoidCallback onAskDoctorTap; + final bool renderWidgetForERDisplay; @override Widget build(BuildContext context) { @@ -99,8 +99,12 @@ class AppointmentDoctorCard extends StatelessWidget { ], ), SizedBox(height: 16.h), - getAppointmentActionButtons( - AppointmentType.isArrived(patientAppointmentHistoryResponseModel), + + Visibility( + visible: renderWidgetForERDisplay == false, + child: getAppointmentActionButtons( + AppointmentType.isArrived(patientAppointmentHistoryResponseModel), + ), ), ], ), diff --git a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart index 6d3307f..660f95e 100644 --- a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart +++ b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart @@ -72,6 +72,7 @@ class HospitalListItem extends StatelessWidget { ); Widget get distanceInfo => Row( + spacing: 4.w, children: [ Visibility( visible: (hospitalData?.distanceInKMs != "0"), @@ -91,9 +92,9 @@ class HospitalListItem extends StatelessWidget { labelText: "Distance not available".needTranslation, textColor: AppColors.blackColor, ), - SizedBox( - width: 8.h, - ) + // SizedBox( + // width: 8.h, + // ) ], )), Visibility( diff --git a/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart b/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart index 9bd6c5d..5e4b885 100644 --- a/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart +++ b/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart @@ -8,10 +8,19 @@ 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/emergency_services/emergency_services_view_model.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/AmbulanceCallingPlace.dart'; +import 'package:hmg_patient_app_new/features/location/GeocodeResponse.dart'; +import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart'; +import 'package:hmg_patient_app_new/features/location/PlacePrediction.dart'; +import 'package:hmg_patient_app_new/features/location/location_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_doctor_card.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/requesting_services_page.dart' show RequestingServicesPage; import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/tracking_screen.dart' show TrackingScreen; import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart'; +import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/appointment_bottom_sheet.dart' show AppointmentBottomSheet; +import 'package:hmg_patient_app_new/presentation/emergency_services/widgets/location_input_bottom_sheet.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/CustomSwitch.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; @@ -30,38 +39,79 @@ class CallAmbulancePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - bottomSheet: ExpandableBottomSheet( - bottomSheetType: - context.watch().bottomSheetType, - children: { - BottomSheetType.EXPANDED: ExpanedBottomSheet(context), - BottomSheetType.FIXED: FixedBottomSheet(context), - }, + floatingActionButton: Visibility( + visible: context.watch().bottomSheetType == + BottomSheetType.FIXED, + child: Padding( + padding: EdgeInsetsDirectional.only(end: 8.h, bottom: 68.h), + child: DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, borderRadius: 12.h), + child: Utils.buildSvgWithAssets( + icon: AppAssets.locate_me, width: 24.h, height: 24.h) + .paddingAll(12.h) + .onPress(() { + context + .read() + .moveToCurrentLocation(); + }), + ), + ), + ), + bottomSheet: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ExpandableBottomSheet( + bottomSheetType: + context.watch().bottomSheetType, + children: { + BottomSheetType.EXPANDED: ExpanedBottomSheet(context), + BottomSheetType.FIXED: FixedBottomSheet(context), + }, + ), + ], ), body: Stack( children: [ - if (context.read().isGMSAvailable || - Platform.isIOS) + if (context.read().isGMSAvailable ) GMSMap( currentLocation: context.read().getGMSLocation(), onCameraMoved: (value) => context - .read() - .handleGMSMapCameraMoved(value)) + .read() + .handleGMSMapCameraMoved(value), + onCameraIdle: + context.read().handleOnCameraIdle, + myLocationEnabled: true, + inputController: + context.read().gmsController, + showCenterMarker: true, + ) else HMSMap( currentLocation: context.read().getHMSLocation(), onCameraMoved: (value) => context - .read() - .handleHMSMapCameraMoved(value)), + .read() + .handleHMSMapCameraMoved(value), + onCameraIdle: + context.read().handleOnCameraIdle, + myLocationEnabled: false, + inputController: + context.read().hmsController, + showCenterMarker: true, + ), Align( alignment: AlignmentDirectional.topStart, child: Utils.buildSvgWithAssets( - icon: AppAssets.closeBottomNav, width: 32.h, height: 32.h), - ).paddingOnly(top: 51.h, left: 24.h).onPress((){ - Navigator.pop(context); - }) + icon: AppAssets.closeBottomNav, width: 32.h, height: 32.h) + .onPress(() { + context + .read() + .flushPickupInformation(); + Navigator.pop(context); + }), + ).paddingOnly(top: 51.h, left: 24.h), ], ), ); @@ -70,33 +120,34 @@ class CallAmbulancePage extends StatelessWidget { Widget FixedBottomSheet(BuildContext context) { return GestureDetector( onVerticalDragUpdate: (details){ - print("the delta is ${details.delta.dy}"); - - if(details.delta.dy<0){ - context.read().updateBottomSheetState(BottomSheetType.EXPANDED); - } + // if(details.delta.dy<0){ + // + // context.read().updateBottomSheetState(BottomSheetType.EXPANDED); + // } }, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.end, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Padding( - - padding: EdgeInsetsDirectional.only(end:24.h, bottom: 24.h), - child: DecoratedBox( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, borderRadius: 12.h), - child: Utils.buildSvgWithAssets( - icon: AppAssets.locate_me, width: 24.h, height: 24.h) - .paddingAll(12.h), - ), - ), - ], - ), + // Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // Padding( + // + // padding: EdgeInsetsDirectional.only(end:24.h, bottom: 24.h), + // child: DecoratedBox( + // decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + // color: AppColors.whiteColor, borderRadius: 12.h), + // child: Utils.buildSvgWithAssets( + // icon: AppAssets.locate_me, width: 24.h, height: 24.h) + // .paddingAll(12.h).onPress((){ + // context.read().moveToCurrentLocation(); + // }), + // ), + // ), + // ], + // ), // PositionedDirectional( // end: 1, // child: Padding( @@ -116,25 +167,7 @@ class CallAmbulancePage extends StatelessWidget { mainAxisSize: MainAxisSize.min, spacing: 24.h, children: [ - TextInputWidget( - labelText: "Enter Pickup Location Manually".needTranslation, - hintText: "Enter Pickup Location".needTranslation, - leadingIcon: AppAssets.location_pickup, - isAllowLeadingIcon: true, - isEnable: false, - prefix: null, - autoFocus: false, - isBorderAllowed: false, - keyboardType: TextInputType.text, - padding: EdgeInsets.symmetric( - vertical: ResponsiveExtension(10).h, - horizontal: ResponsiveExtension(15).h, - ), - ).onPress(() { - context - .read() - .updateBottomSheetState(BottomSheetType.EXPANDED); - }).paddingOnly(right: 24.h, left: 24.h), + inputFields(context), SizedBox( height: 200.h, child: DecoratedBox( @@ -152,11 +185,11 @@ class CallAmbulancePage extends StatelessWidget { Column( spacing: 4.h, children: [ - "Select Pickup Location".needTranslation.toText21( + "Select Pickup Details".needTranslation.toText21( weight: FontWeight.w600, color: AppColors.textColor, ), - " Please select the location of pickup" + " Please select the details of pickup" .needTranslation .toText12( fontWeight: FontWeight.w500, @@ -165,7 +198,7 @@ class CallAmbulancePage extends StatelessWidget { ], ), CustomButton( - text: "Select Location".needTranslation, + text: "Select Details".needTranslation, onPressed: () { context .read() @@ -202,21 +235,25 @@ class CallAmbulancePage extends StatelessWidget { ), child: Column( children: [ - SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 16.h, - children: [ - "Select Pickup Location".needTranslation.toText21( - weight: FontWeight.w600, - color: AppColors.textColor, - ), - locationsSections(context), - hospitalAndPickUpSection(context), - - ], - ).paddingOnly(top: 24.h, bottom: 32.h,left: 24.h, right: 24.h), - ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 16.h, + children: [ + + hospitalAndPickUpSection(context), + if(context.read().appointment != null) + AppointmentDoctorCard( + renderWidgetForERDisplay: true, + patientAppointmentHistoryResponseModel: context.read().appointment!, + onAskDoctorTap: () {}, + onCancelTap: () async {}, + onRescheduleTap: () async {}, + ).onPress((){ + openAppointmentList(context); + }) + + ], + ).paddingOnly(top: 24.h, bottom: 32.h,left: 24.h, right: 24.h), bottomPriceContent(context) ], @@ -281,44 +318,74 @@ class CallAmbulancePage extends StatelessWidget { return DecoratedBox( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, - borderRadius: 24.h, + borderRadius: 24.r, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, + spacing: 16.h, children: [ + // Row( + // children: [ + // hospitalAndPickUpItemContent( + // title: "Select Hospital".needTranslation, + // subTitle: "hospital".needTranslation, + // leadingIcon: AppAssets.hospital, + // ), + // Utils.buildSvgWithAssets(icon: AppAssets.down_cheveron, + // width: 24.h, height: 24.h) + // .paddingAll(16.h) + // ], + // ).onPress((){ + // showHospitalBottomSheet(context); + // }), + // SizedBox(height: 16.h), + // Divider( + // color: AppColors.bottomNAVBorder, + // height: 1, + // thickness: 1, + // ), + // SizedBox(height: 16.h), + Row( children: [ hospitalAndPickUpItemContent( - title: "Select Hospital".needTranslation, - subTitle: "hospital".needTranslation, - leadingIcon: AppAssets.hospital, + title: "Pick".needTranslation, + subTitle: "Inside the home".needTranslation, + leadingIcon: AppAssets.pickup_bed, ), - Utils.buildSvgWithAssets(icon: AppAssets.down_cheveron, - width: 24.h, height: 24.h) - .paddingAll(16.h) + CustomSwitch( + value: context + .watch() + .pickupFromInsideTheLocation, + onChanged: (value){ + context + .read() + .updateThePickupPlaceFromLocation(value); + }, + ) ], - ).onPress((){ - showHospitalBottomSheet(context); - }), - SizedBox(height: 16.h), - Divider( - color: AppColors.bottomNAVBorder, - height: 1, - thickness: 1, ), - SizedBox(height: 16.h), Row( children: [ hospitalAndPickUpItemContent( - title: "Pick".needTranslation, - subTitle: "Inside the home".needTranslation, - leadingIcon: AppAssets.pickup_bed, + title: '', + subTitle: "Have any appointment".needTranslation, + leadingIcon: AppAssets.appointment_checkin_icon, ), CustomSwitch( - value: context.watch().isGMSAvailable, - onChanged: (value){ - context.read().setIsGMSAvailable( value); + value: context + .watch() + .haveAnAppointment, + onChanged: (value) async { + // if (value) { + // openAppointmentList(context); + // } + await context.read() + .updateAppointment(value); + if (context.read().appointments?.isNotEmpty == true) { + openAppointmentList(context); + } }, ) ], @@ -354,13 +421,22 @@ class CallAmbulancePage extends StatelessWidget { this.leadingIcon(leadingIcon), Expanded( child: Column( + mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, children: [ - title.toText12( - color: AppColors.greyTextColor, - fontWeight: FontWeight.w500, + Visibility( + visible: title.isNotEmpty, + child: Column( + children: [ + title.toText12( + color: AppColors.greyTextColor, + fontWeight: FontWeight.w500, + ), + SizedBox(height: 2.h), + ], + ), ), - SizedBox(height: 2.h), subTitle.toText14( color: AppColors.textColor, weight: FontWeight.w500, @@ -418,17 +494,30 @@ class CallAmbulancePage extends StatelessWidget { ], ), ), - "\$250".toText24( - fontWeight: FontWeight.w600, - color: AppColors.textColor, - ) - ], - ), - CustomButton( - text: "Submit Request".needTranslation, - onPressed: () { - Navigator.push(context, CustomPageRoute(page: RequestingServicesPage())); - }) + Utils.getPaymentAmountWithSymbol( + (Utils.formatNumberToInternationalFormat(context + .read() + .getTotalPrice() ?? + 0)) + .toText24( + fontWeight: FontWeight.w600, + color: AppColors.textColor, + letterSpacing: -2), + AppColors.blackColor, + 17.h) + + // Utils.getPaymentAmountWithSymbol2(context.read().selectedTransportOption?.priceTotal??"0", letterSpacing: -2) + ], + ), + CustomButton( + text: "Submit Request".needTranslation, + onPressed: () { + LocationViewModel locationViewModel = context.read(); + GeocodeResponse? response = locationViewModel.geocodeResponse; + PlaceDetails? placeDetails = locationViewModel.placeDetails; + PlacePrediction? placePrediction = locationViewModel.selectedPrediction; + context.read().submitAmbulanceRequest(response?.results.first, placeDetails, placePrediction); + }) ], ).paddingOnly(top: 24.h, bottom: 12.h, left: 24.h, right: 24.h), ), @@ -443,6 +532,7 @@ class CallAmbulancePage extends StatelessWidget { context, child: Consumer( builder:(_,vm,__)=> HospitalBottomSheetBody( + searchText: vm.searchController, displayList: vm.displayList, onFacilityClicked: (value) { vm.setSelectedFacility(value); @@ -465,7 +555,137 @@ class CallAmbulancePage extends StatelessWidget { isCloseButtonVisible: true, hasBottomPadding: false, backgroundColor: AppColors.bottomSheetBgColor, + callBackFunc: () { + context.read().clearSearchText(); + }, + ); + } + + ///it will show the places field first and then hospital field + PlaceFirstThanHospitalField(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 16.h, + children: [ + textPlaceInput(context), + hospitalField(context), + ], + ).paddingOnly(right: 24.h, left: 24.h); + } + + HospitalFieldFirstThanPlaces(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 16.h, + children: [hospitalField(context), textPlaceInput(context)], + ).paddingOnly(right: 24.h, left: 24.h); + } + + textPlaceInput(context) { + return Consumer(builder: (_, vm, __) { + print( + "the data is ${vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description}"); + return SizedBox( + width: MediaQuery.sizeOf(context).width, + child: TextInputWidget( + labelText: "Enter Pickup Location Manually".needTranslation, + hintText: "Enter Pickup Location".needTranslation, + controller: TextEditingController( + text: vm.geocodeResponse?.results.first.formattedAddress ?? + vm.selectedPrediction?.description, + ), + leadingIcon: AppAssets.location_pickup, + isAllowLeadingIcon: true, + isEnable: false, + prefix: null, + autoFocus: false, + isBorderAllowed: false, + keyboardType: TextInputType.text, + padding: EdgeInsets.symmetric( + vertical: ResponsiveExtension(10).h, + horizontal: ResponsiveExtension(15).h, + ), + ).onPress(() { + openLocationInputBottomSheet(context); + }), + ); + }); + } + + ///decide which field to show first based on the selected calling place + inputFields(BuildContext context) { + return context.read().callingPlace == + AmbulanceCallingPlace.FROM_HOSPITAL + ? HospitalFieldFirstThanPlaces(context) + : PlaceFirstThanHospitalField(context); + } + + openLocationInputBottomSheet(BuildContext context) { + context.read().flushSearchPredictions(); + showCommonBottomSheetWithoutHeight( + title: "".needTranslation, + context, + child: SizedBox( + height: MediaQuery.sizeOf(context).height * .8, + child: LocationInputBottomSheet(), + ), + isFullScreen: false, + isCloseButtonVisible: true, + hasBottomPadding: false, + backgroundColor: AppColors.bottomSheetBgColor, callBackFunc: () {}, ); } + + hospitalField(BuildContext context) { + return DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, borderRadius: 12.h), + child: Row( + children: [ + hospitalAndPickUpItemContent( + title: "Select Hospital".needTranslation, + subTitle: context + .read() + .getSelectedHospitalName() ?? + "Select Hospital".needTranslation, + leadingIcon: AppAssets.hospital, + ), + Utils.buildSvgWithAssets( + icon: AppAssets.down_cheveron, width: 24.h, height: 24.h) + .paddingAll(16.h) + ], + ).onPress(() { + print("the item is clicked"); + showHospitalBottomSheet(context); + }).paddingSymmetrical( + 10.w, + 12.h, + ), + ); + } + + void openAppointmentList(BuildContext context) { + showCommonBottomSheetWithoutHeight( + title: "Select Appointment".needTranslation, + context, + child: SizedBox( + height: MediaQuery.sizeOf(context).height * .5, + child: AppointmentBottomSheet( + list: context.read().appointments!, + onAppointmentSelection: (appointment) { + Navigator.pop(context); + context.read().setSelectedAppointment(appointment); + }), + ), + isFullScreen: false, + isCloseButtonVisible: true, + hasBottomPadding: false, + backgroundColor: AppColors.bottomSheetBgColor, + callBackFunc: () { + context.read().checkHasAppointment(); + }, + ); + // ); + } } diff --git a/lib/presentation/emergency_services/call_ambulance/requesting_services_page.dart b/lib/presentation/emergency_services/call_ambulance/requesting_services_page.dart index 7f79be0..83c16b6 100644 --- a/lib/presentation/emergency_services/call_ambulance/requesting_services_page.dart +++ b/lib/presentation/emergency_services/call_ambulance/requesting_services_page.dart @@ -5,27 +5,17 @@ import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.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/emergency_services/models/request_model/PatientER_RC.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/tracking_screen.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart' show CustomPageRoute; import 'package:lottie/lottie.dart'; -class RequestingServicesPage extends StatefulWidget { - @override - State createState() => _RequestingServicesPageState(); -} +class RequestingServicesPage extends StatelessWidget { -class _RequestingServicesPageState extends State { - @override - void initState() { - Timer(Duration(seconds: 3), () { - Navigator.pushReplacement( - context, CustomPageRoute(page: TrackingScreen())); - }); - super.initState(); - } + const RequestingServicesPage({super.key,}); @override Widget build(BuildContext context) { return Material( diff --git a/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart b/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart index 3de469f..eddca11 100644 --- a/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart +++ b/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart @@ -7,6 +7,7 @@ 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/emergency_services/emergency_services_view_model.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/AmbulanceRequestOrdersModel.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; @@ -17,11 +18,33 @@ import 'package:hmg_patient_app_new/widgets/order_tracking/order_tracking_state. import 'package:hmg_patient_app_new/widgets/order_tracking/request_tracking.dart'; import 'package:lottie/lottie.dart'; import 'package:provider/provider.dart'; +import 'package:url_launcher/url_launcher.dart' show launchUrl; class TrackingScreen extends StatelessWidget { - final OrderTrackingState state = OrderTrackingState.ended; + OrderTrackingState? state ; + final AmbulanceRequestOrdersModel? order; - const TrackingScreen({super.key}); + TrackingScreen({super.key, OrderTrackingState? state, this.order}){ + if(state == null){ + switch (order?.statusId) { + case 1: //pending + case 2: //processing + this.state = OrderTrackingState.waitingForCall; + break; + case 3: //completed + this.state = OrderTrackingState.dispactched; + break; + case 4: //cancel + case 6: + case 7: + this.state = OrderTrackingState.cancel; + break; + } + + }else { + this.state = state; + } + } @override Widget build(BuildContext context) { @@ -62,6 +85,10 @@ class TrackingScreen extends StatelessWidget { return Lottie.asset(AppAnimations.ambulance, repeat: true, reverse: false, width: 260.h, height: 200.h); case OrderTrackingState.ended: return Lottie.asset(AppAnimations.checkmark, repeat: true, width: 260.h, height: 200.h); + case OrderTrackingState.failed: + case OrderTrackingState.cancel: + case null: + return Lottie.asset(AppAnimations.errorAnimation, repeat: true, width: 260.h, height: 200.h); } } @@ -92,7 +119,11 @@ class TrackingScreen extends StatelessWidget { } Widget trackingBottomSection(BuildContext context) { - switch (state) { + if(state == null ) + { + return SizedBox.shrink(); + } + switch (state!) { case OrderTrackingState.waitingForCall: return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -102,7 +133,9 @@ class TrackingScreen extends StatelessWidget { ), CustomButton( text: "Cancel Request".needTranslation, - onPressed: () async {}, + onPressed: () async { + openCancelOrderBottomSheet(context); + }, backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor, textColor: Colors.white, @@ -146,6 +179,10 @@ class TrackingScreen extends StatelessWidget { doctorSection(), ], ); + case OrderTrackingState.failed: + case OrderTrackingState.cancel: + return SizedBox.shrink(); + } } @@ -203,11 +240,17 @@ class TrackingScreen extends StatelessWidget { if (context.read().isGMSAvailable || Platform.isIOS) { return GMSMap( myLocationEnabled: false, + onCameraIdle: (){ + + }, currentLocation: context.read().getGMSLocation(), onCameraMoved: (value) => context.read().handleGMSMapCameraMoved(value)); } else { return HMSMap( myLocationEnabled: false, + onCameraIdle: (){ + + }, currentLocation: context.read().getHMSLocation(), onCameraMoved: (value) => context.read().handleHMSMapCameraMoved(value)); } @@ -221,7 +264,10 @@ class TrackingScreen extends StatelessWidget { } currentStatus() { - switch (state) { + if(state == null){ + return SizedBox.shrink(); + } + switch (state!) { case OrderTrackingState.waitingForCall: case OrderTrackingState.dispactched: return RichText( @@ -276,10 +322,16 @@ class TrackingScreen extends StatelessWidget { case OrderTrackingState.ended: return "Arrived".needTranslation.toText21(color: AppColors.textColor, weight: FontWeight.w600); + case OrderTrackingState.failed: + case OrderTrackingState.cancel: + return SizedBox.shrink(); } } orderTrackingId() { + if(state == OrderTrackingState.failed){ + return SizedBox.shrink(); + } return Container( padding: EdgeInsets.all(16.h), decoration: RoundedRectangleBorder().toSmoothCornerDecoration( @@ -291,15 +343,21 @@ class TrackingScreen extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, spacing: 8.h, children: [ - "Req ID:1234567890".toText16(color: AppColors.textColor, weight: FontWeight.w600), + + Visibility(visible:order != null ,child: "Req ID: ${order?.iD}".toText16(color: AppColors.textColor, weight: FontWeight.w600)), Row( spacing: 8.h, children: [ - chip("location name".needTranslation, AppAssets.location_pickup, AppColors.blackBgColor), - chip("hotel name ".needTranslation, AppAssets.hospital, AppColors.blackBgColor), + Flexible(child: + chip(order?.pickupLocation??"", AppAssets.location_pickup, AppColors.blackBgColor), + + ), + Flexible(child: + chip(order?.dropOffLocation??"", AppAssets.hospital, AppColors.blackBgColor), + + ) ], ), - chip("hotel name ".needTranslation, AppAssets.hospital, AppColors.blackBgColor), ], ), ); @@ -330,7 +388,7 @@ class TrackingScreen extends StatelessWidget { spacing: 4.h, children: [ "Contact Rapid Response Team (RRT)".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w600), - "1223456789".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + "0115259555".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), SizedBox(height: 8.h), ], ), @@ -348,7 +406,11 @@ class TrackingScreen extends StatelessWidget { borderColor: Colors.transparent, height: 36.h, ), - ) + ).onPress((){ + launchUrl( + Uri.parse("tel://0115259555"), + ); + }) ], ), ); @@ -363,7 +425,10 @@ class TrackingScreen extends StatelessWidget { // ] // } - getTitle(OrderTrackingState state) { + getTitle(OrderTrackingState? state) { + if(state == null) + return "Failed".needTranslation.toText16(color: AppColors.textColor, weight: FontWeight.w600); + switch (state) { case OrderTrackingState.waitingForCall: return "Confirmation Call".needTranslation.toText16(color: AppColors.textColor, weight: FontWeight.w600); @@ -373,6 +438,11 @@ class TrackingScreen extends StatelessWidget { return " On The Way To Hospital".needTranslation.toText16(color: AppColors.textColor, weight: FontWeight.w600); case OrderTrackingState.ended: return "Arrived at Hospital".needTranslation.toText16(color: AppColors.textColor, weight: FontWeight.w600); + case OrderTrackingState.failed: + return "Failed".needTranslation.toText16(color: AppColors.textColor, weight: FontWeight.w600); + + case OrderTrackingState.cancel: + return "Order Cancel".needTranslation.toText16(color: AppColors.textColor, weight: FontWeight.w600); } } @@ -383,4 +453,9 @@ class TrackingScreen extends StatelessWidget { child: ClipRRect(borderRadius: BorderRadius.circular(20.h), child: mapForRequestedCoordinate(context)), ); } + + + openCancelOrderBottomSheet(BuildContext context){ + context.read().cancelOrder(order, shouldPop: true); + } } diff --git a/lib/presentation/emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart b/lib/presentation/emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart index e264488..71e589e 100644 --- a/lib/presentation/emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart +++ b/lib/presentation/emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart @@ -24,8 +24,8 @@ import 'package:provider/provider.dart'; class HospitalBottomSheetBody extends StatelessWidget { - final TextEditingController searchText = TextEditingController(); - final Debouncer debouncer = Debouncer(milliseconds: 500); + final TextEditingController searchText ; + final Debouncer debouncer = Debouncer(milliseconds: 500); final int hmcCount; final int hmgCount; @@ -35,15 +35,7 @@ class HospitalBottomSheetBody extends StatelessWidget { final Function(HospitalsModel) onHospitalClicked; final Function(String) onHospitalSearch; - HospitalBottomSheetBody( - {super.key, - required this.hmcCount, - required this.hmgCount, - this.displayList, - required this.selectedFacility, - required this.onFacilityClicked, - required this.onHospitalClicked, - required this.onHospitalSearch}); + HospitalBottomSheetBody({super.key, required this.hmcCount, required this.hmgCount, this.displayList, required this.selectedFacility, required this.onFacilityClicked, required this.onHospitalClicked, required this.onHospitalSearch, required this.searchText}); @override Widget build(BuildContext context) { @@ -56,12 +48,13 @@ class HospitalBottomSheetBody extends StatelessWidget { hintText: LocaleKeys.searchHospital.tr(), controller: searchText, onChange: (value) { - debouncer.run(() { - onHospitalSearch(value ?? ""); + debouncer.run((){ + onHospitalSearch(value??""); }); }, isEnable: true, prefix: null, + autoFocus: false, isBorderAllowed: false, keyboardType: TextInputType.text, @@ -74,7 +67,7 @@ class HospitalBottomSheetBody extends StatelessWidget { ), SizedBox(height: 24.h), TypeSelectionWidget( - selectedFacility: selectedFacility, + selectedFacility:selectedFacility , hmcCount: hmcCount.toString(), hmgCount: hmgCount.toString(), onitemClicked: (selectedValue){ @@ -83,16 +76,16 @@ class HospitalBottomSheetBody extends StatelessWidget { ), SizedBox(height: 21.h), SizedBox( - height: MediaQuery.sizeOf(context).height * .4, - child: ListView.separated( + height: MediaQuery.sizeOf(context).height * .4, + child: ListView.separated( itemBuilder: (_, index) { var hospital = displayList?[index]; return HospitalListItem( hospitalData: Utils.convertToPatientDoctorAppointmentList(hospital), isLocationEnabled: true, - ).onPress(() { - onHospitalClicked(hospital!); + ).onPress(() { + onHospitalClicked(hospital!); });}, separatorBuilder: (_, __) => SizedBox( height: 16.h, diff --git a/lib/presentation/emergency_services/call_ambulance/widgets/ambulance_option_selection_bottomsheet.dart b/lib/presentation/emergency_services/call_ambulance/widgets/ambulance_option_selection_bottomsheet.dart index 564bd64..de20d1a 100644 --- a/lib/presentation/emergency_services/call_ambulance/widgets/ambulance_option_selection_bottomsheet.dart +++ b/lib/presentation/emergency_services/call_ambulance/widgets/ambulance_option_selection_bottomsheet.dart @@ -1,10 +1,14 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart' show AppAssets; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils; 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/emergency_services/emergency_services_view_model.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/PatientERTransportationMethod.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/transport_option_Item.dart' show TransportOptionItem; import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:provider/provider.dart'; class AmbulanceOptionSelectionBottomSheet extends StatelessWidget { final Function onTap; @@ -12,40 +16,36 @@ class AmbulanceOptionSelectionBottomSheet extends StatelessWidget { const AmbulanceOptionSelectionBottomSheet({super.key, required this.onTap}); @override Widget build(BuildContext context) { - return Column( - spacing: 16.h, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ + return Selector>( + selector: (_, model) => model.transportationOptions, + builder: (_, data, __) => Column( + spacing: 16.h, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: ListView.separated( + separatorBuilder: (_,__)=> SizedBox( + height: 16.h, + ), + itemCount: data.length??0, + itemBuilder: (_, index) => TransportOptionItem( + title: Utils.getTextWRTCurrentLanguage(data[index].text, data[index].textN), + subTitle: "", + firstIcon: AppAssets.location_pickup, + middleIcon: AppAssets.to_arrow, + lastIcon: AppAssets.hospital, + price: data[index].priceTotal.toString(), + onTap: () { + onTap(); + context.read().setTransportationOption(data[index]); + }, + ), + ), + ), - TransportOptionItem( - title: "Home to Hospital (One Way)", - subTitle: "Pickup from home location to hospital", - firstIcon: AppAssets.location_pickup, - middleIcon:AppAssets.to_arrow, - lastIcon: AppAssets.hospital, - price: "150 AED", - onTap: (){onTap();}, - ), - TransportOptionItem( - title: "Hospital to Home (One Way)", - subTitle: "Pickup from hospital to home", - lastIcon: AppAssets.location_pickup, - middleIcon:AppAssets.to_arrow, - firstIcon: AppAssets.hospital, - price: "150 AED", - onTap: (){onTap();}, - ), - TransportOptionItem( - title: "Home to Hospital (Two Way)", - subTitle: "Pick from home to hospital then drop off to home", - firstIcon: AppAssets.location_pickup, - middleIcon:AppAssets.dual_arrow, - lastIcon: AppAssets.hospital, - price: "150 AED", - onTap: (){onTap();}, - ), - SizedBox(height: 16.h,) - ], + ], + ), ); } } diff --git a/lib/presentation/emergency_services/call_ambulance/widgets/appointment_bottom_sheet.dart b/lib/presentation/emergency_services/call_ambulance/widgets/appointment_bottom_sheet.dart new file mode 100644 index 0000000..6f5db92 --- /dev/null +++ b/lib/presentation/emergency_services/call_ambulance/widgets/appointment_bottom_sheet.dart @@ -0,0 +1,54 @@ +import 'package:flutter/cupertino.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_doctor_card.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/doctor_card.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:provider/provider.dart'; + +class AppointmentBottomSheet extends StatelessWidget{ + + + final Function(PatientAppointmentHistoryResponseModel) onAppointmentSelection; + final List list; + + const AppointmentBottomSheet({super.key, required this.onAppointmentSelection, required this.list}); + + @override + Widget build(BuildContext context) => getAppointList(list); + + Widget getAppointList( List list) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: ListView.separated( + padding: EdgeInsets.only(top: 24.h), + shrinkWrap: true, + physics: BouncingScrollPhysics(), + itemCount: list.length, + itemBuilder: (context, index) { + PatientAppointmentHistoryResponseModel item = list[index]; + return AppointmentDoctorCard( + renderWidgetForERDisplay: true, + patientAppointmentHistoryResponseModel: item, + onAskDoctorTap: () {}, + onCancelTap: () async {}, + onRescheduleTap: () async {}, + ).onPress((){ + onAppointmentSelection(item); + }); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), + ), + ), + SizedBox(height: 24.h), + ], + ); + } + + +} \ No newline at end of file diff --git a/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart b/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart new file mode 100644 index 0000000..5d073d4 --- /dev/null +++ b/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart @@ -0,0 +1,172 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.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/emergency_services/emergency_services_view_model.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/AmbulanceCallingPlace.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/ambulance_direction.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:provider/provider.dart'; + +import '../../../../generated/locale_keys.g.dart' show LocaleKeys; + +class PickupLocation extends StatelessWidget { + final VoidCallback onTap; + const PickupLocation({super.key, required this.onTap}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Select Pickup Direction" + .needTranslation + .toText24(color: AppColors.textColor,isBold: true), + SizedBox( + height: 16.h, + ), + "Select Direction" + .needTranslation + .toText14(color: AppColors.textColor, weight: FontWeight.w600), + SizedBox( + height: 12.h, + ), + Selector( + selector: (context, viewModel) => viewModel.callingPlace, + builder: (context, value, _) { + return Column( + spacing: 12.h, + children: [ + RadioGroup( + groupValue: value, + onChanged: (value) { + context + .read() + .updateCallingPlace(value); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + spacing: 24.h, + children: [ + Row( + children: [ + Radio( + value: AmbulanceCallingPlace.TO_HOSPITAL, + groupValue: value, + activeColor: AppColors.primaryRedColor, + + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "To Hospital" + .needTranslation + .toText12(color: AppColors.textColor) + ], + ).onPress((){ + context + .read() + .updateCallingPlace(AmbulanceCallingPlace.TO_HOSPITAL); + }), + Row( + children: [ + Radio( + value: AmbulanceCallingPlace.FROM_HOSPITAL, + activeColor: AppColors.primaryRedColor, + + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "From Hospital" + .needTranslation + .toText12(color: AppColors.textColor) + ], + ).onPress((){ + context + .read() + .updateCallingPlace(AmbulanceCallingPlace.FROM_HOSPITAL); + }), + ], + ), + ), + Visibility( + visible: value == AmbulanceCallingPlace.TO_HOSPITAL, + child: Selector( + selector: (context, viewModel) => + viewModel.ambulanceDirection, + builder: (context, directionValue, _) { + return Column( + spacing: 12.h, + children: [ + "Select Way" + .needTranslation + .toText14(color: AppColors.textColor, weight: FontWeight.w600), + RadioGroup( + groupValue: directionValue, + onChanged: (value) { + context + .read() + .updateDirection(value); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + spacing: 24.h, + children: [ + Row( + children: [ + Radio( + value: AmbulanceDirection.ONE_WAY, + activeColor: AppColors.primaryRedColor, + + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "One Way" + .needTranslation + .toText12(color: AppColors.textColor) + ], + ).onPress((){ + context + .read() + .updateDirection(AmbulanceDirection.ONE_WAY); + }), + Row( + children: [ + Radio( + value: AmbulanceDirection.TWO_WAY, + // activeColor: AppColors.primaryRedColor, + + fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + ), + "Two Way" + .needTranslation + .toText12(color: AppColors.textColor) + ], + ).onPress((){ + context + .read() + .updateDirection(AmbulanceDirection.TWO_WAY); + }), + ], + ), + ), + ], + ); + }), + ) + ], + ); + }), + SizedBox( + height: 16.h, + ), + CustomButton( + text: LocaleKeys.confirm.tr(context: context), + onPressed: onTap, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, + iconColor: AppColors.whiteColor, + ), + ], + ); + } +} diff --git a/lib/presentation/emergency_services/call_ambulance/widgets/transport_option_Item.dart b/lib/presentation/emergency_services/call_ambulance/widgets/transport_option_Item.dart index 2511824..9a959eb 100644 --- a/lib/presentation/emergency_services/call_ambulance/widgets/transport_option_Item.dart +++ b/lib/presentation/emergency_services/call_ambulance/widgets/transport_option_Item.dart @@ -48,7 +48,6 @@ class TransportOptionItem extends StatelessWidget { titleSection() { return Row( - children: [ Expanded( child: Column( @@ -56,8 +55,8 @@ class TransportOptionItem extends StatelessWidget { children: [ title.toText16( color: AppColors.textColor, weight: FontWeight.w600), - subTitle.toText12( - color: AppColors.greyTextColor, fontWeight: FontWeight.w500), + // subTitle.toText12( + // color: AppColors.greyTextColor, fontWeight: FontWeight.w500), ], ), ), @@ -69,18 +68,23 @@ class TransportOptionItem extends StatelessWidget { headerSection() { return Row( + mainAxisAlignment: MainAxisAlignment.end, children: [ - Expanded( - child: Row( - children: [ - buildIcon(firstIcon), - Utils.buildSvgWithAssets( - icon: middleIcon, width: 24.h, height: 24.h).paddingAll(8.h), - buildIcon(lastIcon) - ], - ), + // Expanded( + // child: Row( + // children: [ + // buildIcon(firstIcon), + // Utils.buildSvgWithAssets( + // icon: middleIcon, width: 24.h, height: 24.h).paddingAll(8.h), + // buildIcon(lastIcon) + // ], + // ), + // ), + Utils.getPaymentAmountWithSymbol2( + num.tryParse(price) ?? 0.0, + fontSize: 18.f, + letterSpacing:-2 ), - price.toText18(color: AppColors.textColor, weight: FontWeight.w600) ], ); } diff --git a/lib/presentation/emergency_services/call_ambulance/widgets/type_selection_widget.dart b/lib/presentation/emergency_services/call_ambulance/widgets/type_selection_widget.dart index 17f3d9c..3b8d8d7 100644 --- a/lib/presentation/emergency_services/call_ambulance/widgets/type_selection_widget.dart +++ b/lib/presentation/emergency_services/call_ambulance/widgets/type_selection_widget.dart @@ -17,10 +17,10 @@ class TypeSelectionWidget extends StatelessWidget { const TypeSelectionWidget( {super.key, - required this.hmcCount, - required this.hmgCount, - required this.onitemClicked, - required this.selectedFacility}); + required this.hmcCount, + required this.hmgCount, + required this.onitemClicked, + required this.selectedFacility}); @override Widget build(BuildContext context) { diff --git a/lib/presentation/emergency_services/emergency_services_page.dart b/lib/presentation/emergency_services/emergency_services_page.dart index d9a5c6a..3cbce23 100644 --- a/lib/presentation/emergency_services/emergency_services_page.dart +++ b/lib/presentation/emergency_services/emergency_services_page.dart @@ -8,10 +8,13 @@ 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/emergency_services/emergency_services_view_model.dart'; +import 'package:hmg_patient_app_new/features/location/location_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/RRT/rrt_request_type_select.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/call_ambulance_page.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/ambulance_option_selection_bottomsheet.dart'; +import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart'; +import 'package:hmg_patient_app_new/presentation/emergency_services/history/er_history_listing.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/nearest_er_page.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; @@ -35,7 +38,9 @@ class EmergencyServicesPage extends StatelessWidget { locationUtils!.isShowConfirmDialog = true; return CollapsingListView( title: LocaleKeys.emergencyServices.tr(), - requests: () {}, + requests: () { + Navigator.of(context).push(CustomPageRoute(page: ErHistoryListing(), direction: AxisDirection.up)); + }, child: Padding( padding: EdgeInsets.all(24.h), child: Column( @@ -65,6 +70,8 @@ class EmergencyServicesPage extends StatelessWidget { Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h), ], ).onPress(() { + + showCommonBottomSheetWithoutHeight( context, child: Container( @@ -101,7 +108,8 @@ class EmergencyServicesPage extends StatelessWidget { height: 120.h, fit: BoxFit.contain), SizedBox(height: 8.h), - LocaleKeys.confirm.tr().toText28(color: AppColors.whiteColor, isBold: true), + "Confirmation".needTranslation.toText28( + color: AppColors.whiteColor, isBold: true), SizedBox(height: 8.h), "Are you sure you want to call an ambulance?" .needTranslation @@ -114,23 +122,8 @@ class EmergencyServicesPage extends StatelessWidget { onPressed: () async { // Navigator.of(context).pop(); - showCommonBottomSheetWithoutHeight( - title: - "Transport Options".needTranslation, - context, - child: AmbulanceOptionSelectionBottomSheet( - onTap: () { - Navigator.of(context).pop(); - context - .read() - .navigateTOAmbulancePage(); - }), - isFullScreen: false, - isCloseButtonVisible: true, - hasBottomPadding: false, - backgroundColor: AppColors.bottomSheetBgColor, - callBackFunc: () {}, - ); + await emergencyServicesViewModel.getTransportationOption(); + openTranportationSelectionBottomSheet(context); }, backgroundColor: AppColors.whiteColor, borderColor: AppColors.whiteColor, @@ -368,4 +361,68 @@ class EmergencyServicesPage extends StatelessWidget { ), ); } + + openPickupDetailsBottomSheet(BuildContext context){ + showCommonBottomSheetWithoutHeight( + onCloseClicked: (){ + context + .read() + .flushPickupInformation(); + }, + titleWidget: Transform.flip( + flipX: emergencyServicesViewModel.isArabic ? true : false, + child: Utils.buildSvgWithAssets( + icon: AppAssets.arrow_back, + iconColor: Color(0xff2B353E), + fit: BoxFit.contain, + ), + ).onPress(() { + context + .read() + .flushPickupInformation(); + Navigator.pop(context); + openTranportationSelectionBottomSheet(context); + }), + context, + child: PickupLocation( + onTap: () { + Navigator.of(context).pop(); + context.read().flushSearchPredictions(); + context + .read() + .navigateTOAmbulancePage(); + }), + isFullScreen: false, + isCloseButtonVisible: true, + hasBottomPadding: false, + + backgroundColor: AppColors.bottomSheetBgColor, + callBackFunc: () {}, + ); + } + + void openTranportationSelectionBottomSheet(BuildContext context) { + if(emergencyServicesViewModel.transportationOptions.isNotEmpty) { + showCommonBottomSheetWithoutHeight( + title: "Transport Options".needTranslation, + context, + child: SizedBox( + height: 400.h, + child: AmbulanceOptionSelectionBottomSheet( + onTap: () { + Navigator.of(context).pop(); + openPickupDetailsBottomSheet(context); + // context + // .read() + // .navigateTOAmbulancePage(); + }), + ), + isFullScreen: false, + isCloseButtonVisible: true, + hasBottomPadding: false, + backgroundColor: AppColors.bottomSheetBgColor, + callBackFunc: () {}, + ); + } + } } diff --git a/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart b/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart index d389414..0dfd081 100644 --- a/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart +++ b/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart @@ -168,6 +168,7 @@ class ErOnlineCheckinHome extends StatelessWidget { selectedFacility: vm.selectedFacility, hmcCount: vm.hmcCount, hmgCount: vm.hmgCount, + searchText: vm.searchController, ), ), isFullScreen: false, diff --git a/lib/presentation/emergency_services/history/er_history_listing.dart b/lib/presentation/emergency_services/history/er_history_listing.dart new file mode 100644 index 0000000..e5dd1e4 --- /dev/null +++ b/lib/presentation/emergency_services/history/er_history_listing.dart @@ -0,0 +1,62 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.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/emergency_services/emergency_services_view_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/emergency_services/history/widget/ambulance_history_item.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; +import 'package:provider/provider.dart'; + +class ErHistoryListing extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Column( + children: [ + Expanded( + child: CollapsingListView( + title: "History Log".needTranslation, + child: SingleChildScrollView( + physics: NeverScrollableScrollPhysics(), + child: Column( + children: [Visibility( + visible: context + .read() + .orders + ?.isNotEmpty == true, + child: ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: context + .read() + .orders + ?.length ?? 0, + itemBuilder: (_, index) => + Consumer( builder: (_, vm, __)=> + AmbulanceHistoryItem(order: vm.orders![index]), + ) + ), + ),] + ).paddingAll(16.h) + + )) + + ), + + + Visibility( + visible: context + .read() + .orders + ?.isEmpty == true, + child: Utils.getNoDataWidget(context, + noDataText: "You don't have any history" + .needTranslation)), + ], + ), + ); + } +} diff --git a/lib/presentation/emergency_services/history/widget/RequestStatus.dart b/lib/presentation/emergency_services/history/widget/RequestStatus.dart new file mode 100644 index 0000000..de95bdc --- /dev/null +++ b/lib/presentation/emergency_services/history/widget/RequestStatus.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; + +class RequestStatus extends StatelessWidget { + final int status; + + const RequestStatus({super.key, required this.status}); + + @override + Widget build(BuildContext context) { + return AppCustomChipWidget( + labelText: getTitle(status), + backgroundColor: getBackgroundColor(status), + textColor: getTextColor(status), + ); + } + + String? getTitle(int status) { + switch (status) { + case 1: //pending + case 2: //processing + return "Under Processing".needTranslation; + case 3: //completed + return "Completed".needTranslation; + break; + case 4: //cancel + case 6: + case 7: + return "Canceled by patient".needTranslation; + break; + } + return null; + } + + Color? getBackgroundColor(int status) { + switch (status) { + case 1: //pending + case 2: //processing + case 3: //completed + return AppColors.switchBackgroundColor; + break; + case 4: //cancel + case 6: + case 7: + return AppColors.lightRedButtonColor; + break; + } + return null; + } + + Color? getTextColor(int status) { + switch (status) { + case 1: //pending + case 2: + return AppColors.successColor;//processing + case 3: //completed + case 4: //cancel + case 6: + case 7: + return AppColors.errorColor; + break; + } + return null; + } +} diff --git a/lib/presentation/emergency_services/history/widget/ambulance_history_item.dart b/lib/presentation/emergency_services/history/widget/ambulance_history_item.dart new file mode 100644 index 0000000..5f4c8b2 --- /dev/null +++ b/lib/presentation/emergency_services/history/widget/ambulance_history_item.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/utils/date_util.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/emergency_services/emergency_services_view_model.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/AmbulanceRequestOrdersModel.dart'; +import 'package:hmg_patient_app_new/presentation/emergency_services/history/widget/RequestStatus.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'; +import 'package:provider/provider.dart'; + +import '../../../../core/utils/utils.dart'; + + +class AmbulanceHistoryItem extends StatelessWidget { + final AmbulanceRequestOrdersModel order; + + const AmbulanceHistoryItem({super.key, required this.order}); + + @override + Widget build(BuildContext context) { + return DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: Colors.white, + hasShadow: true, + customBorder: BorderRadius.all( + Radius.circular(20.h), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 8.h, + children: [ + RequestStatus(status: order.statusId ?? 0), + "Req ID: ${order.iD}".toText16(color: AppColors.textColor, weight: FontWeight.w600), + Row( + spacing: 4.w, + children: [ + chip( Utils.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(order.time)), AppAssets.calendar, AppColors.blackBgColor), + chip("Ambulance".needTranslation, AppAssets.ambulance, AppColors.blackBgColor), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Flexible(child: chip(order?.dropOffLocation ?? "", (order.dropOffLocation?.toLowerCase().contains("hospital") == true)?AppAssets.hospital:AppAssets.location_pickup, AppColors + .blackBgColor),) + ], + ), + if (order.statusId == 1 || order.statusId == 2) + CustomButton( + text: "Cancel Request".needTranslation, + onPressed: () async { + openCancelOrderBottomSheet(context); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: Colors.white, + icon: AppAssets.cancel, + ), + ], + ).paddingAll(16.h), + ).paddingOnly(bottom: 16.h); + } + + chip(String title, String iconString, Color iconColor) { + return AppCustomChipWidget( + labelText: title, + icon: iconString, + iconColor: iconColor, + iconSize: 14.h, + ); + } + + openCancelOrderBottomSheet(BuildContext context) { + context.read().cancelOrder(order); + } +} diff --git a/lib/presentation/emergency_services/nearest_er_page.dart b/lib/presentation/emergency_services/nearest_er_page.dart index 8138f57..13373f1 100644 --- a/lib/presentation/emergency_services/nearest_er_page.dart +++ b/lib/presentation/emergency_services/nearest_er_page.dart @@ -7,7 +7,7 @@ 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/emergency_services/emergency_services_view_model.dart'; -import 'package:hmg_patient_app_new/features/emergency_services/model/resp_model/ProjectAvgERWaitingTime.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/ProjectAvgERWaitingTime.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/widgets/nearestERItem.dart' show NearestERItem; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; diff --git a/lib/presentation/emergency_services/widgets/location_input_bottom_sheet.dart b/lib/presentation/emergency_services/widgets/location_input_bottom_sheet.dart new file mode 100644 index 0000000..559a0a5 --- /dev/null +++ b/lib/presentation/emergency_services/widgets/location_input_bottom_sheet.dart @@ -0,0 +1,118 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_animations/flutter_staggered_animations.dart' + show AnimationConfiguration, SlideAnimation, FadeInAnimation; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/core/utils/debouncer.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/emergency_services/emergency_services_view_model.dart'; +import 'package:hmg_patient_app_new/features/location/GeocodeResponse.dart'; +import 'package:hmg_patient_app_new/features/location/PlacePrediction.dart'; +import 'package:hmg_patient_app_new/features/location/location_view_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/widgets/input_widget.dart'; +import 'package:provider/provider.dart'; + +import '../../../theme/colors.dart'; +import '../../appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart'; + +class LocationInputBottomSheet extends StatelessWidget { + final Debouncer debouncer = Debouncer(milliseconds: 500); + + LocationInputBottomSheet({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextInputWidget( + labelText: LocaleKeys.search.tr(), + hintText: "Search Location".needTranslation, + controller: TextEditingController(), + onChange: (value){ + debouncer.run(() { + context.read().updateSearchQuery(value); + }); + }, + isEnable: true, + prefix: null, + autoFocus: false, + isBorderAllowed: false, + keyboardType: TextInputType.text, + isAllowLeadingIcon: true, + selectionType: SelectionTypeEnum.search, + padding: EdgeInsets.symmetric( + vertical: 10.h, + horizontal: 15.h, + ), + ), + SizedBox(height: 24.h), + Selector>( + selector: (_, vm) => vm.predictions ?? [], + builder: (context, predictions, _) { + if (predictions.isEmpty) return SizedBox.shrink(); + return Expanded( + child: ListView.separated( + separatorBuilder: (_,__)=>SizedBox(height: 16.h,), + itemCount: predictions.length, + itemBuilder: (_, index) { + final prediction = predictions[index]; + + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 500), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: AnimatedContainer( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + decoration: RoundedRectangleBorder() + .toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.h, + hasShadow: true), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: prediction.description.toText14( + color: AppColors.textColor, + weight: FontWeight.w500), + ), + + Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon_small, + iconColor: AppColors.blackColor, + width: 20.h, + height: 15.h, + fit: BoxFit.contain, + ), + ], + ).paddingAll(24.h),), + ), + ), + ).onPress(() async { + await context.read().selectPlacePrediction(prediction); + Navigator.of(context).pop(); + var location = context.read().placeDetails; + if(location != null) { + context.read().moveController( + Location(lat: location.lat, lng: location.lng)); + } + }); + + }, + ), + ); + }, + ) + ], + ); + } +} diff --git a/lib/presentation/emergency_services/widgets/nearestERItem.dart b/lib/presentation/emergency_services/widgets/nearestERItem.dart index 3dc2aa1..bbce56a 100644 --- a/lib/presentation/emergency_services/widgets/nearestERItem.dart +++ b/lib/presentation/emergency_services/widgets/nearestERItem.dart @@ -5,7 +5,7 @@ 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/emergency_services/emergency_services_view_model.dart'; -import 'package:hmg_patient_app_new/features/emergency_services/model/resp_model/ProjectAvgERWaitingTime.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/ProjectAvgERWaitingTime.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'; diff --git a/lib/presentation/home/widgets/small_service_card.dart b/lib/presentation/home/widgets/small_service_card.dart index ee5fcca..234fad1 100644 --- a/lib/presentation/home/widgets/small_service_card.dart +++ b/lib/presentation/home/widgets/small_service_card.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/size_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/emergency_services/emergency_services_view_model.dart'; import 'package:hmg_patient_app_new/presentation/appointments/my_doctors_page.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/emergency_services_page.dart'; import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; @@ -10,6 +11,7 @@ import 'package:hmg_patient_app_new/presentation/medical_file/patient_sickleaves import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.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:provider/provider.dart'; import '../../../core/utils/utils.dart'; import '../../../theme/colors.dart'; @@ -106,9 +108,12 @@ class SmallServiceCard extends StatelessWidget { ); break; case "emergency": + context.read().flushData(); + context.read().getTransportationOrders(showLoader: false,); Navigator.of(context).push( CustomPageRoute( page: EmergencyServicesPage(), + settings: const RouteSettings(name: '/EmergencyServicesPage'), ), ); break; diff --git a/lib/services/dialog_service.dart b/lib/services/dialog_service.dart index 9f1f503..b91e093 100644 --- a/lib/services/dialog_service.dart +++ b/lib/services/dialog_service.dart @@ -62,16 +62,20 @@ class DialogServiceImp implements DialogService { message: message, showCancel: onCancelPressed != null ? true : false, onOkPressed: () { + print('ok button is pressed'); if (onOkPressed != null) { + print('onOkPressed is not null'); onOkPressed(); + }else { + Navigator.pop(context); } - context.pop(); }, onCancelPressed: () { if (onCancelPressed != null) { onCancelPressed(); + }else { + Navigator.pop(context); } - context.pop(); }, ), ); @@ -211,12 +215,12 @@ Widget exceptionBottomSheetWidget({required BuildContext context, required Strin Padding( padding: EdgeInsets.only(bottom: 10.h), child: CustomButton( - text: LocaleKeys.ok.tr(), + text: LocaleKeys.cancel.tr(), onPressed: (onCancelPressed == null) ? () { context.pop(); } - : onOkPressed, + : onCancelPressed, backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedBorderColor, textColor: Colors.white, diff --git a/lib/services/navigation_service.dart b/lib/services/navigation_service.dart index b5c5e03..cb4405c 100644 --- a/lib/services/navigation_service.dart +++ b/lib/services/navigation_service.dart @@ -13,6 +13,10 @@ class NavigationService { return navigatorKey.currentState!.push(route); } + Future pushAndRemoveUntil(Route route,RoutePredicate predicate) { + return navigatorKey.currentState!.pushAndRemoveUntil(route,predicate); + } + void pop([T? result]) { navigatorKey.currentState!.pop(result); } @@ -34,6 +38,8 @@ class NavigationService { navigatorKey.currentState?.pushReplacementNamed(routeName); } + + Future pushToOtpScreen({required String phoneNumber, required Function(int code) checkActivationCode, required Function(String phoneNumber) onResendOTPPressed, bool isFormFamilyFile = false}) { return navigatorKey.currentState!.push( MaterialPageRoute(builder: (_) => OTPVerificationScreen(phoneNumber: phoneNumber, checkActivationCode: checkActivationCode, onResendOTPPressed: onResendOTPPressed, isFormFamilyFile : isFormFamilyFile)), diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index ce2f87c..6ccf8b1 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -63,6 +63,8 @@ static const Color alertLightColor = Color(0xFFD48D05); static const Color infoLightColor = Color(0xFF0B85F7); static const Color warningLightColor = Color(0xFFFFCC00); static const Color greyLightColor = Color(0xFFEFEFF0); +static const Color thumbColor = Color(0xFF18C273); +static const Color switchBackgroundColor = Color(0x2618C273); static const Color bottomNAVBorder = Color(0xFFEEEEEE); diff --git a/lib/widgets/CustomSwitch.dart b/lib/widgets/CustomSwitch.dart index 8e3364b..784e446 100644 --- a/lib/widgets/CustomSwitch.dart +++ b/lib/widgets/CustomSwitch.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; class CustomSwitch extends StatefulWidget { final bool value; @@ -17,21 +18,21 @@ class _CustomSwitchState extends State { return GestureDetector( onTap: () => widget.onChanged(!widget.value), child: Container( - width: 48.h, + width: 48.w, height: 30.h, decoration: BoxDecoration( - color: widget.value ? Color(0xFFE6F7F0) : Color(0xFFE6F7F0), + color: AppColors.switchBackgroundColor , borderRadius: BorderRadius.circular(18), ), child: AnimatedAlign( - duration: Duration(milliseconds: 200), + duration: Duration(milliseconds: 300), alignment: widget.value ? Alignment.centerRight : Alignment.centerLeft, child: Container( - margin: EdgeInsets.all(4), - width: 28, - height: 28, + margin: EdgeInsets.all(2.h), + width: 28.w, + height: 28.h, decoration: BoxDecoration( - color: Color(0xFF5FCB89), + color: AppColors.thumbColor, shape: BoxShape.circle, ), ), diff --git a/lib/widgets/common_bottom_sheet.dart b/lib/widgets/common_bottom_sheet.dart index 1d857b5..318751f 100644 --- a/lib/widgets/common_bottom_sheet.dart +++ b/lib/widgets/common_bottom_sheet.dart @@ -116,6 +116,7 @@ void showCommonBottomSheetWithoutHeight( bool useSafeArea = false, bool hasBottomPadding = true, Color backgroundColor = AppColors.bottomSheetBgColor, + VoidCallback? onCloseClicked }) { showModalBottomSheet( sheetAnimationStyle: AnimationStyle( @@ -143,12 +144,13 @@ void showCommonBottomSheetWithoutHeight( ), child: SingleChildScrollView( physics: ClampingScrollPhysics(), - child: Container( + child: isCloseButtonVisible + ? Container( padding: EdgeInsets.only( - left: 24.w, - top: 24.h, - right: 24.w, - bottom: 12.h, + left: 24, + top: 24, + right: 24, + bottom: 12, ), decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.bottomSheetBgColor, @@ -171,16 +173,17 @@ void showCommonBottomSheetWithoutHeight( icon: AppAssets.close_bottom_sheet_icon, iconColor: Color(0xff2B353E), ).onPress(() { - Navigator.of(context).pop(); - }), - ], + onCloseClicked?.call(); + Navigator.of(context).pop(); + }),], ], ), SizedBox(height: 16.h), child, ], ), - ), + ) + : child, ), ), ); diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart index aeeb543..d943731 100644 --- a/lib/widgets/input_widget.dart +++ b/lib/widgets/input_widget.dart @@ -113,7 +113,7 @@ class TextInputWidget extends StatelessWidget { children: [ Container( padding: padding, - height: 63.h, + height: 64.h, alignment: Alignment.center, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: Colors.white, diff --git a/lib/widgets/map/HMSMap.dart b/lib/widgets/map/HMSMap.dart index 852af41..f655479 100644 --- a/lib/widgets/map/HMSMap.dart +++ b/lib/widgets/map/HMSMap.dart @@ -1,41 +1,63 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:huawei_map/huawei_map.dart' ; class HMSMap extends StatefulWidget{ final CameraPosition currentLocation; final Function(CameraPosition) onCameraMoved; + final VoidCallback onCameraIdle; final MapType mapType; final bool compassEnabled; final bool myLocationEnabled; + final bool showCenterMarker; + final Completer? inputController; - HMSMap({super.key, required this.currentLocation ,required this.onCameraMoved, this.mapType = MapType.normal,this.compassEnabled = false, this.myLocationEnabled = true}); + HMSMap({super.key, required this.currentLocation ,required this.onCameraMoved,required this.onCameraIdle, + this.mapType = MapType.normal,this.compassEnabled = false,this.showCenterMarker = false, + this.myLocationEnabled = true, this.inputController}); @override State createState() => _HMSMapState(); } class _HMSMapState extends State { - final Completer _controller = Completer(); + Completer? controller; @override void initState() { HuaweiMapInitializer.initializeMap(); + controller = widget.inputController ?? Completer(); super.initState(); } // @override @override Widget build(BuildContext context) => - HuaweiMap( - mapType: widget.mapType, - zoomControlsEnabled: false, - myLocationEnabled: widget.myLocationEnabled, - compassEnabled: widget.compassEnabled, - initialCameraPosition: widget.currentLocation, - onCameraMove: (value) => widget.onCameraMoved(value), - onMapCreated: (HuaweiMapController controller) { - _controller.complete(controller); - }, - ); + Stack( + children: [ + HuaweiMap( + mapType: widget.mapType, + zoomControlsEnabled: false, + myLocationEnabled: widget.myLocationEnabled, + myLocationButtonEnabled: false, + compassEnabled: widget.compassEnabled, + onCameraIdle:()=> widget.onCameraIdle(), + initialCameraPosition: widget.currentLocation, + onCameraMove: (value) => widget.onCameraMoved(value), + onMapCreated: (HuaweiMapController controller) { + this.controller?.complete(controller); + }, + ), + Visibility( + visible: widget.showCenterMarker, + child: Align( + alignment: Alignment.center, + child: Utils.buildSvgWithAssets(icon: AppAssets.pin_location, width: 24.w, height: 36.h), + ), + ) + ], + ); } \ No newline at end of file diff --git a/lib/widgets/map/map.dart b/lib/widgets/map/map.dart index cd06838..0c67f1b 100644 --- a/lib/widgets/map/map.dart +++ b/lib/widgets/map/map.dart @@ -2,31 +2,52 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; class GMSMap extends StatelessWidget{ - final Completer _controller = Completer(); + Completer? controller; final CameraPosition currentLocation; final Function(CameraPosition) onCameraMoved; + final VoidCallback onCameraIdle; final MapType mapType; final bool compassEnabled; final bool myLocationEnabled; + final bool showCenterMarker; - GMSMap({super.key, required this.currentLocation ,required this.onCameraMoved, this.mapType = MapType.normal,this.compassEnabled = false, this.myLocationEnabled = true}); + GMSMap({super.key, required this.currentLocation ,required this.onCameraMoved,required this.onCameraIdle, + this.mapType = MapType.normal,this.compassEnabled = false,this.showCenterMarker = false, + this.myLocationEnabled = true,Completer? inputController}){ + controller = inputController ?? Completer(); + } @override Widget build(BuildContext context) { - return GoogleMap( - mapType: mapType, - zoomControlsEnabled: false, - myLocationEnabled: myLocationEnabled, - compassEnabled: compassEnabled, - initialCameraPosition: currentLocation, - onCameraMove: (value) => onCameraMoved(value), - onMapCreated: (GoogleMapController controller) { - - _controller.complete(controller); - }, + return Stack( + children: [ + GoogleMap( + mapType: mapType, + zoomControlsEnabled: false, + myLocationEnabled: myLocationEnabled, + myLocationButtonEnabled: false, + compassEnabled: compassEnabled, + initialCameraPosition: currentLocation, + onCameraMove: (value) => onCameraMoved(value), + onCameraIdle: ()=>onCameraIdle(), + onMapCreated: (GoogleMapController controller) { + this.controller?.complete(controller); + }, + ), + Visibility( + visible: showCenterMarker, + child: Align( + alignment: Alignment.center, + child: Utils.buildSvgWithAssets(icon: AppAssets.pin_location, width: 24.w, height: 36.h), + ), + ) + ], ); } } \ No newline at end of file diff --git a/lib/widgets/order_tracking/order_tracking_state.dart b/lib/widgets/order_tracking/order_tracking_state.dart index d33e762..5389488 100644 --- a/lib/widgets/order_tracking/order_tracking_state.dart +++ b/lib/widgets/order_tracking/order_tracking_state.dart @@ -3,4 +3,6 @@ enum OrderTrackingState { dispactched, returning, ended, + failed, + cancel } \ No newline at end of file diff --git a/lib/widgets/order_tracking/request_tracking.dart b/lib/widgets/order_tracking/request_tracking.dart index d25d8bc..c0847f9 100644 --- a/lib/widgets/order_tracking/request_tracking.dart +++ b/lib/widgets/order_tracking/request_tracking.dart @@ -5,7 +5,7 @@ import 'package:hmg_patient_app_new/widgets/order_tracking/order_tracking_state. class OrderTrackingWidget extends StatelessWidget { final double? height; - final OrderTrackingState state; + final OrderTrackingState? state; final double parentPaddingValue; const OrderTrackingWidget({super.key, this.height, required this.state, required this.parentPaddingValue}); @@ -23,7 +23,7 @@ class OrderTrackingWidget extends StatelessWidget { ); } - Widget? getBarAndDot(OrderTrackingState state, BuildContext context, + Widget? getBarAndDot(OrderTrackingState? state, BuildContext context, double width) { print("the parent padding value is $parentPaddingValue"); print("the parent padding value is ${(parentPaddingValue+24)}"); @@ -31,6 +31,16 @@ class OrderTrackingWidget extends StatelessWidget { double widthOfOneState = (width/4) - (parentPaddingValue+8.h); print("te width of one state is $widthOfOneState"); print("the value of${width/4}"); + if(state == null) { + return Row( + children: List.generate(4, (index) { + if (index == 0) { + return oneProgressBar(widthOfOneState, AppColors.primaryRedColor, true); + } else { + return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); + } + })); + } switch (state) { case OrderTrackingState.waitingForCall: return Row( @@ -73,6 +83,26 @@ class OrderTrackingWidget extends StatelessWidget { // } }) ); + case OrderTrackingState.failed: + return Row( + children: List.generate(4, (index){ + // if(index == 3) { + return oneProgressBar(widthOfOneState, AppColors.errorColor, index == 3); + // } else { + // return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); + // } + }) + ); + case OrderTrackingState.cancel: + return Row( + children: List.generate(4, (index){ + // if(index == 3) { + return oneProgressBar(widthOfOneState, AppColors.errorColor, index == 3); + // } else { + // return oneProgressBar(widthOfOneState, AppColors.greyLightColor, false); + // } + }) + ); } } diff --git a/lib/widgets/routes/custom_page_route.dart b/lib/widgets/routes/custom_page_route.dart index 475b2ee..b89d710 100644 --- a/lib/widgets/routes/custom_page_route.dart +++ b/lib/widgets/routes/custom_page_route.dart @@ -7,8 +7,10 @@ class CustomPageRoute extends PageRouteBuilder { final AxisDirection direction; final bool fullScreenDialog; - CustomPageRoute({required this.page, this.direction = AxisDirection.right, this.fullScreenDialog = false}) + + CustomPageRoute({required this.page, this.direction = AxisDirection.right, this.fullScreenDialog = false, super.settings}) : super( + transitionDuration: const Duration(milliseconds: 1500), reverseTransitionDuration: const Duration(milliseconds: 500), fullscreenDialog: fullScreenDialog,