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 fd34e55..08ef65c 100644
--- a/assets/langs/ar-SA.json
+++ b/assets/langs/ar-SA.json
@@ -817,5 +817,14 @@
"news": "أخبار",
"ready": "جاهز",
"enterValidNationalId": "الرجاء إدخال رقم الهوية الوطنية أو رقم الملف الصحيح",
- "enterValidPhoneNumber": "الرجاء إدخال رقم هاتف صالح"
+ "enterValidPhoneNumber": "الرجاء إدخال رقم هاتف صالح",
+ "medicalCentersWithCount": "{count} مراكز طبية",
+ "medicalCenters": "مراكز طبية",
+ "hospitalsWithCount": "{count} مستشفيات",
+ "selectRegion": "اختر المنطقة",
+ "selectFacility": "اختر المرافق",
+ "selectFacilitiesSubTitle": "يرجى اختيار المرفق للموعد",
+ "selectHospitalSubTitle": "يرجى اختيار المستشفى للموعد",
+ "iAcceptThe" : "أوافق على",
+ "personalDetailsVerification": "التحقق من التفاصيل الشخصية",
}
\ No newline at end of file
diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json
index b4cd340..b8506f4 100644
--- a/assets/langs/en-US.json
+++ b/assets/langs/en-US.json
@@ -813,5 +813,15 @@
"enterValidNationalId": "Please enter a valid national ID or file number",
"enterValidPhoneNumber": "Please enter a valid phone number",
"ready": "Ready",
- "news": "News"
+ "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",
+ "news": "News",
+ "iAcceptThe" : "I Accept the",
+ "personalDetailsVerification": "Personal Details Verification"
+
}
\ No newline at end of file
diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart
index bad2e4c..d31679c 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';
@@ -212,14 +212,11 @@ var GET_QR_PARKING = 'Services/SWP.svc/REST/GetQRParkingByID';
//URL to get clinic list
var GET_CLINICS_LIST_URL = "Services/lists.svc/REST/GetClinicCentralized";
-var GET_CLINICS_LIST_WRT_HOSPITAL_URL = "Services/Lists.svc/REST/GetClinicFromDoctorSchedule";
+var GET_CLINICS_LIST_WRT_HOSPITAL_ID_URL = "Services/Lists.svc/REST/GetClinicFromDoctorSchedule";
//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";
diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart
index fbd4945..0a414d0 100644
--- a/lib/core/app_assets.dart
+++ b/lib/core/app_assets.dart
@@ -110,6 +110,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';
static const String livecare_clinic_icon = '$svgBasePath/livecare_clinic_icon.svg';
static const String immediate_service_icon = '$svgBasePath/immediate_service_icon.svg';
static const String no_visit_icon = '$svgBasePath/no_visit_icon.svg';
diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart
index 52500bf..f4c0fd8 100644
--- a/lib/core/app_state.dart
+++ b/lib/core/app_state.dart
@@ -62,7 +62,7 @@ class AppState {
SelectDeviceByImeiRespModelElement? _selectDeviceByImeiRespModelElement;
- void setSelectDeviceByImeiRespModelElement(SelectDeviceByImeiRespModelElement value) {
+ void setSelectDeviceByImeiRespModelElement(SelectDeviceByImeiRespModelElement? value) {
_selectDeviceByImeiRespModelElement = value;
}
@@ -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 90e3702..f5b3d05 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 2f0b078..05a6f43 100644
--- a/lib/core/enums.dart
+++ b/lib/core/enums.dart
@@ -29,7 +29,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/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart
index 713350f..db3ff79 100644
--- a/lib/extensions/string_extensions.dart
+++ b/lib/extensions/string_extensions.dart
@@ -237,10 +237,15 @@ extension EmailValidator on String {
style: TextStyle(height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
- Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false}) => Text(
+ Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false, FontWeight? weight, double? letterSpacing}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
- style: TextStyle(height: height ?? 23 / 26, color: color ?? AppColors.blackColor, fontSize: 26.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
+ style: TextStyle(
+ height: height ?? 23 / 26,
+ color: color ?? AppColors.blackColor,
+ fontSize: 26.fSize,
+ letterSpacing: letterSpacing ?? -1,
+ fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
);
Widget toText28({Color? color, bool isBold = false, double? height, bool isCenter = false}) => Text(
diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart
index bb41af6..cd550c7 100644
--- a/lib/features/authentication/authentication_view_model.dart
+++ b/lib/features/authentication/authentication_view_model.dart
@@ -1,5 +1,6 @@
import 'dart:convert';
import 'dart:developer';
+import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter/material.dart';
@@ -36,6 +37,7 @@ import 'package:hmg_patient_app_new/services/localauth_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/bottomsheet/exception_bottom_sheet.dart';
+import 'package:sms_otp_auto_verify/sms_otp_auto_verify.dart';
import 'models/request_models/get_user_mobile_device_data.dart';
import 'models/request_models/insert_patient_mobile_deviceinfo.dart';
@@ -48,6 +50,7 @@ class AuthenticationViewModel extends ChangeNotifier {
final DialogService _dialogService;
final NavigationService _navigationService;
final LocalAuthService _localAuthService;
+
AuthenticationViewModel({
required AppState appState,
required AuthenticationRepo authenticationRepo,
@@ -79,6 +82,8 @@ class AuthenticationViewModel extends ChangeNotifier {
CalenderEnum calenderType = CalenderEnum.gregorian;
LoginTypeEnum loginTypeEnum = LoginTypeEnum.sms;
+ final ValueNotifier otpScreenNotifier = ValueNotifier(false);
+
//==================
String errorMsg = '';
@@ -106,7 +111,6 @@ class AuthenticationViewModel extends ChangeNotifier {
}
Future clearDefaultInputValues() async {
-
nationalIdController.clear();
phoneNumberController.clear();
emailController.clear();
@@ -159,6 +163,10 @@ class AuthenticationViewModel extends ChangeNotifier {
notifyListeners();
}
+ void clearEmailInput() {
+ emailController.text = "";
+ }
+
void onUAEUserCountrySelection(String? value) {
pickedCountryByUAEUser = countriesList!.firstWhere((element) => element.name == value);
notifyListeners();
@@ -355,7 +363,7 @@ class AuthenticationViewModel extends ChangeNotifier {
);
// TODO: GET APP SMS SIGNATURE HERE
- request.sMSSignature = "enKTDcqbOVd";
+ request.sMSSignature =await getSignature();
if (checkIsUserComingForRegister(request: payload)) {
_appState.setUserRegistrationPayload = RegistrationDataModelPayload.fromJson(payload);
@@ -394,12 +402,8 @@ class AuthenticationViewModel extends ChangeNotifier {
return isUserComingForRegister;
}
- Future checkActivationCode({
- required String? activationCode,
- required OTPTypeEnum otpTypeEnum,
- required Function(String? message) onWrongActivationCode,
- Function()? onResendActivation,
- }) async {
+ Future checkActivationCode(
+ {required String? activationCode, required OTPTypeEnum otpTypeEnum, required Function(String? message) onWrongActivationCode, Function()? onResendActivation}) async {
bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true || _appState.getUserRegistrationPayload.patientOutSa == 1);
final request = RequestUtils.getCommonRequestWelcome(
@@ -422,6 +426,7 @@ class AuthenticationViewModel extends ChangeNotifier {
countryCode: _appState.getSelectDeviceByImeiRespModelElement != null && _appState.getSelectDeviceByImeiRespModelElement!.outSa == true
? CountryEnum.unitedArabEmirates.countryCode
: selectedCountrySignup.countryCode,
+ //TODO: Error Here IN Zip Code.
loginType: loginTypeEnum.toInt)
.toJson();
LoaderBottomSheet.showLoader();
@@ -469,6 +474,7 @@ class AuthenticationViewModel extends ChangeNotifier {
failure: failure,
onUnHandledFailure: (failure) async {
LoaderBottomSheet.hideLoader();
+ otpScreenNotifier.value = true;
await _dialogService.showCommonBottomSheetWithoutH(message: failure.message, label: LocaleKeys.notice.tr(), onOkPressed: () {});
},
onMessageStatusFailure: (failure) async {
@@ -595,6 +601,7 @@ class AuthenticationViewModel extends ChangeNotifier {
}
Future onWrongActivationCode({String? message}) async {
+ otpScreenNotifier.value = true;
await _dialogService.showErrorBottomSheet(message: message ?? "Something went wrong. ", onOkPressed: () {});
}
@@ -622,19 +629,20 @@ class AuthenticationViewModel extends ChangeNotifier {
}
checkLastLoginStatus(Function() onSuccess) async {
-
Future.delayed(Duration(seconds: 1), () async {
- if(cacheService.getBool(key: CacheConst.quickLoginEnabled) == null){
+ if (cacheService.getBool(key: CacheConst.quickLoginEnabled) == null) {
if (_appState.getSelectDeviceByImeiRespModelElement != null &&
(_appState.getSelectDeviceByImeiRespModelElement!.logInType == 1 || _appState.getSelectDeviceByImeiRespModelElement!.logInType == 4)) {
- phoneNumberController.text =
- (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0") ? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "") : _appState.getAuthenticatedUser()!.mobileNumber)!;
+ phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0")
+ ? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "")
+ : _appState.getAuthenticatedUser()!.mobileNumber)!;
nationalIdController.text = _appState.getAuthenticatedUser()!.nationalityId!;
onSuccess();
} else if ((loginTypeEnum == LoginTypeEnum.sms || loginTypeEnum == LoginTypeEnum.whatsapp && _appState.getSelectDeviceByImeiRespModelElement == null) &&
_appState.getAuthenticatedUser() != null) {
- phoneNumberController.text =
- (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0") ? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "") : _appState.getAuthenticatedUser()!.mobileNumber)!;
+ phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0")
+ ? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "")
+ : _appState.getAuthenticatedUser()!.mobileNumber)!;
nationalIdController.text = _appState.getAuthenticatedUser()!.nationalityId!;
onSuccess();
}
@@ -674,21 +682,27 @@ class AuthenticationViewModel extends ChangeNotifier {
}
Future onRegistrationComplete() async {
- LoaderBottomSheet.showLoader();
+ // LoaderBottomSheet.showLoader();
+ LoadingUtils.showFullScreenLoader(loadingText: "Setting up your medical file.\nMay take a moment.");
+
var request = RequestUtils.getUserSignupCompletionRequest(fullName: nameController.text, emailAddress: emailController.text, gender: genderType, maritalStatus: maritalStatus);
final resultEither = await _authenticationRepo.registerUser(registrationPayloadDataModelRequest: request);
resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async {
if (apiResponse.data is String) {
//TODO: This Section Need to Be Testing.
+ LoadingUtils.hideFullScreenLoader();
_dialogService.showExceptionBottomSheet(message: apiResponse.data, onOkPressed: () {}, onCancelPressed: () {});
//TODO: Here We Need to Show a Dialog Of Something in the case of Fail With OK and Cancel and the Display Variable WIll be result.
} else {
- print(apiResponse.data as Map);
+ LoadingUtils.hideFullScreenLoader();
if (apiResponse.data["MessageStatus"] == 1) {
- LoaderBottomSheet.hideLoader();
+ LoadingUtils.showFullScreenLoader(isSuccessDialog: true);
//TODO: Here We Need to Show a Dialog Of Something in the case of Success.
await clearDefaultInputValues(); // This will Clear All Default Values Of User.
- _navigationService.pushAndReplace(AppRoutes.loginScreen);
+ Future.delayed(Duration(seconds: 1), () {
+ LoadingUtils.hideFullScreenLoader();
+ _navigationService.pushAndReplace(AppRoutes.loginScreen);
+ });
}
}
});
@@ -736,6 +750,8 @@ class AuthenticationViewModel extends ChangeNotifier {
} else {
//TODO: Here Hide Loader And Show TOAST
//TODO: if (response['ErrorCode'] == '-986') Toast With OK, And Show response as Output.
+ LoaderBottomSheet.hideLoader();
+ _dialogService.showErrorBottomSheet(message: response['ErrorMessage']);
}
}
@@ -921,4 +937,12 @@ class AuthenticationViewModel extends ChangeNotifier {
},
);
}
+ Future getSignature() async {
+ if (Platform.isAndroid) {
+ return await SmsVerification.getAppSignature();
+ } else {
+ return null;
+ }
+ }
+
}
diff --git a/lib/features/authentication/widgets/otp_verification_screen.dart b/lib/features/authentication/widgets/otp_verification_screen.dart
index db7b1eb..6d66061 100644
--- a/lib/features/authentication/widgets/otp_verification_screen.dart
+++ b/lib/features/authentication/widgets/otp_verification_screen.dart
@@ -7,8 +7,11 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart';
+import 'package:sms_otp_auto_verify/sms_otp_auto_verify.dart';
+import 'package:provider/provider.dart';
typedef OnDone = void Function(String text);
@@ -90,6 +93,7 @@ class OTPWidgetState extends State with SingleTickerProviderStateMixi
int currentIndex = 0;
List strList = [];
bool hasFocus = false;
+ AuthenticationViewModel? authVm;
@override
void didUpdateWidget(OTPWidget oldWidget) {
@@ -124,6 +128,7 @@ class OTPWidgetState extends State with SingleTickerProviderStateMixi
@override
void initState() {
super.initState();
+ authVm = context.read();
focusNode = widget.focusNode ?? FocusNode();
_highlightAnimationController = AnimationController(vsync: this);
_initTextController();
@@ -132,12 +137,15 @@ class OTPWidgetState extends State with SingleTickerProviderStateMixi
widget.controller!.addListener(_controllerListener);
}
focusNode.addListener(_focusListener);
+ authVm?.otpScreenNotifier.addListener(_onOtpScreenNotifierChanged);
}
void _controllerListener() {
if (mounted == true) {
setState(() {
_initTextController();
+ text = widget.controller?.text ?? "";
+ currentIndex = text.length;
});
var onTextChanged = widget.onTextChanged;
if (onTextChanged != null) {
@@ -154,6 +162,41 @@ class OTPWidgetState extends State with SingleTickerProviderStateMixi
}
}
+ void onWrongOtpClear() {
+ if (mounted) {
+ setState(() {
+ text = "";
+ currentIndex = 0;
+ strList.clear();
+ _calculateStrList();
+ });
+
+ // Clear the controller if it exists
+ if (widget.controller != null) {
+ widget.controller!.clear();
+ }
+
+ // Remove focus from the input
+ if (focusNode.hasFocus) {
+ focusNode.unfocus();
+ }
+
+ // Optionally refocus after a short delay to allow user to re-enter OTP
+ Future.delayed(const Duration(milliseconds: 100), () {
+ if (mounted && widget.autoFocus) {
+ FocusScope.of(context).requestFocus(focusNode);
+ }
+ });
+ }
+ }
+
+ void _onOtpScreenNotifierChanged() {
+ if (authVm?.otpScreenNotifier.value == true) {
+ onWrongOtpClear();
+ authVm?.otpScreenNotifier.value = false;
+ }
+ }
+
void _initTextController() {
if (widget.controller == null) {
return;
@@ -304,19 +347,17 @@ class OTPWidgetState extends State with SingleTickerProviderStateMixi
);
}
-
Widget _buildPinCode(int i, BuildContext context) {
Color pinBoxColor;
if (widget.hasError) {
pinBoxColor = widget.errorBorderColor;
} else if (text.length == widget.maxLength) {
- // Check for completion first, before individual box logic
pinBoxColor = AppColors.successColor;
} else if (i < text.length) {
- pinBoxColor = AppColors.blackBgColor; // Custom color for filled boxes
+ pinBoxColor = AppColors.blackBgColor;
} else {
- pinBoxColor = widget.pinBoxColor; // Default white color
+ pinBoxColor = widget.pinBoxColor;
}
EdgeInsets insets;
@@ -355,7 +396,6 @@ class OTPWidgetState extends State with SingleTickerProviderStateMixi
);
}
-
// Widget _buildPinCode(int i, BuildContext context) {
// Color pinBoxColor = widget.pinBoxColor;
//
@@ -455,13 +495,14 @@ class _OTPVerificationScreenState extends State {
Timer? _resendTimer;
int _resendTime = 120;
bool _isOtpComplete = false;
- bool _isVerifying = false; // Flag to prevent multiple verification calls
+ bool _isVerifying = false;
@override
void initState() {
super.initState();
_otpController = TextEditingController();
_startResendTimer();
+ checkSignature();
}
@override
@@ -495,6 +536,30 @@ class _OTPVerificationScreenState extends State {
}
}
+ void checkSignature() async {
+ SmsVerification.startListeningSms().then((message) {
+ final intRegex = RegExp(r'\d+', multiLine: true);
+ var otp = SmsVerification.getCode(message, intRegex);
+ if (otp != null && otp.length == _otpLength) {
+ autoFillOtp(otp); // Use autoFillOtp to update controller and UI
+ }
+ SmsVerification.stopListening();
+ });
+ }
+
+ void _onAutoOtpChanged(String value) {
+ setState(() {
+ _isOtpComplete = value.length == _otpLength;
+ });
+
+ if (_isOtpComplete && !_isVerifying) {
+ _isVerifying = true;
+ _verifyOtp(value);
+ } else if (!_isOtpComplete) {
+ _isVerifying = false;
+ }
+ }
+
void _resendOtp() {
if (_resendTime == 0) {
setState(() {
@@ -504,7 +569,6 @@ class _OTPVerificationScreenState extends State {
});
_otpController.clear();
_startResendTimer();
- // autoFillOtp("1234");
widget.onResendOTPPressed(widget.phoneNumber);
}
}
@@ -618,5 +682,7 @@ class _OTPVerificationScreenState extends State {
if (otp.length != _otpLength) return;
_isVerifying = false;
_otpController.text = otp;
+ setState(() {});
+ _onOtpChanged(otp); // Ensure verification and color update
}
}
diff --git a/lib/features/book_appointments/book_appointments_repo.dart b/lib/features/book_appointments/book_appointments_repo.dart
index 5a93879..7454b80 100644
--- a/lib/features/book_appointments/book_appointments_repo.dart
+++ b/lib/features/book_appointments/book_appointments_repo.dart
@@ -7,6 +7,7 @@ import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctor_profile_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.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/services/logger_service.dart';
@@ -40,6 +41,12 @@ abstract class BookAppointmentsRepo {
String? invoiceNoVP,
Function(dynamic)? onSuccess,
Function(String)? onError});
+
+ Future>>>
+ getProjectList();
+
+ Future>>> getClinicsWithRespectToClinicId(String projectID);
+
}
class BookAppointmentsRepoImp implements BookAppointmentsRepo {
@@ -362,4 +369,85 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
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()));
+ }
+ }
+
+ @override
+ Future>>> getClinicsWithRespectToClinicId(String projectID) async {
+ Map mapDevice = {"ProjectID": projectID};
+
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ GET_CLINICS_LIST_WRT_HOSPITAL_ID_URL,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final list = response['ListClinic'];
+
+ final clinicsList = list.map((item) => GetClinicsListResponseModel.fromJson(item as Map)).toList().cast();
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: clinicsList,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
}
diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart
index 854b4fc..c2b9639 100644
--- a/lib/features/book_appointments/book_appointments_view_model.dart
+++ b/lib/features/book_appointments/book_appointments_view_model.dart
@@ -1,8 +1,10 @@
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/utils/date_util.dart';
+import 'package:hmg_patient_app_new/core/utils/doctor_response_mapper.dart';
import 'package:hmg_patient_app_new/core/utils/loading_utils.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
@@ -13,6 +15,8 @@ import 'package:hmg_patient_app_new/features/book_appointments/models/resp_model
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/models/timeslots.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/patient_appointment_history_response_model.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';
@@ -21,6 +25,7 @@ import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
+import 'package:location/location.dart' show Location;
class BookAppointmentsViewModel extends ChangeNotifier {
int selectedTabIndex = 0;
@@ -60,6 +65,14 @@ class BookAppointmentsViewModel extends ChangeNotifier {
MyAppointmentsViewModel myAppointmentsViewModel;
late AppState _appState;
+ RegionList? hospitalList;
+ RegionList? filteredHospitalList;
+ FacilitySelection currentlySelectedFacility = FacilitySelection.ALL;
+ bool isRegionListLoading = false;
+
+ ///this will be used to call the clinic call when navigating from my REGION SELECTION to book appointment screen
+ bool shouldLoadSpecificClinic = false;
+ String? currentlySelectedHospitalFromRegionFlow;
BookAppointmentsViewModel({required this.bookAppointmentsRepo, required this.errorHandlerService, required this.navigationService, required this.myAppointmentsViewModel});
@@ -100,7 +113,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
clinicsList.clear();
}
isClinicsListLoading = value;
- notifyListeners();
+ // notifyListeners();
}
setSelectedClinic(GetClinicsListResponseModel clinic) {
@@ -139,7 +152,18 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
- Future getClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ /// this function will decide which clinic api to be called
+ /// either api for region flow or the select clinic api
+ Future getClinics() async
+ {
+ if(shouldLoadSpecificClinic) {
+ getRegionSelectedClinics();
+ } else {
+ getAllClinics();
+ }
+ }
+
+ Future getAllClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await bookAppointmentsRepo.getClinics();
result.fold(
@@ -164,6 +188,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
Future getDoctorsList(
{int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, 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);
result.fold(
@@ -380,4 +405,114 @@ class BookAppointmentsViewModel extends ChangeNotifier {
},
);
}
+
+ Future getRegionMappedProjectList() async {
+ //todo handle the case in the location is switch on
+ if(hospitalList != null && hospitalList!.registeredDoctorMap != null && hospitalList!.registeredDoctorMap!.isNotEmpty){
+ filteredHospitalList = hospitalList;
+ return;
+ }
+ isRegionListLoading = true;
+ notifyListeners();
+ final result = await bookAppointmentsRepo.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;
+ }
+
+ void setLoadSpecificClinic(bool status) {
+ shouldLoadSpecificClinic = status;
+ }
+
+ void setProjectID(String? mainProjectID) {
+ currentlySelectedHospitalFromRegionFlow = mainProjectID;
+ }
+
+ Future getRegionSelectedClinics() async{
+ final result = await bookAppointmentsRepo.getClinicsWithRespectToClinicId(currentlySelectedHospitalFromRegionFlow??"");
+
+ result.fold(
+ (failure) async => await errorHandlerService.handleError(failure: failure),
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
+ } else if (apiResponse.messageStatus == 1) {
+ clinicsList = apiResponse.data!;
+ isClinicsListLoading = false;
+ initializeFilteredList();
+ notifyListeners();
+ }
+ },
+ );
+ }
+ void resetFilterList(){
+ filteredHospitalList = hospitalList;
+ }
}
diff --git a/lib/features/insurance/insurance_repo.dart b/lib/features/insurance/insurance_repo.dart
index 874cc28..65f97ae 100644
--- a/lib/features/insurance/insurance_repo.dart
+++ b/lib/features/insurance/insurance_repo.dart
@@ -9,7 +9,7 @@ import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patien
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class InsuranceRepo {
- Future>>> getPatientInsuranceDetails({required String patientId});
+ Future>>> getPatientInsuranceDetails();
Future>>> getPatientInsuranceCardHistory({required String patientId});
@@ -23,7 +23,7 @@ class InsuranceRepoImp implements InsuranceRepo {
InsuranceRepoImp({required this.loggerService, required this.apiClient});
@override
- Future>>> getPatientInsuranceDetails({required String patientId}) async {
+ Future>>> getPatientInsuranceDetails() async {
Map mapDevice = {};
try {
diff --git a/lib/features/insurance/insurance_view_model.dart b/lib/features/insurance/insurance_view_model.dart
index 527c180..8319634 100644
--- a/lib/features/insurance/insurance_view_model.dart
+++ b/lib/features/insurance/insurance_view_model.dart
@@ -49,10 +49,13 @@ class InsuranceViewModel extends ChangeNotifier {
}
Future getPatientInsuranceDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async {
- final result = await insuranceRepo.getPatientInsuranceDetails(patientId: "1231755");
+ final result = await insuranceRepo.getPatientInsuranceDetails();
result.fold(
- (failure) async => await errorHandlerService.handleError(failure: failure),
+ // (failure) async => await errorHandlerService.handleError(failure: failure),
+ (failure) async {
+ isInsuranceLoading = false;
+ },
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
diff --git a/lib/features/medical_file/medical_file_repo.dart b/lib/features/medical_file/medical_file_repo.dart
index affe564..bbb4fcd 100644
--- a/lib/features/medical_file/medical_file_repo.dart
+++ b/lib/features/medical_file/medical_file_repo.dart
@@ -91,13 +91,13 @@ class MedicalFileRepoImp implements MedicalFileRepo {
// throw Exception("lab list is empty");
// }
- final vaccinesList = list.map((item) => PatientSickLeavesResponseModel.fromJson(item as Map)).toList().cast();
+ final sickLeavesList = list.map((item) => PatientSickLeavesResponseModel.fromJson(item as Map)).toList().cast();
apiResponse = GenericApiModel>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
- data: vaccinesList,
+ data: sickLeavesList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
diff --git a/lib/features/medical_file/models/patient_sickleave_response_model.dart b/lib/features/medical_file/models/patient_sickleave_response_model.dart
index b0381a8..3bf732c 100644
--- a/lib/features/medical_file/models/patient_sickleave_response_model.dart
+++ b/lib/features/medical_file/models/patient_sickleave_response_model.dart
@@ -14,7 +14,7 @@ class PatientSickLeavesResponseModel {
num? actualDoctorRate;
String? appointmentDate;
String? clinicName;
- double? decimalDoctorRate;
+ num? decimalDoctorRate;
String? doctorImageURL;
String? doctorName;
num? doctorRate;
@@ -32,7 +32,7 @@ class PatientSickLeavesResponseModel {
String? isInOutPatientDescriptionN;
bool? isLiveCareAppointment;
dynamic medicalDirectorApprovedStatus;
- int? noOfPatientsRate;
+ num? noOfPatientsRate;
dynamic patientName;
String? projectName;
String? qR;
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..a49e1c4
--- /dev/null
+++ b/lib/features/my_appointments/appointment_via_region_viewmodel.dart
@@ -0,0 +1,71 @@
+import 'package:flutter/foundation.dart' show ChangeNotifier;
+import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
+import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart';
+import 'package:hmg_patient_app_new/services/navigation_service.dart';
+import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
+
+enum AppointmentViaRegionState {
+ REGION_SELECTION,
+ TYPE_SELECTION,
+ HOSPITAL_SELECTION,
+ CLINIC_SELECTION,
+ DOCTOR_SELECTION
+}
+
+class AppointmentViaRegionViewmodel extends ChangeNotifier {
+ String? selectedRegionId;
+ String? selectedFacilityType;
+ PatientDoctorAppointmentList? selectedHospital;
+ final NavigationService navigationService;
+ AppointmentViaRegionState bottomSheetState =
+ AppointmentViaRegionState.REGION_SELECTION;
+
+ AppointmentViaRegionViewmodel({required this.navigationService});
+
+ void setSelectedRegionId(String? regionId) {
+ selectedRegionId = regionId;
+ notifyListeners();
+ }
+
+ void setFacility(String? facility) {
+ selectedFacilityType = facility;
+ notifyListeners();
+ }
+
+ void setBottomSheetState(AppointmentViaRegionState state) {
+ bottomSheetState = state;
+ notifyListeners();
+ }
+
+ void handleLastStep(){
+ navigationService.pop();
+ navigationService.push(FadePage(
+ page: SelectClinicPage(),
+ ),);
+ }
+
+ void handleBackPress() {
+ switch (bottomSheetState) {
+ case AppointmentViaRegionState.REGION_SELECTION:
+ break;
+ case AppointmentViaRegionState.TYPE_SELECTION:
+ setBottomSheetState(AppointmentViaRegionState.REGION_SELECTION);
+ setSelectedRegionId(null);
+ break;
+ case AppointmentViaRegionState.HOSPITAL_SELECTION:
+ setBottomSheetState(AppointmentViaRegionState.TYPE_SELECTION);
+ break;
+ default:
+ }
+ }
+
+ void flush() {
+ setSelectedRegionId(null);
+ setFacility(null);
+ setBottomSheetState(AppointmentViaRegionState.REGION_SELECTION);
+ }
+
+ void setHospitalModel(PatientDoctorAppointmentList? hospital) {
+ selectedHospital = hospital;
+ }
+}
diff --git a/lib/features/my_appointments/models/facility_selection.dart b/lib/features/my_appointments/models/facility_selection.dart
new file mode 100644
index 0000000..b1f12de
--- /dev/null
+++ b/lib/features/my_appointments/models/facility_selection.dart
@@ -0,0 +1,8 @@
+enum FacilitySelection{
+ALL('ALL'),
+HMG('hmg'),
+HMC('hmc');
+
+final String value;
+const FacilitySelection(this.value);
+}
\ No newline at end of file
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..e4745cb 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,8 @@ abstract class MyAppointmentsRepo {
Future>>> getPatientAppointmentsForTimeLine();
Future>>> getPatientDoctorsList();
+
+
}
class MyAppointmentsRepoImp implements MyAppointmentsRepo {
diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart
index c6d3499..779f2b6 100644
--- a/lib/features/my_appointments/my_appointments_view_model.dart
+++ b/lib/features/my_appointments/my_appointments_view_model.dart
@@ -1,14 +1,18 @@
import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_state.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/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 '../../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;
@@ -26,7 +30,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
PatientAppointmentShareResponseModel? patientAppointmentShareResponseModel;
- MyAppointmentsViewModel({required this.myAppointmentsRepo, required this.errorHandlerService});
+ MyAppointmentsViewModel({required this.myAppointmentsRepo, required this.errorHandlerService, required this.appState});
void onTabChange(int index) {
selectedTabIndex = index;
@@ -278,7 +282,10 @@ class MyAppointmentsViewModel extends ChangeNotifier {
final result = await myAppointmentsRepo.getPatientDoctorsList();
result.fold(
- (failure) async => await errorHandlerService.handleError(failure: failure),
+ // (failure) async => await errorHandlerService.handleError(failure: failure),
+ (failure) async {
+ isPatientMyDoctorsLoading = false;
+ },
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
diff --git a/lib/features/prescriptions/prescriptions_view_model.dart b/lib/features/prescriptions/prescriptions_view_model.dart
index 535d78f..f5f32a7 100644
--- a/lib/features/prescriptions/prescriptions_view_model.dart
+++ b/lib/features/prescriptions/prescriptions_view_model.dart
@@ -68,7 +68,10 @@ class PrescriptionsViewModel extends ChangeNotifier {
final result = await prescriptionsRepo.getPatientPrescriptionOrders(patientId: "1231755");
result.fold(
- (failure) async => await errorHandlerService.handleError(failure: failure),
+ // (failure) async => await errorHandlerService.handleError(failure: failure),
+ (failure) async {
+ isPrescriptionsOrdersLoading = false;
+ },
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart
index 9a341a2..85c4d6d 100644
--- a/lib/generated/locale_keys.g.dart
+++ b/lib/generated/locale_keys.g.dart
@@ -812,9 +812,18 @@ abstract class LocaleKeys {
static const notNow = 'notNow';
static const pendingActivation = 'pendingActivation';
static const awaitingApproval = 'awaitingApproval';
+ static const news = 'news';
static const ready = 'ready';
static const enterValidNationalId = 'enterValidNationalId';
static const enterValidPhoneNumber = 'enterValidPhoneNumber';
+ 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';
+ static const iAcceptThe = 'iAcceptThe';
+ static const personalDetailsVerification = 'personalDetailsVerification';
- static const news = 'news';
}
diff --git a/lib/main.dart b/lib/main.dart
index e727d19..c75cf91 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(
@@ -143,6 +145,9 @@ void main() async {
localAuthService: getIt(),
),
),
+ ChangeNotifierProvider(
+ create: (_) =>
+ AppointmentViaRegionViewmodel(navigationService: getIt()))
], child: MyApp()),
),
);
diff --git a/lib/presentation/appointments/widgets/faculity_selection/facility_selection_item.dart b/lib/presentation/appointments/widgets/faculity_selection/facility_selection_item.dart
new file mode 100644
index 0000000..1660146
--- /dev/null
+++ b/lib/presentation/appointments/widgets/faculity_selection/facility_selection_item.dart
@@ -0,0 +1,88 @@
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+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/theme/colors.dart' show AppColors;
+import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+
+class FacilitySelectionItem extends StatelessWidget {
+ final String svgPath;
+ final String title;
+ final String subTitle;
+
+ const FacilitySelectionItem(
+ {super.key,
+ required this.svgPath,
+ required this.subTitle,
+ required this.title});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: false,
+ ),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Utils.buildSvgWithAssets(
+ icon: svgPath,
+ width: 32,
+ height: 32,
+ fit: BoxFit.contain,
+ ),
+ SizedBox(height: 16,),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ info,
+ Utils.buildSvgWithAssets(
+ icon: AppAssets.forward_arrow_icon,
+ iconColor: AppColors.blackColor,
+ width: 18,
+ height: 13,
+ fit: BoxFit.contain,
+ ),
+ ],
+ )
+ ],
+ ).paddingAll(16.h),
+ );
+ }
+
+
+ Widget get info => Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Align(
+ alignment: Alignment.centerLeft,
+ child: Text(
+ title,
+ style: TextStyle(
+ fontSize: 16.h,
+ fontWeight: FontWeight.w600,
+ color: AppColors.blackColor,
+ ),
+ ),
+ ),
+ Align(
+ alignment: Alignment.centerLeft,
+ child: Text(
+ subTitle,
+ style: TextStyle(
+ fontSize: 12.h,
+ fontWeight: FontWeight.w500,
+ color: AppColors.greyTextColor,
+ ),
+ ),
+ ),
+ ],
+ );
+}
diff --git a/lib/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart b/lib/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart
new file mode 100644
index 0000000..e1ff677
--- /dev/null
+++ b/lib/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart
@@ -0,0 +1,80 @@
+import 'package:easy_localization/easy_localization.dart'
+ show tr, StringTranslateExtension;
+import 'package:flutter/cupertino.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/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'
+ show MyAppointmentsViewModel;
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_selection_item.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:provider/provider.dart' show Provider;
+
+class FacilityTypeSelectionWidget extends StatelessWidget {
+ late BookAppointmentsViewModel bookAppointmentViewModel;
+ late AppointmentViaRegionViewmodel regionalViewModel;
+ final String selectedRegion;
+
+ FacilityTypeSelectionWidget({super.key, required this.selectedRegion});
+
+ @override
+ Widget build(BuildContext context) {
+ bookAppointmentViewModel = Provider.of(context);
+ regionalViewModel = Provider.of(context);
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ LocaleKeys.selectFacility.tr(),
+ style: TextStyle(
+ fontSize: 21,
+ fontWeight: FontWeight.w600,
+ color: AppColors.blackColor,
+ ),
+ ),
+ Text(
+ LocaleKeys.selectFacilitiesSubTitle,
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ color: AppColors.greyTextColor,
+ ),
+ ),
+ SizedBox(height: 24.h),
+ FacilitySelectionItem(
+ svgPath: AppAssets.hmg,
+ title: "HMG".needTranslation,
+ subTitle: LocaleKeys.hospitalsWithCount.tr(namedArgs: {
+ 'count':
+ "${bookAppointmentViewModel.hospitalList?.registeredDoctorMap?[selectedRegion]?.hmgSize ?? 0}"
+ }),
+ ).onPress(
+ () {
+ regionalViewModel.setFacility(FacilitySelection.HMG.name);
+ regionalViewModel.setBottomSheetState(
+ AppointmentViaRegionState.HOSPITAL_SELECTION);
+ },
+ ),
+ SizedBox(height: 16.h),
+ FacilitySelectionItem(
+ svgPath: AppAssets.hmc,
+ title: "HMC".needTranslation,
+ subTitle: LocaleKeys.medicalCentersWithCount.tr(namedArgs: {
+ 'count':
+ "${bookAppointmentViewModel.hospitalList?.registeredDoctorMap?[selectedRegion]?.hmcSize ?? 0}"
+ })).onPress(
+ () {
+ regionalViewModel.setFacility(FacilitySelection.HMC.name);
+ regionalViewModel.setBottomSheetState(
+ AppointmentViaRegionState.HOSPITAL_SELECTION);
+ },
+ ),
+ ],
+ );
+ }
+}
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
new file mode 100644
index 0000000..71cc07d
--- /dev/null
+++ b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart
@@ -0,0 +1,118 @@
+import 'package:easy_localization/easy_localization.dart'
+ show tr, 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/presentation/lab/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';
+
+class HospitalBottomSheetBody extends StatelessWidget {
+ late BookAppointmentsViewModel appointmentsViewModel;
+ late AppointmentViaRegionViewmodel regionalViewModel;
+ final TextEditingController searchText = TextEditingController();
+
+ HospitalBottomSheetBody({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ appointmentsViewModel = Provider.of(context);
+ regionalViewModel = Provider.of(context);
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ LocaleKeys.selectHospital.tr(),
+ style: TextStyle(
+ fontSize: 21,
+ fontWeight: FontWeight.w600,
+ color: AppColors.blackColor,
+ ),
+ ),
+ Text(
+ LocaleKeys.selectHospitalSubTitle.tr(),
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ color: AppColors.greyTextColor,
+ ),
+ ),
+ SizedBox(height: 16.h),
+ TextInputWidget(
+ labelText: LocaleKeys.search.tr(),
+ hintText: "Search Hospital".tr(),
+ controller: searchText,
+ onChange: (value) {
+ appointmentsViewModel.filterHospitalListByString(value, regionalViewModel.selectedRegionId , regionalViewModel.selectedFacilityType ==
+ FacilitySelection.HMG.name);
+ },
+ 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: 24.h),
+ // TypeSelectionWidget(
+ // hmcCount: "0",
+ // hmgCount: "0",
+ // ),
+ // SizedBox(height: 21.h),
+ 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];
+ return HospitalListItem(
+ hospitalData: hospital,
+ isLocationEnabled: appointmentsViewModel.getLocationStatus(),
+ ).onPress(() {
+ regionalViewModel.setHospitalModel(hospital);
+ regionalViewModel.setBottomSheetState(AppointmentViaRegionState.CLINIC_SELECTION);
+ regionalViewModel.handleLastStep();
+ });},
+ separatorBuilder: (_, __) => SizedBox(
+ height: 16.h,
+ ),
+ 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/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart
new file mode 100644
index 0000000..41fb924
--- /dev/null
+++ b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart
@@ -0,0 +1,113 @@
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_export.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.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/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/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
+
+class HospitalListItem extends StatelessWidget {
+ final PatientDoctorAppointmentList? hospitalData;
+ final bool isLocationEnabled;
+
+ late AppState appState;
+
+ HospitalListItem(
+ {super.key, required this.hospitalData, required this.isLocationEnabled});
+
+ @override
+ Widget build(BuildContext context) {
+ appState = getIt.get();
+ return DecoratedBox(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: false,
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ spacing: 8.h,
+ children: [hospitalName, distanceInfo],
+ ),
+ ),
+ Utils.buildSvgWithAssets(
+ icon: AppAssets.forward_arrow_icon,
+ iconColor: AppColors.blackColor,
+ width: 18,
+ height: 13,
+ fit: BoxFit.contain,
+ ),
+ ],
+ ).paddingSymmetrical(16.h, 16.h),
+ );
+ }
+
+ Widget get hospitalName => Row(
+ children: [
+ Utils.buildSvgWithAssets(
+ icon: (hospitalData?.isHMC == true) ? AppAssets.hmc : AppAssets.hmg,
+ ).paddingOnly(right: 10),
+ Expanded(
+ child: Text(
+ hospitalData?.filterName ?? "",
+ style: TextStyle(
+ fontWeight: FontWeight.w600,
+ fontSize: 16,
+ color: AppColors.blackColor,
+ ),
+ ),
+ )
+ ],
+ );
+
+ Widget get distanceInfo => Row(
+
+ children: [
+ Visibility(
+ visible: (hospitalData?.distanceInKMs != "0"),
+ child:
+
+
+ AppCustomChipWidget(
+ labelText:
+ "${hospitalData?.distanceInKMs ?? ""} km".needTranslation,
+ deleteIcon: AppAssets.location_red,
+ deleteIconSize: Size(9, 12),
+ backgroundColor: AppColors.secondaryLightRedColor,
+ textColor: AppColors.errorColor,
+ ),
+
+
+ ),
+ Visibility(
+ visible: (hospitalData?.distanceInKMs == "0"),
+ child: Row(
+ children: [
+ AppCustomChipWidget(
+ labelText: "Distance not available".needTranslation,
+ textColor: AppColors.blackColor,
+ ),
+ SizedBox(width: 8.h,)
+
+ ],
+ )),
+ Visibility(
+ visible: !isLocationEnabled,
+ child: AppCustomChipWidget(
+ labelText: "Location turned off".needTranslation,
+ deleteIcon: AppAssets.location_unavailable,
+ deleteIconSize: Size(9, 12),
+ textColor: AppColors.blackColor,
+ )),
+ ],
+ );
+}
diff --git a/lib/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart b/lib/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart
new file mode 100644
index 0000000..b023f7a
--- /dev/null
+++ b/lib/presentation/appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart
@@ -0,0 +1,94 @@
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.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/models/facility_selection.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
+import 'package:provider/provider.dart' show Consumer;
+
+class TypeSelectionWidget extends StatelessWidget {
+ final String hmcCount;
+ final String hmgCount;
+
+ const TypeSelectionWidget(
+ {super.key, required this.hmcCount, required this.hmgCount});
+
+ @override
+ Widget build(BuildContext context) {
+ return Consumer(
+ builder: (_, data, __) => Row(
+ spacing: 8,
+ mainAxisSize: MainAxisSize.max,
+ children: [
+ AppCustomChipWidget(
+ labelText: "All Facilities".needTranslation,
+ shape: RoundedRectangleBorder(
+ side: BorderSide(
+ color: data.currentlySelectedFacility == FacilitySelection.ALL
+ ? AppColors.errorColor
+ : AppColors.chipBorderColorOpacity20,
+ width: 1,
+ ),
+ borderRadius: BorderRadius.circular(10)),
+ backgroundColor:
+ data.currentlySelectedFacility == FacilitySelection.ALL
+ ? AppColors.secondaryLightRedColor
+ : AppColors.whiteColor,
+ textColor: data.currentlySelectedFacility == FacilitySelection.ALL
+ ? AppColors.errorColor
+ : AppColors.blackColor,
+ ).onPress((){
+ data.setSelectedFacility(FacilitySelection.ALL);
+ }),
+ AppCustomChipWidget(
+ icon: AppAssets.hmg,
+ iconHasColor: false,
+ labelText: "Hospitals".needTranslation,
+ shape: RoundedRectangleBorder(
+ side: BorderSide(
+ color: data.currentlySelectedFacility == FacilitySelection.HMG
+ ? AppColors.errorColor
+ : AppColors.chipBorderColorOpacity20,
+ width: 1,
+ ),
+ borderRadius: BorderRadius.circular(10)),
+ backgroundColor:
+ data.currentlySelectedFacility == FacilitySelection.HMG
+ ? AppColors.secondaryLightRedColor
+ : AppColors.whiteColor,
+ textColor: data.currentlySelectedFacility == FacilitySelection.HMG
+ ? AppColors.errorColor
+ : AppColors.blackColor,
+ ).onPress((){
+ data.setSelectedFacility(FacilitySelection.HMG);
+ }),
+ AppCustomChipWidget(
+ icon: AppAssets.hmc,
+ iconHasColor: false,
+ labelText: "Medical Centers".needTranslation,
+ shape: RoundedRectangleBorder(
+ side: BorderSide(
+ color: data.currentlySelectedFacility == FacilitySelection.HMC
+ ? AppColors.errorColor
+ : AppColors.chipBorderColorOpacity20,
+ width: 1,
+ ),
+ borderRadius: BorderRadius.circular(10)),
+ backgroundColor:
+ data.currentlySelectedFacility == FacilitySelection.HMC
+ ? AppColors.secondaryLightRedColor
+ : AppColors.whiteColor,
+ textColor: data.currentlySelectedFacility == FacilitySelection.HMC
+ ? AppColors.errorColor
+ : AppColors.blackColor,
+ ).onPress((){
+ data.setSelectedFacility(FacilitySelection.HMC);
+ }),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart
new file mode 100644
index 0000000..ab8e21f
--- /dev/null
+++ b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart
@@ -0,0 +1,114 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+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/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors;
+import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
+
+class RegionListItem extends StatelessWidget {
+ final String title;
+ final String hmcCount;
+ final String hmgCount;
+ final String subTitle;
+
+ const RegionListItem(
+ {super.key,
+ required this.title,
+ required this.subTitle,
+ required this.hmcCount,
+ required this.hmgCount});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: false,
+ ),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ header,
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ spacing: 8.h,
+ children: [
+ placesCountItem(
+ AppAssets.hmg, hmgCount, " ${LocaleKeys.hospital.tr()}"),
+ placesCountItem(AppAssets.hmc, hmcCount,
+ " ${LocaleKeys.medicalCenters.tr()}"),
+ ],
+ ),
+ Utils.buildSvgWithAssets(
+ icon: AppAssets.forward_arrow_icon,
+ iconColor: AppColors.blackColor,
+ width: 18,
+ height: 13,
+ fit: BoxFit.contain,
+ ),
+ ],
+ )
+ ],
+ ).paddingAll(16.h),
+ );
+ }
+
+ Widget placesCountItem(String svgPath, String count, String title) {
+ return AppCustomChipWidget(
+ iconSize: 14,
+ icon: svgPath,
+ iconHasColor: false,
+ richText: RichText(
+ text: TextSpan(
+ text: count,
+ style: TextStyle(
+ fontSize: 12.h,
+ fontWeight: FontWeight.w700,
+ color: AppColors.blackColor),
+ children: [
+ TextSpan(
+ text: title,
+ style: TextStyle(
+ fontSize: 12.h,
+ fontWeight: FontWeight.w500,
+ color: AppColors.blackColor))
+ ])),
+ );
+ }
+
+ Widget get header => Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Align(
+ alignment: Alignment.centerLeft,
+ child: Text(
+ title,
+ style: TextStyle(
+ fontSize: 16.h,
+ fontWeight: FontWeight.w600,
+ color: AppColors.blackColor,
+ ),
+ ),
+ ),
+ Align(
+ alignment: Alignment.centerLeft,
+ child: Text(
+ subTitle,
+ style: TextStyle(
+ fontSize: 12.h,
+ fontWeight: FontWeight.w500,
+ color: AppColors.greyTextColor,
+ ),
+ ),
+ ),
+ ],
+ );
+}
diff --git a/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart
new file mode 100644
index 0000000..180185f
--- /dev/null
+++ b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart
@@ -0,0 +1,87 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart' show Utils;
+import 'package:hmg_patient_app_new/extensions/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/my_appointments_view_model.dart'
+ show MyAppointmentsViewModel;
+import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart'
+ show RegionListItem;
+import 'package:provider/provider.dart';
+
+class RegionBottomSheetBody extends StatefulWidget {
+
+ const RegionBottomSheetBody({super.key});
+
+ @override
+ State createState() => _RegionBottomSheetBodyState();
+}
+
+class _RegionBottomSheetBodyState extends State {
+ late BookAppointmentsViewModel myAppointmentsViewModel;
+ late AppointmentViaRegionViewmodel regionalViewModel;
+
+ @override
+ void initState() {
+ scheduleMicrotask(() {
+ myAppointmentsViewModel.getRegionMappedProjectList();
+ });
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ myAppointmentsViewModel = Provider.of(context);
+ regionalViewModel = Provider.of(context);
+ return Consumer(
+ builder: (context, myAppointmentsVM, child) {
+ if (myAppointmentsVM.isRegionListLoading) {
+ return Container(
+ height: MediaQuery.of(context).size.height * 0.3,
+ decoration: const BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
+ ),
+ child: Center(
+ child: Utils.getLoadingWidget(),
+ ),
+ );
+ } else {
+ return SizedBox(
+ height: MediaQuery.of(context).size.height * 0.5,
+ child: ListView.separated(
+ itemCount:
+ myAppointmentsVM.hospitalList?.registeredDoctorMap?.length ??
+ 0,
+ separatorBuilder: (_, __) {
+ return SizedBox(
+ height: 16.h,
+ );
+ },
+ itemBuilder: (_, index) {
+ String key = myAppointmentsVM
+ .hospitalList?.registeredDoctorMap?.keys
+ .toList()[index] ??
+ '';
+ return RegionListItem(
+ title: key,
+ subTitle: "",
+ hmcCount:
+ "${myAppointmentsVM.hospitalList?.registeredDoctorMap?[key]?.hmcSize ?? 0}",
+ hmgCount:
+ "${myAppointmentsVM.hospitalList?.registeredDoctorMap?[key]?.hmgSize ?? 0}",
+ ).onPress(() {
+ regionalViewModel.setSelectedRegionId(key);
+ regionalViewModel.setBottomSheetState(AppointmentViaRegionState.TYPE_SELECTION);
+ });
+ },
+ ),
+ );
+ }
+ },
+ );
+ }
+}
diff --git a/lib/presentation/authentication/login.dart b/lib/presentation/authentication/login.dart
index 2a8c744..f3d7664 100644
--- a/lib/presentation/authentication/login.dart
+++ b/lib/presentation/authentication/login.dart
@@ -4,6 +4,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/gestures.dart';
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/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
@@ -153,6 +155,7 @@ class LoginScreenState extends State {
required TextEditingController? phoneNumberController,
required AuthenticationViewModel authViewModel,
}) {
+ AppState appState = getIt();
context.showBottomSheet(
isScrollControlled: true,
isDismissible: false,
@@ -180,6 +183,8 @@ class LoginScreenState extends State {
onOkPress: () {
Navigator.of(context).pop();
})) {
+ Navigator.of(context).pop();
+ appState.setSelectDeviceByImeiRespModelElement(null);
await authViewModel.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms);
}
},
@@ -209,6 +214,8 @@ class LoginScreenState extends State {
onOkPress: () {
Navigator.of(context).pop();
})) {
+ Navigator.of(context).pop();
+ appState.setSelectDeviceByImeiRespModelElement(null);
await authViewModel.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp);
}
},
diff --git a/lib/presentation/authentication/register.dart b/lib/presentation/authentication/register.dart
index 8ec988d..5150b0c 100644
--- a/lib/presentation/authentication/register.dart
+++ b/lib/presentation/authentication/register.dart
@@ -2,6 +2,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/gestures.dart';
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/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
@@ -152,12 +154,36 @@ class _RegisterNew extends State {
},
),
SizedBox(width: 12.h),
- Expanded(
- child: Text(
- LocaleKeys.iAcceptTermsConditions.tr(),
- style: context.dynamicTextStyle(fontSize: 14.fSize, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)),
- ),
+ Row(
+ children: [
+ Text(
+ LocaleKeys.iAcceptThe.tr(),
+ style: context.dynamicTextStyle(fontSize: 14.fSize, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)),
+ ),
+ GestureDetector(
+ onTap: () {
+ // Navigate to terms and conditions page
+ Navigator.of(context).pushNamed('/terms');
+ },
+ child: Text(
+ LocaleKeys.termsConditoins.tr(),
+ style: context.dynamicTextStyle(
+ fontSize: 14.fSize,
+ fontWeight: FontWeight.w500,
+ color: AppColors.primaryRedColor,
+ decoration: TextDecoration.underline,
+ decorationColor: AppColors.primaryRedBorderColor,
+ ),
+ ),
+ ),
+ ],
),
+ // Expanded(
+ // child: Text(
+ // LocaleKeys.iAcceptTermsConditions.tr().split("the").first,
+ // style: context.dynamicTextStyle(fontSize: 14.fSize, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)),
+ // ),
+ // ),
],
),
),
@@ -224,6 +250,7 @@ class _RegisterNew extends State {
}
void showRegisterModel({required BuildContext context, required AuthenticationViewModel authVM}) {
+ AppState appState = getIt.get();
showModalBottomSheet(
context: context,
isScrollControlled: true,
@@ -235,7 +262,7 @@ class _RegisterNew extends State {
child: SingleChildScrollView(
child: GenericBottomSheet(
countryCode: authVM.selectedCountrySignup.countryCode,
- initialPhoneNumber: "",
+ initialPhoneNumber: authVM.phoneNumberController.text,
textController: authVM.phoneNumberController,
isEnableCountryDropdown: false,
onCountryChange: authVM.onCountryChange,
@@ -256,6 +283,7 @@ class _RegisterNew extends State {
Navigator.of(context).pop();
},
)) {
+ appState.setSelectDeviceByImeiRespModelElement(null);
await authVM.onRegistrationStart(otpTypeEnum: OTPTypeEnum.sms);
}
},
@@ -280,15 +308,14 @@ class _RegisterNew extends State {
child: CustomButton(
text: LocaleKeys.sendOTPWHATSAPP.tr(),
onPressed: () async {
- // Dismiss keyboard before validation
FocusScope.of(context).unfocus();
-
if (ValidationUtils.isValidatePhone(
phoneNumber: authVM.phoneNumberController.text,
onOkPress: () {
Navigator.of(context).pop();
},
)) {
+ appState.setSelectDeviceByImeiRespModelElement(null);
await authVM.onRegistrationStart(otpTypeEnum: OTPTypeEnum.whatsapp);
}
},
@@ -296,6 +323,7 @@ class _RegisterNew extends State {
borderColor: AppColors.borderOnlyColor,
textColor: AppColors.textColor,
icon: AppAssets.whatsapp,
+ iconColor: null,
),
),
],
diff --git a/lib/presentation/authentication/register_step2.dart b/lib/presentation/authentication/register_step2.dart
index f31909f..23ea7ae 100644
--- a/lib/presentation/authentication/register_step2.dart
+++ b/lib/presentation/authentication/register_step2.dart
@@ -44,20 +44,24 @@ class _RegisterNew extends State {
Widget build(BuildContext context) {
AppState appState = getIt.get();
return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
appBar: CustomAppBar(
onBackPressed: () {
Navigator.of(context).pop();
// authVM!.clearDefaultInputValues();
+ authVM!.clearEmailInput();
},
onLanguageChanged: (lang) {},
hideLogoAndLang: true,
),
body: SingleChildScrollView(
reverse: false,
- padding: EdgeInsets.only(left: 24.h, right: 24.h, top: 24.h),
+ padding: EdgeInsets.only(left: 24.h, right: 24.h, top: 0.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
+ LocaleKeys.personalDetailsVerification.tr().toText26(color: AppColors.textColor, weight: FontWeight.w600, letterSpacing: -2),
+ SizedBox(height: 24.h),
Directionality(
textDirection: Directionality.of(context),
child: Container(
@@ -66,18 +70,20 @@ class _RegisterNew extends State {
child: Column(
children: [
TextInputWidget(
- labelText: authVM!.isUserFromUAE() ? LocaleKeys.fullName.tr() : LocaleKeys.name.tr(),
- hintText: authVM!.isUserFromUAE() ? LocaleKeys.enterNameHere.tr() : ("${appState.getNHICUserData.firstNameEn!.toUpperCase()} ${appState.getNHICUserData.lastNameEn!.toUpperCase()}"),
- controller: authVM!.isUserFromUAE() ? authVM!.nameController : null,
- isEnable: true,
- prefix: null,
- isAllowRadius: false,
- isBorderAllowed: false,
- keyboardType: TextInputType.text,
- isAllowLeadingIcon: true,
- isReadOnly: authVM!.isUserFromUAE() ? false : true,
- leadingIcon: AppAssets.user_circle)
- .paddingSymmetrical(0.h, 16.h),
+ labelText: authVM!.isUserFromUAE() ? LocaleKeys.fullName.tr() : LocaleKeys.name.tr(),
+ hintText:
+ authVM!.isUserFromUAE() ? LocaleKeys.enterNameHere.tr() : ("${appState.getNHICUserData.firstNameEn!.toUpperCase()} ${appState.getNHICUserData.lastNameEn!.toUpperCase()}"),
+ controller: authVM!.isUserFromUAE() ? authVM!.nameController : null,
+ isEnable: true,
+ prefix: null,
+ isAllowRadius: false,
+ isBorderAllowed: false,
+ keyboardType: TextInputType.text,
+ isAllowLeadingIcon: true,
+ isReadOnly: authVM!.isUserFromUAE() ? false : true,
+ leadingIcon: AppAssets.user_circle,
+ labelColor: AppColors.textColor,
+ ).paddingSymmetrical(0.h, 16.h),
Divider(height: 1, color: AppColors.greyColor),
TextInputWidget(
labelText: LocaleKeys.nationalIdNumber.tr(),
@@ -89,6 +95,7 @@ class _RegisterNew extends State {
isBorderAllowed: false,
isAllowLeadingIcon: true,
isReadOnly: true,
+ labelColor: AppColors.textColor,
leadingIcon: AppAssets.student_card)
.paddingSymmetrical(0.h, 16.h),
Divider(height: 1, color: AppColors.greyColor),
@@ -108,6 +115,7 @@ class _RegisterNew extends State {
isBorderAllowed: false,
hasSelectionCustomIcon: true,
isAllowRadius: false,
+ labelColor: AppColors.textColor,
padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0),
selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.user_full,
@@ -124,6 +132,7 @@ class _RegisterNew extends State {
isAllowLeadingIcon: true,
isReadOnly: authVM!.isUserFromUAE() ? false : true,
leadingIcon: AppAssets.user_full,
+ labelColor: AppColors.textColor,
onChange: (value) {})
.paddingSymmetrical(0.h, 16.h),
Divider(height: 1, color: AppColors.greyColor),
@@ -143,6 +152,7 @@ class _RegisterNew extends State {
isBorderAllowed: false,
hasSelectionCustomIcon: true,
isAllowRadius: false,
+ labelColor: AppColors.textColor,
padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0),
selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.smart_phone,
@@ -160,6 +170,7 @@ class _RegisterNew extends State {
isBorderAllowed: false,
isAllowLeadingIcon: true,
isReadOnly: true,
+ labelColor: AppColors.textColor,
leadingIcon: AppAssets.smart_phone,
onChange: (value) {})
.paddingSymmetrical(0.h, 16.h),
@@ -190,6 +201,7 @@ class _RegisterNew extends State {
onChange: authVM.onUAEUserCountrySelection,
isBorderAllowed: false,
hasSelectionCustomIcon: true,
+ labelColor: AppColors.textColor,
isAllowRadius: false,
padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0),
selectionCustomIcon: AppAssets.arrow_down,
@@ -208,6 +220,7 @@ class _RegisterNew extends State {
isBorderAllowed: false,
isAllowLeadingIcon: true,
isReadOnly: true,
+ labelColor: AppColors.textColor,
leadingIcon: AppAssets.globe,
onChange: (value) {})
.paddingSymmetrical(0.h, 16.h),
@@ -224,6 +237,7 @@ class _RegisterNew extends State {
isAllowRadius: false,
isBorderAllowed: false,
isAllowLeadingIcon: true,
+ labelColor: AppColors.textColor,
isReadOnly: true,
leadingIcon: AppAssets.call)
.paddingSymmetrical(0.h, 16.h),
@@ -240,6 +254,7 @@ class _RegisterNew extends State {
isBorderAllowed: false,
isAllowLeadingIcon: true,
isReadOnly: true,
+ labelColor: AppColors.textColor,
leadingIcon: AppAssets.birthday_cake,
selectionType: null,
).paddingSymmetrical(0.h, 16.h),
@@ -269,12 +284,12 @@ class _RegisterNew extends State {
),
Expanded(
child: CustomButton(
- backgroundColor: AppColors.lightGreenColor,
- borderColor: AppColors.lightGreenColor,
- textColor: AppColors.textGreenColor,
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedColor,
+ textColor: AppColors.whiteColor,
text: LocaleKeys.confirm.tr(),
icon: AppAssets.confirm,
- iconColor: AppColors.textGreenColor,
+ iconColor: AppColors.whiteColor,
onPressed: () {
if (appState.getUserRegistrationPayload.zipCode != CountryEnum.saudiArabia.countryCode) {
if (ValidationUtils.validateUaeRegistration(
diff --git a/lib/presentation/authentication/saved_login_screen.dart b/lib/presentation/authentication/saved_login_screen.dart
index 0a6138c..073ff8b 100644
--- a/lib/presentation/authentication/saved_login_screen.dart
+++ b/lib/presentation/authentication/saved_login_screen.dart
@@ -12,6 +12,8 @@ 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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
+import 'package:hmg_patient_app_new/presentation/home/landing_page.dart';
+import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart';
import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart';
@@ -164,17 +166,16 @@ class _SavedLogin extends State {
Padding(
padding: EdgeInsets.only(bottom: 10.h),
child: CustomButton(
- text: LocaleKeys.sendOTPSMS.tr(),
- onPressed: () {
- Navigator.of(context).pop();
- loginType = LoginTypeEnum.sms;
- authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms);
- },
- backgroundColor: AppColors.primaryRedColor,
- borderColor: AppColors.primaryRedBorderColor,
- textColor: AppColors.whiteColor,
- icon: AppAssets.sms,
- ),
+ text: LocaleKeys.sendOTPSMS.tr(),
+ onPressed: () {
+ Navigator.of(context).pop();
+ loginType = LoginTypeEnum.sms;
+ authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms);
+ },
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedColor,
+ textColor: AppColors.whiteColor,
+ icon: AppAssets.sms),
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
@@ -247,13 +248,13 @@ class _SavedLogin extends State {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
- child: Container(
+ child: SizedBox(
height: 56,
child: CustomButton(
text: LocaleKeys.guest.tr(),
onPressed: () {
Navigator.of(context).pushReplacement(
- MaterialPageRoute(builder: (BuildContext context) => LoginScreen()),
+ MaterialPageRoute(builder: (BuildContext context) => LandingNavigation()),
);
},
backgroundColor: Color(0xffFEE9EA),
@@ -277,7 +278,7 @@ class _SavedLogin extends State {
text: LocaleKeys.switchAccount.tr(),
onPressed: () async {
await authVm.clearDefaultInputValues();
- Navigator.of(context).pushReplacement(
+ Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) => LoginScreen()),
);
},
diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart
index 9533d6c..482d864 100644
--- a/lib/presentation/book_appointment/book_appointment_page.dart
+++ b/lib/presentation/book_appointment/book_appointment_page.dart
@@ -10,15 +10,24 @@ 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/search_doctor_by_name.dart';
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});
@@ -28,6 +37,7 @@ class BookAppointmentPage extends StatefulWidget {
class _BookAppointmentPageState extends State {
late AppState appState;
+ late AppointmentViaRegionViewmodel regionalViewModel;
late BookAppointmentsViewModel bookAppointmentsViewModel;
@override
@@ -42,6 +52,8 @@ class _BookAppointmentPageState extends State {
Widget build(BuildContext context) {
bookAppointmentsViewModel = Provider.of(context, listen: false);
appState = getIt.get();
+ regionalViewModel =
+ Provider.of(context, listen: true);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
@@ -110,6 +122,8 @@ class _BookAppointmentPageState extends State {
],
).onPress(() {
bookAppointmentsViewModel.setIsClinicsListLoading(true);
+ bookAppointmentsViewModel.setLoadSpecificClinic(false);
+ bookAppointmentsViewModel.setProjectID(null);
Navigator.of(context).push(
FadePage(
page: SelectClinicPage(),
@@ -166,7 +180,9 @@ class _BookAppointmentPageState extends State {
),
Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h),
],
- ).onPress(() {}),
+ ).onPress(() {
+ openRegionListBottomSheet(context);
+ }),
],
),
),
@@ -178,4 +194,54 @@ class _BookAppointmentPageState extends State {
}
return Container();
}
+
+ void openRegionListBottomSheet(BuildContext context) {
+ regionalViewModel.flush();
+ // AppointmentViaRegionViewmodel? viewmodel = null;
+ showCommonBottomSheetWithoutHeight(context,
+ title: "",
+ titleWidget: Consumer(
+ builder: (_, data, __) => getTitle(data)),
+ isDismissible: false,
+ child: Consumer(builder: (_, data, __) {
+ return getRegionalSelectionWidget(data);
+ }), callBackFunc: () {
+ });
+ }
+
+ Widget getRegionalSelectionWidget(AppointmentViaRegionViewmodel data) {
+ if (data.bottomSheetState == AppointmentViaRegionState.REGION_SELECTION) {
+ return RegionBottomSheetBody();
+ }
+ if(data.bottomSheetState == AppointmentViaRegionState.TYPE_SELECTION){
+ bookAppointmentsViewModel.resetFilterList();
+ return FacilityTypeSelectionWidget(selectedRegion: data.selectedRegionId??"",);
+ }
+ if (data.bottomSheetState == AppointmentViaRegionState.HOSPITAL_SELECTION) {
+ return HospitalBottomSheetBody();
+ }
+ if (data.bottomSheetState == AppointmentViaRegionState.CLINIC_SELECTION) {
+ // Navigator.of(context).pop();
+ bookAppointmentsViewModel.setIsClinicsListLoading(true);
+ bookAppointmentsViewModel.setLoadSpecificClinic(true);
+ bookAppointmentsViewModel.setProjectID(regionalViewModel.selectedHospital?.hospitalList.first?.mainProjectID.toString());
+
+ }
+ 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/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart
index 871a97c..acaf622 100644
--- a/lib/presentation/medical_file/medical_file_page.dart
+++ b/lib/presentation/medical_file/medical_file_page.dart
@@ -70,6 +70,7 @@ class _MedicalFilePageState extends State {
insuranceViewModel.initInsuranceProvider();
medicalFileViewModel.setIsPatientSickLeaveListLoading(true);
medicalFileViewModel.getPatientSickLeaveList();
+ medicalFileViewModel.onTabChanged(0);
}
});
super.initState();
@@ -647,9 +648,14 @@ class _MedicalFilePageState extends State {
Consumer(builder: (context, insuranceVM, child) {
return insuranceVM.isInsuranceLoading
? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0.0)
- : PatientInsuranceCard(
- insuranceCardDetailsModel: insuranceVM.patientInsuranceList.first,
- isInsuranceExpired: DateTime.now().isAfter(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo)));
+ : insuranceVM.patientInsuranceList.isNotEmpty
+ ? PatientInsuranceCard(
+ insuranceCardDetailsModel: insuranceVM.patientInsuranceList.first,
+ isInsuranceExpired: DateTime.now().isAfter(
+ DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
+ ),
+ )
+ : SizedBox.shrink();
}),
SizedBox(height: 10.h),
GridView(
diff --git a/lib/services/navigation_service.dart b/lib/services/navigation_service.dart
index 9835bba..420c4cf 100644
--- a/lib/services/navigation_service.dart
+++ b/lib/services/navigation_service.dart
@@ -22,19 +22,9 @@ class NavigationService {
navigatorKey.currentState?.pushReplacementNamed(routeName);
}
- Future pushToOtpScreen({
- required String phoneNumber,
- required Function(int code) checkActivationCode,
- required Function(String phoneNumber) onResendOTPPressed,
- }) {
+ Future pushToOtpScreen({required String phoneNumber, required Function(int code) checkActivationCode, required Function(String phoneNumber) onResendOTPPressed}) {
return navigatorKey.currentState!.push(
- MaterialPageRoute(
- builder: (_) => OTPVerificationScreen(
- phoneNumber: phoneNumber,
- checkActivationCode: checkActivationCode,
- onResendOTPPressed: onResendOTPPressed,
- ),
- ),
+ MaterialPageRoute(builder: (_) => OTPVerificationScreen(phoneNumber: phoneNumber, checkActivationCode: checkActivationCode, onResendOTPPressed: onResendOTPPressed)),
);
}
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/chip/app_custom_chip_widget.dart b/lib/widgets/chip/app_custom_chip_widget.dart
index 203cb86..85c13f2 100644
--- a/lib/widgets/chip/app_custom_chip_widget.dart
+++ b/lib/widgets/chip/app_custom_chip_widget.dart
@@ -10,20 +10,34 @@ import 'package:smooth_corner/smooth_corner.dart';
class AppCustomChipWidget extends StatelessWidget {
AppCustomChipWidget({
super.key,
- required this.labelText,
+ this.labelText,
this.textColor = AppColors.textColor,
this.backgroundColor = AppColors.greyColor,
this.iconSize = 12,
this.icon = "",
this.iconColor = AppColors.textColor,
+ this.richText,
+ this.iconHasColor = true,
+ this.shape,
+ this.deleteIcon,
+ this.deleteIconSize = const Size(12, 12),
+ this.deleteIconColor = AppColors.textColor,
+ this.deleteIconHasColor = false,
});
- String? labelText;
- Color? textColor;
- Color? backgroundColor;
- num iconSize;
- String icon;
- Color iconColor;
+ final String? labelText;
+ final Widget? richText;
+ final Color? textColor;
+ final Color? backgroundColor;
+ final num iconSize;
+ final String icon;
+ final String? deleteIcon;
+ final Size? deleteIconSize;
+ final Color iconColor;
+ final Color? deleteIconColor;
+ final bool iconHasColor;
+ final bool deleteIconHasColor;
+ final OutlinedBorder? shape;
@override
Widget build(BuildContext context) {
@@ -41,18 +55,55 @@ class AppCustomChipWidget extends StatelessWidget {
),
child: icon.isNotEmpty
? Chip(
- avatar: icon.isNotEmpty ? Utils.buildSvgWithAssets(icon: icon, width: iconSize.h, height: iconSize.h, iconColor: iconColor) : SizedBox.shrink(),
- label: labelText!.toText10(weight: FontWeight.w500, letterSpacing: -0.64, color: textColor),
+ avatar: icon.isNotEmpty
+ ? Utils.buildSvgWithAssets(
+ icon: icon,
+ width: iconSize.h,
+ height: iconSize.h,
+ iconColor: iconHasColor ? iconColor : null)
+ : SizedBox.shrink(),
+ label: richText ??
+ labelText!.toText10(
+ weight: FontWeight.w500,
+ letterSpacing: -0.64,
+ color: textColor),
padding: EdgeInsets.all(0.0),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
- labelPadding: EdgeInsets.only(left: -4.h, right: 8.h),
+ labelPadding: EdgeInsets.only(
+ left: -4.h,
+ right: deleteIcon?.isNotEmpty == true ? 2.h : 8.h),
backgroundColor: backgroundColor,
+ shape: shape,
+ deleteIcon: deleteIcon?.isNotEmpty == true
+ ? Utils.buildSvgWithAssets(
+ icon: deleteIcon!,
+ width: deleteIconSize!.width!.h,
+ height: deleteIconSize!.height.h,
+ iconColor: deleteIconHasColor ? deleteIconColor : null)
+ : null,
+ onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null,
)
: Chip(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
- label: labelText!.toText10(weight: FontWeight.w500, letterSpacing: -0.64, color: textColor),
+ label: richText ??
+ labelText!.toText10(
+ weight: FontWeight.w500,
+ letterSpacing: -0.64,
+ color: textColor),
padding: EdgeInsets.all(0.0),
backgroundColor: backgroundColor,
+ shape: shape,
+ labelPadding: EdgeInsets.only(
+ left: 8.h,
+ right: deleteIcon?.isNotEmpty == true ? -2.h : 8.h),
+ deleteIcon: deleteIcon?.isNotEmpty == true
+ ? Utils.buildSvgWithAssets(
+ icon: deleteIcon!,
+ width: deleteIconSize!.width.h,
+ height: deleteIconSize!.height.h,
+ iconColor: deleteIconHasColor ? deleteIconColor : null)
+ : null,
+ onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null,
),
);
}
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/lib/widgets/dropdown/dropdown_widget.dart b/lib/widgets/dropdown/dropdown_widget.dart
index 2a0cad3..f093f0b 100644
--- a/lib/widgets/dropdown/dropdown_widget.dart
+++ b/lib/widgets/dropdown/dropdown_widget.dart
@@ -18,29 +18,31 @@ class DropdownWidget extends StatelessWidget {
final bool hasSelectionCustomIcon;
final String? selectionCustomIcon;
final String? leadingIcon;
+ final Color? labelColor;
- const DropdownWidget({
- Key? key,
- required this.labelText,
- required this.hintText,
- required this.dropdownItems,
- this.selectedValue,
- this.onChange,
- this.isEnable = true,
- this.isBorderAllowed = true,
- this.isAllowRadius = true,
- this.padding,
- this.hasSelectionCustomIcon = false,
- this.selectionCustomIcon,
- this.leadingIcon,
- }) : super(key: key);
+ const DropdownWidget(
+ {Key? key,
+ required this.labelText,
+ required this.hintText,
+ required this.dropdownItems,
+ this.selectedValue,
+ this.onChange,
+ this.isEnable = true,
+ this.isBorderAllowed = true,
+ this.isAllowRadius = true,
+ this.padding,
+ this.hasSelectionCustomIcon = false,
+ this.selectionCustomIcon,
+ this.leadingIcon,
+ this.labelColor})
+ : super(key: key);
@override
Widget build(BuildContext context) {
Widget content = Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
- children: [_buildLabelText(), _buildDropdown(context)],
+ children: [_buildLabelText(labelColor), _buildDropdown(context)],
);
return Container(
@@ -75,13 +77,13 @@ class DropdownWidget extends StatelessWidget {
child: Utils.buildSvgWithAssets(icon: leadingIcon!));
}
- Widget _buildLabelText() {
+ Widget _buildLabelText(Color? labelColor) {
return Text(
labelText,
style: TextStyle(
fontSize: 12.fSize,
fontWeight: FontWeight.w500,
- color: Color(0xff898A8D),
+ color: labelColor ?? Color(0xff898A8D),
letterSpacing: -0.2,
height: 18 / 12,
),
diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart
index 03d7b5f..fa2d873 100644
--- a/lib/widgets/input_widget.dart
+++ b/lib/widgets/input_widget.dart
@@ -41,39 +41,41 @@ class TextInputWidget extends StatelessWidget {
final num? fontSize;
final bool? isWalletAmountInput;
final Widget? suffix;
+ final Color? labelColor;
// final List countryList;
// final Function(Country)? onCountryChange;
- TextInputWidget({
- super.key,
- required this.labelText,
- required this.hintText,
- this.controller,
- this.onChange,
- this.onCalendarTypeChanged,
- this.prefix,
- this.isEnable = true,
- this.isBorderAllowed = true,
- this.isAllowRadius = true,
- this.isReadOnly = false,
- this.keyboardType = TextInputType.number,
- this.focusNode,
- this.autoFocus = false,
- this.padding,
- this.isAllowLeadingIcon = false,
- this.leadingIcon,
- this.isCountryDropDown = false,
- this.hasError = false,
- this.errorMessage,
- this.onCountryChange,
- this.selectionType,
- this.fontSize = 14,
- this.isWalletAmountInput = false,
- this.suffix,
- // this.countryList = const [],
- // this.onCountryChange,
- });
+ TextInputWidget(
+ {super.key,
+ required this.labelText,
+ required this.hintText,
+ this.controller,
+ this.onChange,
+ this.onCalendarTypeChanged,
+ this.prefix,
+ this.isEnable = true,
+ this.isBorderAllowed = true,
+ this.isAllowRadius = true,
+ this.isReadOnly = false,
+ this.keyboardType = TextInputType.number,
+ this.focusNode,
+ this.autoFocus = false,
+ this.padding,
+ this.isAllowLeadingIcon = false,
+ this.leadingIcon,
+ this.isCountryDropDown = false,
+ this.hasError = false,
+ this.errorMessage,
+ this.onCountryChange,
+ this.selectionType,
+ this.fontSize = 14,
+ this.isWalletAmountInput = false,
+ this.suffix,
+ this.labelColor
+ // this.countryList = const [],
+ // this.onCountryChange,
+ });
final FocusNode _focusNode = FocusNode();
@@ -135,12 +137,13 @@ class TextInputWidget extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- _buildLabelText().paddingOnly(right: (appState.getLanguageCode() == "ar" ? 10 : 0)),
+ _buildLabelText(labelColor).paddingOnly(right: (appState.getLanguageCode() == "ar" ? 10 : 0)),
_buildTextField(context),
],
),
),
if (selectionType == SelectionTypeEnum.calendar) _buildTrailingIcon(context),
+ if (selectionType == SelectionTypeEnum.search) _buildTrailingIconForSearch(context),
],
),
),
@@ -205,13 +208,13 @@ class TextInputWidget extends StatelessWidget {
);
}
- Widget _buildLabelText() {
+ Widget _buildLabelText(Color? labelColor) {
return Text(
labelText,
style: TextStyle(
fontSize: 12.fSize,
fontWeight: FontWeight.w500,
- color: AppColors.inputLabelTextColor,
+ color: labelColor ?? AppColors.inputLabelTextColor,
letterSpacing: -0.2,
height: 18 / 12,
),
@@ -250,4 +253,16 @@ class TextInputWidget extends StatelessWidget {
),
);
}
+
+ _buildTrailingIconForSearch(BuildContext context) {
+ final AppState appState = getIt.get();
+ return Container(
+ height: 40.h,
+ width: 40.h,
+ margin: EdgeInsets.zero,
+ padding: EdgeInsets.all(8.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 10.h, color: AppColors.whiteColor),
+ child: Utils.buildSvgWithAssets(icon: AppAssets.search_icon),
+ );
+ }
}
diff --git a/pubspec.yaml b/pubspec.yaml
index 055fb7b..e55d241 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -66,6 +66,7 @@ dependencies:
firebase_analytics: ^11.5.1
jiffy: ^6.4.3
hijri_gregorian_calendar: ^0.1.1
+ sms_otp_auto_verify: ^2.2.0
web: any
flutter_staggered_animations: ^1.1.1
@@ -81,6 +82,8 @@ dependencies:
flutter_swiper_view: ^1.1.8
family_bottom_sheet: ^0.1.0
+ location: ^8.0.1
+
dev_dependencies:
flutter_test:
sdk: flutter