diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json
index 69e0d66d..1f006d2c 100644
--- a/assets/langs/en-US.json
+++ b/assets/langs/en-US.json
@@ -21,7 +21,7 @@
"mySchedule": "My Schedule",
"logout": "Logout",
"respirationRate": "Respiration Rate",
- "bookAppo": "New Appointment",
+ "bookAppo": "Book Appointment",
"searchBy": "Search By:",
"clinic": "Clinic",
"byClinic": "By Clinic",
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 5b3b2681..caa84333 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -86,7 +86,7 @@
NSMicrophoneUsageDescription
This app requires microphone access to enable virtual consultation between patient & doctor
NSMotionUsageDescription
- This app requires motion detection access to function properly.
+ This app requires access to motion detection to count your daily steps.
NSPhotoLibraryUsageDescription
This app requires photo library access to select image as document & upload it.
NSPhotoLibraryAddUsageDescription
diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart
index 3a5ff281..ebac476d 100644
--- a/lib/core/api/api_client.dart
+++ b/lib/core/api/api_client.dart
@@ -210,8 +210,8 @@ class ApiClientImp implements ApiClient {
body['TokenID'] = "@dm!n";
}
- body['TokenID'] = "@dm!n";
- // body['PatientID'] = 1307867;
+ // body['TokenID'] = "@dm!n";
+ // body['PatientID'] = 3310954;
// body['PatientID'] = 53320;
// body['PatientTypeID'] = 1;
// body['PatientOutSA'] = 0;
diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart
index 9fb7050a..865b7e75 100644
--- a/lib/core/api_consts.dart
+++ b/lib/core/api_consts.dart
@@ -5,7 +5,7 @@ import 'package:hmg_patient_app_new/core/enums.dart';
class ApiConsts {
static const maxSmallScreen = 660;
- static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat;
+ static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT
@@ -911,8 +911,6 @@ var AUTO_GENERATE_INVOICE_TAMARA = 'Services/PayFort_Serv.svc/REST/Tamara_Getinf
var GET_ONESIGNAL_VOIP_TOKEN = 'https://onesignal.com/api/v1/players';
-var CANCEL_PHARMA_LIVECARE_REQUEST = 'https://vcallapi.hmg.com/api/PharmaLiveCare/SendPaymentStatus';
-
var INSERT_FREE_SLOTS_LOGS = 'Services/Doctors.svc/Rest/InsertDoctorFreeSlotsLogs';
var GET_NATIONALITY = 'Services/Lists.svc/REST/GetNationality';
diff --git a/lib/core/utils/request_utils.dart b/lib/core/utils/request_utils.dart
index 4d4ae4b5..62399abd 100644
--- a/lib/core/utils/request_utils.dart
+++ b/lib/core/utils/request_utils.dart
@@ -27,7 +27,7 @@ class RequestUtils {
}) {
bool fileNo = false;
if (nationId.isNotEmpty) {
- final numericRegex = RegExp(r'^[0-9]+$');
+ final numericRegex = RegExp(r'^[0-9]+$');
fileNo = nationId.length < 10 && numericRegex.hasMatch(nationId);
//fileNo = nationId.length < 10 && nationId.isNumericOnly() ;
if (fileNo) {
@@ -40,14 +40,23 @@ class RequestUtils {
if (zipCode == "0") {
request.patientMobileNumberOthers = phoneNumber;
} else {
- request.patientMobileNumber = int.parse(phoneNumber);
+ // Remove any non-numeric characters before parsing
+ final numericPhone = phoneNumber.replaceAll(RegExp(r'[^0-9]'), '');
+ final parsedPhone = int.tryParse(numericPhone);
+ if (parsedPhone != null) {
+ request.patientMobileNumber = parsedPhone;
+ } else {
+ // If parsing fails, use as string in Others field
+ request.patientMobileNumberOthers = phoneNumber;
+ }
}
}
request.oTPSendType = otpTypeEnum.toInt(); // could map OTPTypeEnum if needed
request.zipCode = zipCode; // or countryCode if defined elsewhere
if (isForRegister) {
- request.patientIdentificationID = int.parse(nationId);
+ final parsedNationId = int.tryParse(nationId.replaceAll(RegExp(r'[^0-9]'), ''));
+ request.patientIdentificationID = parsedNationId ?? 0;
request.searchType = 1;
request.isHijri = calenderType.toInt;
request.patientID = patientId;
@@ -56,7 +65,7 @@ class RequestUtils {
request.isDentalAllowedBackend = false;
} else {
if (fileNo) {
- request.patientID = patientId ?? int.parse(nationId);
+ request.patientID = patientId ?? (int.tryParse(nationId.replaceAll(RegExp(r'[^0-9]'), '')) ?? 0);
request.patientIdentificationID = request.nationalID;
request.searchType = 2;
} else {
@@ -71,15 +80,15 @@ class RequestUtils {
static dynamic getCommonRequestWelcome(
{required String phoneNumber,
- required OTPTypeEnum otpTypeEnum,
- required String? deviceToken,
- required bool patientOutSA,
- required String? loginTokenID,
- RegistrationDataModelPayload? registeredData,
- int? patientId,
- required String nationIdText,
- required String countryCode,
- required int loginType}) {
+ required OTPTypeEnum otpTypeEnum,
+ required String? deviceToken,
+ required bool patientOutSA,
+ required String? loginTokenID,
+ RegistrationDataModelPayload? registeredData,
+ int? patientId,
+ required String nationIdText,
+ required String countryCode,
+ required int loginType}) {
bool fileNo = false;
if (nationIdText.isNotEmpty) {
final numericRegex = RegExp(r'^[0-9]+$');
@@ -91,8 +100,17 @@ class RequestUtils {
request.patientMobileNumberOthers = phoneNumber;
request.mobileNo = phoneNumber;
} else {
- request.patientMobileNumber = int.parse(phoneNumber);
- request.mobileNo = '0$phoneNumber';
+ // Remove any non-numeric characters before parsing
+ final numericPhone = phoneNumber.replaceAll(RegExp(r'[^0-9]'), '');
+ final parsedPhone = int.tryParse(numericPhone);
+ if (parsedPhone != null) {
+ request.patientMobileNumber = parsedPhone;
+ request.mobileNo = '0$numericPhone';
+ } else {
+ // If parsing fails, use as string in Others field
+ request.patientMobileNumberOthers = phoneNumber;
+ request.mobileNo = phoneNumber;
+ }
}
request.deviceToken = deviceToken;
request.projectOutSA = patientOutSA;
@@ -106,8 +124,8 @@ class RequestUtils {
request.searchType = registeredData.searchType != null
? registeredData.searchType
: fileNo
- ? 1
- : 2;
+ ? 1
+ : 2;
request.patientID = registeredData.patientId ?? 0;
request.patientIdentificationID = request.nationalID = (registeredData.patientIdentificationId ?? 0);
request.dob = registeredData.dob;
@@ -115,7 +133,8 @@ class RequestUtils {
log("nationIdText: ${nationIdText}");
} else {
if (fileNo) {
- request.patientID = patientId ?? int.parse(nationIdText);
+ final numericNationId = nationIdText.replaceAll(RegExp(r'[^0-9]'), '');
+ request.patientID = patientId ?? (int.tryParse(numericNationId) ?? 0);
request.patientIdentificationID = request.nationalID = '0';
request.searchType = 2;
//TODO: Issue HEre is Not Login
@@ -155,8 +174,17 @@ class RequestUtils {
request.patientMobileNumberOthers = mobileNumber;
request.mobileNo = mobileNumber;
} else {
- request.patientMobileNumber = int.parse(mobileNumber);
- request.mobileNo = '0$mobileNumber';
+ // Remove any non-numeric characters before parsing
+ final numericMobile = mobileNumber.replaceAll(RegExp(r'[^0-9]'), '');
+ final parsedMobile = int.tryParse(numericMobile);
+ if (parsedMobile != null) {
+ request.patientMobileNumber = parsedMobile;
+ request.mobileNo = '0$numericMobile';
+ } else {
+ // If parsing fails, use as string in Others field
+ request.patientMobileNumberOthers = mobileNumber;
+ request.mobileNo = mobileNumber;
+ }
}
}
request.projectOutSA = patientOutSA;
@@ -240,16 +268,16 @@ class RequestUtils {
"Patientobject": {
"TempValue": true,
"PatientIdentificationType": (isDubai
- ? appState.getUserRegistrationPayload.patientIdentificationId?.toString().substring(0, 1)
- : appState.getNHICUserData.idNumber!.substring(0, 1)) ==
- "1"
+ ? appState.getUserRegistrationPayload.patientIdentificationId?.toString().substring(0, 1)
+ : appState.getNHICUserData.idNumber!.substring(0, 1)) ==
+ "1"
? 1
: 2,
"PatientIdentificationNo":
- isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
+ isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
"MobileNumber": appState.getUserRegistrationPayload.patientMobileNumber ?? 0,
"PatientOutSA": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode ||
- appState.getUserRegistrationPayload.zipCode == '+966')
+ appState.getUserRegistrationPayload.zipCode == '+966')
? 0
: 1,
"FirstNameN": isDubai ? "..." : appState.getNHICUserData.firstNameAr,
@@ -266,31 +294,31 @@ class RequestUtils {
"DateofBirthN": date,
"EmailAddress": emailAddress,
"SourceType": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode ||
- appState.getUserRegistrationPayload.zipCode == '+966')
+ appState.getUserRegistrationPayload.zipCode == '+966')
? "1"
: "2",
"PreferredLanguage": appState.getLanguageCode() == "ar" ? (isDubai ? "1" : 1) : (isDubai ? "2" : 2),
"Marital": isDubai
? (maritalStatus == MaritalStatusTypeEnum.single
- ? '0'
- : maritalStatus == MaritalStatusTypeEnum.married
- ? '1'
- : '2')
+ ? '0'
+ : maritalStatus == MaritalStatusTypeEnum.married
+ ? '1'
+ : '2')
: (appState.getNHICUserData.maritalStatusCode == 'U'
- ? '0'
- : appState.getNHICUserData.maritalStatusCode == 'M'
- ? '1'
- : '2'),
+ ? '0'
+ : appState.getNHICUserData.maritalStatusCode == 'M'
+ ? '1'
+ : '2'),
},
"PatientIdentificationID":
- isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
+ isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
"PatientMobileNumber": appState.getUserRegistrationPayload.patientMobileNumber.toString()[0] == '0'
? appState.getUserRegistrationPayload.patientMobileNumber
: '0${appState.getUserRegistrationPayload.patientMobileNumber}',
"DOB": dob,
"IsHijri": appState.getUserRegistrationPayload.isHijri,
"PatientOutSA": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode ||
- appState.getUserRegistrationPayload.zipCode == '+966')
+ appState.getUserRegistrationPayload.zipCode == '+966')
? 0
: 1,
"isDentalAllowedBackend": appState.getUserRegistrationPayload.isDentalAllowedBackend,
@@ -314,7 +342,9 @@ class RequestUtils {
request.sharedPatientId = 0;
request.sharedPatientIdentificationId = nationalIDorFile;
} else if (loginType == 2) {
- request.sharedPatientId = int.parse(nationalIDorFile);
+ // Remove any non-numeric characters before parsing
+ final numericId = nationalIDorFile.replaceAll(RegExp(r'[^0-9]'), '');
+ request.sharedPatientId = int.tryParse(numericId) ?? 0;
request.sharedPatientIdentificationId = '';
}
request.searchType = loginType;
@@ -325,4 +355,4 @@ class RequestUtils {
request.isDentalAllowedBackend = false;
return request;
}
-}
+}
\ No newline at end of file
diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart
index 8ec370a4..4831901d 100644
--- a/lib/features/book_appointments/book_appointments_view_model.dart
+++ b/lib/features/book_appointments/book_appointments_view_model.dart
@@ -676,7 +676,6 @@ class BookAppointmentsViewModel extends ChangeNotifier {
(failure) async {
isDoctorsListLoading = false;
if (onError != null) onError(LocaleKeys.noDoctorFound.tr());
-
notifyListeners();
},
(apiResponse) {
@@ -691,7 +690,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
clearSearchFilters();
getFiltersFromDoctorList();
_groupDoctorsList();
- setIsNearestAppointmentSelected(isNearest);
+ // setIsNearestAppointmentSelected(isNearest);
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart
index 1689b57a..832d94fe 100644
--- a/lib/features/my_appointments/my_appointments_view_model.dart
+++ b/lib/features/my_appointments/my_appointments_view_model.dart
@@ -56,6 +56,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
bool isAppointmentDataToBeLoaded = true;
bool isMyDoctorsDataToBeLoaded = true;
bool isArrivedAppointmentDataLoaded = false;
+ bool isFullArrivedAppointmentsLoaded = false;
bool isEyeMeasurementsAppointmentsLoading = false;
@@ -77,7 +78,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
List patientUpcomingAppointmentsHistoryList = [];
List patientArrivedAppointmentsHistoryList = [];
- List patientAllArrivedAppointmentsHistoryList = [];
+ // List patientAllArrivedAppointmentsHistoryList = [];
List patientMyDoctorsList = [];
@@ -173,11 +174,12 @@ class MyAppointmentsViewModel extends ChangeNotifier {
patientAppointmentsHistoryList.clear();
patientUpcomingAppointmentsHistoryList.clear();
patientArrivedAppointmentsHistoryList.clear();
- patientAllArrivedAppointmentsHistoryList.clear();
+ // patientAllArrivedAppointmentsHistoryList.clear();
patientEyeMeasurementsAppointmentsHistoryList.clear();
isMyAppointmentsLoading = true;
isTimeLineAppointmentsLoading = true;
patientMyDoctorsList.clear();
+ isFullArrivedAppointmentsLoaded = false;
}
isTamaraDetailsLoading = true;
isAppointmentPatientShareLoading = true;
@@ -276,13 +278,19 @@ class MyAppointmentsViewModel extends ChangeNotifier {
getPatientAppointmentQueueDetails();
}
}
+
+ // Skip API call if full data is already loaded and we're not requesting timeline view
+ if (!isForTimeLine && isFullArrivedAppointmentsLoaded && !isAppointmentDataToBeLoaded) {
+ return;
+ }
+
if (!isAppointmentDataToBeLoaded) return;
patientAppointmentsByClinic.clear();
patientAppointmentsByHospital.clear();
patientAppointmentsViewList.clear();
- patientAllArrivedAppointmentsHistoryList.clear();
+ // patientAllArrivedAppointmentsHistoryList.clear();
filteredAppointmentList.clear();
patientAppointmentsHistoryList.clear();
patientUpcomingAppointmentsHistoryList.clear();
@@ -321,8 +329,9 @@ class MyAppointmentsViewModel extends ChangeNotifier {
isMyAppointmentsLoading = false;
isAppointmentDataToBeLoaded = false;
if (!isForTimeLine) {
- patientAllArrivedAppointmentsHistoryList = apiResponse.data!;
+ // patientAllArrivedAppointmentsHistoryList = apiResponse.data!;
isArrivedAppointmentDataLoaded = true;
+ isFullArrivedAppointmentsLoaded = true;
}
notifyListeners();
if (onSuccess != null) {
@@ -354,6 +363,69 @@ class MyAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
+ changeTabToArrived() {
+ if (isFullArrivedAppointmentsLoaded && patientUpcomingAppointmentsHistoryList.isEmpty && patientArrivedAppointmentsHistoryList.isNotEmpty && selectedTabIndex == 0) {
+ selectedTabIndex = 1;
+ updateListWRTTab(1);
+ notifyListeners();
+ }
+ }
+
+ /// Loads full arrived appointments list if not already loaded
+ /// This is called when navigating to My Appointments page from landing page
+ Future loadFullArrivedAppointmentsIfNeeded({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ // Skip if already loaded
+ if (isFullArrivedAppointmentsLoaded) {
+ // Auto-switch to Arrived tab if no upcoming appointments (even when data is already loaded)
+ if (patientUpcomingAppointmentsHistoryList.isEmpty && patientArrivedAppointmentsHistoryList.isNotEmpty && selectedTabIndex == 0) {
+ selectedTabIndex = 1;
+ updateListWRTTab(1);
+ notifyListeners();
+ }
+ return;
+ }
+
+ setIsAppointmentsHistoryLoading(true);
+
+ // Fetch full arrived appointments (isForTimeLine = false)
+ final result = await myAppointmentsRepo.getPatientAppointments(isActiveAppointment: false, isArrivedAppointments: true, isForTimeLine: false);
+
+ result.fold(
+ (failure) async => await errorHandlerService.handleError(failure: failure),
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ // Handle error
+ } else if (apiResponse.messageStatus == 1) {
+ patientArrivedAppointmentsHistoryList = apiResponse.data!;
+ // patientAllArrivedAppointmentsHistoryList = apiResponse.data!;
+ isArrivedAppointmentDataLoaded = true;
+ isFullArrivedAppointmentsLoaded = true;
+
+ // Rebuild the combined list
+ patientAppointmentsHistoryList.clear();
+ patientAppointmentsHistoryList.addAll(patientUpcomingAppointmentsHistoryList);
+ patientAppointmentsHistoryList.addAll(patientArrivedAppointmentsHistoryList);
+
+ isMyAppointmentsLoading = false;
+
+ // Auto-switch to Arrived tab if no upcoming appointments
+ if (patientUpcomingAppointmentsHistoryList.isEmpty && patientArrivedAppointmentsHistoryList.isNotEmpty && selectedTabIndex == 0) {
+ selectedTabIndex = 1;
+ updateListWRTTab(1);
+ } else {
+ // Update filtered list based on current tab
+ updateListWRTTab(selectedTabIndex);
+ }
+
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
+
void getFiltersForSelectedAppointmentList(List filteredAppointmentList) {
availableFilters.clear();
if (filteredAppointmentList.isEmpty == true) return;
@@ -362,9 +434,9 @@ class MyAppointmentsViewModel extends ChangeNotifier {
availableFilters.add(AppointmentListingFilters.LIVECARE);
}
- if (filteredAppointmentList.any((element) => element.isLiveCareAppointment == false)) {
- availableFilters.add(AppointmentListingFilters.WALKIN);
- }
+ // if (filteredAppointmentList.any((element) => element.isLiveCareAppointment == false)) {
+ // availableFilters.add(AppointmentListingFilters.WALKIN);
+ // }
if (filteredAppointmentList.any((element) => AppointmentType.isArrived(element) == true)) {
availableFilters.add(AppointmentListingFilters.ARRIVED);
@@ -801,15 +873,15 @@ class MyAppointmentsViewModel extends ChangeNotifier {
selectedFilter = [];
// if(previouslySelectedTab == selectedTabIndex ) return;
switch (index) {
+ // case 0:
+ // filteredAppointmentList.clear();
+ // filteredAppointmentList.addAll(patientAppointmentsHistoryList);
+ // break;
case 0:
- filteredAppointmentList.clear();
- filteredAppointmentList.addAll(patientAppointmentsHistoryList);
- break;
- case 1:
filteredAppointmentList.clear();
filteredAppointmentList.addAll(patientUpcomingAppointmentsHistoryList);
break;
- case 2:
+ case 1:
filteredAppointmentList.clear();
filteredAppointmentList.addAll(patientArrivedAppointmentsHistoryList);
break;
@@ -835,11 +907,12 @@ class MyAppointmentsViewModel extends ChangeNotifier {
this.end = end;
isDateFilterSelected = true;
List sourceList = [];
- if (selectedTabIndex == 0) {
- sourceList = patientAppointmentsHistoryList;
- } else if (selectedTabIndex == 1) {
+ // if (selectedTabIndex == 0) {
+ // sourceList = patientAppointmentsHistoryList;
+ // } else
+ if (selectedTabIndex == 0) {
sourceList = patientUpcomingAppointmentsHistoryList;
- } else if (selectedTabIndex == 2) {
+ } else if (selectedTabIndex == 1) {
sourceList = patientArrivedAppointmentsHistoryList;
}
// if (isDateFilterSelected) sourceList = filteredAppointmentList;
diff --git a/lib/features/prescriptions/prescriptions_view_model.dart b/lib/features/prescriptions/prescriptions_view_model.dart
index cdd85b9f..d75b3f8e 100644
--- a/lib/features/prescriptions/prescriptions_view_model.dart
+++ b/lib/features/prescriptions/prescriptions_view_model.dart
@@ -191,7 +191,9 @@ class PrescriptionsViewModel extends ChangeNotifier {
result.fold(
// (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) async {
- onError!(failure.message);
+ if (onError != null) {
+ onError(failure.message);
+ }
},
(apiResponse) async {
if (apiResponse.messageStatus == 2) {
diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart
index 4d181230..a26995b1 100644
--- a/lib/presentation/appointments/appointment_details_page.dart
+++ b/lib/presentation/appointments/appointment_details_page.dart
@@ -1200,8 +1200,8 @@ class _AppointmentDetailsPageState extends State {
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onSuccess: (apiResponse) {
LoaderBottomSheet.hideLoader();
- myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
- myAppointmentsViewModel.getPatientAppointments(true, false);
+ // myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
+ // myAppointmentsViewModel.getPatientAppointments(true, false);
showCommonBottomSheet(context,
child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)), callBackFunc: (str) {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart
index 0d864b64..57b2c71d 100644
--- a/lib/presentation/appointments/appointment_payment_page.dart
+++ b/lib/presentation/appointments/appointment_payment_page.dart
@@ -69,12 +69,18 @@ class _AppointmentPaymentPageState extends State {
widget.patientAppointmentHistoryResponseModel.clinicID,
widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false, onSuccess: (val) {
myAppointmentsViewModel.getTamaraInstallmentsDetails().then((val) {
- if (myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel != null) {
+ // if (myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel != null && myAppointmentsViewModel.patientAppointmentShareResponseModel != null) {
+ if (myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel != null &&
+ myAppointmentsViewModel.patientAppointmentShareResponseModel?.patientShareWithTax != null &&
+ myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel?.minLimit?.amount != null &&
+ myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel?.maxLimit?.amount != null) {
if (myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! >= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.minLimit!.amount! &&
myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! <= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.maxLimit!.amount!) {
- setState(() {
- isShowTamara = true;
- });
+ if (mounted) {
+ setState(() {
+ isShowTamara = true;
+ });
+ }
}
}
});
diff --git a/lib/presentation/appointments/my_appointments_page.dart b/lib/presentation/appointments/my_appointments_page.dart
index 3512472d..da20e183 100644
--- a/lib/presentation/appointments/my_appointments_page.dart
+++ b/lib/presentation/appointments/my_appointments_page.dart
@@ -45,8 +45,9 @@ class _MyAppointmentsPageState extends State {
scheduleMicrotask(() {
if (!myAppointmentsViewModel.isMyAppointmentsLoading) {
myAppointmentsViewModel.initAppointmentsViewModel();
- myAppointmentsViewModel.getPatientAppointments(true, false);
+ myAppointmentsViewModel.getPatientAppointments(true, false, isForTimeLine: false);
}
+ // myAppointmentsViewModel.changeTabToArrived();
});
super.initState();
}
@@ -69,9 +70,9 @@ class _MyAppointmentsPageState extends State {
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
- CustomTabBarModel(null, LocaleKeys.allAppt.tr(context: context)),
+ // CustomTabBarModel(null, LocaleKeys.allAppt.tr(context: context)),
CustomTabBarModel(null, LocaleKeys.upcoming.tr(context: context)),
- CustomTabBarModel(null, LocaleKeys.completed.tr(context: context)),
+ CustomTabBarModel(null, LocaleKeys.arrived.tr(context: context)),
],
onTabChange: (index) {
setState(() {
@@ -80,6 +81,10 @@ class _MyAppointmentsPageState extends State {
myAppointmentsViewModel.onTabChange(index);
myAppointmentsViewModel.updateListWRTTab(index);
context.read().flush();
+
+ if(index == 1) {
+ myAppointmentsViewModel.loadFullArrivedAppointmentsIfNeeded();
+ }
},
).paddingSymmetrical(24.h, 0.h),
// Consumer(builder: (context, myAppointmentsVM, child) {
@@ -150,7 +155,7 @@ class _MyAppointmentsPageState extends State {
? myAppointmentsVM.patientAppointmentsViewList.length
: 1,
itemBuilder: (context, index) {
- final isExpanded = myAppointmentsVM.selectedTabIndex == 1 ? true : expandedIndex == index;
+ final isExpanded = myAppointmentsVM.selectedTabIndex == 0 ? true : expandedIndex == index;
return myAppointmentsVM.isMyAppointmentsLoading
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart
index e0a810ed..bf4b5d58 100644
--- a/lib/presentation/book_appointment/select_clinic_page.dart
+++ b/lib/presentation/book_appointment/select_clinic_page.dart
@@ -1021,6 +1021,7 @@ class _SelectClinicPageState extends State {
void onClinicSelected(GetClinicsListResponseModel clinic) {
bookAppointmentsViewModel.setSelectedClinic(clinic);
bookAppointmentsViewModel.setIsDoctorsListLoading(true);
+ searchEditingController.text = "";
if (clinic.isLiveCareClinicAndOnline ?? false) {
Navigator.of(context).push(
CustomPageRoute(
diff --git a/lib/presentation/book_appointment/select_doctor_page.dart b/lib/presentation/book_appointment/select_doctor_page.dart
index 8813da07..cad4fe0e 100644
--- a/lib/presentation/book_appointment/select_doctor_page.dart
+++ b/lib/presentation/book_appointment/select_doctor_page.dart
@@ -48,7 +48,7 @@ class _SelectDoctorPageState extends State {
Clarity.setCurrentScreenName('Select Doctor Page');
_scrollController = ScrollController();
scheduleMicrotask(() {
- bookAppointmentsViewModel.setIsNearestAppointmentSelected(true);
+ bookAppointmentsViewModel.setIsNearestAppointmentSelected(false);
if (bookAppointmentsViewModel.isLiveCareSchedule) {
bookAppointmentsViewModel.getLiveCareDoctorsList();
} else {
@@ -57,8 +57,7 @@ class _SelectDoctorPageState extends State {
} else if (bookAppointmentsViewModel.isGetDocForHealthCal) {
bookAppointmentsViewModel.getDoctorsListByHealthCal();
} else {
- bookAppointmentsViewModel.setIsNearestAppointmentSelected(true);
- bookAppointmentsViewModel.getDoctorsList(isNearest: true);
+ bookAppointmentsViewModel.getDoctorsList(isNearest: false);
}
}
});
diff --git a/lib/presentation/book_appointment/widgets/doctor_card.dart b/lib/presentation/book_appointment/widgets/doctor_card.dart
index 180edae0..bb5a599d 100644
--- a/lib/presentation/book_appointment/widgets/doctor_card.dart
+++ b/lib/presentation/book_appointment/widgets/doctor_card.dart
@@ -144,8 +144,9 @@ class DoctorCard extends StatelessWidget {
spacing: 3.h,
runSpacing: 4.h,
children: [
- bookAppointmentsViewModel.isNearestAppointmentSelected
- ? doctorsListResponseModel.nearestFreeSlot != null
+ // bookAppointmentsViewModel.isNearestAppointmentSelected
+ // ?
+ doctorsListResponseModel.nearestFreeSlot != null
? AppCustomChipWidget(
labelText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot)),
// richText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot))
@@ -155,7 +156,8 @@ class DoctorCard extends StatelessWidget {
textColor: AppColors.successColor,
).toShimmer2(isShow: isLoading)
: SizedBox.shrink()
- : SizedBox.shrink(),
+ // : SizedBox.shrink()
+ ,
AppCustomChipWidget(
labelText: "${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}",
).toShimmer2(isShow: isLoading),
diff --git a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart
index eea9ec10..537a7dd8 100644
--- a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart
+++ b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart
@@ -176,6 +176,7 @@ class _WalletPaymentConfirmPageState extends State {
);
});
}, onError: (err) {
+ LoaderBottomSheet.showLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err.toString()),
diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart
index 1aec4e38..3caed75a 100644
--- a/lib/presentation/home/landing_page.dart
+++ b/lib/presentation/home/landing_page.dart
@@ -165,7 +165,7 @@ class _LandingPageState extends State {
immediateLiveCareViewModel.initImmediateLiveCare();
immediateLiveCareViewModel.getPatientLiveCareHistory();
myAppointmentsViewModel.initAppointmentsViewModel();
- myAppointmentsViewModel.getPatientAppointments(true, false);
+ myAppointmentsViewModel.getPatientAppointments(true, false, isForTimeLine: true);
emergencyServicesViewModel.checkPatientERAdvanceBalance();
// myAppointmentsViewModel.getPatientAppointmentQueueDetails();
notificationsViewModel.initNotificationsViewModel();
@@ -230,7 +230,7 @@ class _LandingPageState extends State {
// Refresh Appointments Data
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel();
- myAppointmentsViewModel.getPatientAppointments(true, false);
+ myAppointmentsViewModel.getPatientAppointments(true, false, isForTimeLine: true);
// Refresh Appointments Data
habibWalletVM.initHabibWalletProvider();
diff --git a/lib/presentation/profile_settings/widgets/preferred_language_widget.dart b/lib/presentation/profile_settings/widgets/preferred_language_widget.dart
index d0f85090..aa0d8c09 100644
--- a/lib/presentation/profile_settings/widgets/preferred_language_widget.dart
+++ b/lib/presentation/profile_settings/widgets/preferred_language_widget.dart
@@ -74,7 +74,7 @@ class _PreferredLanguageWidgetState extends State {
callBackFunc: () async {
Navigator.of(GetIt.instance().navigatorKey.currentContext!).pop();
profileSettingsViewModel.getProfileSettings();
- }, isFullScreen: false, isAutoDismiss: true);
+ }, isFullScreen: false, isAutoDismiss: true, isCloseButtonVisible: false);
},
onError: (error) {
LoaderBottomSheet.hideLoader();
diff --git a/lib/presentation/profile_settings/widgets/update_email_widget.dart b/lib/presentation/profile_settings/widgets/update_email_widget.dart
index 55f848a6..73b50849 100644
--- a/lib/presentation/profile_settings/widgets/update_email_widget.dart
+++ b/lib/presentation/profile_settings/widgets/update_email_widget.dart
@@ -134,7 +134,7 @@ class _UpdateEmailDialogState extends State {
callBackFunc: () async {
Navigator.of(getIt().navigatorKey.currentContext!).pop();
profileSettingsViewModel!.getProfileSettings();
- }, isFullScreen: false, isAutoDismiss: true);
+ }, isFullScreen: false, isAutoDismiss: true, isCloseButtonVisible: false);
},
onError: (error) {
LoaderBottomSheet.hideLoader();
diff --git a/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart b/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart
index f1a34565..d617ac22 100644
--- a/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart
+++ b/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart
@@ -41,7 +41,7 @@ class _UpdateEmergencyContactDialogState extends State countryList;
@@ -35,7 +36,9 @@ class CustomCountryDropdown extends StatefulWidget {
class CustomCountryDropdownState extends State {
CountryEnum? selectedCountry;
- late OverlayEntry _overlayEntry;
+ OverlayEntry? _overlayEntry;
+ Timer? _showDropdownTimer;
+ Timer? _refocusTimer;
bool _isDropdownOpen = false;
FocusNode textFocusNode = FocusNode();
@@ -55,6 +58,9 @@ class CustomCountryDropdownState extends State {
@override
void dispose() {
+ _showDropdownTimer?.cancel();
+ _refocusTimer?.cancel();
+ _removeOverlayEntry();
textFocusNode.dispose();
super.dispose();
}
@@ -74,9 +80,18 @@ class CustomCountryDropdownState extends State {
},
child: Row(
children: [
- Utils.buildSvgWithAssets(icon: selectedCountry != null ? selectedCountry!.iconPath : AppAssets.ksa, width: 40.h, height: 40.h, applyThemeColor: false),
+ Utils.buildSvgWithAssets(
+ icon: selectedCountry != null
+ ? selectedCountry!.iconPath
+ : AppAssets.ksa,
+ width: 40.h,
+ height: 40.h,
+ applyThemeColor: false),
SizedBox(width: 8.h),
- Utils.buildSvgWithAssets(icon: _isDropdownOpen ? AppAssets.dropdow_icon : AppAssets.dropdow_icon),
+ Utils.buildSvgWithAssets(
+ icon: _isDropdownOpen
+ ? AppAssets.dropdow_icon
+ : AppAssets.dropdow_icon),
],
),
),
@@ -94,7 +109,11 @@ class CustomCountryDropdownState extends State {
children: [
Text(
LocaleKeys.phoneNumber.tr(),
- style: TextStyle(fontSize: 12.f, height: 1.5, fontWeight: FontWeight.w600, letterSpacing: -1),
+ style: TextStyle(
+ fontSize: 12.f,
+ height: 1.5,
+ fontWeight: FontWeight.w600,
+ letterSpacing: -1),
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
@@ -102,9 +121,15 @@ class CustomCountryDropdownState extends State {
if (selectedCountry != CountryEnum.others)
Text(
selectedCountry!.countryCode,
- style: TextStyle(fontSize: 12.f, fontWeight: FontWeight.w600, letterSpacing: -0.4, height: 1.5, fontFamily: "Poppins"),
+ style: TextStyle(
+ fontSize: 12.f,
+ fontWeight: FontWeight.w600,
+ letterSpacing: -0.4,
+ height: 1.5,
+ fontFamily: "Poppins"),
),
- if (selectedCountry != CountryEnum.others) SizedBox(width: 4.h),
+ if (selectedCountry != CountryEnum.others)
+ SizedBox(width: 4.h),
if (widget.isEnableTextField)
SizedBox(
height: 20.h,
@@ -113,14 +138,19 @@ class CustomCountryDropdownState extends State {
alignment: Alignment.centerLeft,
child: TextField(
focusNode: textFocusNode,
- style: TextStyle(fontSize: 12.f, fontWeight: FontWeight.w600, letterSpacing: -0.4, height: 1.5, fontFamily: "Poppins"),
-
+ style: TextStyle(
+ fontSize: 12.f,
+ fontWeight: FontWeight.w600,
+ letterSpacing: -0.4,
+ height: 1.5,
+ fontFamily: "Poppins"),
decoration: InputDecoration(
- hintText: selectedCountry == CountryEnum.others ? "001*******" : "",
+ hintText: selectedCountry == CountryEnum.others
+ ? "001*******"
+ : "",
isDense: true,
border: InputBorder.none,
contentPadding: EdgeInsets.zero,
-
),
keyboardType: TextInputType.phone,
onChanged: widget.onPhoneNumberChanged,
@@ -136,21 +166,31 @@ class CustomCountryDropdownState extends State {
Text(
selectedCountry != null
? appState.getLanguageCode() == "ar"
- ? selectedCountry!.nameArabic
- : selectedCountry!.displayName
+ ? selectedCountry!.nameArabic
+ : selectedCountry!.displayName
: LocaleKeys.selectCountry.tr(),
- style: TextStyle(fontSize: 14.f, height: 21 / 14, fontWeight: FontWeight.w600, letterSpacing: -0.2),
+ style: TextStyle(
+ fontSize: 14.f,
+ height: 21 / 14,
+ fontWeight: FontWeight.w600,
+ letterSpacing: -0.2),
),
],
);
}
void _openDropdown() {
+ if (!mounted || _isDropdownOpen || _overlayEntry != null) return;
+
+ _showDropdownTimer?.cancel();
if (textFocusNode.hasFocus) {
textFocusNode.unfocus();
// Wait for keyboard to close before calculating position
- Future.delayed(Duration(milliseconds: 300), () {
- _showDropdown();
+ _showDropdownTimer = Timer(const Duration(milliseconds: 300), () {
+ _showDropdownTimer = null;
+ if (mounted) {
+ _showDropdown();
+ }
});
} else {
_showDropdown();
@@ -158,8 +198,18 @@ class CustomCountryDropdownState extends State {
}
void _showDropdown() {
+ if (!mounted || _isDropdownOpen || _overlayEntry != null) return;
+
AppState appState = getIt.get();
- RenderBox renderBox = context.findRenderObject() as RenderBox;
+ final renderObject = context.findRenderObject();
+ final overlay = Overlay.maybeOf(context);
+ if (renderObject is! RenderBox ||
+ !renderObject.attached ||
+ overlay == null) {
+ return;
+ }
+
+ final renderBox = renderObject;
Offset offset = renderBox.localToGlobal(Offset.zero);
bool isRtl = appState.getLanguageCode() == "ar";
double leftPosition;
@@ -169,7 +219,7 @@ class CustomCountryDropdownState extends State {
leftPosition = offset.dx;
}
- _overlayEntry = OverlayEntry(
+ final overlayEntry = OverlayEntry(
builder: (context) => Stack(
children: [
Positioned.fill(
@@ -185,32 +235,41 @@ class CustomCountryDropdownState extends State {
width: !widget.isFromBottomSheet ? renderBox.size.width : 60.h,
child: Material(
child: Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.white, borderRadius: 12),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: Colors.white, borderRadius: 12),
child: Column(
children: widget.countryList
.map(
(country) => GestureDetector(
- onTap: () {
- setState(() {
- selectedCountry = country;
- });
- widget.onCountryChange?.call(country);
- _closeDropdown();
- },
- child: Container(
- padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 16.h),
- child: Row(
- children: [
- Utils.buildSvgWithAssets(icon: country.iconPath, width: 38.h, height: 38.h, applyThemeColor: false),
- if (!widget.isFromBottomSheet) SizedBox(width: 12.h),
- if (!widget.isFromBottomSheet)
- Text(appState.getLanguageCode() == "ar" ? country.nameArabic : country.displayName,
- style: TextStyle(fontSize: 14.f, height: 21 / 14, fontWeight: FontWeight.w600, letterSpacing: -0.2)),
- ],
- ),
- )),
- )
+ onTap: () => _selectCountry(country),
+ child: Container(
+ padding: EdgeInsets.symmetric(
+ vertical: 8.h, horizontal: 8.h),
+ decoration: RoundedRectangleBorder()
+ .toSmoothCornerDecoration(borderRadius: 16.h),
+ child: Row(
+ children: [
+ Utils.buildSvgWithAssets(
+ icon: country.iconPath,
+ width: 38.h,
+ height: 38.h,
+ applyThemeColor: false),
+ if (!widget.isFromBottomSheet)
+ SizedBox(width: 12.h),
+ if (!widget.isFromBottomSheet)
+ Text(
+ appState.getLanguageCode() == "ar"
+ ? country.nameArabic
+ : country.displayName,
+ style: TextStyle(
+ fontSize: 14.f,
+ height: 21 / 14,
+ fontWeight: FontWeight.w600,
+ letterSpacing: -0.2)),
+ ],
+ ),
+ )),
+ )
.toList(),
),
),
@@ -220,7 +279,8 @@ class CustomCountryDropdownState extends State {
),
);
- Overlay.of(context)?.insert(_overlayEntry);
+ overlay.insert(overlayEntry);
+ _overlayEntry = overlayEntry;
setState(() {
_isDropdownOpen = true;
});
@@ -291,18 +351,61 @@ class CustomCountryDropdownState extends State {
// });
// }
- void _closeDropdown() {
- _overlayEntry.remove();
+ void _selectCountry(CountryEnum country) {
+ if (!mounted) {
+ _removeOverlayEntry();
+ return;
+ }
+
+ _removeOverlayEntry();
setState(() {
+ selectedCountry = country;
_isDropdownOpen = false;
});
+ // Notify the parent only after this widget has finished updating and
+ // removing its overlay. The callback may synchronously close the parent.
+ widget.onCountryChange?.call(country);
+ _scheduleTextFieldRefocus();
+ }
+
+ void _closeDropdown() {
+ _showDropdownTimer?.cancel();
+ _showDropdownTimer = null;
+ _removeOverlayEntry();
+
+ if (!mounted) {
+ _isDropdownOpen = false;
+ return;
+ }
+
+ if (_isDropdownOpen) {
+ setState(() {
+ _isDropdownOpen = false;
+ });
+ }
+
+ _scheduleTextFieldRefocus();
+ }
+
+ void _removeOverlayEntry() {
+ final overlayEntry = _overlayEntry;
+ _overlayEntry = null;
+ if (overlayEntry == null) return;
+
+ overlayEntry.remove();
+ overlayEntry.dispose();
+ }
+
+ void _scheduleTextFieldRefocus() {
+ _refocusTimer?.cancel();
if (widget.isEnableTextField && widget.isFromBottomSheet) {
- Future.delayed(Duration(milliseconds: 100), () {
+ _refocusTimer = Timer(const Duration(milliseconds: 100), () {
+ _refocusTimer = null;
if (mounted && textFocusNode.canRequestFocus) {
FocusScope.of(context).requestFocus(textFocusNode);
}
});
}
}
-}
+}
\ No newline at end of file
diff --git a/pubspec.yaml b/pubspec.yaml
index 1685f8df..9a05eb01 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -2,8 +2,8 @@ name: hmg_patient_app_new
description: "New HMG Patient App"
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
-#version: 0.0.42+43
-version: 0.0.12+1
+version: 0.0.44+45
+#version: 0.0.14+1
environment:
sdk: ">=3.6.0 <4.0.0"