diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 3faef34..d10434d 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -842,6 +842,7 @@ class ApiConsts { static final String allCMCOrdersRc = 'api/cmc/list'; static final String allCMCServicesRc = 'api/cmc/getallcmc'; static final String updateCMCOrder = 'api/cmc/update'; + static final String getHospitalsList = 'Services/Lists.svc/REST/GetProject'; // CMC RC SERVICES var GET_ALL_CMC_SERVICES_RC = 'api/cmc/getallcmc'; diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index 835e9bf..8fa6f89 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -215,7 +215,7 @@ class AppDependencies { ); getIt.registerLazySingleton( - () => HmgServicesViewModel(hmgServicesRepo: getIt(), errorHandlerService: getIt()), + () => HmgServicesViewModel(bookAppointmentsRepo: getIt(), hmgServicesRepo: getIt(), errorHandlerService: getIt()), ); // Screen-specific VMs → Factory diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index f24766b..a50b683 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -3,7 +3,6 @@ import 'dart:async'; 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/cache_consts.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'; @@ -105,8 +104,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { List searchedRegionList = []; List facilityList = ["hmgHospitals", "hmcMedicalClinic"]; List searchedHospitalList = []; - List - searchedPatientDoctorAppointmentHospitalsList = []; + List searchedPatientDoctorAppointmentHospitalsList = []; List searchedClinicList = []; PatientDoctorAppointmentList? selectedHospitalForFilters; @@ -114,15 +112,14 @@ class BookAppointmentsViewModel extends ChangeNotifier { String? selectedClinicForFilters; bool applyFilters = false; - ///variables for laser clinic - List femaleLaserCategory = [ + List femaleLaserCategory = [ LaserCategoryType(1, 'bodyString'), LaserCategoryType(2, 'face'), - LaserCategoryType(10,'bikini'), + LaserCategoryType(10, 'bikini'), LaserCategoryType(11, 'retouch'), ]; - List maleLaserCategory =[ + List maleLaserCategory = [ LaserCategoryType(1, 'body'), LaserCategoryType(2, 'face'), LaserCategoryType(11, 'retouch'), @@ -136,9 +133,13 @@ class BookAppointmentsViewModel extends ChangeNotifier { bool isBodyPartsLoading = false; int duration = 0; - BookAppointmentsViewModel( - {required this.bookAppointmentsRepo, required this.errorHandlerService, required this.navigationService, required this.myAppointmentsViewModel, required this.locationUtils, required this.dialogService }) { + {required this.bookAppointmentsRepo, + required this.errorHandlerService, + required this.navigationService, + required this.myAppointmentsViewModel, + required this.locationUtils, + required this.dialogService}) { initBookAppointmentViewModel(); } @@ -287,7 +288,8 @@ class BookAppointmentsViewModel extends ChangeNotifier { Future getLiveCareScheduleClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async { liveCareClinicsList.clear(); - final result = await bookAppointmentsRepo.getLiveCareScheduleClinics(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!); + final result = + await bookAppointmentsRepo.getLiveCareScheduleClinics(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!); result.fold( (failure) async => await errorHandlerService.handleError(failure: failure), @@ -309,8 +311,9 @@ class BookAppointmentsViewModel extends ChangeNotifier { Future getLiveCareDoctorsList({Function(dynamic)? onSuccess, Function(String)? onError}) async { doctorsList.clear(); - final result = - await bookAppointmentsRepo.getLiveCareDoctorsList(selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, onError: onError); + final result = await bookAppointmentsRepo.getLiveCareDoctorsList( + selectedLiveCareClinic.serviceID!, _appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, + onError: onError); result.fold( (failure) async { @@ -333,10 +336,17 @@ class BookAppointmentsViewModel extends ChangeNotifier { } //TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName - Future getDoctorsList({int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", Function(dynamic)? onSuccess, Function(String)? onError}) async { + Future getDoctorsList( + {int projectID = 0, + bool isNearest = false, + int doctorId = 0, + String doctorName = "", + Function(dynamic)? onSuccess, + Function(String)? onError}) async { doctorsList.clear(); projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID; - final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName, isContinueDentalPlan: isContinueDentalPlan); + final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName, + isContinueDentalPlan: isContinueDentalPlan); result.fold( (failure) async { @@ -365,7 +375,13 @@ class BookAppointmentsViewModel extends ChangeNotifier { } Future getMappedDoctors( - {int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { + {int projectID = 0, + bool isNearest = false, + int doctorId = 0, + String doctorName = "", + isContinueDentalPlan = false, + Function(dynamic)? onSuccess, + Function(String)? onError}) async { filteredHospitalList = null; hospitalList = null; isRegionListLoading = true; @@ -374,10 +390,10 @@ class BookAppointmentsViewModel extends ChangeNotifier { final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName); result.fold( - (failure) async { + (failure) async { onError?.call("No doctors found for the search criteria".needTranslation); }, - (apiResponse) async { + (apiResponse) async { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); } else if (apiResponse.messageStatus == 1) { @@ -401,7 +417,8 @@ class BookAppointmentsViewModel extends ChangeNotifier { } Future getDoctorProfile({Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await bookAppointmentsRepo.getDoctorProfile(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, onError: onError); + final result = await bookAppointmentsRepo + .getDoctorProfile(selectedDoctor.clinicID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, onError: onError); result.fold( (failure) async {}, @@ -457,7 +474,8 @@ class BookAppointmentsViewModel extends ChangeNotifier { // : date = DateUtil.convertStringToDateSaudiTimezone(element, int.parse(selectedDoctor.projectID.toString())); slotsList.add(FreeSlot(date, ['slot'])); - docFreeSlots.add(TimeSlot(isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element)); + docFreeSlots.add(TimeSlot( + isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element)); }); notifyListeners(); @@ -476,8 +494,8 @@ class BookAppointmentsViewModel extends ChangeNotifier { final DateFormat dateFormatter = DateFormat('yyyy-MM-dd'); Map _eventsParsed; - final result = await bookAppointmentsRepo.getLiveCareDoctorFreeSlots( - selectedDoctor.clinicID ?? 0, selectedLiveCareClinic.serviceID ?? 0, selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare, + final result = await bookAppointmentsRepo.getLiveCareDoctorFreeSlots(selectedDoctor.clinicID ?? 0, selectedLiveCareClinic.serviceID ?? 0, + selectedDoctor.projectID ?? 0, selectedDoctor.doctorID ?? 0, isBookingForLiveCare, onError: onError); result.fold( @@ -501,7 +519,8 @@ class BookAppointmentsViewModel extends ChangeNotifier { // : date = DateUtil.convertStringToDateSaudiTimezone(element, int.parse(selectedDoctor.projectID.toString())); slotsList.add(FreeSlot(date, ['slot'])); - docFreeSlots.add(TimeSlot(isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element)); + docFreeSlots.add(TimeSlot( + isoTime: formatter.format(date), start: new DateTime(date.year, date.month, date.day, 0, 0, 0, 0), end: date, vidaDate: element)); }); notifyListeners(); @@ -513,7 +532,10 @@ class BookAppointmentsViewModel extends ChangeNotifier { ); } - Future cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async { + Future cancelAppointment( + {required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, + Function(dynamic)? onSuccess, + Function(String)? onError}) async { final result = await bookAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel); result.fold( @@ -597,13 +619,15 @@ class BookAppointmentsViewModel extends ChangeNotifier { await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async { navigationService.pop(); Future.delayed(Duration(milliseconds: 50)).then((value) async {}); - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation); + LoadingUtils.showFullScreenLoader( + barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation); await insertSpecificAppointment( onError: (err) {}, onSuccess: (apiResp) async { LoadingUtils.hideFullScreenLoader(); await Future.delayed(Duration(milliseconds: 50)).then((value) async { - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); + LoadingUtils.showFullScreenLoader( + barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); await Future.delayed(Duration(milliseconds: 4000)).then((value) { LoadingUtils.hideFullScreenLoader(); Navigator.pushAndRemoveUntil( @@ -693,13 +717,15 @@ class BookAppointmentsViewModel extends ChangeNotifier { await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async { navigationService.pop(); Future.delayed(Duration(milliseconds: 50)).then((value) async {}); - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation); + LoadingUtils.showFullScreenLoader( + barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation); await insertSpecificAppointment( onError: (err) {}, onSuccess: (apiResp) async { LoadingUtils.hideFullScreenLoader(); await Future.delayed(Duration(milliseconds: 50)).then((value) async { - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); + LoadingUtils.showFullScreenLoader( + barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); await Future.delayed(Duration(milliseconds: 4000)).then((value) { LoadingUtils.hideFullScreenLoader(); Navigator.pushAndRemoveUntil( @@ -773,7 +799,9 @@ class BookAppointmentsViewModel extends ChangeNotifier { } else { filteredHospitalList = RegionList(); - var list = isHMG ? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList : hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList; + var list = isHMG + ? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList + : hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList; if (list != null && list.isEmpty) { notifyListeners(); @@ -856,12 +884,8 @@ class BookAppointmentsViewModel extends ChangeNotifier { notifyListeners(); } - void setSelections( - List? selectedFacilityForFilters, - List? selectedRegionForFilters, - String? selectedClinicForFilters, - PatientDoctorAppointmentList? selectedHospitalForFilters, - bool applyFilters) { + void setSelections(List? selectedFacilityForFilters, List? selectedRegionForFilters, String? selectedClinicForFilters, + PatientDoctorAppointmentList? selectedHospitalForFilters, bool applyFilters) { this.selectedFacilityForFilters = selectedFacilityForFilters; this.selectedClinicForFilters = selectedClinicForFilters; this.selectedHospitalForFilters = selectedHospitalForFilters; @@ -872,17 +896,14 @@ class BookAppointmentsViewModel extends ChangeNotifier { void getFiltersFromDoctorList() { doctorsList.forEach((element) { - if (!searchedRegionList - .contains(element.getRegionName(_appState.isArabic()))) { - searchedRegionList - .add(element.getRegionName(_appState.isArabic()) ?? ""); + if (!searchedRegionList.contains(element.getRegionName(_appState.isArabic()))) { + searchedRegionList.add(element.getRegionName(_appState.isArabic()) ?? ""); } if (!searchedHospitalList.contains(element.projectName)) { - searchedPatientDoctorAppointmentHospitalsList - .add(PatientDoctorAppointmentList() - ..filterName = element.projectName - ..isHMC = element.isHMC - ..distanceInKMs = "0"); + searchedPatientDoctorAppointmentHospitalsList.add(PatientDoctorAppointmentList() + ..filterName = element.projectName + ..isHMC = element.isHMC + ..distanceInKMs = "0"); searchedHospitalList.add(element.projectName ?? ""); } if (!searchedClinicList.contains(element.clinicName)) { @@ -939,27 +960,15 @@ class BookAppointmentsViewModel extends ChangeNotifier { return doctorsList; } var list = doctorsList.where((element) { - var isInSelectedRegion = (selectedRegionForFilters?.isEmpty == true) - ? true - : selectedRegionForFilters - ?.any((region) => region == element.getRegionName(isArabic())); - var shouldApplyFacilityFilter = - (selectedFacilityForFilters?.isEmpty == true) ? false : true; - var isHMC = (selectedFacilityForFilters?.isEmpty == true) - ? true - : selectedFacilityForFilters?.any((item) => item.contains("hmc")); - var isInSelectedClinic = (selectedClinicForFilters == null) - ? true - : selectedClinicForFilters == element.clinicName; - var isInSelectedHospital = (selectedHospitalForFilters == null) - ? true - : element.projectName == selectedHospitalForFilters?.filterName; + var isInSelectedRegion = + (selectedRegionForFilters?.isEmpty == true) ? true : selectedRegionForFilters?.any((region) => region == element.getRegionName(isArabic())); + var shouldApplyFacilityFilter = (selectedFacilityForFilters?.isEmpty == true) ? false : true; + var isHMC = (selectedFacilityForFilters?.isEmpty == true) ? true : selectedFacilityForFilters?.any((item) => item.contains("hmc")); + var isInSelectedClinic = (selectedClinicForFilters == null) ? true : selectedClinicForFilters == element.clinicName; + var isInSelectedHospital = (selectedHospitalForFilters == null) ? true : element.projectName == selectedHospitalForFilters?.filterName; var facilityFilter = ((shouldApplyFacilityFilter == true) ? isHMC : true); - return (isInSelectedRegion ?? true) && - (facilityFilter ?? true) && - isInSelectedClinic && - isInSelectedHospital; + return (isInSelectedRegion ?? true) && (facilityFilter ?? true) && isInSelectedClinic && isInSelectedHospital; }).toList(); return list; } @@ -1003,7 +1012,8 @@ class BookAppointmentsViewModel extends ChangeNotifier { dentalChiefComplaintsList.clear(); notifyListeners(); int patientID = _appState.isAuthenticated ? _appState.getAuthenticatedUser()!.patientId ?? -1 : -1; - final result = await bookAppointmentsRepo.getDentalChiefComplaintsList(patientID: patientID, projectID: int.parse(currentlySelectedHospitalFromRegionFlow ?? "0"), clinicID: 17); + final result = await bookAppointmentsRepo.getDentalChiefComplaintsList( + patientID: patientID, projectID: int.parse(currentlySelectedHospitalFromRegionFlow ?? "0"), clinicID: 17); result.fold( (failure) async => await errorHandlerService.handleError(failure: failure), @@ -1051,7 +1061,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { ); } - setBodyType(int bodyType){ + setBodyType(int bodyType) { selectedBodyTypeIndex = bodyType; selectedCategory = 0; selectedBodyPartList = []; @@ -1059,33 +1069,33 @@ class BookAppointmentsViewModel extends ChangeNotifier { notifyListeners(); } - FutureOr getLaserClinic() async{ + FutureOr getLaserClinic() async { isBodyPartsLoading = true; int id = bodyTypes[selectedBodyTypeIndex][selectedCategory].laserCategoryID; int projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : 0; int languageID = _appState.isArabic() ? 1 : 0; final result = await bookAppointmentsRepo.getLaserClinics(id, projectID, languageID); result.fold( - (failure) { + (failure) { isBodyPartsLoading = false; notifyListeners(); }, - (apiResponse) {isBodyPartsLoading = false; + (apiResponse) { + isBodyPartsLoading = false; if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); } else if (apiResponse.messageStatus == 1) { - List response =apiResponse.data!; - if(response.first.category == 2 || response.first.category == 10 ) response.remove(response.first); + List response = apiResponse.data!; + if (response.first.category == 2 || response.first.category == 10) response.remove(response.first); laserBodyPartsList = response; } - notifyListeners(); - + notifyListeners(); }, ); } int getDuration() { - var duration = 0; + var duration = 0; var lowerUpperLegsList = selectedBodyPartList.where((element) => element.mappingCode == "47" || element.mappingCode == "48")?.toList() ?? []; var upperLowerArmsList = selectedBodyPartList.where((element) => element.mappingCode == "40" || element.mappingCode == "41")?.toList() ?? []; @@ -1110,21 +1120,25 @@ class BookAppointmentsViewModel extends ChangeNotifier { } void setSelectedBodyPart(LaserBodyPart part) { - if(selectedBodyPartList.contains(part)){ + if (selectedBodyPartList.contains(part)) { selectedBodyPartList.remove(part); this.duration = getDuration(); notifyListeners(); } else { - if(this.duration == 90){ - dialogService.showErrorBottomSheet(message: "Duration can not exceed 90 min".needTranslation,); + if (this.duration == 90) { + dialogService.showErrorBottomSheet( + message: "Duration can not exceed 90 min".needTranslation, + ); return; } selectedBodyPartList.add(part); var duration = getDuration(); - if(duration > 90){ + if (duration > 90) { selectedBodyPartList.remove(part); - dialogService.showErrorBottomSheet(message: "Duration Exceeds 90 min".needTranslation,); + dialogService.showErrorBottomSheet( + message: "Duration Exceeds 90 min".needTranslation, + ); return; } this.duration = duration; @@ -1137,10 +1151,10 @@ class BookAppointmentsViewModel extends ChangeNotifier { } String getLaserProcedureNameWRTLanguage(LaserBodyPart part) { - if(_appState.isArabic()){ - return part.bodyPartN??""; - }else { - return part.bodyPart??""; + if (_appState.isArabic()) { + return part.bodyPartN ?? ""; + } else { + return part.bodyPart ?? ""; } } diff --git a/lib/features/emergency_services/emergency_services_view_model.dart b/lib/features/emergency_services/emergency_services_view_model.dart index 9526f16..cba6399 100644 --- a/lib/features/emergency_services/emergency_services_view_model.dart +++ b/lib/features/emergency_services/emergency_services_view_model.dart @@ -2,22 +2,18 @@ import 'dart:async'; 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/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/EROnlineCheckInPaymentDetailsResponse.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'; @@ -25,16 +21,14 @@ import 'package:hmg_patient_app_new/features/emergency_services/models/resp_mode 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/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/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'; @@ -104,8 +98,7 @@ class EmergencyServicesViewModel extends ChangeNotifier { notifyListeners(); } - get isGMSAvailable - { + get isGMSAvailable { return appState.isGMSAvailable; } @@ -173,7 +166,8 @@ class EmergencyServicesViewModel extends ChangeNotifier { if (query.isEmpty) { nearestERFilteredList = nearestERList; } else { - nearestERFilteredList = nearestERList.where((er) => er.projectName != null && er.projectName!.toLowerCase().contains(query.toLowerCase())).toList(); + nearestERFilteredList = + nearestERList.where((er) => er.projectName != null && er.projectName!.toLowerCase().contains(query.toLowerCase())).toList(); } notifyListeners(); } @@ -256,8 +250,7 @@ class EmergencyServicesViewModel extends ChangeNotifier { onSuccess: (position) { updateBottomSheetState(BottomSheetType.FIXED); navServices.push( - CustomPageRoute( - page: CallAmbulancePage(), direction: AxisDirection.down), + CustomPageRoute(page: CallAmbulancePage(), direction: AxisDirection.down), ); }); } else { @@ -265,9 +258,7 @@ class EmergencyServicesViewModel extends ChangeNotifier { message: "You Need To Login First To Continue".needTranslation, onOkPressed: () { navServices.pop(); - navServices.pushAndReplace( - AppRoutes.loginScreen - ); + navServices.pushAndReplace(AppRoutes.loginScreen); }); } } @@ -299,19 +290,20 @@ class EmergencyServicesViewModel extends ChangeNotifier { 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; @@ -332,12 +324,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!; @@ -350,7 +342,8 @@ class EmergencyServicesViewModel extends ChangeNotifier { ); } - Future ER_CreateAdvancePayment({required String paymentMethodName, required String paymentReference, Function(dynamic)? onSuccess, Function(String)? onError}) async { + Future ER_CreateAdvancePayment( + {required String paymentMethodName, required String paymentReference, Function(dynamic)? onSuccess, Function(String)? onError}) async { final result = await emergencyServicesRepo.createAdvancePaymentForER( projectID: selectedHospital!.iD, authUser: appState.getAuthenticatedUser()!, @@ -359,12 +352,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!; @@ -378,12 +371,17 @@ class EmergencyServicesViewModel extends ChangeNotifier { } Future addAdvanceNumberRequest( - {required String advanceNumber, required String paymentReference, required String appointmentNo, Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await emergencyServicesRepo.addAdvanceNumberRequest(advanceNumber: advanceNumber, paymentReference: paymentReference, appointmentNo: appointmentNo); + {required String advanceNumber, + required String paymentReference, + required String appointmentNo, + Function(dynamic)? onSuccess, + Function(String)? onError}) async { + 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) { @@ -401,12 +399,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!); @@ -426,12 +424,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"]); @@ -457,7 +455,7 @@ class EmergencyServicesViewModel extends ChangeNotifier { onOkPressed: () { navServices.pop(); print("inside the ok button"); - getIt().onLoginPressed(); + getIt().onLoginPressed(); }); return; } @@ -465,8 +463,7 @@ class EmergencyServicesViewModel extends ChangeNotifier { if (transportationOptions.isNotEmpty) return; int? id = appState.getAuthenticatedUser()?.patientId; - LoaderBottomSheet.showLoader( - loadingText: "Getting Ambulance Transport Option".needTranslation); + LoaderBottomSheet.showLoader(loadingText: "Getting Ambulance Transport Option".needTranslation); notifyListeners(); var response = await emergencyServicesRepo.getTransportationMethods(id: id); @@ -485,8 +482,7 @@ class EmergencyServicesViewModel extends ChangeNotifier { Future getTransportationMethods() async { int? id = appState.getAuthenticatedUser()?.patientId; - LoaderBottomSheet.showLoader( - loadingText: "Getting Ambulance Transport Option".needTranslation); + LoaderBottomSheet.showLoader(loadingText: "Getting Ambulance Transport Option".needTranslation); notifyListeners(); var response = await emergencyServicesRepo.getTransportationMethods(id: id); @@ -597,11 +593,7 @@ class EmergencyServicesViewModel extends ChangeNotifier { sourceList = hmcHospitalList; break; } - displayList = sourceList - ?.where((hospital) => - hospital.name != null && - hospital.name!.toLowerCase().contains(query.toLowerCase())) - .toList(); + displayList = sourceList?.where((hospital) => hospital.name != null && hospital.name!.toLowerCase().contains(query.toLowerCase())).toList(); notifyListeners(); } @@ -620,7 +612,6 @@ class EmergencyServicesViewModel extends ChangeNotifier { notifyListeners(); } - void setSelectedHospital(HospitalsModel? hospital) { selectedHospital = hospital; notifyListeners(); @@ -668,13 +659,12 @@ class EmergencyServicesViewModel extends ChangeNotifier { } Future updateAppointment(bool value) async { - if (value) { await getAppointments(); } else { clearAppointmentData(); } - if(appointments?.isNotEmpty == true) { + if (appointments?.isNotEmpty == true) { haveAnAppointment = value; } notifyListeners(); @@ -729,18 +719,20 @@ class EmergencyServicesViewModel extends ChangeNotifier { } Future getTransportationOrders({bool shouldNavigateToTrackingScreen = false, bool showLoader = false}) async { - if(shouldNavigateToTrackingScreen == false && showLoader ) { + 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();} + if (shouldNavigateToTrackingScreen == false && showLoader) { + LoaderBottomSheet.hideLoader(); + } response.fold( (failure) async { if (shouldNavigateToTrackingScreen) { - navServices.pushAndRemoveUntil(CustomPageRoute(page: TrackingScreen(state: OrderTrackingState.waitingForCall)), ModalRoute.withName("/EmergencyServicesPage")); + navServices.pushAndRemoveUntil( + CustomPageRoute(page: TrackingScreen(state: OrderTrackingState.waitingForCall)), ModalRoute.withName("/EmergencyServicesPage")); } }, (apiResponse) { diff --git a/lib/features/hmg_services/hmg_services_repo.dart b/lib/features/hmg_services/hmg_services_repo.dart index 39a6142..9264385 100644 --- a/lib/features/hmg_services/hmg_services_repo.dart +++ b/lib/features/hmg_services/hmg_services_repo.dart @@ -8,6 +8,7 @@ import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_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 HmgServicesRepo { @@ -16,6 +17,8 @@ abstract class HmgServicesRepo { Future>> updateCmcPresOrder(OrderUpdateRequestModel requestModel); Future>>> getAllCmcServices({required int patientID}); + + Future>>> getHospitalsList(); } class HmgServicesRepoImp implements HmgServicesRepo { @@ -175,4 +178,66 @@ class HmgServicesRepoImp implements HmgServicesRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>>> getHospitalsList() async { + Map requestBody = {}; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + + await apiClient.post( + ApiConsts.getHospitalsList, + isRCService: false, // This uses the base HIS API URL, not RC + body: requestBody, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + loggerService.logError("Get Hospitals List API Failed: $error, Status: $statusCode"); + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + List hospitalsList = []; + + loggerService.logInfo("Hospitals List Raw Response: $response"); + + if (response['ListProject'] != null && response['ListProject'] is List) { + final projectsList = response['ListProject'] as List; + + for (var projectJson in projectsList) { + try { + if (projectJson is Map) { + hospitalsList.add(HospitalsModel.fromJson(projectJson)); + } + } catch (e) { + loggerService.logError('Error parsing hospital item: ${e.toString()}'); + } + } + } else { + loggerService.logInfo('Hospitals list response array is empty or missing'); + } + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: errorMessage, + data: hospitalsList, + ); + + loggerService.logInfo("Hospitals fetched successfully: ${hospitalsList.length} hospitals"); + } catch (e) { + loggerService.logError("Error parsing hospitals list: ${e.toString()}"); + failure = DataParsingFailure(e.toString()); + } + }, + ); + + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + loggerService.logError("Unknown error in getHospitalsList: ${e.toString()}"); + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/hmg_services/hmg_services_view_model.dart b/lib/features/hmg_services/hmg_services_view_model.dart index 84e3bb4..19e7a1f 100644 --- a/lib/features/hmg_services/hmg_services_view_model.dart +++ b/lib/features/hmg_services/hmg_services_view_model.dart @@ -1,22 +1,35 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart'; import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_repo.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; class HmgServicesViewModel extends ChangeNotifier { final HmgServicesRepo hmgServicesRepo; + final BookAppointmentsRepo bookAppointmentsRepo; final ErrorHandlerService errorHandlerService; - HmgServicesViewModel({required this.hmgServicesRepo, required this.errorHandlerService}); + HmgServicesViewModel({required this.bookAppointmentsRepo, required this.hmgServicesRepo, required this.errorHandlerService}); bool isCmcOrdersLoading = false; bool isCmcServicesLoading = false; bool isUpdatingOrder = false; + bool isHospitalListLoading = false; List cmcOrdersList = []; List cmcServicesList = []; + List hospitalsList = []; + List filteredHospitalsList = []; + HospitalsModel? selectedHospital; + + // CMC order creation state + HospitalsModel? selectedHospitalForOrder; + GetCMCServicesResponseModel? selectedServiceForOrder; Future getOrdersList() async { cmcOrdersList.clear(); @@ -134,4 +147,81 @@ class HmgServicesViewModel extends ChangeNotifier { return success; } + + Future getHospitalsList({Function(dynamic)? onSuccess, Function(String)? onError}) async { + isHospitalListLoading = true; + notifyListeners(); + + final result = await hmgServicesRepo.getHospitalsList(); + + result.fold( + (failure) async { + isHospitalListLoading = false; + notifyListeners(); + await errorHandlerService.handleError(failure: failure); + if (onError != null) { + onError(failure.toString()); + } + }, + (apiResponse) { + isHospitalListLoading = false; + if (apiResponse.messageStatus == 1) { + log("apiResponse.messageStatus: ${apiResponse.data}"); + hospitalsList = apiResponse.data ?? []; + filteredHospitalsList = List.from(hospitalsList); + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } else { + notifyListeners(); + if (onError != null) { + onError(apiResponse.errorMessage ?? 'Unknown error'); + } + } + }, + ); + } + + void filterHospitalsByString(String searchText, bool isArabic) { + if (searchText.isEmpty) { + filteredHospitalsList = List.from(hospitalsList); + } else { + filteredHospitalsList = hospitalsList.where((HospitalsModel hospital) { + final name = isArabic ? (hospital.nameN ?? '') : (hospital.name ?? ''); + return name.toLowerCase().contains(searchText.toLowerCase()); + }).toList(); + } + notifyListeners(); + } + + void setSelectedHospital(HospitalsModel? hospital) { + selectedHospital = hospital; + notifyListeners(); + } + + void clearHospitalSelection() { + selectedHospital = null; + filteredHospitalsList = List.from(hospitalsList); + notifyListeners(); + } + + // CMC Order management methods + void setSelectedHospitalForOrder(HospitalsModel? hospital) { + selectedHospitalForOrder = hospital; + notifyListeners(); + } + + void setSelectedServiceForOrder(GetCMCServicesResponseModel? service) { + selectedServiceForOrder = service; + notifyListeners(); + } + + void clearOrderSelection() { + selectedHospitalForOrder = null; + selectedServiceForOrder = null; + notifyListeners(); + } + + bool get isOrderReadyToConfirm => selectedHospitalForOrder != null && selectedServiceForOrder != null; } diff --git a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart index 29a7b96..ad48a6d 100644 --- a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart +++ b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart @@ -1,18 +1,13 @@ -import 'package:easy_localization/easy_localization.dart' - show tr, StringTranslateExtension; +import 'package:easy_localization/easy_localization.dart' show StringTranslateExtension; import 'package:flutter/material.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/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_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/models/facility_selection.dart'; -import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart'; -import 'package:hmg_patient_app_new/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart'; -import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:provider/provider.dart'; @@ -53,8 +48,8 @@ class HospitalBottomSheetBody extends StatelessWidget { hintText: LocaleKeys.searchHospital.tr(), controller: searchText, onChange: (value) { - appointmentsViewModel.filterHospitalListByString(value, regionalViewModel.selectedRegionId , regionalViewModel.selectedFacilityType == - FacilitySelection.HMG.name); + appointmentsViewModel.filterHospitalListByString( + value, regionalViewModel.selectedRegionId, regionalViewModel.selectedFacilityType == FacilitySelection.HMG.name); }, isEnable: true, prefix: null, @@ -77,25 +72,15 @@ class HospitalBottomSheetBody extends StatelessWidget { SizedBox( height: MediaQuery.sizeOf(context).height * .4, child: ListView.separated( - itemBuilder: (_, index) - { - var hospital = regionalViewModel.selectedFacilityType == - FacilitySelection.HMG.name - ? appointmentsViewModel - .filteredHospitalList! - .registeredDoctorMap![ - regionalViewModel.selectedRegionId!]! - .hmgDoctorList![index] - : appointmentsViewModel - .filteredHospitalList - ?.registeredDoctorMap?[ - regionalViewModel.selectedRegionId!] - ?.hmcDoctorList?[index]; + itemBuilder: (_, index) { + var hospital = regionalViewModel.selectedFacilityType == FacilitySelection.HMG.name + ? appointmentsViewModel.filteredHospitalList!.registeredDoctorMap![regionalViewModel.selectedRegionId!]!.hmgDoctorList![index] + : appointmentsViewModel.filteredHospitalList?.registeredDoctorMap?[regionalViewModel.selectedRegionId!]?.hmcDoctorList?[index]; return HospitalListItem( - hospitalData: hospital, - isLocationEnabled: appointmentsViewModel.isLocationEnabled(), - ).onPress(() { - regionalViewModel.setHospitalModel(hospital); + hospitalData: hospital, + isLocationEnabled: appointmentsViewModel.isLocationEnabled(), + ).onPress(() { + regionalViewModel.setHospitalModel(hospital); if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_REGION) { regionalViewModel.setBottomSheetState(AppointmentViaRegionState.CLINIC_SELECTION); regionalViewModel.handleLastStepForRegion(); @@ -104,21 +89,18 @@ class HospitalBottomSheetBody extends StatelessWidget { regionalViewModel.handleLastStepForClinic(); } else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER) { regionalViewModel.setBottomSheetState(AppointmentViaRegionState.DOCTOR_SELECTION); - regionalViewModel.handleLastStepForClinicForDentalAndLaser(appointmentsViewModel.selectedClinic.clinicID??-1); + regionalViewModel.handleLastStepForClinicForDentalAndLaser(appointmentsViewModel.selectedClinic.clinicID ?? -1); // regionalViewModel.handleLastStepForClinic(); } - });}, + }); + }, separatorBuilder: (_, __) => SizedBox( height: 16.h, ), - itemCount: (regionalViewModel.selectedFacilityType == - FacilitySelection.HMG.name - ? (appointmentsViewModel.filteredHospitalList?.registeredDoctorMap?[ - regionalViewModel.selectedRegionId]?.hmgDoctorList) - : (appointmentsViewModel - .filteredHospitalList - ?.registeredDoctorMap?[ - regionalViewModel.selectedRegionId]?.hmcDoctorList))?.length ?? + itemCount: (regionalViewModel.selectedFacilityType == FacilitySelection.HMG.name + ? (appointmentsViewModel.filteredHospitalList?.registeredDoctorMap?[regionalViewModel.selectedRegionId]?.hmgDoctorList) + : (appointmentsViewModel.filteredHospitalList?.registeredDoctorMap?[regionalViewModel.selectedRegionId]?.hmcDoctorList)) + ?.length ?? 0), ) ], diff --git a/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart b/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart new file mode 100644 index 0000000..1186e2f --- /dev/null +++ b/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart @@ -0,0 +1,380 @@ +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/route_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart'; +import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_hospital_selection_helper.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:maps_launcher/maps_launcher.dart'; +import 'package:provider/provider.dart'; + +class CmcSelectionReviewPage extends StatefulWidget { + final GetCMCServicesResponseModel selectedService; + final HospitalsModel? preSelectedHospital; + + const CmcSelectionReviewPage({ + super.key, + required this.selectedService, + this.preSelectedHospital, + }); + + @override + State createState() => _CmcSelectionReviewPageState(); +} + +class _CmcSelectionReviewPageState extends State { + @override + void initState() { + super.initState(); + // Initialize ViewModel state with preselected hospital if provided + if (widget.preSelectedHospital != null) { + WidgetsBinding.instance.addPostFrameCallback((_) { + final hmgServicesViewModel = context.read(); + hmgServicesViewModel.setSelectedHospitalForOrder(widget.preSelectedHospital); + hmgServicesViewModel.setSelectedServiceForOrder(widget.selectedService); + }); + } + } + + @override + Widget build(BuildContext context) { + final appState = getIt.get(); + final isArabic = appState.isArabic(); + + return CollapsingListView( + title: "Summary".needTranslation, + bottomChild: _buildBottomButton(), + child: SingleChildScrollView( + padding: EdgeInsets.all(16.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildOrderSummaryCard(isArabic), + SizedBox(height: 16.h), + _buildSelectedServiceCard(isArabic), + ], + ), + ), + ); + } + + Widget _buildOrderSummaryCard(bool isArabic) { + return Consumer( + builder: (context, hmgServicesViewModel, child) { + final selectedHospital = hmgServicesViewModel.selectedHospitalForOrder; + final isLocationSelected = selectedHospital != null; + + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 16.r, + ), + padding: EdgeInsets.all(16.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Select Hospital".needTranslation, + style: TextStyle( + fontSize: 16.f, + fontWeight: FontWeight.w700, + color: AppColors.blackColor, + letterSpacing: -0.5, + ), + ), + SizedBox(height: 12.h), + _buildHospitalSelector(isArabic, selectedHospital, isLocationSelected), + if (isLocationSelected && selectedHospital != null) ...[ + SizedBox(height: 16.h), + _buildHospitalMap(selectedHospital), + ], + ], + ), + ); + }, + ); + } + + Widget _buildHospitalSelector(bool isArabic, HospitalsModel? selectedHospital, bool isLocationSelected) { + return InkWell( + onTap: _showHospitalSelectionBottomSheet, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 14.h), + decoration: BoxDecoration( + color: AppColors.bgScaffoldColor, + borderRadius: BorderRadius.circular(12.r), + border: Border.all( + color: AppColors.greyColor.withAlpha(51), + width: 1, + ), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + isLocationSelected && selectedHospital != null + ? (isArabic ? (selectedHospital.nameN ?? selectedHospital.name ?? '') : (selectedHospital.name ?? '')) + : "Select Hospital".needTranslation, + style: TextStyle( + fontSize: 14.f, + fontWeight: isLocationSelected ? FontWeight.w600 : FontWeight.w400, + color: isLocationSelected ? AppColors.blackColor : AppColors.greyTextColor, + letterSpacing: -0.4, + ), + ), + ], + ), + ), + Icon( + Icons.keyboard_arrow_down, + color: AppColors.greyTextColor, + size: 24.h, + ), + ], + ), + ), + ); + } + + Widget _buildHospitalMap(HospitalsModel selectedHospital) { + final String lat = selectedHospital.latitude ?? "0.0"; + final String lng = selectedHospital.longitude ?? "0.0"; + + if (lat == "0.0" || lng == "0.0") return SizedBox.shrink(); + + // final String staticMapUrl = + // "https://maps.googleapis.com/maps/api/staticmap?center=$lat,$lng&zoom=16&size=600x300&maptype=roadmap&markers=color:red%7C$lat,$lng&key=AIzaSyCyDbWUM9d_sBUGIE8PcuShzPaqO08NSC8"; + + return Stack( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(12.r), + child: Image.network( + "staticMapUrl", + height: 200.h, + width: double.infinity, + fit: BoxFit.cover, + loadingBuilder: (context, child, loadingProgress) { + if (loadingProgress == null) return child; + return Container( + height: 200.h, + decoration: BoxDecoration( + color: AppColors.bgScaffoldColor, + borderRadius: BorderRadius.circular(12.r), + ), + child: Center( + child: CircularProgressIndicator( + color: AppColors.primaryRedColor, + ), + ), + ); + }, + errorBuilder: (context, error, stackTrace) { + return Container( + height: 200.h, + decoration: BoxDecoration( + color: AppColors.bgScaffoldColor, + borderRadius: BorderRadius.circular(12.r), + ), + child: Center( + child: Icon( + Icons.error_outline, + size: 48.h, + color: AppColors.greyTextColor, + ), + ), + ); + }, + ), + ), + Positioned( + bottom: 12.h, + right: 12.w, + child: InkWell( + onTap: () => _launchDirections(selectedHospital), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h), + decoration: BoxDecoration( + color: AppColors.whiteColor, + borderRadius: BorderRadius.circular(1000.r), + boxShadow: [ + BoxShadow( + color: Color.fromARGB(26, 0, 0, 0), + blurRadius: 8, + offset: Offset(0, 2), + ), + ], + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Utils.buildSvgWithAssets( + icon: AppAssets.directions_icon, + width: 16.w, + height: 16.h, + ), + SizedBox(width: 6.w), + Text( + "Get Directions".needTranslation, + style: TextStyle( + fontSize: 12.f, + fontWeight: FontWeight.w600, + color: AppColors.blackColor, + letterSpacing: -0.4, + ), + ), + ], + ), + ), + ), + ), + ], + ); + } + + Widget _buildSelectedServiceCard(bool isArabic) { + final serviceName = isArabic ? (widget.selectedService.textN ?? widget.selectedService.text ?? '') : (widget.selectedService.text ?? ''); + final price = widget.selectedService.priceTotal ?? 0.0; + + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 16.r, + ), + padding: EdgeInsets.all(16.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Selected Service".needTranslation.toText14( + weight: FontWeight.w600, + color: AppColors.greyTextColor, + letterSpacing: -0.4, + ), + SizedBox(height: 12.h), + Row( + children: [ + Expanded(child: serviceName.toText16(weight: FontWeight.w700, color: AppColors.blackColor, letterSpacing: -0.5)), + ], + ), + SizedBox(height: 8.h), + Row( + children: [ + "${"Price".needTranslation}: ".toText14(weight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: -0.4), + Utils.getPaymentAmountWithSymbol( + isExpanded: false, + price.toString().toText14(weight: FontWeight.w700), + AppColors.primaryRedColor, + 16, + isSaudiCurrency: true, + ), + ], + ), + ], + ), + ); + } + + Widget _buildBottomButton() { + return Consumer( + builder: (context, hmgServicesViewModel, child) { + final isLocationSelected = hmgServicesViewModel.selectedHospitalForOrder != null; + + return SafeArea( + top: false, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h), + decoration: BoxDecoration( + color: AppColors.whiteColor, + boxShadow: [ + BoxShadow( + color: Color.fromARGB(13, 0, 0, 0), + blurRadius: 8, + offset: Offset(0, -2), + ), + ], + ), + child: CustomButton( + text: "Confirm".needTranslation, + onPressed: () { + isLocationSelected ? _handleConfirm : null; + }, + textColor: AppColors.whiteColor, + backgroundColor: isLocationSelected ? AppColors.successColor : AppColors.greyColor, + borderRadius: 12.r, + borderColor: Colors.transparent, + borderWidth: 0, + padding: EdgeInsets.symmetric(vertical: 14.h), + ), + ), + ); + }, + ); + } + + void _showHospitalSelectionBottomSheet() { + CmcHospitalSelectionHelper.showHospitalSelectionBottomSheet(context: context, onHospitalSelected: (hospital) => context.pop()); + } + + void _launchDirections(HospitalsModel selectedHospital) { + final double lat = double.parse(selectedHospital.latitude ?? "0.0"); + final double lng = double.parse(selectedHospital.longitude ?? "0.0"); + + if (lat != 0.0 && lng != 0.0) { + MapsLauncher.launchCoordinates( + lat, + lng, + selectedHospital.name ?? "Hospital", + ); + } + } + + void _handleConfirm() { + final hmgServicesViewModel = context.read(); + final selectedHospital = hmgServicesViewModel.selectedHospitalForOrder; + + if (selectedHospital == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text("Please select a hospital to continue".needTranslation), + backgroundColor: AppColors.errorColor, + ), + ); + return; + } + + // TODO: Implement order creation API call + // Create CMC order with: + // - hmgServicesViewModel.selectedServiceForOrder (the medical checkup service) + // - hmgServicesViewModel.selectedHospitalForOrder (the chosen hospital) + // - Call hmgServicesViewModel.createCmcOrder() or similar method + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + "Order confirmed! Service: ${widget.selectedService.text}, Hospital: ${selectedHospital.name}", + ), + backgroundColor: AppColors.successColor, + ), + ); + + // Navigate back after confirmation + Future.delayed(Duration(seconds: 2), () { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + // Clear order selection after successful confirmation + hmgServicesViewModel.clearOrderSelection(); + }); + } +} diff --git a/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart b/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart index d242e55..699152c 100644 --- a/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart +++ b/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart @@ -12,6 +12,8 @@ import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_mode import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart'; import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/cmc_order_detail_page.dart'; +import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/cmc_selection_review_page.dart'; +import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_hospital_selection_helper.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'; @@ -264,10 +266,14 @@ class _ComprehensiveCheckupPageState extends State { color: Color.fromARGB(51, 0, 0, 0), borderRadius: BorderRadius.circular(1000.r), ), - margin: EdgeInsets.all(12.h), + margin: EdgeInsets.all(16.h), child: IconButton( - icon: Icon(Icons.zoom_in, color: Colors.white), - padding: EdgeInsets.all(12.h), + icon: Icon( + Icons.zoom_in, + color: Colors.white, + size: 26.w, + ), + padding: EdgeInsets.all(10.h), onPressed: () => _showFullScreenImage(context, imagePath, isSvg: false), ), ), @@ -281,13 +287,26 @@ class _ComprehensiveCheckupPageState extends State { void _proceedWithSelectedService() { if (_selectedService != null) { - // TODO: Navigate to next step or create order - // This will be implemented based on your flow - // For now, just show a message - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Selected: ${_selectedService!.text}'), - ), + final hmgServicesViewModel = context.read(); + + // Store selected service in ViewModel + hmgServicesViewModel.setSelectedServiceForOrder(_selectedService); + hmgServicesViewModel.getHospitalsList(); + // Show hospital selection bottom sheet using common helper + CmcHospitalSelectionHelper.showHospitalSelectionBottomSheet( + context: context, + onHospitalSelected: (hospital) { + // Navigate to review page + Navigator.of(context).pushReplacement( + CustomPageRoute( + page: CmcSelectionReviewPage( + selectedService: _selectedService!, + preSelectedHospital: hospital, + ), + direction: AxisDirection.down, + ), + ); + }, ); } } @@ -319,18 +338,11 @@ class _ComprehensiveCheckupPageState extends State { Widget build(BuildContext context) { return CollapsingListView( title: "Comprehensive Checkup".needTranslation, - requests: () { - Navigator.of(context).push(CustomPageRoute(page: CmcOrderDetailPage(), direction: AxisDirection.up)); - }, - // bottomChild sticks to the bottom of the scaffold; make it reactive using Consumer + history: () => Navigator.of(context).push(CustomPageRoute(page: CmcOrderDetailPage(), direction: AxisDirection.up)), bottomChild: Consumer( builder: (context, hmgServicesViewModel, child) { - // if still loading, don't show the bottom button if (hmgServicesViewModel.isCmcOrdersLoading || hmgServicesViewModel.isCmcServicesLoading) return SizedBox.shrink(); - final pendingOrder = _getPendingOrder(hmgServicesViewModel.cmcOrdersList); - - // show button only when there is no pending order and a service is selected if (pendingOrder == null && _selectedServiceId != null) { return SafeArea( top: false, @@ -348,7 +360,6 @@ class _ComprehensiveCheckupPageState extends State { ), ); } - return SizedBox.shrink(); }, ), @@ -357,12 +368,8 @@ class _ComprehensiveCheckupPageState extends State { if (hmgServicesViewModel.isCmcOrdersLoading || hmgServicesViewModel.isCmcServicesLoading) { return _buildLoadingShimmer(); } - final pendingOrder = _getPendingOrder(hmgServicesViewModel.cmcOrdersList); - if (pendingOrder != null) { - // Return the pending order card directly. The outer CollapsingListView's - // CustomScrollView/SliverList will provide proper scroll constraints. return _buildPendingOrderCard(pendingOrder); } else { return _buildServiceSelectionList(hmgServicesViewModel.cmcServicesList); diff --git a/lib/presentation/comprehensive_checkup/old_cmc_page.dart b/lib/presentation/comprehensive_checkup/old_cmc_page.dart index 042eab0..b1a6f69 100644 --- a/lib/presentation/comprehensive_checkup/old_cmc_page.dart +++ b/lib/presentation/comprehensive_checkup/old_cmc_page.dart @@ -1,195 +1,427 @@ +// import 'dart:async'; +// // import 'package:hmg_patient_app/core/enum/viewstate.dart'; // import 'package:hmg_patient_app/core/model/AlHabibMedicalService/ComprehensiveMedicalCheckup/cmc_insert_pres_order_request_model.dart'; +// import 'package:hmg_patient_app/core/model/ImagesInfo.dart'; +// import 'package:hmg_patient_app/core/model/hospitals/hospitals_model.dart'; // import 'package:hmg_patient_app/core/viewModels/AlHabibMedicalService/cmc_view_model.dart'; // import 'package:hmg_patient_app/core/viewModels/project_view_model.dart'; -// import 'package:hmg_patient_app/pages/AlHabibMedicalService/ComprehensiveMedicalCheckup/NewCMC/new_cmc_step_three_page.dart'; +// import 'package:hmg_patient_app/pages/AlHabibMedicalService/ComprehensiveMedicalCheckup/Dialog/confirm_dialog.dart'; +// import 'package:hmg_patient_app/services/clinic_services/get_clinic_service.dart'; +// import 'package:hmg_patient_app/theme/colors.dart'; +// import 'package:hmg_patient_app/uitl/app_toast.dart'; +// import 'package:hmg_patient_app/uitl/gif_loader_dialog_utils.dart'; // import 'package:hmg_patient_app/uitl/translations_delegate_base.dart'; // import 'package:hmg_patient_app/uitl/utils.dart'; // import 'package:hmg_patient_app/uitl/utils_new.dart'; // import 'package:hmg_patient_app/widgets/buttons/defaultButton.dart'; -// import 'package:hmg_patient_app/widgets/dragable_sheet.dart'; // import 'package:hmg_patient_app/widgets/others/app_scaffold_widget.dart'; -// import 'package:hmg_patient_app/widgets/photo_view_page.dart'; // import 'package:flutter/cupertino.dart'; // import 'package:flutter/material.dart'; -// import 'package:flutter_svg/svg.dart'; +// import 'package:flutter_svg/flutter_svg.dart'; +// import 'package:google_maps_flutter/google_maps_flutter.dart'; +// import 'package:maps_launcher/maps_launcher.dart'; // import 'package:provider/provider.dart'; // -// import 'new_cmc_step_tow_page.dart'; -// -// class NewCMCStepOnePage extends StatefulWidget { -// final CMCInsertPresOrderRequestModel cMCInsertPresOrderRequestModel; -// final Function changePageViewIndex; +// class NewCMCStepThreePage extends StatefulWidget { +// final CMCInsertPresOrderRequestModel cmcInsertPresOrderRequestModel; // final CMCViewModel model; // -// final double latitude; -// final double longitude; -// -// const NewCMCStepOnePage({Key? key, required this.cMCInsertPresOrderRequestModel, required this.model, required this.changePageViewIndex, required this.latitude, required this.longitude}) -// : super(key: key); +// NewCMCStepThreePage( +// {Key? key, +// required this.model, +// required this.cmcInsertPresOrderRequestModel}); // // @override -// _NewCMCStepOnePageState createState() => _NewCMCStepOnePageState(); +// _NewCMCStepThreePageState createState() => _NewCMCStepThreePageState(); // } // -// class _NewCMCStepOnePageState extends State { -// int selectedItem = 0; +// class _NewCMCStepThreePageState extends State { +// Completer _controller = Completer(); +// +// String? projectDropdownValue; +// late List projectsList = []; +// HospitalsModel? selectedHospital; +// final GlobalKey projectDropdownKey = GlobalKey(); +// bool isLocationSelected = false; +// ProjectViewModel? projectViewModel; +// +// static CameraPosition _kGooglePlex = CameraPosition( +// target: LatLng(37.42796133580664, -122.085749655962), +// zoom: 14.4746, +// ); +// final Set markers = new Set(); // // @override // void initState() { +// if (widget.cmcInsertPresOrderRequestModel.latitude != null) { +// markers.clear(); +// markers.add( +// Marker( +// markerId: MarkerId( +// widget.cmcInsertPresOrderRequestModel.latitude.hashCode +// .toString(), +// ), +// position: LatLng(widget.cmcInsertPresOrderRequestModel.latitude!, +// widget.cmcInsertPresOrderRequestModel.longitude!)), +// ); +// _kGooglePlex = CameraPosition( +// target: LatLng(widget.cmcInsertPresOrderRequestModel.latitude!, +// widget.cmcInsertPresOrderRequestModel.longitude!), +// zoom: 14.4746, +// ); +// } +// WidgetsBinding.instance.addPostFrameCallback((_) { +// // if (projectViewModel.isLogin) { +// getProjectsList(); +// // } +// }); // super.initState(); // } // // @override // Widget build(BuildContext context) { -// ProjectViewModel projectViewModel = Provider.of(context); -// +// projectViewModel = Provider.of(context); // return AppScaffold( -// isShowAppBar: false, +// isShowAppBar: true, +// description: TranslationBase.of(context).infoCMC, +// imagesInfo: [ +// ImagesInfo( +// imageAr: 'https://hmgwebservices.com/Images/MobileApp/CMC/ar/0.png', +// imageEn: +// 'https://hmgwebservices.com/Images/MobileApp/CMC/en/0.png'), +// ], +// appBarTitle: TranslationBase.of(context).comprehensiveMedicalCheckup, +// showNewAppBar: true, +// showNewAppBarTitle: true, +// backgroundColor: CustomColors.appBackgroudGreyColor, // baseViewModel: widget.model, -// body: Column( -// children: [ -// Expanded( -// child: SingleChildScrollView( -// physics: BouncingScrollPhysics(), -// padding: EdgeInsets.all(21), -// child: Column( -// children: [ -// ListView.separated( -// physics: NeverScrollableScrollPhysics(), -// shrinkWrap: true, -// itemBuilder: (context, index) { -// return Row( +// body: SingleChildScrollView( +// physics: BouncingScrollPhysics(), +// child: Container( +// height: 500, +// width: double.maxFinite, +// margin: EdgeInsets.only(left: 12, right: 12), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// SizedBox( +// height: 12, +// ), +// Text( +// TranslationBase.of(context).orderSummary, +// style: TextStyle( +// fontSize: 16, +// fontWeight: FontWeight.bold, +// letterSpacing: -0.46, +// ), +// ), +// SizedBox( +// height: 12, +// ), +// Container( +// decoration: cardRadius(12), +// child: Container( +// padding: EdgeInsets.all(8), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Container( +// width: double.infinity, +// decoration: containerRadius(Colors.white, 12), +// margin: EdgeInsets.only(top: 12), +// padding: EdgeInsets.only( +// left: 0, right: 0, top: 0, bottom: 12), +// child: Row( // children: [ -// Radio( -// value: num.tryParse(widget.model.cmcAllServicesList[index].serviceID!), -// activeColor: Colors.red[800], -// onChanged: (newValue) async { -// selectedItem = index; -// PatientERCMCInsertServicesList patientERCMCInsertServicesList = PatientERCMCInsertServicesList( -// price: widget.model.cmcAllServicesList[index].price, -// serviceID: widget.model.cmcAllServicesList[index].serviceID, -// selectedServiceName: widget.model.cmcAllServicesList[index].text, -// selectedServiceNameAR: widget.model.cmcAllServicesList[index].textN, -// recordID: 1, -// totalPrice: widget.model.cmcAllServicesList[index].priceTotal, -// vAT: widget.model.cmcAllServicesList[index].priceVAT); -// setState(() { -// widget.cMCInsertPresOrderRequestModel.patientERCMCInsertServicesList = [patientERCMCInsertServicesList]; -// }); -// // CMCGetItemsRequestModel cMCGetItemsRequestModel = new CMCGetItemsRequestModel(checkupType: newValue); -// // await widget.model.getCheckupItems(cMCGetItemsRequestModel: cMCGetItemsRequestModel); -// }, -// groupValue: widget.cMCInsertPresOrderRequestModel.patientERCMCInsertServicesList!.length > 0 -// ? int.parse(widget.cMCInsertPresOrderRequestModel.patientERCMCInsertServicesList![0].serviceID!) -// : 1), -// Expanded( -// child: Padding( -// padding: const EdgeInsets.only( -// left: 10, -// right: 10, -// top: 20, -// bottom: 20, -// ), -// child: Text( -// projectViewModel.isArabic ? widget.model.cmcAllServicesList[index].textN! : widget.model.cmcAllServicesList[index].text!, -// style: TextStyle( -// fontSize: 14, -// fontWeight: FontWeight.w600, -// letterSpacing: -0.45, +// Flexible( +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Text( +// TranslationBase.of(context).selectLocation, +// style: TextStyle( +// fontSize: 11, +// letterSpacing: -0.44, +// fontWeight: FontWeight.w600, +// ), // ), -// ), +// Container( +// height: 18, +// child: DropdownButtonHideUnderline( +// child: DropdownButton( +// key: projectDropdownKey, +// hint: new Text( +// TranslationBase.of(context) +// .selectHospital), +// value: selectedHospital, +// iconSize: 0, +// isExpanded: true, +// style: TextStyle( +// fontSize: 14, +// letterSpacing: -0.56, +// color: Colors.black), +// items: projectsList.map((item) { +// return new DropdownMenuItem< +// HospitalsModel>( +// value: item, +// child: new Text(item.name!), +// ); +// }).toList(), +// onChanged: +// (HospitalsModel? newValue) async { +// setState(() { +// selectedHospital = newValue!; +// projectDropdownValue = newValue +// .mainProjectID +// .toString(); +// isLocationSelected = true; +// widget.cmcInsertPresOrderRequestModel +// .projectID = +// newValue.mainProjectID; +// // getDoctorsList(context); +// }); +// }, +// ), +// ), +// ), +// ], // ), // ), +// Icon(Icons.keyboard_arrow_down), // ], -// ); -// }, -// separatorBuilder: (context, index) { -// return mDivider(Colors.grey); -// }, -// itemCount: widget.model.cmcAllServicesList.length), -// Stack( -// children: [ -// Image.asset( -// projectViewModel.isArabic ? "assets/images/cc_ar.png" : "assets/images/cc_en.png", -// width: double.infinity, +// ), // ), -// Align( -// alignment: Alignment.topRight, -// child: Container( -// decoration: containerColorRadiusBorder( -// Colors.black.withOpacity(0.2), -// 1000, -// Colors.white, -// ), -// margin: EdgeInsets.all(12), -// child: IconButton( -// icon: SvgPicture.asset( -// "assets/images/new/ic_zoom.svg", -// color: Colors.white, +// SizedBox( +// height: 6, +// ), +// isLocationSelected +// ? Padding( +// padding: const EdgeInsets.all(8.0), +// child: Stack( +// children: [ +// Container( +// height: 200, +// decoration: containerColorRadiusBorder( +// Colors.white, 12, Colors.grey), +// clipBehavior: Clip.antiAlias, +// child: Container( +// decoration: cardRadius(12), +// clipBehavior: Clip.antiAlias, +// margin: const EdgeInsets.all(0), +// child: Image.network( +// "https://maps.googleapis.com/maps/api/staticmap?center=" + +// selectedHospital!.latitude +// .toString() + +// "," + +// selectedHospital!.longitude +// .toString() + +// "&zoom=16&size=600x300&maptype=roadmap&markers=color:red%7C" + +// selectedHospital!.latitude +// .toString() + +// "," + +// selectedHospital!.longitude +// .toString() + +// "&key=AIzaSyCyDbWUM9d_sBUGIE8PcuShzPaqO08NSC8", +// width: double.infinity, +// height: double.infinity, +// fit: BoxFit.cover, +// ), +// ), // ), -// padding: EdgeInsets.all(12), -// onPressed: () { -// showDraggableDialog(context, PhotoViewPage(projectViewModel.isArabic ? "assets/images/cc_ar.png" : "assets/images/cc_en.png")); -// }, -// ), +// Row( +// mainAxisAlignment: MainAxisAlignment.end, +// crossAxisAlignment: CrossAxisAlignment.end, +// children: [ +// InkWell( +// onTap: () { +// getDirections(); +// }, +// child: Container( +// decoration: cardRadius(1000), +// margin: EdgeInsets.all(12), +// child: Padding( +// padding: const EdgeInsets.only( +// left: 12, +// right: 12, +// top: 6, +// bottom: 6), +// child: Row( +// children: [ +// Padding( +// padding: +// const EdgeInsets.all(3.0), +// child: SvgPicture.asset( +// "assets/images/new/direction.svg", +// width: 13, +// height: 13, +// ), +// ), +// mWidth(6), +// Text( +// TranslationBase.of(context) +// .getDirections, +// style: TextStyle( +// color: Colors.black, +// fontSize: 11, +// letterSpacing: -0.44, +// fontWeight: FontWeight.w600, +// ), +// ) +// ], +// ), +// ), +// ), +// ), +// ], +// ), +// ], // ), +// ) +// : Container(), +// SizedBox( +// height: 12, // ), +// Text( +// TranslationBase.of(context).selectedService, +// style: TextStyle( +// fontSize: 14, +// fontWeight: FontWeight.bold, +// letterSpacing: -0.46, +// color: CustomColors.grey, +// ), +// ), +// ...List.generate( +// widget.cmcInsertPresOrderRequestModel +// .patientERCMCInsertServicesList!.length, +// (index) => Container( +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Text( +// projectViewModel!.isArabic +// ? widget +// .cmcInsertPresOrderRequestModel +// .patientERCMCInsertServicesList![index] +// .selectedServiceNameAR! +// : widget +// .cmcInsertPresOrderRequestModel +// .patientERCMCInsertServicesList![index] +// .selectedServiceName!, +// style: TextStyle( +// fontSize: 12, +// fontWeight: FontWeight.bold, +// letterSpacing: -0.46, +// ), +// ), +// ], +// ), +// ), +// ) // ], // ), -// ], -// ), -// ), +// ), +// ) +// ], // ), -// Container( -// color: Colors.white, -// padding: EdgeInsets.only(top: 16, bottom: 16, right: 21, left: 21), -// child: DefaultButton( -// TranslationBase.of(context).next, -// () async { -// if (widget.cMCInsertPresOrderRequestModel.patientERCMCInsertServicesList!.length != 0 || widget.cMCInsertPresOrderRequestModel.patientERCMCInsertServicesList == null) { -// // int index = widget.model.cmcAllServicesList.length; -// PatientERCMCInsertServicesList patientERCMCInsertServicesList = new PatientERCMCInsertServicesList( -// price: widget.model.cmcAllServicesList[selectedItem].price, -// serviceID: widget.model.cmcAllServicesList[selectedItem].serviceID.toString(), -// selectedServiceName: widget.model.cmcAllServicesList[selectedItem].text, -// selectedServiceNameAR: widget.model.cmcAllServicesList[selectedItem].textN, -// recordID: 1, -// totalPrice: widget.model.cmcAllServicesList[selectedItem].priceTotal, -// vAT: widget.model.cmcAllServicesList[selectedItem].priceVAT, -// ); -// -// widget.cMCInsertPresOrderRequestModel.patientID = projectViewModel.user!.patientID; -// widget.cMCInsertPresOrderRequestModel.patientOutSA = projectViewModel.user!.outSA; -// -// widget.cMCInsertPresOrderRequestModel.patientERCMCInsertServicesList = [patientERCMCInsertServicesList]; -// navigateTo( -// context, -// NewCMCStepThreePage( -// cmcInsertPresOrderRequestModel: widget.cMCInsertPresOrderRequestModel, -// model: widget.model, -// ), -// ); -// // await widget.model.getCustomerInfo(); -// if (widget.model.state == ViewState.ErrorLocal) { -// Utils.showErrorToast(); +// ), +// ), +// bottomSheet: Container( +// height: MediaQuery.of(context).size.height * 0.10, +// width: double.infinity, +// color: Colors.white, +// child: Column( +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// Container( +// width: MediaQuery.of(context).size.width * 0.9, +// child: DefaultButton( +// TranslationBase.of(context).confirm, +// !isLocationSelected +// ? null +// : () async { +// GifLoaderDialogUtils.showMyDialog(context); +// String requestId = await widget.model.insertCMCOrderRC( +// order: widget.cmcInsertPresOrderRequestModel); +// GifLoaderDialogUtils.hideDialog(context); +// if (widget.model.state != ViewState.ErrorLocal) { +// showCMCConfirmDialog( +// context, +// requestId, +// onClick: () { +// Navigator.pop(context); +// Navigator.pop(context); +// }, +// ); // } else { -// // navigateTo( -// // context, -// // NewCMCStepTowPage( -// // longitude: widget.longitude, -// // latitude: widget.latitude, -// // cmcInsertPresOrderRequestModel: widget.cMCInsertPresOrderRequestModel, -// // model: widget.model, -// // ), -// // ); +// AppToast.showErrorToast(message: widget.model.error); // } -// } -// }, +// }, +// color: CustomColors.green, +// disabledColor: CustomColors.grey, +// ), +// // SecondaryButton( +// // label: TranslationBase.of(context).confirm, +// // color: CustomColors.green, +// // onTap: () async { +// // if(isLocationSelected) { +// // GifLoaderDialogUtils.showMyDialog(context); +// // String requestId = await widget.model.insertCMCOrderRC(order: widget.cmcInsertPresOrderRequestModel); +// // GifLoaderDialogUtils.hideDialog(context); +// // if (widget.model.state != ViewState.ErrorLocal) { +// // showCMCConfirmDialog( +// // context, +// // requestId, +// // onClick: () { +// // Navigator.pop(context); +// // Navigator.pop(context); +// // }, +// // ); +// // } else { +// // AppToast.showErrorToast(message: widget.model.error); +// // } +// // } else { +// // Utils.showErrorToast("Please select hospital from the dropdown menu to continue"); +// // } +// // }, +// // textColor: Theme.of(context).backgroundColor), // ), -// ), -// ], +// ], +// ), // ), // ); // } +// +// getDirections() { +// if (isLocationSelected) { +// MapsLauncher.launchCoordinates(double.parse(selectedHospital!.latitude!), +// double.parse(selectedHospital!.longitude!), selectedHospital!.name); +// } else { +// Utils.showErrorToast( +// "Please select address from the dropdown menu to get directions"); +// } +// } +// +// getProjectsList() { +// int languageID = projectViewModel!.isArabic ? 1 : 2; +// ClinicListService service = new ClinicListService(); +// GifLoaderDialogUtils.showMyDialog(context); +// List projectsListLocal = []; +// service.getProjectsList(languageID, context).then((res) { +// if (res['MessageStatus'] == 1) { +// setState(() { +// res['ListProject'].forEach((v) { +// projectsListLocal.add(new HospitalsModel.fromJson(v)); +// }); +// projectsList = projectsListLocal; +// }); +// } +// GifLoaderDialogUtils.hideDialog(context); +// }).catchError((err) { +// GifLoaderDialogUtils.hideDialog(context); +// }).catchError((err) { +// GifLoaderDialogUtils.hideDialog(context); +// print(err); +// }); +// } // } diff --git a/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_bottom_sheet_body.dart b/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_bottom_sheet_body.dart new file mode 100644 index 0000000..98e91b8 --- /dev/null +++ b/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_bottom_sheet_body.dart @@ -0,0 +1,111 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_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/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.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/presentation/comprehensive_checkup/widgets/cmc_hospital_list_item.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/input_widget.dart'; +import 'package:provider/provider.dart'; +import 'package:shimmer/shimmer.dart'; + +class CmcHospitalBottomSheetBody extends StatelessWidget { + final Function(HospitalsModel) onHospitalSelected; + + const CmcHospitalBottomSheetBody({super.key, required this.onHospitalSelected}); + + Widget _buildLoadingShimmer() { + return ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: 4, + separatorBuilder: (_, __) => SizedBox(height: 12.h), + itemBuilder: (context, index) { + return Shimmer.fromColors( + baseColor: Colors.grey[300]!, + highlightColor: Colors.grey[100]!, + child: Container( + height: 80.h, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10.r), + ), + ), + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + final appState = getIt.get(); + final bool isArabic = appState.isArabic(); + final bool isLocationEnabled = (appState.userLat != 0) && (appState.userLong != 0); + + return Consumer( + builder: (BuildContext context, HmgServicesViewModel hmgServicesViewModel, Widget? child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Choose your preferred hospital for the service".needTranslation.toText14( + weight: FontWeight.w400, + color: AppColors.greyTextColor, + letterSpacing: -0.4, + ), + SizedBox(height: 16.h), + TextInputWidget( + labelText: LocaleKeys.search.tr(), + hintText: LocaleKeys.searchHospital.tr(), + onChange: (value) { + hmgServicesViewModel.filterHospitalsByString(value ?? '', isArabic); + }, + isEnable: true, + prefix: null, + autoFocus: false, + isBorderAllowed: false, + keyboardType: TextInputType.text, + isAllowLeadingIcon: true, + selectionType: SelectionTypeEnum.search, + padding: EdgeInsets.symmetric(vertical: ResponsiveExtension(10).h, horizontal: ResponsiveExtension(15).h), + ), + ], + ), + SizedBox(height: 8.h), + SizedBox( + height: MediaQuery.of(context).size.height * 0.4, + child: hmgServicesViewModel.isHospitalListLoading + ? _buildLoadingShimmer() + : hmgServicesViewModel.filteredHospitalsList.isEmpty + ? Center( + child: "No hospitals Found".needTranslation.toText16(weight: FontWeight.w500, color: AppColors.greyTextColor), + ) + : ListView.separated( + itemCount: hmgServicesViewModel.filteredHospitalsList.length, + separatorBuilder: (context, index) => SizedBox(height: 12.h), + itemBuilder: (context, index) { + final hospital = hmgServicesViewModel.filteredHospitalsList[index]; + return CmcHospitalListItem( + hospital: hospital, + isLocationEnabled: isLocationEnabled, + onPress: () { + hmgServicesViewModel.setSelectedHospital(hospital); + onHospitalSelected(hospital); + }, + ); + }, + ), + ), + ], + ); + }, + ); + } +} diff --git a/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_list_item.dart b/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_list_item.dart new file mode 100644 index 0000000..5ed54d2 --- /dev/null +++ b/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_list_item.dart @@ -0,0 +1,123 @@ +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/my_appointments/models/resp_models/hospital_model.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; + +class CmcHospitalListItem extends StatelessWidget { + final HospitalsModel hospital; + final VoidCallback onPress; + final bool isLocationEnabled; + + const CmcHospitalListItem({ + super.key, + required this.hospital, + required this.onPress, + this.isLocationEnabled = false, + }); + + @override + Widget build(BuildContext context) { + final appState = getIt.get(); + final bool isArabic = appState.isArabic(); + final String hospitalName = isArabic ? (hospital.nameN ?? hospital.name ?? '') : (hospital.name ?? ''); + + return InkWell( + onTap: onPress, + child: 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: [ + _buildHospitalName(hospitalName), + _buildDistanceInfo(), + ], + ), + ), + Transform.flip( + flipX: isArabic, + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon_small, + iconColor: AppColors.blackColor, + width: 18.h, + height: 13.h, + fit: BoxFit.contain, + ), + ), + ], + ).paddingSymmetrical(16.h, 16.h), + ), + ); + } + + Widget _buildHospitalName(String hospitalName) { + return Row( + children: [ + Utils.buildSvgWithAssets( + icon: (hospital.isHMC == true) ? AppAssets.hmc : AppAssets.hmg, + ).paddingOnly(right: 10), + Expanded( + child: Text( + hospitalName, + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 16, + color: AppColors.blackColor, + ), + ), + ) + ], + ); + } + + Widget _buildDistanceInfo() { + final distanceText = hospital.distanceInKilometers != null ? hospital.distanceInKilometers!.toStringAsFixed(1) : "0"; + + return Row( + spacing: 4.w, + children: [ + Visibility( + visible: (hospital.distanceInKilometers != null && hospital.distanceInKilometers! > 0), + child: AppCustomChipWidget( + labelText: "$distanceText km".needTranslation, + deleteIcon: AppAssets.location_red, + deleteIconSize: Size(9, 12), + backgroundColor: AppColors.secondaryLightRedColor, + textColor: AppColors.errorColor, + ), + ), + Visibility( + visible: (hospital.distanceInKilometers == null || hospital.distanceInKilometers == 0), + child: AppCustomChipWidget( + labelText: " Distance not available".needTranslation, + textColor: AppColors.blackColor, + ), + ), + // Visibility( + // visible: !isLocationEnabled, + // child: AppCustomChipWidget( + // labelText: "Location turned off".needTranslation, + // deleteIcon: AppAssets.location_unavailable, + // deleteIconSize: Size(9.w, 12.h), + // textColor: AppColors.blackColor, + // ), + // ), + ], + ); + } +} diff --git a/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_selection_helper.dart b/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_selection_helper.dart new file mode 100644 index 0000000..d7a63d9 --- /dev/null +++ b/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_selection_helper.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart'; +import 'package:hmg_patient_app_new/presentation/comprehensive_checkup/widgets/cmc_hospital_bottom_sheet_body.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; +import 'package:provider/provider.dart'; + +class CmcHospitalSelectionHelper { + static void showHospitalSelectionBottomSheet({ + required BuildContext context, + required Function(dynamic) onHospitalSelected, + }) { + final hmgServicesViewModel = context.read(); + + showCommonBottomSheetWithoutHeight( + context, + title: "Select Hospital".needTranslation, + child: CmcHospitalBottomSheetBody( + onHospitalSelected: (hospital) { + hmgServicesViewModel.setSelectedHospitalForOrder(hospital); + onHospitalSelected(hospital); + }, + ), + ); + } +} 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 5e4b885..fc3d77a 100644 --- a/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart +++ b/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -14,10 +12,7 @@ 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'; @@ -29,7 +24,6 @@ import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/model/Bottom import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:hmg_patient_app_new/widgets/map/HMSMap.dart'; import 'package:hmg_patient_app_new/widgets/map/map.dart'; -import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:provider/provider.dart'; import '../../../widgets/common_bottom_sheet.dart'; @@ -40,20 +34,13 @@ class CallAmbulancePage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( floatingActionButton: Visibility( - visible: context.watch().bottomSheetType == - BottomSheetType.FIXED, + 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(); + 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(); }), ), ), @@ -62,8 +49,7 @@ class CallAmbulancePage extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ ExpandableBottomSheet( - bottomSheetType: - context.watch().bottomSheetType, + bottomSheetType: context.watch().bottomSheetType, children: { BottomSheetType.EXPANDED: ExpanedBottomSheet(context), BottomSheetType.FIXED: FixedBottomSheet(context), @@ -73,42 +59,28 @@ class CallAmbulancePage extends StatelessWidget { ), body: Stack( children: [ - if (context.read().isGMSAvailable ) + if (context.read().isGMSAvailable) GMSMap( - currentLocation: - context.read().getGMSLocation(), - onCameraMoved: (value) => context - .read() - .handleGMSMapCameraMoved(value), - onCameraIdle: - context.read().handleOnCameraIdle, + currentLocation: context.read().getGMSLocation(), + onCameraMoved: (value) => context.read().handleGMSMapCameraMoved(value), + onCameraIdle: context.read().handleOnCameraIdle, myLocationEnabled: true, - inputController: - context.read().gmsController, + inputController: context.read().gmsController, showCenterMarker: true, ) else HMSMap( - currentLocation: - context.read().getHMSLocation(), - onCameraMoved: (value) => context - .read() - .handleHMSMapCameraMoved(value), - onCameraIdle: - context.read().handleOnCameraIdle, + currentLocation: context.read().getHMSLocation(), + onCameraMoved: (value) => context.read().handleHMSMapCameraMoved(value), + onCameraIdle: context.read().handleOnCameraIdle, myLocationEnabled: false, - inputController: - context.read().hmsController, + inputController: context.read().hmsController, showCenterMarker: true, ), Align( alignment: AlignmentDirectional.topStart, - child: Utils.buildSvgWithAssets( - icon: AppAssets.closeBottomNav, width: 32.h, height: 32.h) - .onPress(() { - context - .read() - .flushPickupInformation(); + child: Utils.buildSvgWithAssets(icon: AppAssets.closeBottomNav, width: 32.h, height: 32.h).onPress(() { + context.read().flushPickupInformation(); Navigator.pop(context); }), ).paddingOnly(top: 51.h, left: 24.h), @@ -119,8 +91,7 @@ class CallAmbulancePage extends StatelessWidget { Widget FixedBottomSheet(BuildContext context) { return GestureDetector( - onVerticalDragUpdate: (details){ - + onVerticalDragUpdate: (details) { // if(details.delta.dy<0){ // // context.read().updateBottomSheetState(BottomSheetType.EXPANDED); @@ -161,7 +132,7 @@ class CallAmbulancePage extends StatelessWidget { // ), // ), // ), - // .paddingOnly(right: 24.h, bottom: 24.h), + // .paddingOnly(right: 24.h, bottom: 24.h), Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, @@ -189,9 +160,7 @@ class CallAmbulancePage extends StatelessWidget { weight: FontWeight.w600, color: AppColors.textColor, ), - " Please select the details of pickup" - .needTranslation - .toText12( + " Please select the details of pickup".needTranslation.toText12( fontWeight: FontWeight.w500, color: AppColors.greyTextColor, ) @@ -200,9 +169,7 @@ class CallAmbulancePage extends StatelessWidget { CustomButton( text: "Select Details".needTranslation, onPressed: () { - context - .read() - .updateBottomSheetState(BottomSheetType.EXPANDED); + context.read().updateBottomSheetState(BottomSheetType.EXPANDED); }) ], ).paddingOnly(top: 24.h, bottom: 32.h, left: 24.h, right: 24.h), @@ -217,11 +184,11 @@ class CallAmbulancePage extends StatelessWidget { Widget ExpanedBottomSheet(BuildContext context) { return GestureDetector( - onVerticalDragUpdate: (details){ - if(details.delta.dy>0){ - context.read().updateBottomSheetState(BottomSheetType.FIXED); - } - }, + onVerticalDragUpdate: (details) { + if (details.delta.dy > 0) { + context.read().updateBottomSheetState(BottomSheetType.FIXED); + } + }, child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -239,22 +206,19 @@ class CallAmbulancePage extends StatelessWidget { 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); - }) - + 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), - + ).paddingOnly(top: 24.h, bottom: 32.h, left: 24.h, right: 24.h), bottomPriceContent(context) ], ), @@ -286,8 +250,7 @@ class CallAmbulancePage extends StatelessWidget { shrinkWrap: true, itemCount: 3, itemBuilder: (__, index) { - if (index == - 2) // todo means the end of the list so handle as per the viewmodel + if (index == 2) // todo means the end of the list so handle as per the viewmodel { return CustomButton( height: 40.h, @@ -301,11 +264,8 @@ class CallAmbulancePage extends StatelessWidget { } else { return AddressItem( isSelected: index == 0, - address: - "Flat No 301, Building No 12, Palm Spring Apartment, Sector 45, Gurugram, Haryana 122003", - title: index == 0 - ? "Home".needTranslation - : "Work".needTranslation, + address: "Flat No 301, Building No 12, Palm Spring Apartment, Sector 45, Gurugram, Haryana 122003", + title: index == 0 ? "Home".needTranslation : "Work".needTranslation, onTap: () {}, ); } @@ -354,13 +314,9 @@ class CallAmbulancePage extends StatelessWidget { leadingIcon: AppAssets.pickup_bed, ), CustomSwitch( - value: context - .watch() - .pickupFromInsideTheLocation, - onChanged: (value){ - context - .read() - .updateThePickupPlaceFromLocation(value); + value: context.watch().pickupFromInsideTheLocation, + onChanged: (value) { + context.read().updateThePickupPlaceFromLocation(value); }, ) ], @@ -374,15 +330,12 @@ class CallAmbulancePage extends StatelessWidget { leadingIcon: AppAssets.appointment_checkin_icon, ), CustomSwitch( - value: context - .watch() - .haveAnAppointment, + value: context.watch().haveAnAppointment, onChanged: (value) async { // if (value) { // openAppointmentList(context); // } - await context.read() - .updateAppointment(value); + await context.read().updateAppointment(value); if (context.read().appointments?.isNotEmpty == true) { openAppointmentList(context); } @@ -454,55 +407,47 @@ class CallAmbulancePage extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.end, children: [ - DecoratedBox( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.scaffoldBgColor, - customBorder: BorderRadius.only( - topLeft: Radius.circular(24.h), - topRight: Radius.circular(24.h), - ), - hasShadow: true + DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.scaffoldBgColor, + customBorder: BorderRadius.only( + topLeft: Radius.circular(24.h), + topRight: Radius.circular(24.h), ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 12.h, + hasShadow: true), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 12.h, + children: [ + Row( children: [ - Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 4.h, - children: [ - "Total amount to pay".needTranslation.toText18( + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 4.h, + children: [ + "Total amount to pay".needTranslation.toText18( weight: FontWeight.w600, color: AppColors.textColor, ), - Row( - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.warning, - height: 18.h, width: 18.h), - SizedBox(width: 4.h,), - "Amount will be paid at the hospital" - .needTranslation - .toText12( + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.warning, height: 18.h, width: 18.h), + SizedBox( + width: 4.h, + ), + "Amount will be paid at the hospital".needTranslation.toText12( fontWeight: FontWeight.w500, color: AppColors.greyTextColor, ), - ], - ) ], - ), - ), + ) + ], + ), + ), Utils.getPaymentAmountWithSymbol( - (Utils.formatNumberToInternationalFormat(context - .read() - .getTotalPrice() ?? - 0)) - .toText24( - fontWeight: FontWeight.w600, - color: AppColors.textColor, - letterSpacing: -2), + (Utils.formatNumberToInternationalFormat(context.read().getTotalPrice() ?? 0)) + .toText24(fontWeight: FontWeight.w600, color: AppColors.textColor, letterSpacing: -2), AppColors.blackColor, 17.h) @@ -518,20 +463,19 @@ class CallAmbulancePage extends StatelessWidget { 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), - ), + ], + ).paddingOnly(top: 24.h, bottom: 12.h, left: 24.h, right: 24.h), + ), ], ); } - showHospitalBottomSheet(BuildContext context){ + showHospitalBottomSheet(BuildContext context) { showCommonBottomSheetWithoutHeight( - title: - LocaleKeys.selectHospital.tr(), + title: LocaleKeys.selectHospital.tr(), context, child: Consumer( - builder:(_,vm,__)=> HospitalBottomSheetBody( + builder: (_, vm, __) => HospitalBottomSheetBody( searchText: vm.searchController, displayList: vm.displayList, onFacilityClicked: (value) { @@ -545,8 +489,7 @@ class CallAmbulancePage extends StatelessWidget { onHospitalSearch: (value) { vm.searchHospitals(value ?? ""); }, - selectedFacility: - vm.selectedFacility, + selectedFacility: vm.selectedFacility, hmcCount: vm.hmcCount, hmgCount: vm.hmgCount, ), @@ -583,16 +526,14 @@ class CallAmbulancePage extends StatelessWidget { textPlaceInput(context) { return Consumer(builder: (_, vm, __) { - print( - "the data is ${vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description}"); + 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, + text: vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description, ), leadingIcon: AppAssets.location_pickup, isAllowLeadingIcon: true, @@ -614,8 +555,7 @@ class CallAmbulancePage extends StatelessWidget { ///decide which field to show first based on the selected calling place inputFields(BuildContext context) { - return context.read().callingPlace == - AmbulanceCallingPlace.FROM_HOSPITAL + return context.read().callingPlace == AmbulanceCallingPlace.FROM_HOSPITAL ? HospitalFieldFirstThanPlaces(context) : PlaceFirstThanHospitalField(context); } @@ -639,21 +579,15 @@ class CallAmbulancePage extends StatelessWidget { hospitalField(BuildContext context) { return DecoratedBox( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, borderRadius: 12.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h), child: Row( children: [ hospitalAndPickUpItemContent( title: "Select Hospital".needTranslation, - subTitle: context - .read() - .getSelectedHospitalName() ?? - "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) + Utils.buildSvgWithAssets(icon: AppAssets.down_cheveron, width: 24.h, height: 24.h).paddingAll(16.h) ], ).onPress(() { print("the item is clicked"); diff --git a/lib/presentation/emergency_services/emergency_services_page.dart b/lib/presentation/emergency_services/emergency_services_page.dart index ce4ae2b..9e70b3b 100644 --- a/lib/presentation/emergency_services/emergency_services_page.dart +++ b/lib/presentation/emergency_services/emergency_services_page.dart @@ -109,7 +109,6 @@ class EmergencyServicesPage extends StatelessWidget { CustomButton( text: LocaleKeys.confirm.tr(context: context), onPressed: () async { - // Navigator.of(context).pop(); await emergencyServicesViewModel.getTransportationOption(); openTranportationSelectionBottomSheet(context); diff --git a/lib/widgets/chip/app_custom_chip_widget.dart b/lib/widgets/chip/app_custom_chip_widget.dart index b3948e2..2b2ac90 100644 --- a/lib/widgets/chip/app_custom_chip_widget.dart +++ b/lib/widgets/chip/app_custom_chip_widget.dart @@ -44,6 +44,7 @@ class AppCustomChipWidget extends StatelessWidget { final EdgeInsetsDirectional? labelPadding; final void Function()? onChipTap; final void Function()? onDeleteTap; + @override Widget build(BuildContext context) { final iconS = iconSize ?? 12.w; @@ -65,7 +66,8 @@ class AppCustomChipWidget extends StatelessWidget { child: icon.isNotEmpty ? Chip( avatar: icon.isNotEmpty - ? Utils.buildSvgWithAssets(icon: icon, width: iconS, height: iconS, iconColor: iconHasColor ? iconColor : null, fit: BoxFit.contain) + ? Utils.buildSvgWithAssets( + icon: icon, width: iconS, height: iconS, iconColor: iconHasColor ? iconColor : null, fit: BoxFit.contain) : SizedBox.shrink(), label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor), padding: padding, @@ -79,13 +81,15 @@ class AppCustomChipWidget extends StatelessWidget { side: BorderSide(color: AppColors.transparent, width: 1.5), ), deleteIcon: deleteIcon?.isNotEmpty == true - ? InkWell(onTap: onDeleteTap,child:Utils.buildSvgWithAssets( - icon: deleteIcon!, - width: iconS, - height: iconS, - iconColor: deleteIconHasColor ? deleteIconColor : null, - - ),) + ? InkWell( + onTap: onDeleteTap, + child: Utils.buildSvgWithAssets( + icon: deleteIcon!, + width: iconS, + height: iconS, + iconColor: deleteIconHasColor ? deleteIconColor : null, + ), + ) : null, onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, ) @@ -102,7 +106,10 @@ class AppCustomChipWidget extends StatelessWidget { ), labelPadding: labelPadding ?? EdgeInsetsDirectional.only(start: 2.w, end: deleteIcon?.isNotEmpty == true ? 2.w : 8.w), deleteIcon: deleteIcon?.isNotEmpty == true - ? InkWell(onTap: onDeleteTap,child: Utils.buildSvgWithAssets(icon: deleteIcon!, width: iconS, height: iconS, iconColor: deleteIconHasColor ? deleteIconColor : null)) + ? InkWell( + onTap: onDeleteTap, + child: Utils.buildSvgWithAssets( + icon: deleteIcon!, width: iconS, height: iconS, iconColor: deleteIconHasColor ? deleteIconColor : null)) : null, onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, ), diff --git a/lib/widgets/common_bottom_sheet.dart b/lib/widgets/common_bottom_sheet.dart index 318751f..1bbe472 100644 --- a/lib/widgets/common_bottom_sheet.dart +++ b/lib/widgets/common_bottom_sheet.dart @@ -104,20 +104,18 @@ class ButtonSheetContent extends StatelessWidget { } } -void showCommonBottomSheetWithoutHeight( - BuildContext context, { - required Widget child, - VoidCallback? callBackFunc, - String title = "", - bool isCloseButtonVisible = true, - bool isFullScreen = true, - bool isDismissible = true, - Widget? titleWidget, - bool useSafeArea = false, - bool hasBottomPadding = true, - Color backgroundColor = AppColors.bottomSheetBgColor, - VoidCallback? onCloseClicked -}) { +void showCommonBottomSheetWithoutHeight(BuildContext context, + {required Widget child, + VoidCallback? callBackFunc, + String title = "", + bool isCloseButtonVisible = true, + bool isFullScreen = true, + bool isDismissible = true, + Widget? titleWidget, + bool useSafeArea = false, + bool hasBottomPadding = true, + Color backgroundColor = AppColors.bottomSheetBgColor, + VoidCallback? onCloseClicked}) { showModalBottomSheet( sheetAnimationStyle: AnimationStyle( duration: Duration(milliseconds: 500), @@ -146,43 +144,44 @@ void showCommonBottomSheetWithoutHeight( physics: ClampingScrollPhysics(), child: isCloseButtonVisible ? Container( - padding: EdgeInsets.only( - left: 24, - top: 24, - right: 24, - bottom: 12, - ), - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.bottomSheetBgColor, - borderRadius: 24.h, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - titleWidget ?? - Expanded( - child: title.toText20(weight: FontWeight.w600), - ), - if (isCloseButtonVisible) ...[ - Utils.buildSvgWithAssets( - icon: AppAssets.close_bottom_sheet_icon, - iconColor: Color(0xff2B353E), - ).onPress(() { - onCloseClicked?.call(); - Navigator.of(context).pop(); - }),], - ], - ), - SizedBox(height: 16.h), - child, - ], - ), - ) + padding: EdgeInsets.only( + left: 24, + top: 24, + right: 24, + bottom: 12, + ), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.bottomSheetBgColor, + borderRadius: 24.h, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + titleWidget ?? + Expanded( + child: title.toText20(weight: FontWeight.w600), + ), + if (isCloseButtonVisible) ...[ + Utils.buildSvgWithAssets( + icon: AppAssets.close_bottom_sheet_icon, + iconColor: Color(0xff2B353E), + ).onPress(() { + onCloseClicked?.call(); + Navigator.of(context).pop(); + }), + ], + ], + ), + SizedBox(height: 16.h), + child, + ], + ), + ) : child, ), ),