diff --git a/assets/images/svg/blood_type.svg b/assets/images/svg/blood_type.svg new file mode 100644 index 0000000..5aded31 --- /dev/null +++ b/assets/images/svg/blood_type.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/genderInputIcon.svg b/assets/images/svg/genderInputIcon.svg new file mode 100644 index 0000000..4482ae3 --- /dev/null +++ b/assets/images/svg/genderInputIcon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 1846d1e..c103291 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -848,6 +848,12 @@ class ApiConsts { static String h2oUpdateUserDetail = "Services/H2ORemainder.svc/REST/H2O_UpdateUserDetails_New"; static String h2oUndoUserActivity = "Services/H2ORemainder.svc/REST/H2o_UndoUserActivity"; + //Blood Donation + static String bloodGroupUpdate = "Services/PatientVarification.svc/REST/BloodDonation_RegisterBloodType"; + static String userAgreementForBloodGroupUpdate = "Services/PatientVarification.svc/REST/AddUserAgreementForBloodDonation"; + static String getProjectsHaveBDClinics = "Services/OUTPs.svc/REST/BD_getProjectsHaveBDClinics"; + static String getClinicsBDFreeSlots = "Services/OUTPs.svc/REST/BD_GetFreeSlots"; + // ************ static values for Api **************** static final double appVersionID = 50.3; static final int appChannelId = 3; diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index a581e78..9763758 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -218,6 +218,8 @@ class AppAssets { static const String activity = '$svgBasePath/activity.svg'; static const String age = '$svgBasePath/age_icon.svg'; static const String gender = '$svgBasePath/gender_icon.svg'; + static const String genderInputIcon = '$svgBasePath/genderInputIcon.svg'; + static const String bloodType = '$svgBasePath/blood_type.svg'; static const String trade_down_yellow = '$svgBasePath/trade_down_yellow.svg'; static const String trade_down_red = '$svgBasePath/trade_down_red.svg'; diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index ebf0a84..7db5d41 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -154,26 +154,20 @@ class AppDependencies { () => RadiologyViewModel(radiologyRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt()), ); - getIt.registerLazySingleton( - () => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt(), navServices: getIt())); + getIt.registerLazySingleton(() => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt(), navServices: getIt())); getIt.registerLazySingleton(() => InsuranceViewModel(insuranceRepo: getIt(), errorHandlerService: getIt())); - getIt.registerLazySingleton( - () => MyAppointmentsViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt())); + getIt.registerLazySingleton(() => MyAppointmentsViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt())); - getIt.registerLazySingleton( - () => AppointmentRatingViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt())); + getIt.registerLazySingleton(() => AppointmentRatingViewModel(myAppointmentsRepo: getIt(), errorHandlerService: getIt(), appState: getIt())); getIt.registerLazySingleton( () => PayfortViewModel(payfortRepo: getIt(), errorHandlerService: getIt()), ); getIt.registerLazySingleton( - () => HabibWalletViewModel( - habibWalletRepo: getIt(), - errorHandlerService: getIt() - ), + () => HabibWalletViewModel(habibWalletRepo: getIt(), errorHandlerService: getIt()), ); getIt.registerLazySingleton( @@ -185,12 +179,7 @@ class AppDependencies { getIt.registerLazySingleton( () => BookAppointmentsViewModel( - bookAppointmentsRepo: getIt(), - errorHandlerService: getIt(), - navigationService: getIt(), - myAppointmentsViewModel: getIt(), - locationUtils: getIt(), - dialogService: getIt()), + bookAppointmentsRepo: getIt(), errorHandlerService: getIt(), navigationService: getIt(), myAppointmentsViewModel: getIt(), locationUtils: getIt(), dialogService: getIt()), ); getIt.registerLazySingleton( @@ -204,13 +193,7 @@ class AppDependencies { getIt.registerLazySingleton( () => AuthenticationViewModel( - authenticationRepo: getIt(), - cacheService: getIt(), - navigationService: getIt(), - dialogService: getIt(), - appState: getIt(), - errorHandlerService: getIt(), - localAuthService: getIt()), + authenticationRepo: getIt(), cacheService: getIt(), navigationService: getIt(), dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), localAuthService: getIt()), ); getIt.registerLazySingleton(() => ProfileSettingsViewModel()); @@ -271,6 +254,7 @@ class AppDependencies { navigationService: getIt(), dialogService: getIt(), appState: getIt(), + navServices: getIt(), ), ); diff --git a/lib/features/blood_donation/blood_donation_repo.dart b/lib/features/blood_donation/blood_donation_repo.dart index dce0975..5643635 100644 --- a/lib/features/blood_donation/blood_donation_repo.dart +++ b/lib/features/blood_donation/blood_donation_repo.dart @@ -3,14 +3,26 @@ import 'package:hmg_patient_app_new/core/api/api_client.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_hospitals_model.dart'; import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_response_model.dart'; import 'package:hmg_patient_app_new/features/blood_donation/models/cities_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'; abstract class BloodDonationRepo { Future>>> getAllCities(); + Future>>> getProjectList(); + + Future>>> getBloodDonationProjectsList(); + Future>> getPatientBloodGroupDetails(); + + Future>> updateBloodGroup({required Map request}); + + Future>> getFreeBloodDonationSlots({required Map request}); + + Future>> addUserAgreementForBloodDonation({required Map request}); } class BloodDonationRepoImp implements BloodDonationRepo { @@ -93,4 +105,186 @@ class BloodDonationRepoImp implements BloodDonationRepo { return Left(UnknownFailure(e.toString())); } } -} \ No newline at end of file + + @override + Future>>> getProjectList() async { + Map request = {}; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_PROJECT_LIST, + body: request, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['ListProject']; + + final appointmentsList = list.map((item) => HospitalsModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: appointmentsList, + ); + } 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>>> getBloodDonationProjectsList() async { + Map request = {}; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + ApiConsts.getProjectsHaveBDClinics, + body: request, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final listData = (response['BD_getProjectsHaveBDClinics'] as List); + final list = listData.map((item) => BdGetProjectsHaveBdClinic.fromJson(item as Map)).toList(); + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: list, + ); + } 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>>> updateBloodGroup({required Map request}) async { + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + ApiConsts.bloodGroupUpdate, + body: request, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + // final list = response['ListProject']; + + // final appointmentsList = list.map((item) => HospitalsModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future>>> getFreeBloodDonationSlots({required Map request}) async { + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + ApiConsts.getClinicsBDFreeSlots, + body: request, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + // final list = response['ListProject']; + + // final appointmentsList = list.map((item) => HospitalsModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future>>> addUserAgreementForBloodDonation({required Map request}) async { + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + ApiConsts.userAgreementForBloodGroupUpdate, + body: request, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + // final list = response['ListProject']; + + // final appointmentsList = list.map((item) => HospitalsModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } +} diff --git a/lib/features/blood_donation/blood_donation_view_model.dart b/lib/features/blood_donation/blood_donation_view_model.dart index b8f0e9c..8325cf7 100644 --- a/lib/features/blood_donation/blood_donation_view_model.dart +++ b/lib/features/blood_donation/blood_donation_view_model.dart @@ -1,15 +1,25 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/enums.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/blood_donation/blood_donation_repo.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_hospitals_model.dart'; import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_list_model.dart'; import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_response_model.dart'; import 'package:hmg_patient_app_new/features/blood_donation/models/cities_model.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.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/generated/locale_keys.g.dart'; 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/loader/bottomsheet_loader.dart'; class BloodDonationViewModel extends ChangeNotifier { final DialogService dialogService; @@ -17,8 +27,21 @@ class BloodDonationViewModel extends ChangeNotifier { ErrorHandlerService errorHandlerService; final NavigationService navigationService; final AppState appState; + bool isTermsAccepted = false; + BdGetProjectsHaveBdClinic? selectedHospital; + CitiesModel? selectedCity; + BloodGroupListModel? selectedBloodGroup; + int _selectedHospitalIndex = 0; + int _selectedBloodTypeIndex = 0; + GenderTypeEnum? selectedGender; + String? selectedBloodType; + final NavigationService navServices; + + List hospitalList = []; List citiesList = []; + List_BloodGroupDetailsModel patientBloodGroupDetailsModel = List_BloodGroupDetailsModel(); + List bloodGroupList = [ BloodGroupListModel("O+", 0), BloodGroupListModel("O-", 1), @@ -30,35 +53,34 @@ class BloodDonationViewModel extends ChangeNotifier { BloodGroupListModel("B-", 7), ]; - List genderList = [ - BloodGroupListModel(LocaleKeys.malE.tr(), 1), - BloodGroupListModel("Female".needTranslation.tr(), 2), - ]; - - late CitiesModel selectedCity; - late BloodGroupListModel selectedBloodGroup; - int _selectedHospitalIndex = 0; - int _selectedBloodTypeIndex = 0; - String selectedBloodType = ''; - - List_BloodGroupDetailsModel patientBloodGroupDetailsModel = List_BloodGroupDetailsModel(); - - BloodDonationViewModel({required this.bloodDonationRepo, required this.errorHandlerService, required this.navigationService, required this.dialogService, required this.appState}); + BloodDonationViewModel({ + required this.bloodDonationRepo, + required this.errorHandlerService, + required this.navigationService, + required this.dialogService, + required this.appState, + required this.navServices, + }); setSelectedCity(CitiesModel city) { selectedCity = city; notifyListeners(); } + void onGenderChange(String? status) { + selectedGender = GenderTypeExtension.fromType(status)!; + notifyListeners(); + } + setSelectedBloodGroup(BloodGroupListModel bloodGroup) { selectedBloodGroup = bloodGroup; - selectedBloodType = selectedBloodGroup.name; + selectedBloodType = selectedBloodGroup!.name; notifyListeners(); } Future getRegionSelectedClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async { citiesList.clear(); - selectedCity = CitiesModel(); + selectedCity = null; notifyListeners(); final result = await bloodDonationRepo.getAllCities(); @@ -71,6 +93,7 @@ class BloodDonationViewModel extends ChangeNotifier { onError!(apiResponse.errorMessage ?? 'An unexpected error occurred'); } else if (apiResponse.messageStatus == 1) { citiesList = apiResponse.data!; + citiesList.sort((a, b) => a.description!.compareTo(b.description!)); notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); @@ -100,7 +123,7 @@ class BloodDonationViewModel extends ChangeNotifier { citiesModel.descriptionN = citiesList[_selectedHospitalIndex].descriptionN; selectedCity = citiesModel; selectedBloodType = patientBloodGroupDetailsModel.bloodGroup!; - _selectedBloodTypeIndex = getBloodIndex(selectedBloodType); + _selectedBloodTypeIndex = getBloodIndex(selectedBloodType ?? ''); notifyListeners(); if (onSuccess != null) { @@ -113,11 +136,11 @@ class BloodDonationViewModel extends ChangeNotifier { int getSelectedCityID() { int cityID = 1; - citiesList.forEach((element) { + for (var element in citiesList) { if (element.description == patientBloodGroupDetailsModel.city) { cityID = element.iD!; } - }); + } return cityID; } @@ -144,4 +167,120 @@ class BloodDonationViewModel extends ChangeNotifier { return 0; } } + + void onTermAccepted() { + isTermsAccepted = !isTermsAccepted; + notifyListeners(); + } + + bool isUserAuthanticated() { + print("the app state is ${appState.isAuthenticated}"); + if (!appState.isAuthenticated) { + return false; + } else { + return true; + } + } + + Future fetchHospitalsList() async { + // hospitalList.clear(); + notifyListeners(); + final result = await bloodDonationRepo.getBloodDonationProjectsList(); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) async { + if (apiResponse.messageStatus == 2) { + } else if (apiResponse.messageStatus == 1) { + hospitalList = apiResponse.data!; + hospitalList.sort((a, b) => a.projectName!.compareTo(b.projectName!)); + notifyListeners(); + } + }, + ); + } + + Future getFreeBloodDonationSlots({required Map request}) async { + final result = await bloodDonationRepo.getFreeBloodDonationSlots(request: request); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) async { + if (apiResponse.messageStatus == 2) { + } else if (apiResponse.messageStatus == 1) { + // TODO: Handle free slots data + print(apiResponse.data['BD_FreeSlots']); + notifyListeners(); + } + }, + ); + } + + bool isLocationEnabled() { + return appState.userLong != 0.0 && appState.userLong != 0.0; + } + + setSelectedHospital(BdGetProjectsHaveBdClinic hospital) { + selectedHospital = hospital; + notifyListeners(); + } + + Future validateSelections() async { + if (selectedCity == null) { + await dialogService.showErrorBottomSheet( + message: "Please choose city", + ); + return false; + } + + if (selectedBloodGroup == null) { + await dialogService.showErrorBottomSheet( + message: "Please choose Gender", + ); + return false; + } + + if (selectedBloodType == null) { + await dialogService.showErrorBottomSheet( + message: "Please choose Blood Group", + ); + return false; + } + + if (!isTermsAccepted) { + await dialogService.showErrorBottomSheet( + message: "Please accept Terms and Conditions to continue", + ); + return false; + } + return true; + } + + Future updateBloodGroup() async { + LoaderBottomSheet.showLoader(); + // body['City'] = detailsModel.city; + // body['cityCode'] = detailsModel.cityCode; + // body['Gender'] = detailsModel.gender; + // body['BloodGroup'] = detailsModel.bloodGroup; + // body['CellNumber'] = user.mobileNumber; + // body['LanguageID'] = languageID; + // body['NationalID'] = user.nationalityID; + // body['ZipCode'] = user.zipCode ?? "+966"; + // body['isDentalAllowedBackend'] = false; + Map payload = { + "City": selectedCity?.description, + "cityCode": selectedCity?.iD, + "Gender": selectedGender?.value, + "isDentalAllowedBackend": false + // "Gender": selectedGender?.value, + }; + await bloodDonationRepo.updateBloodGroup(request: payload); + await addUserAgreementForBloodDonation(); + LoaderBottomSheet.hideLoader(); + } + + Future addUserAgreementForBloodDonation() async { + Map payload = {"IsAgreed": true}; + await bloodDonationRepo.addUserAgreementForBloodDonation(request: payload); + } } diff --git a/lib/features/blood_donation/models/blood_group_hospitals_model.dart b/lib/features/blood_donation/models/blood_group_hospitals_model.dart new file mode 100644 index 0000000..10b4e67 --- /dev/null +++ b/lib/features/blood_donation/models/blood_group_hospitals_model.dart @@ -0,0 +1,81 @@ +import 'dart:convert'; + +class BdProjectsHaveBdClinicsModel { + List? bdGetProjectsHaveBdClinics; + + BdProjectsHaveBdClinicsModel({ + this.bdGetProjectsHaveBdClinics, + }); + + factory BdProjectsHaveBdClinicsModel.fromRawJson(String str) => BdProjectsHaveBdClinicsModel.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory BdProjectsHaveBdClinicsModel.fromJson(Map json) => BdProjectsHaveBdClinicsModel( + bdGetProjectsHaveBdClinics: json["BD_getProjectsHaveBDClinics"] == null ? [] : List.from(json["BD_getProjectsHaveBDClinics"]!.map((x) => BdGetProjectsHaveBdClinic.fromJson(x))), + ); + + Map toJson() => { + "BD_getProjectsHaveBDClinics": bdGetProjectsHaveBdClinics == null ? [] : List.from(bdGetProjectsHaveBdClinics!.map((x) => x.toJson())), + }; +} + +class BdGetProjectsHaveBdClinic { + int? rowId; + int? id; + int? projectId; + int? numberOfRooms; + bool? isActive; + int? createdBy; + String? createdOn; + dynamic editedBy; + dynamic editedOn; + String? projectName; + dynamic projectNameN; + + BdGetProjectsHaveBdClinic({ + this.rowId, + this.id, + this.projectId, + this.numberOfRooms, + this.isActive, + this.createdBy, + this.createdOn, + this.editedBy, + this.editedOn, + this.projectName, + this.projectNameN, + }); + + factory BdGetProjectsHaveBdClinic.fromRawJson(String str) => BdGetProjectsHaveBdClinic.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory BdGetProjectsHaveBdClinic.fromJson(Map json) => BdGetProjectsHaveBdClinic( + rowId: json["RowID"], + id: json["ID"], + projectId: json["ProjectID"], + numberOfRooms: json["NumberOfRooms"], + isActive: json["IsActive"], + createdBy: json["CreatedBy"], + createdOn: json["CreatedOn"], + editedBy: json["EditedBy"], + editedOn: json["EditedON"], + projectName: json["ProjectName"], + projectNameN: json["ProjectNameN"], + ); + + Map toJson() => { + "RowID": rowId, + "ID": id, + "ProjectID": projectId, + "NumberOfRooms": numberOfRooms, + "IsActive": isActive, + "CreatedBy": createdBy, + "CreatedOn": createdOn, + "EditedBy": editedBy, + "EditedON": editedOn, + "ProjectName": projectName, + "ProjectNameN": projectNameN, + }; +} diff --git a/lib/features/blood_donation/widgets/hospital_selection.dart b/lib/features/blood_donation/widgets/hospital_selection.dart new file mode 100644 index 0000000..288ac34 --- /dev/null +++ b/lib/features/blood_donation/widgets/hospital_selection.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_hospitals_model.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors; +import 'package:provider/provider.dart'; + +class HospitalBottomSheetBodySelection extends StatelessWidget { + final Function(BdGetProjectsHaveBdClinic userSelection) onUserHospitalSelection; + + const HospitalBottomSheetBodySelection({super.key, required this.onUserHospitalSelection(BdGetProjectsHaveBdClinic userSelection)}); + + @override + Widget build(BuildContext context) { + final bloodDonationVm = Provider.of(context, listen: false); + AppState appState = getIt.get(); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Please select the hospital you want to make an appointment.".needTranslation, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: AppColors.greyTextColor, + ), + ), + SizedBox(height: 16.h), + SizedBox( + height: MediaQuery.sizeOf(context).height * .4, + child: ListView.separated( + itemBuilder: (_, index) { + return DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: false, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 8.h, + children: [ + hospitalName(bloodDonationVm.hospitalList[index]).onPress(() { + onUserHospitalSelection(bloodDonationVm.hospitalList[index]); + Navigator.of(context).pop(); + }) + ], + ), + ), + Transform.flip( + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.blackColor, width: 40.h, height: 40.h, fit: BoxFit.contain), + ), + ], + ).paddingSymmetrical(16.h, 16.h), + ).onPress(() { + bloodDonationVm.setSelectedHospital(bloodDonationVm.hospitalList[index]); + Navigator.of(context).pop(); + }); + }, + separatorBuilder: (_, __) => SizedBox(height: 16.h), + itemCount: bloodDonationVm.hospitalList.length), + ) + ], + ); + } + + Widget hospitalName(dynamic hospital) => Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.hmg).paddingOnly(right: 10), + Expanded( + child: Text(hospital.projectName ?? "", style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16, color: AppColors.blackColor)), + ) + ], + ); +} diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index cbed940..380d2db 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -49,6 +49,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { bool isLiveCareSchedule = false; bool isGetDocForHealthCal = false; + bool showSortFilterButtons = false; int? calculationID = 0; bool isSortByClinic = true; @@ -200,8 +201,10 @@ class BookAppointmentsViewModel extends ChangeNotifier { void filterClinics(String? query) { if (query!.isEmpty) { _filteredClinicsList = List.from(clinicsList); + showSortFilterButtons = false; } else { _filteredClinicsList = clinicsList.where((clinic) => clinic.clinicDescription?.toLowerCase().contains(query!.toLowerCase()) ?? false).toList(); + showSortFilterButtons = query.length >= 3; } notifyListeners(); } diff --git a/lib/features/medical_file/medical_file_repo.dart b/lib/features/medical_file/medical_file_repo.dart index 2f5cae6..09856ca 100644 --- a/lib/features/medical_file/medical_file_repo.dart +++ b/lib/features/medical_file/medical_file_repo.dart @@ -293,14 +293,13 @@ class MedicalFileRepoImp implements MedicalFileRepo { Failure? failure; await apiClient.post( ApiConsts.getAllSharedRecordsByStatus, - body: {if (status != null) "Status": status, "PatientID": patientID}, + body: {if (status != null) "Status": status, "PatientID": patientID.toString()}, onFailure: (error, statusCode, {messageStatus, failureType}) { failure = failureType; }, onSuccess: (response, statusCode, {messageStatus, errorMessage}) { try { - final list = response['GetAllSharedRecordsByStatusList']; - + final list = response['GetAllSharedRecordsByStatusList'] ?? []; final familyLists = list.map((item) => FamilyFileResponseModelLists.fromJson(item as Map)).toList().cast(); @@ -336,7 +335,7 @@ class MedicalFileRepoImp implements MedicalFileRepo { }, onSuccess: (response, statusCode, {messageStatus, errorMessage}) { try { - final list = response['GetAllPendingRecordsList']; + final list = response['GetAllPendingRecordsList'] ?? []; final familyLists = list.map((item) => FamilyFileResponseModelLists.fromJson(item as Map)).toList().cast(); apiResponse = GenericApiModel>( diff --git a/lib/presentation/blood_donation/blood_donation_page.dart b/lib/presentation/blood_donation/blood_donation_page.dart index 5f3fa86..a987220 100644 --- a/lib/presentation/blood_donation/blood_donation_page.dart +++ b/lib/presentation/blood_donation/blood_donation_page.dart @@ -7,16 +7,26 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/models/blood_group_hospitals_model.dart'; +import 'package:hmg_patient_app_new/features/blood_donation/widgets/hospital_selection.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/blood_donation/widgets/select_blood_group_widget.dart'; import 'package:hmg_patient_app_new/presentation/blood_donation/widgets/select_city_widget.dart'; import 'package:hmg_patient_app_new/presentation/blood_donation/widgets/select_gender_widget.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart'; +import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.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'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; +import 'package:lottie/lottie.dart'; import 'package:provider/provider.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart'; class BloodDonationPage extends StatelessWidget { BloodDonationPage({super.key}); @@ -25,7 +35,7 @@ class BloodDonationPage extends StatelessWidget { @override Widget build(BuildContext context) { - appState = getIt.get(); + appState = getIt(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: Consumer(builder: (context, bloodDonationVM, child) { @@ -34,15 +44,83 @@ class BloodDonationPage extends StatelessWidget { Expanded( child: CollapsingListView( title: LocaleKeys.bloodDonation.tr(), + trailing: CustomButton( + text: "Book", + onPressed: () { + // if (bloodDonationVM.isUserAuthanticated()) { + bloodDonationVM.fetchHospitalsList().then((value) { + showCommonBottomSheetWithoutHeight(context, title: "Select Hospital", isDismissible: false, child: Consumer(builder: (_, data, __) { + return HospitalBottomSheetBodySelection( + onUserHospitalSelection: (BdGetProjectsHaveBdClinic userChoice) { + print("============User Choice==============="); + + bloodDonationVM.getFreeBloodDonationSlots(request: {"ClinicID": 134, "ProjectID": userChoice.projectId}); + }, + ); + }), callBackFunc: () {}); + }); + // } else { + // return showCommonBottomSheetWithoutHeight( + // context, + // title: LocaleKeys.notice.tr(context: context), + // child: Column( + // mainAxisAlignment: MainAxisAlignment.center, + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // Lottie.asset(AppAnimations.errorAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), + // SizedBox(height: 8.h), + // (LocaleKeys.loginToUseService.tr()).toText16(color: AppColors.blackColor), + // SizedBox(height: 16.h), + // Row( + // children: [ + // Expanded( + // child: CustomButton( + // text: LocaleKeys.cancel.tr(), + // onPressed: () { + // Navigator.of(context).pop(); + // }, + // backgroundColor: AppColors.secondaryLightRedColor, + // borderColor: AppColors.secondaryLightRedColor, + // textColor: AppColors.primaryRedColor, + // icon: AppAssets.cancel, + // iconColor: AppColors.primaryRedColor, + // ), + // ), + // SizedBox(width: 8.h), + // Expanded( + // child: CustomButton( + // text: LocaleKeys.confirm.tr(), + // onPressed: () async { + // Navigator.of(context).pop(); + // // Navigator.pushAndRemoveUntil(context, CustomPageRoute(page: LandingNavigation()), (r) => false); + // await getIt().onLoginPressed(); + // }, + // backgroundColor: AppColors.bgGreenColor, + // borderColor: AppColors.bgGreenColor, + // textColor: Colors.white, + // icon: AppAssets.confirm, + // ), + // ), + // ], + // ), + // SizedBox(height: 16.h), + // ], + // ).center, + // callBackFunc: () {}, + // isFullScreen: false, + // isCloseButtonVisible: true, + // ); + // } + }, + backgroundColor: AppColors.bgRedLightColor, + borderColor: AppColors.bgRedLightColor, + textColor: AppColors.primaryRedColor, + padding: EdgeInsetsGeometry.symmetric(vertical: 0.h, horizontal: 20.h)), child: Padding( padding: EdgeInsets.all(24.w), child: SingleChildScrollView( child: Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24.r, - hasShadow: false, - ), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: false), child: Padding( padding: EdgeInsets.all(16.h), child: Column( @@ -60,8 +138,8 @@ class BloodDonationPage extends StatelessWidget { children: [ LocaleKeys.city.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500), (appState.isArabic() - ? (bloodDonationVM.selectedCity.descriptionN ?? LocaleKeys.select.tr()) - : bloodDonationVM.selectedCity.description ?? LocaleKeys.select.tr(context: context)) + ? (bloodDonationVM.selectedCity?.descriptionN ?? LocaleKeys.select.tr()) + : bloodDonationVM.selectedCity?.description ?? LocaleKeys.select.tr(context: context)) .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), ], ), @@ -71,12 +149,7 @@ class BloodDonationPage extends StatelessWidget { ], ).onPress(() async { showCommonBottomSheetWithoutHeight(context, - title: LocaleKeys.selectCity.tr(context: context), - isDismissible: true, - child: SelectCityWidget( - bloodDonationViewModel: bloodDonationVM, - ), - callBackFunc: () {}); + title: LocaleKeys.selectCity.tr(context: context), isDismissible: true, child: SelectCityWidget(bloodDonationViewModel: bloodDonationVM), callBackFunc: () {}); }), SizedBox(height: 16.h), Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), @@ -86,13 +159,16 @@ class BloodDonationPage extends StatelessWidget { children: [ Row( children: [ - Utils.buildSvgWithAssets(icon: AppAssets.my_account_icon, width: 40.h, height: 40.h), + Utils.buildSvgWithAssets(icon: AppAssets.genderInputIcon, width: 40.h, height: 40.h), SizedBox(width: 12.w), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.gender.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500), - "Male".toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + (appState.isArabic() + ? (bloodDonationVM.selectedGender?.typeAr ?? LocaleKeys.select.tr()) + : bloodDonationVM.selectedGender?.type ?? LocaleKeys.select.tr(context: context)) + .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), ], ), ], @@ -103,10 +179,7 @@ class BloodDonationPage extends StatelessWidget { showCommonBottomSheetWithoutHeight(context, title: LocaleKeys.selectGender.tr(context: context), isDismissible: true, - child: SelectGenderWidget( - isArabic: appState.isArabic(), - bloodDonationViewModel: bloodDonationVM, - ), + child: SelectGenderWidget(isArabic: appState.isArabic(), bloodDonationViewModel: bloodDonationVM), callBackFunc: () {}); }), SizedBox(height: 16.h), @@ -117,13 +190,17 @@ class BloodDonationPage extends StatelessWidget { children: [ Row( children: [ - Utils.buildSvgWithAssets(icon: AppAssets.my_account_icon, width: 40.h, height: 40.h), + Utils.buildSvgWithAssets(icon: AppAssets.bloodType, width: 40.h, height: 40.h), SizedBox(width: 12.w), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.bloodType.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500), - bloodDonationVM.selectedBloodType.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + // bloodDonationVM.selectedBloodType?.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + (appState.isArabic() + ? (bloodDonationVM.selectedBloodType ?? LocaleKeys.select.tr()) + : bloodDonationVM.selectedBloodType ?? LocaleKeys.select.tr(context: context)) + .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500) ], ), ], @@ -134,10 +211,7 @@ class BloodDonationPage extends StatelessWidget { showCommonBottomSheetWithoutHeight(context, title: LocaleKeys.select.tr(context: context), isDismissible: true, - child: SelectBloodGroupWidget( - isArabic: appState.isArabic(), - bloodDonationViewModel: bloodDonationVM, - ), + child: SelectBloodGroupWidget(isArabic: appState.isArabic(), bloodDonationViewModel: bloodDonationVM), callBackFunc: () {}); }), ], @@ -149,19 +223,73 @@ class BloodDonationPage extends StatelessWidget { ), ), Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24.r, - hasShadow: true, - ), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true), child: SizedBox( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + GestureDetector( + onTap: bloodDonationVM.onTermAccepted, + child: Row( + children: [ + Selector( + selector: (_, viewModel) => viewModel.isTermsAccepted, + shouldRebuild: (previous, next) => previous != next, + builder: (context, isTermsAccepted, child) { + return AnimatedContainer( + duration: const Duration(milliseconds: 200), + height: 24.h, + width: 24.h, + decoration: BoxDecoration( + color: isTermsAccepted ? AppColors.primaryRedColor : Colors.transparent, + borderRadius: BorderRadius.circular(6), + border: Border.all(color: isTermsAccepted ? AppColors.primaryRedBorderColor : AppColors.greyColor, width: 2.h), + ), + child: isTermsAccepted ? Icon(Icons.check, size: 16.f, color: Colors.white) : null, + ); + }, + ), + SizedBox(width: 12.h), + Row( + children: [ + Text( + LocaleKeys.iAcceptThe.tr(), + style: context.dynamicTextStyle(fontSize: 14.f, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)), + ), + GestureDetector( + onTap: () { + // Navigate to terms and conditions page + Navigator.of(context).pushNamed('/terms'); + }, + child: Text( + LocaleKeys.termsConditoins.tr(), + style: context.dynamicTextStyle( + fontSize: 14.f, + fontWeight: FontWeight.w500, + color: AppColors.primaryRedColor, + decoration: TextDecoration.underline, + decorationColor: AppColors.primaryRedBorderColor, + ), + ), + ), + ], + ), + // Expanded( + // child: Text( + // LocaleKeys.iAcceptTermsConditions.tr().split("the").first, + // style: context.dynamicTextStyle(fontSize: 14.fSize, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)), + // ), + // ), + ], + ), + ).paddingOnly(left: 16.h, right: 16.h, top: 24.h), CustomButton( text: LocaleKeys.save.tr(), - onPressed: () { - // openDoctorScheduleCalendar(); + onPressed: () async { + DialogService dialogService = getIt.get(); + if (await bloodDonationVM.validateSelections()) { + bloodDonationVM.updateBloodGroup(); + } }, backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor, diff --git a/lib/presentation/blood_donation/widgets/select_city_widget.dart b/lib/presentation/blood_donation/widgets/select_city_widget.dart index 8e2f9a0..bf5992d 100644 --- a/lib/presentation/blood_donation/widgets/select_city_widget.dart +++ b/lib/presentation/blood_donation/widgets/select_city_widget.dart @@ -26,9 +26,7 @@ class SelectCityWidget extends StatelessWidget { Navigator.of(context).pop(); }); }, - separatorBuilder: (_, __) => SizedBox( - height: 8.h, - ), + separatorBuilder: (_, __) => SizedBox(height: 8.h), itemCount: bloodDonationViewModel.citiesList.length), ) ], diff --git a/lib/presentation/blood_donation/widgets/select_gender_widget.dart b/lib/presentation/blood_donation/widgets/select_gender_widget.dart index 67cd4bb..0d360a8 100644 --- a/lib/presentation/blood_donation/widgets/select_gender_widget.dart +++ b/lib/presentation/blood_donation/widgets/select_gender_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; @@ -8,10 +9,10 @@ import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_ import 'package:hmg_patient_app_new/theme/colors.dart'; class SelectGenderWidget extends StatelessWidget { - SelectGenderWidget({super.key, required this.bloodDonationViewModel, required this.isArabic}); + const SelectGenderWidget({super.key, required this.bloodDonationViewModel, required this.isArabic}); - BloodDonationViewModel bloodDonationViewModel; - bool isArabic; + final BloodDonationViewModel bloodDonationViewModel; + final bool isArabic; @override Widget build(BuildContext context) { @@ -20,46 +21,45 @@ class SelectGenderWidget extends StatelessWidget { children: [ SizedBox(height: 8.h), SizedBox( - height: MediaQuery.sizeOf(context).height * .4, - child: ListView.separated( - itemBuilder: (_, index) { - return DecoratedBox( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: false, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 8.h, - children: [bloodDonationViewModel.genderList[index].name.toText16(color: AppColors.textColor, isBold: true)], + height: MediaQuery.sizeOf(context).height * .4, + child: ListView.separated( + itemBuilder: (_, index) { + return DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: false, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 8.h, + children: [GenderTypeEnum.values[index].name.toCamelCase.toText16(color: AppColors.textColor, isBold: true)], + ), ), - ), - Transform.flip( - flipX: isArabic, - child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, - iconColor: AppColors.blackColor, - width: 40.h, - height: 40.h, - fit: BoxFit.contain, + Transform.flip( + flipX: isArabic, + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, + iconColor: AppColors.blackColor, + width: 40.h, + height: 40.h, + fit: BoxFit.contain, + ), ), - ), - ], - ).paddingSymmetrical(16.h, 16.h).onPress(() { - // bloodDonationViewModel.setSelectedCity(bloodDonationViewModel.citiesList[index]); - Navigator.of(context).pop(); - })); - }, - separatorBuilder: (_, __) => SizedBox( - height: 8.h, - ), - itemCount: bloodDonationViewModel.genderList.length), - ) + ], + ).paddingSymmetrical(16.h, 16.h).onPress(() { + bloodDonationViewModel.onGenderChange(GenderTypeEnum.values[index].name.toCamelCase); + Navigator.of(context).pop(); + })); + }, + separatorBuilder: (_, __) => SizedBox( + height: 8.h, + ), + itemCount: GenderTypeEnum.values.length)) ], ); } diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index 76dc1be..bcb2131 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -112,10 +112,7 @@ class _BookAppointmentPageState extends State { fit: BoxFit.cover, ).circle(100).toShimmer2(isShow: true, radius: 50.r), SizedBox(height: 8.h), - ("Dr. John Smith Smith Smith") - .toString() - .toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2) - .toShimmer2(isShow: true), + ("Dr. John Smith Smith Smith").toString().toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2).toShimmer2(isShow: true), ], ) : myAppointmentsVM.patientMyDoctorsList.isEmpty @@ -241,10 +238,7 @@ class _BookAppointmentPageState extends State { ), ], ), - Transform.flip( - flipX: appState.isArabic(), - child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), ], ).onPress(() { bookAppointmentsViewModel.setIsClinicsListLoading(true); @@ -276,10 +270,7 @@ class _BookAppointmentPageState extends State { ), ], ), - Transform.flip( - flipX: appState.isArabic(), - child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), ], ).onPress(() { bookAppointmentsViewModel.setIsDoctorSearchByNameStarted(false); @@ -309,10 +300,7 @@ class _BookAppointmentPageState extends State { ), ], ), - Transform.flip( - flipX: appState.isArabic(), - child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), ], ).onPress(() { bookAppointmentsViewModel.setProjectID(null); @@ -332,124 +320,115 @@ class _BookAppointmentPageState extends State { Column( children: [ Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24.h, - hasShadow: false, - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.search_by_clinic_icon, width: 40.h, height: 40.h), - SizedBox(width: 12.h), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - "Immediate Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), - "Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), - ], - ), - ], - ), - Transform.flip( - flipX: appState.isArabic(), - child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), - ], - ).onPress(() async { - //TODO Implement API to check for existing LiveCare Requests + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.h, + hasShadow: false, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.search_by_clinic_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Immediate Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), + "Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + ], + ), + ], + ), + Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + ], + ).onPress(() async { + //TODO Implement API to check for existing LiveCare Requests - LoaderBottomSheet.showLoader(); - await immediateLiveCareViewModel.getPatientLiveCareHistory(); - LoaderBottomSheet.hideLoader(); + LoaderBottomSheet.showLoader(); + await immediateLiveCareViewModel.getPatientLiveCareHistory(); + LoaderBottomSheet.hideLoader(); - if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) { - Navigator.of(context).push( - CustomPageRoute( - page: ImmediateLiveCarePendingRequestPage(), - ), - ); - } else { - Navigator.of(context).push( - CustomPageRoute( - page: SelectImmediateLiveCareClinicPage(), - ), - ); - } - }), - SizedBox(height: 16.h), - Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), - SizedBox(height: 16.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.search_by_doctor_icon, width: 40.h, height: 40.h), - SizedBox(width: 12.h), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - "Scheduled Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), - "Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), - ], - ), - ], - ), - Transform.flip( - flipX: appState.isArabic(), - child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), - ], - ).onPress(() { - bookAppointmentsViewModel.setIsClinicsListLoading(true); - bookAppointmentsViewModel.setIsLiveCareSchedule(true); - Navigator.of(context).push( - CustomPageRoute( - page: SelectClinicPage(), - ), - ); - }), - SizedBox(height: 16.h), - Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), - SizedBox(height: 16.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.search_by_region_icon, width: 40.h, height: 40.h), - SizedBox(width: 12.h), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - "Pharma LiveCare".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), - "".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), - ], - ), - ], - ), - Transform.flip( - flipX: appState.isArabic(), - child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), - ], - ).onPress(() { - openRegionListBottomSheet(context, RegionBottomSheetType.FOR_REGION); - }), - ], - ), - ), - ), - ], - ).paddingSymmetrical(24.h, 0.h) + if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) { + Navigator.of(context).push( + CustomPageRoute( + page: ImmediateLiveCarePendingRequestPage(), + ), + ); + } else { + Navigator.of(context).push( + CustomPageRoute( + page: SelectImmediateLiveCareClinicPage(), + ), + ); + } + }), + SizedBox(height: 16.h), + Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), + SizedBox(height: 16.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.search_by_doctor_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Scheduled Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), + "Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + ], + ), + ], + ), + Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + ], + ).onPress(() { + bookAppointmentsViewModel.setIsClinicsListLoading(true); + bookAppointmentsViewModel.setIsLiveCareSchedule(true); + Navigator.of(context).push( + CustomPageRoute( + page: SelectClinicPage(), + ), + ); + }), + SizedBox(height: 16.h), + Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), + SizedBox(height: 16.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.search_by_region_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Pharma LiveCare".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), + "".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + ], + ), + ], + ), + Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + ], + ).onPress(() { + openRegionListBottomSheet(context, RegionBottomSheetType.FOR_REGION); + }), + ], + ), + ), + ), + ], + ).paddingSymmetrical(24.h, 0.h) // : getLiveCareNotLoggedInUI() ; default: @@ -493,10 +472,8 @@ class _BookAppointmentPageState extends State { regionalViewModel.flush(); regionalViewModel.setBottomSheetType(type); // AppointmentViaRegionViewmodel? viewmodel = null; - showCommonBottomSheetWithoutHeight(context, - title: "", - titleWidget: Consumer(builder: (_, data, __) => getTitle(data)), - isDismissible: false, child: Consumer(builder: (_, data, __) { + showCommonBottomSheetWithoutHeight(context, title: "", titleWidget: Consumer(builder: (_, data, __) => getTitle(data)), isDismissible: false, + child: Consumer(builder: (_, data, __) { return getRegionalSelectionWidget(data); }), callBackFunc: () {}); } @@ -582,9 +559,7 @@ class _BookAppointmentPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ "Immediate service".needTranslation.toText18(color: AppColors.textColor, isBold: true), - "No need to wait, you will get medical consultation immediately via video call" - .needTranslation - .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + "No need to wait, you will get medical consultation immediately via video call".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), ], ), ), @@ -616,9 +591,7 @@ class _BookAppointmentPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ "Doctor will contact".needTranslation.toText18(color: AppColors.textColor, isBold: true), - "A specialised doctor will contact you and will be able to view your medical history" - .needTranslation - .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + "A specialised doctor will contact you and will be able to view your medical history".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), ], ), ), @@ -634,9 +607,7 @@ class _BookAppointmentPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ "Free medicine delivery".needTranslation.toText18(color: AppColors.textColor, isBold: true), - "Offers free medicine delivery for the LiveCare appointment" - .needTranslation - .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + "Offers free medicine delivery for the LiveCare appointment".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), ], ), ), diff --git a/lib/presentation/book_appointment/laser/laser_appointment.dart b/lib/presentation/book_appointment/laser/laser_appointment.dart index 19a3800..aae7990 100644 --- a/lib/presentation/book_appointment/laser/laser_appointment.dart +++ b/lib/presentation/book_appointment/laser/laser_appointment.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart' show CapExtension; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart index 48c0cd5..15c8654 100644 --- a/lib/presentation/book_appointment/select_clinic_page.dart +++ b/lib/presentation/book_appointment/select_clinic_page.dart @@ -1211,9 +1211,7 @@ class _SelectClinicPageState extends State { bookAppointmentsViewModel.setIsContinueDentalPlan(true); Navigator.of(context).pop(); Navigator.of(context).push( - CustomPageRoute( - page: SelectDoctorPage(), - ), + CustomPageRoute(page: SelectDoctorPage()), ); }, backgroundColor: AppColors.bgGreenColor, diff --git a/lib/presentation/book_appointment/select_doctor_page.dart b/lib/presentation/book_appointment/select_doctor_page.dart index d77e152..569e8ac 100644 --- a/lib/presentation/book_appointment/select_doctor_page.dart +++ b/lib/presentation/book_appointment/select_doctor_page.dart @@ -40,9 +40,7 @@ class _SelectDoctorPageState extends State { late AppState appState; late BookAppointmentsViewModel bookAppointmentsViewModel; - // Scroll controller to control page scrolling when a group expands late ScrollController _scrollController; - // Map of keys for each item to allow scrolling to them final Map _itemKeys = {}; @override @@ -79,6 +77,20 @@ class _SelectDoctorPageState extends State { backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( title: "Choose Doctor".needTranslation, + // bottomChild: Container( + // decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, customBorder: BorderRadius.only(topLeft: Radius.circular(24.r), topRight: Radius.circular(24.r))), + // padding: EdgeInsets.symmetric(vertical: 20.h, horizontal: 20.h), + // child: CustomButton( + // text: LocaleKeys.search.tr(), + // onPressed: () { + // }, + // icon: null, + // fontSize: 16.f, + // backgroundColor: AppColors.primaryRedColor, + // borderColor: AppColors.primaryRedColor, + // borderRadius: 12.r, + // fontWeight: FontWeight.w500), + // ), child: SingleChildScrollView( controller: _scrollController, child: Padding( @@ -124,40 +136,42 @@ class _SelectDoctorPageState extends State { ], ), SizedBox(height: 16.h), - Row( - children: [ - CustomButton( - text: LocaleKeys.byClinic.tr(context: context), - onPressed: () { - bookAppointmentsVM.setIsSortByClinic(true); - }, - backgroundColor: bookAppointmentsVM.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor, - borderColor: bookAppointmentsVM.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), - textColor: bookAppointmentsVM.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, - fontSize: 12, - fontWeight: FontWeight.w500, - borderRadius: 10, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - ), - SizedBox(width: 8.h), - CustomButton( - text: LocaleKeys.byHospital.tr(context: context), - onPressed: () { - bookAppointmentsVM.setIsSortByClinic(false); - }, - backgroundColor: bookAppointmentsVM.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor, - borderColor: bookAppointmentsVM.isSortByClinic ? AppColors.textColor.withOpacity(0.2) : AppColors.primaryRedColor, - textColor: bookAppointmentsVM.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, - fontSize: 12, - fontWeight: FontWeight.w500, - borderRadius: 10, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - ), - ], - ).paddingSymmetrical(0.h, 0.h), - SizedBox(height: 16.h), + if (bookAppointmentsViewModel.isGetDocForHealthCal && bookAppointmentsVM.showSortFilterButtons) + Row( + children: [ + CustomButton( + text: LocaleKeys.byClinic.tr(context: context), + onPressed: () { + bookAppointmentsVM.setIsSortByClinic(true); + }, + backgroundColor: bookAppointmentsVM.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor, + borderColor: bookAppointmentsVM.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), + textColor: bookAppointmentsVM.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 10, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), + SizedBox(width: 8.h), + CustomButton( + text: LocaleKeys.byHospital.tr(context: context), + onPressed: () { + bookAppointmentsVM.setIsSortByClinic(false); + }, + backgroundColor: bookAppointmentsVM.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor, + borderColor: bookAppointmentsVM.isSortByClinic ? AppColors.textColor.withOpacity(0.2) : AppColors.primaryRedColor, + textColor: bookAppointmentsVM.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 10, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), + ], + ).paddingSymmetrical(0.h, 0.h), + if (bookAppointmentsViewModel.isGetDocForHealthCal && bookAppointmentsVM.showSortFilterButtons) + SizedBox(height: 16.h), Row( mainAxisSize: MainAxisSize.max, children: [