diff --git a/assets/images/svg/location_red.svg b/assets/images/svg/location_red.svg new file mode 100644 index 0000000..ba8c131 --- /dev/null +++ b/assets/images/svg/location_red.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/images/svg/location_unavailable.svg b/assets/images/svg/location_unavailable.svg new file mode 100644 index 0000000..0ba34eb --- /dev/null +++ b/assets/images/svg/location_unavailable.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index bbfa68c..ec0f9c3 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -814,5 +814,12 @@ "notNow": "ليس الآن", "pendingActivation": "في انتظار التنشيط", "awaitingApproval": "انتظر القبول", - "ready": "جاهز" + "ready": "جاهز", + "medicalCentersWithCount": "{count} مراكز طبية", + "medicalCenters": "مراكز طبية", + "hospitalsWithCount": "{count} مستشفيات", + "selectRegion": "اختر المنطقة", + "selectFacility": "اختر المرافق", + "selectFacilitiesSubTitle": "يرجى اختيار المرفق للموعد", + "selectHospitalSubTitle": "يرجى اختيار المستشفى للموعد" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index e054238..306c358 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -810,5 +810,13 @@ "notNow": "Not Now", "pendingActivation": "Pending Activation", "awaitingApproval": "Awaiting Approval", - "ready": "Ready" + "ready": "Ready", + "medicalCentersWithCount" : "{count} Medical Centers", + "medicalCenters" : " Medical Centers", + "hospitalsWithCount" : "{count} Hospitals", + "selectRegion": "Select Region", + "selectFacility": "Select Facilities", + "selectFacilitiesSubTitle": "Please select the facility for the appointment", + "selectHospitalSubTitle": "Please select the hospital for the appointment" + } \ No newline at end of file diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 42f10f4..bd2eb71 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -56,7 +56,7 @@ var RC_BASE_URL = 'https://rc.hmg.com/'; var PING_SERVICE = 'Services/Weather.svc/REST/CheckConnectivity'; -var GET_PROJECT = 'Services/Lists.svc/REST/GetProject'; +var GET_PROJECT_LIST = 'Services/Lists.svc/REST/GetProject'; ///Geofencing var GET_GEO_ZONES = 'Services/Patients.svc/REST/GeoF_GetAllPoints'; @@ -217,9 +217,6 @@ var GET_CLINICS_LIST_WRT_HOSPITAL_URL = "Services/Lists.svc/REST/GetClinicFromDo //URL to get active appointment list var GET_ACTIVE_APPOINTMENTS_LIST_URL = "Services/Doctors.svc/Rest/Dr_GetAppointmentActiveNumber"; -//URL to get projects list -var GET_PROJECTS_LIST = 'Services/Lists.svc/REST/GetProject'; - //URL to get doctors list var GET_DOCTORS_LIST_URL = "Services/Doctors.svc/REST/SearchDoctorsByTime"; @@ -726,7 +723,7 @@ const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_In class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index 9518fd9..438e820 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -108,6 +108,8 @@ class AppAssets { static const String search_by_clinic_icon = '$svgBasePath/search_by_clinic_icon.svg'; static const String search_by_doctor_icon = '$svgBasePath/search_by_doctor_icon.svg'; static const String search_by_region_icon = '$svgBasePath/search_by_region_icon.svg'; + static const String location_red = '$svgBasePath/location_red.svg'; + static const String location_unavailable = '$svgBasePath/location_unavailable.svg'; //bottom navigation// static const String homeBottom = '$svgBasePath/home_bottom.svg'; diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart index 52500bf..5b08406 100644 --- a/lib/core/app_state.dart +++ b/lib/core/app_state.dart @@ -132,4 +132,6 @@ class AppState { set setUserRegistrationPayload(RegistrationDataModelPayload value) { _userRegistrationPayload = value; } + + } diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index 6edd3cc..96326ba 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -128,6 +128,7 @@ class AppDependencies { () => MyAppointmentsViewModel( myAppointmentsRepo: getIt(), errorHandlerService: getIt(), + appState: getIt() ), ); diff --git a/lib/core/enums.dart b/lib/core/enums.dart index 7ed275a..bb45091 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -26,7 +26,7 @@ enum CountryEnum { saudiArabia, unitedArabEmirates } enum CalenderEnum { gregorian, hijri } -enum SelectionTypeEnum { dropdown, calendar } +enum SelectionTypeEnum { dropdown, calendar, search } enum GenderTypeEnum { male, female } diff --git a/lib/core/utils/doctor_response_mapper.dart b/lib/core/utils/doctor_response_mapper.dart new file mode 100644 index 0000000..fd0cb79 --- /dev/null +++ b/lib/core/utils/doctor_response_mapper.dart @@ -0,0 +1,207 @@ +import 'dart:math'; + +import 'package:hmg_patient_app_new/core/cache_consts.dart' show CacheConst; +import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart' show RegionList, PatientDoctorAppointmentList, DoctorList, PatientDoctorAppointmentListByRegion; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel; + +class DoctorMapper{ + static Future getMappedDoctor(List doctorList, + {bool isArabic = false}) async { + RegionList regionList = RegionList(); + final lat = await Utils.getNumFromPrefs(CacheConst.userLat); + final long = await Utils.getNumFromPrefs(CacheConst.userLat); + + for (var element in doctorList) { + String? region = element.getRegionName(isArabic); + if (region == null) continue; + + var regionDoctorList = regionList.registeredDoctorMap?.putIfAbsent(region, () => PatientDoctorAppointmentListByRegion()); + + List? targetList = element.isHMC == true + ? regionDoctorList?.hmcDoctorList + : regionDoctorList?.hmgDoctorList; + + var doctorByHospital = targetList + ?.where((clinic) => + clinic.filterName == + element.getProjectCompleteNameWithLocale(isArabic: isArabic)) + .toList() ?? + []; + + if (doctorByHospital.isNotEmpty) { + doctorByHospital.first.patientDoctorAppointmentList?.add(element); + } else { + var newAppointment = PatientDoctorAppointmentList( + filterName: + element.getProjectCompleteNameWithLocale(isArabic: isArabic), + distanceInKMs: element.projectDistanceInKiloMeters.toString(), + projectTopName: element.projectTopName, + projectBottomName: element.projectBottomName, + patientDoctorAppointment: element, + isHMC: element.isHMC + ); + if(element.projectDistanceInKiloMeters!= null ){ + if(regionDoctorList!.distance>element.projectDistanceInKiloMeters){ + regionDoctorList.distance = element.projectDistanceInKiloMeters; + } + if (element.isHMC == true && + element.projectDistanceInKiloMeters < + regionDoctorList.hmcDistance) { + regionDoctorList.hmcDistance = element.projectDistanceInKiloMeters; + } else if (element.projectDistanceInKiloMeters < + regionDoctorList.hmgDistance) { + regionDoctorList.hmgDistance = element.projectDistanceInKiloMeters; + } + }else + if (lat != 0&& + long != 0 && element.latitude != null && element.longitude != null) { + + + double distance = calculateDistance(lat.toDouble(), long.toDouble(), double.parse(element.latitude!), double.parse(element.longitude!)); + if(distance<0){ + distance *= -1; + } + if(regionDoctorList!.distance>distance){ + regionDoctorList.distance = distance; + } + if (element.isHMC == true && + element.projectDistanceInKiloMeters < + regionDoctorList.hmcDistance) { + regionDoctorList.hmcDistance = element.projectDistanceInKiloMeters; + } else if (element.projectDistanceInKiloMeters < + regionDoctorList.hmgDistance) { + regionDoctorList.hmgDistance = element.projectDistanceInKiloMeters; + } + } + targetList?.add(newAppointment); + } + + + + + regionDoctorList?.hmcSize = regionDoctorList.hmcDoctorList?.length ?? 0; + regionDoctorList?.hmgSize = regionDoctorList.hmgDoctorList?.length ?? 0; + + regionList.registeredDoctorMap?[region] = regionDoctorList; + } + + return regionList; + } + static double calculateDistance(double lat1, double lon1, double lat2, double lon2) { + var pi = 3.142; + const double R = 6371; + double dLat = (lat2 - lat1) * pi / 180; + double dLon = (lon2 - lon1) * pi / 180; + double a = sin(dLat / 2) * sin(dLat / 2) + + cos(lat1 * pi / 180) * cos(lat2 * pi / 180) * sin(dLon / 2) * sin(dLon / 2); + double c = 2 * atan2(sqrt(a), sqrt(1 - a)); + return R * c; + } + + static Future sortList(bool isGPSEnabled, RegionList unsorted, ) async { + if(isGPSEnabled){ + if(unsorted.registeredDoctorMap == null) return unsorted; + var sortedMap = Map.fromEntries( + unsorted.registeredDoctorMap!.entries.toList() + ..sort((a, b) => a.value!.distance.compareTo(b.value!.distance)), + ); + + unsorted.registeredDoctorMap = sortedMap; + return unsorted; + + } + + List? keys = unsorted.registeredDoctorMap?.keys.toList(); + keys?.sort(); + + if (keys == null) return unsorted; + Map sortedMap = {}; + for (var key in keys) { + sortedMap[key] = unsorted.registeredDoctorMap![key]!; + } + unsorted.registeredDoctorMap = sortedMap; + return unsorted; + } + + static Future getMappedHospitals( + List hospitalList, { + bool isArabic = false, + }) async { + final regionList = RegionList(); + final lat = await Utils.getNumFromPrefs(CacheConst.userLat); + final long = await Utils.getNumFromPrefs(CacheConst.userLat); + for (final hospital in hospitalList) { + final region = hospital.getRegionName(isArabic); + if (region == null) continue; + + final regionData = regionList.registeredDoctorMap?.putIfAbsent( + region, + () => PatientDoctorAppointmentListByRegion(), + ); + + List? targetList = hospital.isHMC == true + ? regionData?.hmcDoctorList + : regionData?.hmgDoctorList; + + List existingEntry = targetList + ?.where( + (entry) => entry.filterName == hospital.getName(isArabic), + ) + .toList() ?? + []; + + if (existingEntry.isNotEmpty) { + existingEntry.first.hospitalList.add(hospital); + } else { + final newEntry = PatientDoctorAppointmentList( + filterName: hospital.name, + distanceInKMs: hospital.distanceInKilometers?.toString(), + projectTopName: hospital.name, + projectBottomName: hospital.name, + model: hospital, + isHMC: hospital.isHMC); + + final distance = hospital.distanceInKilometers; + + if (distance != null) { + if (regionData!.distance > distance) { + regionData.distance = distance; + } + if (hospital.isHMC == true && distance < regionData.hmcDistance) { + regionData.hmcDistance = distance; + } else if (distance < regionData.hmgDistance) { + regionData.hmgDistance = distance; + } + } else if ( lat != 0&& + long != 0 && + hospital.latitude != null && + hospital.longitude != null) { + double calculatedDistance = calculateDistance( + lat.toDouble(), + long.toDouble(), + double.parse(hospital.latitude!), + double.parse(hospital.longitude!), + ).abs(); + + if (regionData!.distance > calculatedDistance) { + regionData.distance = calculatedDistance; + } + if (hospital.isHMC == true && + calculatedDistance < regionData.hmcDistance) { + regionData.hmcDistance = calculatedDistance; + } else if (calculatedDistance < regionData.hmgDistance) { + regionData.hmgDistance = calculatedDistance; + } + } + targetList?.add(newEntry); + } + + regionData?.hmcSize = regionData.hmcDoctorList?.length ?? 0; + regionData?.hmgSize = regionData.hmgDoctorList?.length ?? 0; + regionList.registeredDoctorMap?[region] = regionData; + } + + return regionList; + } +} \ No newline at end of file diff --git a/lib/features/my_appointments/appointment_via_region_viewmodel.dart b/lib/features/my_appointments/appointment_via_region_viewmodel.dart new file mode 100644 index 0000000..7bcd0a6 --- /dev/null +++ b/lib/features/my_appointments/appointment_via_region_viewmodel.dart @@ -0,0 +1,66 @@ +import 'package:flutter/foundation.dart' show ChangeNotifier; + +enum AppointmentViaRegionState { + REGION_SELECTION, + TYPE_SELECTION, + HOSPITAL_SELECTION, + CLINIC_SELECTION, + DOCTOR_SELECTION +} + +class AppointmentViaRegionViewmodel extends ChangeNotifier { + String? selectedRegionId; + String? selectedFacilityType; + AppointmentViaRegionState bottomSheetState = + AppointmentViaRegionState.REGION_SELECTION; + + void setSelectedRegionId(String? regionId) { + selectedRegionId = regionId; + notifyListeners(); + } + + void setFacility(String? facility) { + selectedFacilityType = facility; + notifyListeners(); + } + + void setBottomSheetState(AppointmentViaRegionState state) { + bottomSheetState = state; + notifyListeners(); + } + + void handleBackPress() { + switch (bottomSheetState) { + case AppointmentViaRegionState.REGION_SELECTION: + // Do nothing or exit the bottom sheet + break; + case AppointmentViaRegionState.TYPE_SELECTION: + setBottomSheetState(AppointmentViaRegionState.REGION_SELECTION); + setSelectedRegionId(null); + break; + case AppointmentViaRegionState.HOSPITAL_SELECTION: + setBottomSheetState(AppointmentViaRegionState.TYPE_SELECTION); + break; + + // case AppointmentViaRegionState.HOSPITAL_SELECTION: + // case AppointmentViaRegionState.CLINIC_SELECTION: + // setBottomSheetState(AppointmentViaRegionState.TYPE_SELECTION); + // setFacility(null); + // break; + // case AppointmentViaRegionState.DOCTOR_SELECTION: + // if (selectedFacilityType == 'Hospital') { + // setBottomSheetState(AppointmentViaRegionState.HOSPITAL_SELECTION); + // } else if (selectedFacilityType == 'Medical Center') { + // setBottomSheetState(AppointmentViaRegionState.CLINIC_SELECTION); + // } + // break; + default: + } + } + + void flush() { + setSelectedRegionId(null); + setFacility(null); + setBottomSheetState(AppointmentViaRegionState.REGION_SELECTION); + } +} diff --git a/lib/features/my_appointments/models/resp_models/doctor_list_api_response.dart b/lib/features/my_appointments/models/resp_models/doctor_list_api_response.dart new file mode 100644 index 0000000..c3155e1 --- /dev/null +++ b/lib/features/my_appointments/models/resp_models/doctor_list_api_response.dart @@ -0,0 +1,274 @@ + +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' show HospitalsModel; + +class DoctorList { + int? clinicID; + dynamic appointmentNo; + String? clinicName; + String? doctorTitle; + num? iD; + String? name; + int? projectID; + String? projectName; + int? actualDoctorRate; + num? clinicRoomNo; + dynamic date; + dynamic appointmentDate; + dynamic dayName; + int? doctorID; + String? doctorImageURL; + dynamic doctorProfile; + dynamic doctorProfileInfo; + int? doctorRate; + num? gender; + String? genderDescription; + bool? isAppointmentAllowed; + bool? isDoctorAllowVedioCall; + bool? isDoctorDummy; + bool? isLiveCare; + bool? isLiveCareClinic; + bool? isDoctorHasPrePostImages; + String? latitude; + String? longitude; + String? nationalityFlagURL; + String? nationalityID; + String? nationalityName; + dynamic nearestFreeSlot; + int? noOfPatientsRate; + num? originalClinicID; + num? personRate; + dynamic projectDistanceInKiloMeters; + String? qR; + dynamic qRString; + num? rateNumber; + dynamic serviceID; + String? setupID; + List? speciality; + List? specialityN; + dynamic workingHours; + dynamic decimalDoctorRate; + String? projectBottomName; + String? projectTopName; + bool? isHMC; + String? region; + String? regionArabic; + String? regionEnglish; + String? regionID; + + DoctorList( + {this.clinicID, + this.appointmentNo, + this.clinicName, + this.doctorTitle, + this.iD, + this.name, + this.projectID, + this.projectName, + this.actualDoctorRate, + this.clinicRoomNo, + this.date, + this.appointmentDate, + this.dayName, + this.doctorID, + this.doctorImageURL, + this.doctorProfile, + this.doctorProfileInfo, + this.doctorRate, + this.gender, + this.genderDescription, + this.isAppointmentAllowed, + this.isDoctorAllowVedioCall, + this.isDoctorDummy, + this.isLiveCare, + this.isLiveCareClinic, + this.isDoctorHasPrePostImages, + this.latitude, + this.longitude, + this.nationalityFlagURL, + this.nationalityID, + this.nationalityName, + this.nearestFreeSlot, + this.noOfPatientsRate, + this.originalClinicID, + this.personRate, + this.projectDistanceInKiloMeters, + this.qR, + this.qRString, + this.rateNumber, + this.serviceID, + this.setupID, + this.speciality, + this.specialityN, + this.workingHours, + this.decimalDoctorRate, + this.projectBottomName, + this.projectTopName, + this.isHMC, + this.region, + this.regionArabic, + this.regionEnglish, + this.regionID, + }); + + DoctorList.fromJson( + Map json, + ) { + clinicID = json['ClinicID']; + appointmentNo = json['AppointmentNo']; + clinicName = json['ClinicName']; + doctorTitle = json['DoctorTitle']; + iD = json['ID']; + name = json['DoctorName'] ?? json['Name']; + projectID = json['ProjectID']; + projectName = json['ProjectName']; + actualDoctorRate = json['ActualDoctorRate']; + clinicRoomNo = json['ClinicRoomNo']; + date = json['Date']; + appointmentDate = json['AppointmentDate']; + dayName = json['DayName']; + doctorID = json['DoctorID']; + doctorImageURL = json['DoctorImageURL']; + doctorProfile = json['DoctorProfile']; + doctorProfileInfo = json['DoctorProfileInfo']; + doctorRate = json['DoctorRate']; + gender = json['Gender']; + genderDescription = json['GenderDescription']; + isAppointmentAllowed = json['IsAppointmentAllowed']; + isDoctorAllowVedioCall = json['IsDoctorAllowVedioCall']; + isDoctorDummy = json['IsDoctorDummy']; + isLiveCare = json['IsLiveCare']; + isLiveCareClinic = json['IsLiveCareClinic']; + isDoctorHasPrePostImages = json['IsDoctorHasPrePostImages']; + latitude = json['Latitude']; + longitude = json['Longitude']; + nationalityFlagURL = json['NationalityFlagURL']; + nationalityID = json['NationalityID']; + nationalityName = json['NationalityName']; + nearestFreeSlot = json['NearestFreeSlot']; + noOfPatientsRate = json['NoOfPatientsRate']; + originalClinicID = json['OriginalClinicID']; + personRate = json['PersonRate']; + projectDistanceInKiloMeters = json['ProjectDistanceInKiloMeters']; + qR = json['QR']; + qRString = json['QRString']; + rateNumber = json['RateNumber']; + serviceID = json['ServiceID']; + setupID = json['SetupID']; + if (json.containsKey('Speciality') && json['Speciality'] != null) speciality = json['Speciality'].cast(); + if (json.containsKey('SpecialityN') && json['SpecialityN'] != null) specialityN = json['SpecialityN'].cast(); + workingHours = json['WorkingHours']; + decimalDoctorRate = json['DecimalDoctorRate']; + projectBottomName = json['ProjectNameBottom']; + projectTopName = json['ProjectNameTop']; + this.isHMC = json["IsHMC"]; + this.regionArabic = json['RegionNameN']; + this.regionEnglish = json['RegionName']; + } + + String? getRegionName(bool isArabic) { + if (isArabic) { + return regionArabic; + } + return regionEnglish; + } + + Map toJson() { + final Map data = new Map(); + data['ClinicID'] = this.clinicID; + data['AppointmentNo'] = this.appointmentNo; + data['ClinicName'] = this.clinicName; + data['DoctorTitle'] = this.doctorTitle; + data['ID'] = this.iD; + data['Name'] = this.name; + data['ProjectID'] = this.projectID; + data['ProjectName'] = this.projectName; + data['ActualDoctorRate'] = this.actualDoctorRate; + data['ClinicRoomNo'] = this.clinicRoomNo; + data['Date'] = this.date; + data['DayName'] = this.dayName; + data['DoctorID'] = this.doctorID; + data['DoctorImageURL'] = this.doctorImageURL; + data['DoctorProfile'] = this.doctorProfile; + data['DoctorProfileInfo'] = this.doctorProfileInfo; + data['DoctorRate'] = this.doctorRate; + data['Gender'] = this.gender; + data['GenderDescription'] = this.genderDescription; + data['IsAppointmentAllowed'] = this.isAppointmentAllowed; + data['IsDoctorAllowVedioCall'] = this.isDoctorAllowVedioCall; + data['IsDoctorDummy'] = this.isDoctorDummy; + data['IsLiveCare'] = this.isLiveCare; + data['IsLiveCareClinic'] = this.isLiveCareClinic; + data['IsDoctorHasPrePostImages'] = this.isDoctorHasPrePostImages; + data['Latitude'] = this.latitude; + data['Longitude'] = this.longitude; + data['NationalityFlagURL'] = this.nationalityFlagURL; + data['NationalityID'] = this.nationalityID; + data['NationalityName'] = this.nationalityName; + data['NearestFreeSlot'] = this.nearestFreeSlot; + data['NoOfPatientsRate'] = this.noOfPatientsRate; + data['OriginalClinicID'] = this.originalClinicID; + data['PersonRate'] = this.personRate; + data['ProjectDistanceInKiloMeters'] = this.projectDistanceInKiloMeters; + data['QR'] = this.qR; + data['QRString'] = this.qRString; + data['RateNumber'] = this.rateNumber; + data['ServiceID'] = this.serviceID; + data['SetupID'] = this.setupID; + data['Speciality'] = this.speciality; + data['SpecialityN'] = this.specialityN; + data['WorkingHours'] = this.workingHours; + data['DecimalDoctorRate'] = this.decimalDoctorRate; + return data; + } + + String getProjectCompleteName(){ + return "${this.projectTopName} ${this.projectBottomName}"; + } + + String getProjectCompleteNameWithLocale({bool isArabic = false}) { + if (isArabic) { + return "${this.projectBottomName} ${this.projectTopName}"; + } + return "${this.projectTopName} ${this.projectBottomName}"; + } +} + +class PatientDoctorAppointmentList { + String? filterName = ""; + String? distanceInKMs = ""; + List? patientDoctorAppointmentList = []; + String? projectTopName = ""; + String? projectBottomName = ""; + bool? isHMC; + List hospitalList = []; + + PatientDoctorAppointmentList( + {this.filterName, + this.distanceInKMs, + this.projectTopName, + this.projectBottomName, + DoctorList? patientDoctorAppointment, + HospitalsModel? model, + this.isHMC = false}) { + if (model != null) { + hospitalList.add(model); + } + if (patientDoctorAppointment != null) { + patientDoctorAppointmentList!.add(patientDoctorAppointment!); + } + } +} + +class PatientDoctorAppointmentListByRegion { + List? hmgDoctorList = []; + List? hmcDoctorList = []; + int hmcSize = 0; + int hmgSize = 0; + num distance = double.infinity; + num hmgDistance = double.infinity; + num hmcDistance = double.infinity; +} + +class RegionList { + Map? registeredDoctorMap = {}; +} \ No newline at end of file diff --git a/lib/features/my_appointments/models/resp_models/hospital_model.dart b/lib/features/my_appointments/models/resp_models/hospital_model.dart new file mode 100644 index 0000000..342fcea --- /dev/null +++ b/lib/features/my_appointments/models/resp_models/hospital_model.dart @@ -0,0 +1,104 @@ +class HospitalsModel { + String? desciption; + dynamic desciptionN; + dynamic iD; + String? legalName; + String? legalNameN; + String? name; + dynamic nameN; + String? phoneNumber; + String? setupID; + dynamic distanceInKilometers; + bool? isActive; + String? latitude; + String? longitude; + dynamic mainProjectID; + bool? projectOutSA; + bool? usingInDoctorApp; + bool? isHMC; + String? region; + String? regionArabic; + String? regionEnglish; + String? regionID; + + HospitalsModel({ + this.desciption, + this.desciptionN, + this.iD, + this.legalName, + this.legalNameN, + this.name, + this.nameN, + this.phoneNumber, + this.setupID, + this.distanceInKilometers, + this.isActive, + this.latitude, + this.longitude, + this.mainProjectID, + this.projectOutSA, + this.usingInDoctorApp, + this.isHMC, + this.region, + this.regionArabic, + this.regionEnglish, + this.regionID, + }); + + HospitalsModel.fromJson(Map json) { + desciption = json['Desciption']; + desciptionN = json['DesciptionN']; + iD = json['ID']; + legalName = json['LegalName']; + legalNameN = json['LegalNameN']; + name = json['Name']; + nameN = json['NameN']; + phoneNumber = json['PhoneNumber']; + setupID = json['SetupID']; + distanceInKilometers = json['DistanceInKilometers']; + isActive = json['IsActive']; + latitude = json['Latitude']; + longitude = json['Longitude']; + mainProjectID = json['MainProjectID']; + projectOutSA = json['ProjectOutSA']; + usingInDoctorApp = json['UsingInDoctorApp']; + this.isHMC = json["IsHMC"]; + this.regionArabic = json['RegionNameN']; + this.regionEnglish = json['RegionName']; + } + + String? getRegionName(bool isArabic) { + if (isArabic) { + return regionArabic; + } + return regionEnglish; + } + + String? getName(bool isArabic) { + if (isArabic) { + return "$nameN"; + } + return name; + } + + Map toJson() { + final Map data = new Map(); + data['Desciption'] = this.desciption; + data['DesciptionN'] = this.desciptionN; + data['ID'] = this.iD; + data['LegalName'] = this.legalName; + data['LegalNameN'] = this.legalNameN; + data['Name'] = this.name; + data['NameN'] = this.nameN; + data['PhoneNumber'] = this.phoneNumber; + data['SetupID'] = this.setupID; + data['DistanceInKilometers'] = this.distanceInKilometers; + data['IsActive'] = this.isActive; + data['Latitude'] = this.latitude; + data['Longitude'] = this.longitude; + data['MainProjectID'] = this.mainProjectID; + data['ProjectOutSA'] = this.projectOutSA; + data['UsingInDoctorApp'] = this.usingInDoctorApp; + return data; + } +} diff --git a/lib/features/my_appointments/my_appointments_repo.dart b/lib/features/my_appointments/my_appointments_repo.dart index 2035f30..44c3954 100644 --- a/lib/features/my_appointments/my_appointments_repo.dart +++ b/lib/features/my_appointments/my_appointments_repo.dart @@ -3,6 +3,8 @@ import 'package:hmg_patient_app_new/core/api/api_client.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart' + show HospitalsModel; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; @@ -36,6 +38,9 @@ abstract class MyAppointmentsRepo { Future>>> getPatientAppointmentsForTimeLine(); Future>>> getPatientDoctorsList(); + + Future>>> + getProjectList(); } class MyAppointmentsRepoImp implements MyAppointmentsRepo { @@ -491,4 +496,47 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>>> + getProjectList() async { + Map request = {}; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_PROJECT_LIST, + body: request, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['ListProject']; + + final appointmentsList = list + .map((item) => + HospitalsModel.fromJson(item as Map)) + .toList() + .cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: appointmentsList, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index c6d3499..bde7bfa 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -1,38 +1,60 @@ 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' show CacheConst; +import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils; +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' + show RegionList, PatientDoctorAppointmentListByRegion; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; +import 'package:location/location.dart' show Location; + +import '../../core/utils/doctor_response_mapper.dart' show DoctorMapper; + class MyAppointmentsViewModel extends ChangeNotifier { int selectedTabIndex = 0; MyAppointmentsRepo myAppointmentsRepo; ErrorHandlerService errorHandlerService; + AppState appState; bool isMyAppointmentsLoading = false; bool isAppointmentPatientShareLoading = false; bool isTimeLineAppointmentsLoading = false; bool isPatientMyDoctorsLoading = false; - List patientAppointmentsHistoryList = []; + List patientAppointmentsHistoryList = + []; - List patientUpcomingAppointmentsHistoryList = []; - List patientArrivedAppointmentsHistoryList = []; + List + patientUpcomingAppointmentsHistoryList = []; + List + patientArrivedAppointmentsHistoryList = []; - List patientTimelineAppointmentsList = []; + List patientTimelineAppointmentsList = + []; List patientMyDoctorsList = []; PatientAppointmentShareResponseModel? patientAppointmentShareResponseModel; - MyAppointmentsViewModel({required this.myAppointmentsRepo, required this.errorHandlerService}); + RegionList? hospitalList; + RegionList? filteredHospitalList; + FacilitySelection currentlySelectedFacility = FacilitySelection.ALL; + bool isRegionListLoading = false; + + MyAppointmentsViewModel( + {required this.myAppointmentsRepo, required this.errorHandlerService,required this.appState}); void onTabChange(int index) { selectedTabIndex = index; notifyListeners(); } + initAppointmentsViewModel() { patientAppointmentsHistoryList.clear(); patientUpcomingAppointmentsHistoryList.clear(); @@ -43,6 +65,7 @@ class MyAppointmentsViewModel extends ChangeNotifier { isAppointmentPatientShareLoading = true; isTimeLineAppointmentsLoading = true; isPatientMyDoctorsLoading = true; + isRegionListLoading = true; notifyListeners(); } @@ -66,7 +89,8 @@ class MyAppointmentsViewModel extends ChangeNotifier { notifyListeners(); } - setAppointmentReminder(bool value, PatientAppointmentHistoryResponseModel item) { + setAppointmentReminder( + bool value, PatientAppointmentHistoryResponseModel item) { int index = patientAppointmentsHistoryList.indexOf(item); if (index != -1) { patientAppointmentsHistoryList[index].hasReminder = value; @@ -74,12 +98,18 @@ class MyAppointmentsViewModel extends ChangeNotifier { } } - Future getPatientAppointments(bool isActiveAppointment, bool isArrivedAppointments, {Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: isActiveAppointment, isArrivedAppointments: isArrivedAppointments); - final resultArrived = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: false, isArrivedAppointments: true); + Future getPatientAppointments( + bool isActiveAppointment, bool isArrivedAppointments, + {Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await myAppointmentsRepo.getPatientAppointments( + isActiveAppointment: isActiveAppointment, + isArrivedAppointments: isArrivedAppointments); + final resultArrived = await myAppointmentsRepo.getPatientAppointments( + isActiveAppointment: false, isArrivedAppointments: true); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async => + await errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); @@ -95,7 +125,8 @@ class MyAppointmentsViewModel extends ChangeNotifier { ); resultArrived.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async => + await errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); @@ -110,19 +141,27 @@ class MyAppointmentsViewModel extends ChangeNotifier { }, ); - patientAppointmentsHistoryList.addAll(patientUpcomingAppointmentsHistoryList); - patientAppointmentsHistoryList.addAll(patientArrivedAppointmentsHistoryList); + patientAppointmentsHistoryList + .addAll(patientUpcomingAppointmentsHistoryList); + patientAppointmentsHistoryList + .addAll(patientArrivedAppointmentsHistoryList); - print('Upcoming Appointments: ${patientUpcomingAppointmentsHistoryList.length}'); - print('Arrived Appointments: ${patientArrivedAppointmentsHistoryList.length}'); + print( + 'Upcoming Appointments: ${patientUpcomingAppointmentsHistoryList.length}'); + print( + 'Arrived Appointments: ${patientArrivedAppointmentsHistoryList.length}'); print('All Appointments: ${patientAppointmentsHistoryList.length}'); } - Future getPatientShareAppointment(int projectID, int clinicID, String appointmentNo, {Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await myAppointmentsRepo.getPatientShareAppointment(projectID: projectID, clinicID: clinicID, appointmentNo: appointmentNo); + Future getPatientShareAppointment( + int projectID, int clinicID, String appointmentNo, + {Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await myAppointmentsRepo.getPatientShareAppointment( + projectID: projectID, clinicID: clinicID, appointmentNo: appointmentNo); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async => + await errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); @@ -139,11 +178,19 @@ class MyAppointmentsViewModel extends ChangeNotifier { } Future addAdvanceNumberRequest( - {required String advanceNumber, required String paymentReference, required String appointmentNo, Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await myAppointmentsRepo.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 myAppointmentsRepo.addAdvanceNumberRequest( + advanceNumber: advanceNumber, + paymentReference: paymentReference, + appointmentNo: appointmentNo); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async => + await errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); @@ -158,11 +205,21 @@ class MyAppointmentsViewModel extends ChangeNotifier { } Future generateAppointmentQR( - {required int clinicID, required int projectID, required String appointmentNo, required int isFollowUp, Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await myAppointmentsRepo.generateAppointmentQR(clinicID: clinicID, projectID: projectID, appointmentNo: appointmentNo, isFollowUp: isFollowUp); + {required int clinicID, + required int projectID, + required String appointmentNo, + required int isFollowUp, + Function(dynamic)? onSuccess, + Function(String)? onError}) async { + final result = await myAppointmentsRepo.generateAppointmentQR( + clinicID: clinicID, + projectID: projectID, + appointmentNo: appointmentNo, + isFollowUp: isFollowUp); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async => + await errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); @@ -176,11 +233,18 @@ class MyAppointmentsViewModel extends ChangeNotifier { ); } - Future cancelAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await myAppointmentsRepo.cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel); + Future cancelAppointment( + {required PatientAppointmentHistoryResponseModel + patientAppointmentHistoryResponseModel, + Function(dynamic)? onSuccess, + Function(String)? onError}) async { + final result = await myAppointmentsRepo.cancelAppointment( + patientAppointmentHistoryResponseModel: + patientAppointmentHistoryResponseModel); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async => + await errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.messageStatus == 2) { onError!(apiResponse.errorMessage!); @@ -195,11 +259,18 @@ class MyAppointmentsViewModel extends ChangeNotifier { ); } - Future confirmAppointment({required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await myAppointmentsRepo.confirmAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel); + Future confirmAppointment( + {required PatientAppointmentHistoryResponseModel + patientAppointmentHistoryResponseModel, + Function(dynamic)? onSuccess, + Function(String)? onError}) async { + final result = await myAppointmentsRepo.confirmAppointment( + patientAppointmentHistoryResponseModel: + patientAppointmentHistoryResponseModel); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async => + await errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.messageStatus == 2) { onError!(apiResponse.errorMessage!); @@ -236,7 +307,8 @@ class MyAppointmentsViewModel extends ChangeNotifier { patientType: patientType); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async => + await errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); @@ -251,15 +323,21 @@ class MyAppointmentsViewModel extends ChangeNotifier { } Future sendCheckInNfcRequest( - {required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, + {required PatientAppointmentHistoryResponseModel + patientAppointmentHistoryResponseModel, required String scannedCode, required int checkInType, Function(dynamic)? onSuccess, Function(String)? onError}) async { - final result = await myAppointmentsRepo.sendCheckInNfcRequest(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel, scannedCode: scannedCode, checkInType: checkInType); + final result = await myAppointmentsRepo.sendCheckInNfcRequest( + patientAppointmentHistoryResponseModel: + patientAppointmentHistoryResponseModel, + scannedCode: scannedCode, + checkInType: checkInType); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async => + await errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.messageStatus == 2) { onError!(apiResponse.errorMessage!); @@ -274,11 +352,13 @@ class MyAppointmentsViewModel extends ChangeNotifier { ); } - Future getPatientMyDoctors({Function(dynamic)? onSuccess, Function(String)? onError}) async { + Future getPatientMyDoctors( + {Function(dynamic)? onSuccess, Function(String)? onError}) async { final result = await myAppointmentsRepo.getPatientDoctorsList(); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async => + await errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); @@ -293,4 +373,84 @@ class MyAppointmentsViewModel extends ChangeNotifier { }, ); } + + Future getRegionMappedProjectList() async { + // if(hospitalList != null && hospitalList!.registeredDoctorMap != null && hospitalList!.registeredDoctorMap!.isNotEmpty){ + // filteredHospitalList = hospitalList; + // return; + // } + isRegionListLoading = true; + notifyListeners(); + final result = await myAppointmentsRepo.getProjectList(); + + result.fold( + (failure) async => + await errorHandlerService.handleError(failure: failure), + (apiResponse) async { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + var projectList = apiResponse.data!; + hospitalList = await DoctorMapper.getMappedHospitals(projectList, + isArabic: false); + var lat = await Utils.getNumFromPrefs(CacheConst.userLat); + + var lng = await Utils.getNumFromPrefs(CacheConst.userLong); + var isLocationEnabled = (lat != 0) && (lng != 0); + hospitalList = + await DoctorMapper.sortList(isLocationEnabled, hospitalList!); + + isRegionListLoading = false; + filteredHospitalList = hospitalList; + notifyListeners(); + } + }, + ); + } + + void setSelectedFacility(FacilitySelection selection) { + currentlySelectedFacility = selection; + notifyListeners(); + } + + void filterHospitalListByString(String? value, String? selectedRegionId, bool isHMG) { + if(value ==null || value.isEmpty){ + filteredHospitalList = hospitalList; + } else { + filteredHospitalList = RegionList(); + + var list = isHMG + ? hospitalList?.registeredDoctorMap![selectedRegionId]!.hmgDoctorList + : hospitalList?.registeredDoctorMap![selectedRegionId]!.hmcDoctorList; + + if(list != null && list.isEmpty){ notifyListeners(); return;} + + var filteredList = list!.where((element) => + element.filterName!.toLowerCase().contains(value.toLowerCase()) + ).toList(); + var regionData = PatientDoctorAppointmentListByRegion(); + if(isHMG){ + regionData.hmgDoctorList = filteredList; + regionData.hmgSize = filteredList.length; + } else { + regionData.hmcDoctorList = filteredList; + regionData.hmcSize = filteredList.length; + } + + filteredHospitalList?.registeredDoctorMap = { + selectedRegionId! : regionData + }; + } + notifyListeners(); + } + + Future isLocationEnabled() async{ + return await Location().serviceEnabled(); + } + + bool getLocationStatus() { + bool isLocationAvaiable = false; + isLocationEnabled().then((value) => isLocationAvaiable = value); + return isLocationAvaiable; + } } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 8b7f021..e4eb631 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -813,5 +813,12 @@ abstract class LocaleKeys { static const pendingActivation = 'pendingActivation'; static const awaitingApproval = 'awaitingApproval'; static const ready = 'ready'; + static const medicalCentersWithCount = 'medicalCentersWithCount'; + static const medicalCenters = 'medicalCenters'; + static const hospitalsWithCount = 'hospitalsWithCount'; + static const selectRegion = 'selectRegion'; + static const selectFacility = 'selectFacility'; + static const selectFacilitiesSubTitle = 'selectFacilitiesSubTitle'; + static const selectHospitalSubTitle = 'selectHospitalSubTitle'; } diff --git a/lib/main.dart b/lib/main.dart index 24f5c36..67f03fa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,6 +14,7 @@ import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_mode import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; @@ -110,6 +111,7 @@ void main() async { create: (_) => MyAppointmentsViewModel( myAppointmentsRepo: getIt(), errorHandlerService: getIt(), + appState: getIt(), ), ), ChangeNotifierProvider( @@ -141,6 +143,8 @@ void main() async { localAuthService: getIt(), ), ), + ChangeNotifierProvider( + create: (_) => AppointmentViaRegionViewmodel()) ], child: MyApp()), ), ); diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index 0f9a325..f740fa4 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -8,14 +8,23 @@ 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/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/resp_models/doctor_list_api_response.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart' + show RegionBottomSheetBody; import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart'; import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart' + show showCommonBottomSheetWithoutHeight; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; +import '../appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart'; + class BookAppointmentPage extends StatefulWidget { const BookAppointmentPage({super.key}); @@ -25,10 +34,13 @@ class BookAppointmentPage extends StatefulWidget { class _BookAppointmentPageState extends State { late AppState appState; + late AppointmentViaRegionViewmodel regionalViewModel; @override Widget build(BuildContext context) { appState = getIt.get(); + regionalViewModel = + Provider.of(context, listen: true); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( @@ -144,7 +156,12 @@ class _BookAppointmentPageState extends State { ), Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h), ], - ).onPress(() {}), + ).onPress(() { + // Consumer( + // builder: (_, data, __) => + + openRegionListBottomSheet(context); + }), ], ), ), @@ -156,4 +173,45 @@ class _BookAppointmentPageState extends State { } return Container(); } + + void openRegionListBottomSheet(BuildContext context) { + // AppointmentViaRegionViewmodel? viewmodel = null; + showCommonBottomSheetWithoutHeight(context, + title: "", + titleWidget: Consumer( + builder: (_, data, __) => getTitle(data)), + isDismissible: false, + child: Consumer(builder: (_, data, __) { + return getRegionalSelectionWidget(data); + }), callBackFunc: () { + regionalViewModel.flush(); + }); + } + + Widget getRegionalSelectionWidget(AppointmentViaRegionViewmodel data) { + if (data.bottomSheetState == AppointmentViaRegionState.REGION_SELECTION) { + return RegionBottomSheetBody(); + } + if(data.bottomSheetState == AppointmentViaRegionState.TYPE_SELECTION){ + return FacilityTypeSelectionWidget(selectedRegion: data.selectedRegionId??"",); + } + if (data.bottomSheetState == AppointmentViaRegionState.HOSPITAL_SELECTION) { + return HospitalBottomSheetBody(); + } else { + SizedBox.shrink(); + } + return SizedBox.shrink(); + } + + getTitle(AppointmentViaRegionViewmodel data) { + if (data.selectedRegionId == null) { + return LocaleKeys.selectRegion.tr().toText20(weight: FontWeight.w600); + } else { + return Utils.buildSvgWithAssets( + icon: AppAssets.arrow_back, iconColor: Color(0xff2B353E)) + .onPress(() { + data.handleBackPress(); + }); + } + } } diff --git a/lib/presentation/home/navigation_screen.dart b/lib/presentation/home/navigation_screen.dart index bdce393..f06aab3 100644 --- a/lib/presentation/home/navigation_screen.dart +++ b/lib/presentation/home/navigation_screen.dart @@ -23,7 +23,7 @@ class _LandingNavigationState extends State { physics: const NeverScrollableScrollPhysics(), children: [ const LandingPage(), - MedicalFilePage(), + MedicalFilePage(), const BookAppointmentPage(), const LandingPage(), const LandingPage(), diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index 8f96e57..0c7da58 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -31,6 +31,7 @@ class AppColors { static const Color textColor = Color(0xFF2E3039); static const Color textColorLight = Color(0xFF5E5E5E); static const Color borderOnlyColor = Color(0xFF2E3039); + static const Color chipBorderColorOpacity20 = Color(0x332E3039); static const Color dividerColor = Color(0xFFD2D2D2); static const Color warningColorYellow = Color(0xFFF4A308); static const Color blackBgColor = Color(0xFF2E3039); diff --git a/lib/widgets/common_bottom_sheet.dart b/lib/widgets/common_bottom_sheet.dart index b88c922..576e923 100644 --- a/lib/widgets/common_bottom_sheet.dart +++ b/lib/widgets/common_bottom_sheet.dart @@ -109,9 +109,11 @@ void showCommonBottomSheetWithoutHeight( required Widget child, required VoidCallback callBackFunc, String title = "", - bool isCloseButtonVisible = true, - bool isFullScreen = true, -}) { + + bool isCloseButtonVisible = true, + bool isFullScreen = true, + bool isDismissible = true, + Widget? titleWidget,}) { showModalBottomSheet( sheetAnimationStyle: AnimationStyle( duration: Duration(milliseconds: 500), // Custom animation duration @@ -120,6 +122,7 @@ void showCommonBottomSheetWithoutHeight( context: context, isScrollControlled: true, showDragHandle: false, + isDismissible: isDismissible, backgroundColor: AppColors.bottomSheetBgColor, builder: (BuildContext context) { return SafeArea( @@ -135,9 +138,10 @@ void showCommonBottomSheetWithoutHeight( spacing: 16.h, children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ - title.toText20(weight: FontWeight.w600).expanded, + titleWidget ?? title.toText20(weight: FontWeight.w600), Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, iconColor: Color(0xff2B353E)).onPress(() { Navigator.of(context).pop(); }), diff --git a/pubspec.yaml b/pubspec.yaml index a635cd1..6e14343 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -80,6 +80,8 @@ dependencies: open_filex: ^4.7.0 flutter_swiper_view: ^1.1.8 + location: ^8.0.1 + dev_dependencies: flutter_test: sdk: flutter