Merge branch 'master' into faiz_dev

# Conflicts:
#	lib/core/api_consts.dart
#	lib/presentation/home/landing_page.dart
#	lib/presentation/medical_file/medical_file_page.dart
pull/301/head
faizatflutter 15 hours ago
commit a9d7190348

@ -123,7 +123,7 @@
"forgotFileNoTitle": "نسيت الملف الطبي", "forgotFileNoTitle": "نسيت الملف الطبي",
"enterNationalId": "يرجى إدخال رقم الهاتف المحمول ورقم الهوية", "enterNationalId": "يرجى إدخال رقم الهاتف المحمول ورقم الهوية",
"profileInfo": "يرجى إدخال معلومات الملف الشخصي", "profileInfo": "يرجى إدخال معلومات الملف الشخصي",
"submit": "تحديث", "submit": "بحث",
"forgotDesc": "أدخل رقم الهاتف المحمول لاستلام رقم الملف الطبي عبر الرسائل القصيرة", "forgotDesc": "أدخل رقم الهاتف المحمول لاستلام رقم الملف الطبي عبر الرسائل القصيرة",
"dob": "تاريخ الميلاد", "dob": "تاريخ الميلاد",
"hijriDate": "التاريخ الهجري", "hijriDate": "التاريخ الهجري",
@ -1608,7 +1608,7 @@
"dietModerateCarbsDescription": "تناول معتدل للكربوهيدرات مع توازن في البروتين والدهون: يوفر طاقة مستقرة للأنشطة اليومية والتمارين مع قدر من المرونة. مناسب لكثير من الأشخاص الذين يسعون لإدارة وزن مستدامة وتحسين الأداء.", "dietModerateCarbsDescription": "تناول معتدل للكربوهيدرات مع توازن في البروتين والدهون: يوفر طاقة مستقرة للأنشطة اليومية والتمارين مع قدر من المرونة. مناسب لكثير من الأشخاص الذين يسعون لإدارة وزن مستدامة وتحسين الأداء.",
"dietUSDAGuidelinesDescription": "نمط متوازن مستند إلى (USDA): يركز على الفواكه والخضروات والحبوب الكاملة والبروتينات قليلة الدهن، مع الحد من السكريات المضافة والدهون المشبعة. يُعد إطارًا قائمًا على الأدلة لدعم الصحة العامة، وكفاية العناصر الغذائية، والوقاية من الأمراض المزمنة.", "dietUSDAGuidelinesDescription": "نمط متوازن مستند إلى (USDA): يركز على الفواكه والخضروات والحبوب الكاملة والبروتينات قليلة الدهن، مع الحد من السكريات المضافة والدهون المشبعة. يُعد إطارًا قائمًا على الأدلة لدعم الصحة العامة، وكفاية العناصر الغذائية، والوقاية من الأمراض المزمنة.",
"dietZoneDietDescription": "حمية الزون (~40٪ كربوهيدرات / 30٪ بروتين / 30٪ دهون): تركز على التوازن الهرموني وضبط الحصص الغذائية. قد تُحسّن تركيب الجسم ومستوى الطاقة لدى بعض الأشخاص، لكنها تتطلب تخطيطًا جيدًا؛ ويُفضّل تخصيصها بالتعاون مع مختص تغذية.", "dietZoneDietDescription": "حمية الزون (~40٪ كربوهيدرات / 30٪ بروتين / 30٪ دهون): تركز على التوازن الهرموني وضبط الحصص الغذائية. قد تُحسّن تركيب الجسم ومستوى الطاقة لدى بعض الأشخاص، لكنها تتطلب تخطيطًا جيدًا؛ ويُفضّل تخصيصها بالتعاون مع مختص تغذية.",
"contextDependent": عتمد السياق", "contextDependent": ختلف باختلاف الحالة",
"calculatedValue": "القيمة المحسوبة", "calculatedValue": "القيمة المحسوبة",
"calculationFormula": "صيغة الحساب", "calculationFormula": "صيغة الحساب",
"confirmYourLocation": "قم بتأكيد موقعك", "confirmYourLocation": "قم بتأكيد موقعك",
@ -1822,7 +1822,7 @@
"symptomsCheckSuggestions": "الاحتمالات", "symptomsCheckSuggestions": "الاحتمالات",
"appointmentArrived": "تم الحضور", "appointmentArrived": "تم الحضور",
"calendarPermissionAlertForAppointment": "يرجى منح إذن الوصول إلى التقويم من إعدادات التطبيق لضبط تذكير الموعد", "calendarPermissionAlertForAppointment": "يرجى منح إذن الوصول إلى التقويم من إعدادات التطبيق لضبط تذكير الموعد",
"rechargePageKey": "اختر المستفيد", "rechargePageKey": "شحن المحفظة",
"selectFamilyFile": "اختيار ", "selectFamilyFile": "اختيار ",
"ancillaryOrdersListNew": "طلبات غير مكتملة", "ancillaryOrdersListNew": "طلبات غير مكتملة",
"medicalReportNew": "التقارير الطبية", "medicalReportNew": "التقارير الطبية",
@ -1840,5 +1840,6 @@
"loadingAIAnalysisPrescription": "ٍتحليل الوصفات الطبية بواسطة الذكاء الاصطناعي، يرجى الانتظار...", "loadingAIAnalysisPrescription": "ٍتحليل الوصفات الطبية بواسطة الذكاء الاصطناعي، يرجى الانتظار...",
"thisAboveInfoPrescription": "تم تحليل الوصفة الطبية هذه بواسطة الذكاء الاصطناعي، وهي لا تُعدّ نصيحة طبية. استشر طبيبك المختص للتشخيص والعلاج.", "thisAboveInfoPrescription": "تم تحليل الوصفة الطبية هذه بواسطة الذكاء الاصطناعي، وهي لا تُعدّ نصيحة طبية. استشر طبيبك المختص للتشخيص والعلاج.",
"liveCareNotificationPermissionsMessage": "يتطلب لايف كير أذونات الإشعارات، يرجى السماح بهذه الأذونات للمتابعة.", "liveCareNotificationPermissionsMessage": "يتطلب لايف كير أذونات الإشعارات، يرجى السماح بهذه الأذونات للمتابعة.",
"weatherIndicators": "طقس" "weatherIndicators": "طقس",
"submitRating": "إرسال التقييم"
} }

@ -1830,7 +1830,8 @@
"loadingAIAnalysisPrescription": "Analysing your prescription, let the AI do the magic, This might take some time.", "loadingAIAnalysisPrescription": "Analysing your prescription, let the AI do the magic, This might take some time.",
"thisAboveInfoPrescription": "This prescription was analyzed by AI, and it is not medical advice. Consult your healthcare provider for diagnosis and treatment.", "thisAboveInfoPrescription": "This prescription was analyzed by AI, and it is not medical advice. Consult your healthcare provider for diagnosis and treatment.",
"liveCareNotificationPermissionsMessage": "LiveCare requires Notifications permission, Please allow to proceed.", "liveCareNotificationPermissionsMessage": "LiveCare requires Notifications permission, Please allow to proceed.",
"weatherIndicators": "Weather" "weatherIndicators": "Weather",
"submitRating": "Submit"
} }

@ -34,7 +34,7 @@ class ApiConsts {
// static String QLINE_URL = "https://qline.hmg.com/api/PatientCall/PatientInQueue_Detail"; // static String QLINE_URL = "https://qline.hmg.com/api/PatientCall/PatientInQueue_Detail";
static String CHAT_URL = "https://chat.hmg.com/Index.aspx?RequestedId="; static String CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId=";
// static String GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments"; // static String GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
@ -58,6 +58,7 @@ class ApiConsts {
symptomsCheckerUsername = 'mobile_user'; symptomsCheckerUsername = 'mobile_user';
symptomsCheckerPassword = '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92'; symptomsCheckerPassword = '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92';
CHAT_URL = "https://chat.hmg.com/geneysChat/Index.aspx?RequestedId=";
break; break;
case AppEnvironmentTypeEnum.dev: case AppEnvironmentTypeEnum.dev:
baseUrl = "https://uat.hmgwebservices.com/"; baseUrl = "https://uat.hmgwebservices.com/";

@ -351,6 +351,8 @@ class PushNotificationHandler {
init(BuildContext context) async { init(BuildContext context) async {
this.context = context; this.context = context;
await requestPermissions();
if (Platform.isIOS) { if (Platform.isIOS) {
voIPKit.getVoIPToken().then((value) { voIPKit.getVoIPToken().then((value) {
log("🎈 APNS VOIP KIT TOKEN: $value"); log("🎈 APNS VOIP KIT TOKEN: $value");
@ -441,7 +443,9 @@ class PushNotificationHandler {
sound: true, sound: true,
); );
if (permission.authorizationStatus == AuthorizationStatus.denied) return; if (permission.authorizationStatus == AuthorizationStatus.denied) return;
} else {} } else {
final permission = await FirebaseMessaging.instance.requestPermission();
}
try { try {
FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) async { FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) async {

@ -34,6 +34,7 @@ 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/authentication/login.dart';
import 'package:hmg_patient_app_new/presentation/authentication/saved_login_screen.dart'; import 'package:hmg_patient_app_new/presentation/authentication/saved_login_screen.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/my_family/my_family.dart';
import 'package:hmg_patient_app_new/routes/app_routes.dart'; import 'package:hmg_patient_app_new/routes/app_routes.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart'; import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart';
@ -368,11 +369,7 @@ class AuthenticationViewModel extends ChangeNotifier {
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
if (apiResponse.data['isSMSSent']) { if (apiResponse.data['isSMSSent']) {
_appState.setAppAuthToken = apiResponse.data['LogInTokenID']; _appState.setAppAuthToken = apiResponse.data['LogInTokenID'];
await sendActivationCode( await sendActivationCode(otpTypeEnum: otpTypeEnum, phoneNumber: phoneNumberController.text, nationalIdOrFileNumber: nationalIdController.text, isForRegister: false);
otpTypeEnum: otpTypeEnum,
phoneNumber: phoneNumberController.text,
nationalIdOrFileNumber: nationalIdController.text,
isForRegister: false);
} else { } else {
if (apiResponse.data['IsAuthenticated']) { if (apiResponse.data['IsAuthenticated']) {
await checkActivationCode( await checkActivationCode(
@ -434,10 +431,7 @@ class AuthenticationViewModel extends ChangeNotifier {
} }
final resultEither = await _authenticationRepo.sendActivationCodeRepo( final resultEither = await _authenticationRepo.sendActivationCodeRepo(
sendActivationCodeReq: request, sendActivationCodeReq: request, isRegister: checkIsUserComingForRegister(request: payload), languageID: 'er', isFormFamilyFile: isFormFamilyFile);
isRegister: checkIsUserComingForRegister(request: payload),
languageID: 'er',
isFormFamilyFile: isFormFamilyFile);
resultEither.fold( resultEither.fold(
(failure) async => await _errorHandlerService.handleError(failure: failure), (failure) async => await _errorHandlerService.handleError(failure: failure),
@ -514,9 +508,7 @@ class AuthenticationViewModel extends ChangeNotifier {
bool isSwitchUser = false, bool isSwitchUser = false,
int? patientID, int? patientID,
}) async { }) async {
bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true || _appState.getUserRegistrationPayload.patientOutSa == 1);
_appState.getUserRegistrationPayload.patientOutSa == true ||
_appState.getUserRegistrationPayload.patientOutSa == 1);
MyAppointmentsViewModel myAppointmentsVM = getIt<MyAppointmentsViewModel>(); MyAppointmentsViewModel myAppointmentsVM = getIt<MyAppointmentsViewModel>();
if (isSwitchUser && _appState.getSuperUserID == null) { if (isSwitchUser && _appState.getSuperUserID == null) {
@ -558,8 +550,7 @@ class AuthenticationViewModel extends ChangeNotifier {
request["ForRegisteration"] = _appState.getUserRegistrationPayload.isRegister; request["ForRegisteration"] = _appState.getUserRegistrationPayload.isRegister;
request["isRegister"] = false; request["isRegister"] = false;
final resultEither = final resultEither = await _authenticationRepo.checkActivationCodeRepo(newRequest: request, activationCode: activationCode.toString(), isRegister: true);
await _authenticationRepo.checkActivationCodeRepo(newRequest: request, activationCode: activationCode.toString(), isRegister: true);
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
@ -633,7 +624,9 @@ class AuthenticationViewModel extends ChangeNotifier {
await medicalVm.getFamilyFiles(status: 0); await medicalVm.getFamilyFiles(status: 0);
if (isFormFamilyFile) { if (isFormFamilyFile) {
await medicalVm.getAllPendingRecordsByResponseId(); await medicalVm.getAllPendingRecordsByResponseId();
_navigationService.pushAndReplace(AppRoutes.medicalFilePage); _navigationService.replaceAllRoutesAndNavigateToLandingWithMedicalAuth().whenComplete(() {
_navigationService.pushPageRoute(AppRoutes.familyFilesPage);
});
} else { } else {
_navigationService.replaceAllRoutesAndNavigateToLanding(); _navigationService.replaceAllRoutesAndNavigateToLanding();
} }
@ -870,8 +863,7 @@ class AuthenticationViewModel extends ChangeNotifier {
: _appState.getAuthenticatedUser()!.mobileNumber)!; : _appState.getAuthenticatedUser()!.mobileNumber)!;
nationalIdController.text = _appState.getAuthenticatedUser()!.patientIdentificationNo!; nationalIdController.text = _appState.getAuthenticatedUser()!.patientIdentificationNo!;
onSuccess(); onSuccess();
} else if ((loginTypeEnum == LoginTypeEnum.sms || } else if ((loginTypeEnum == LoginTypeEnum.sms || loginTypeEnum == LoginTypeEnum.whatsapp && _appState.getSelectDeviceByImeiRespModelElement == null) &&
loginTypeEnum == LoginTypeEnum.whatsapp && _appState.getSelectDeviceByImeiRespModelElement == null) &&
_appState.getAuthenticatedUser() != null) { _appState.getAuthenticatedUser() != null) {
phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0") phoneNumberController.text = (_appState.getAuthenticatedUser()!.mobileNumber!.startsWith("0")
? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "") ? _appState.getAuthenticatedUser()!.mobileNumber!.replaceFirst("0", "")
@ -928,8 +920,7 @@ class AuthenticationViewModel extends ChangeNotifier {
LoaderBottomSheet.showLoader(); LoaderBottomSheet.showLoader();
// LoadingUtils.showFullScreenLoader(loadingText: "Setting up your medical file.\nMay take a moment."); // LoadingUtils.showFullScreenLoader(loadingText: "Setting up your medical file.\nMay take a moment.");
var request = RequestUtils.getUserSignupCompletionRequest( var request = RequestUtils.getUserSignupCompletionRequest(fullName: nameController.text, emailAddress: emailController.text, gender: genderType, maritalStatus: maritalStatus);
fullName: nameController.text, emailAddress: emailController.text, gender: genderType, maritalStatus: maritalStatus);
final resultEither = await _authenticationRepo.registerUser(registrationPayloadDataModelRequest: request); final resultEither = await _authenticationRepo.registerUser(registrationPayloadDataModelRequest: request);
resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async { resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async {
@ -1059,8 +1050,7 @@ class AuthenticationViewModel extends ChangeNotifier {
biometricEnabled: loginType == 1 || loginType == 2 ? false : true, biometricEnabled: loginType == 1 || loginType == 2 ? false : true,
firstNameN: _appState.getAuthenticatedUser()!.firstNameN, firstNameN: _appState.getAuthenticatedUser()!.firstNameN,
lastNameN: _appState.getAuthenticatedUser()!.lastNameN, lastNameN: _appState.getAuthenticatedUser()!.lastNameN,
zipCode: zipCode: _appState.getAuthenticatedUser()!.zipCode //selectedCountrySignup == CountryEnum.others ? '0': selectedCountrySignup.countryCode,
_appState.getAuthenticatedUser()!.zipCode //selectedCountrySignup == CountryEnum.others ? '0': selectedCountrySignup.countryCode,
) )
.toJson()); .toJson());
resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async { resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async {

@ -16,6 +16,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/
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_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/models/resp_models/patient_appointment_share_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_queue_details_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_queue_details_response_model.dart';
import 'package:hmg_patient_app_new/features/my_invoices/models/get_invoices_list_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'models/req_model/appointment_rate_req_model.dart'; import 'models/req_model/appointment_rate_req_model.dart';
@ -89,6 +90,8 @@ abstract class MyAppointmentsRepo {
required int clinicID, required int clinicID,
required int appointmentNo, required int appointmentNo,
}); });
Future<Either<Failure, GenericApiModel<List<GetInvoicesListResponseModel>>>> getAppointmentInvoice(num appointmentNum);
} }
class MyAppointmentsRepoImp implements MyAppointmentsRepo { class MyAppointmentsRepoImp implements MyAppointmentsRepo {
@ -1142,4 +1145,42 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo {
return Left(UnknownFailure(e.toString())); return Left(UnknownFailure(e.toString()));
} }
} }
@override
Future<Either<Failure, GenericApiModel<List<GetInvoicesListResponseModel>>>> getAppointmentInvoice(num appointmentNum) async {
Map<String, dynamic> mapDevice = {"AppointmentNo": appointmentNum};
try {
GenericApiModel<List<GetInvoicesListResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_ALL_APPOINTMENTS_FOR_DENTAL_CLINIC,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['List_AllInvoices'];
final invoicesList = list.map((item) => GetInvoicesListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetInvoicesListResponseModel>();
apiResponse = GenericApiModel<List<GetInvoicesListResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: invoicesList,
);
} 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()));
}
}
} }

@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/app_state.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/calender_utils_new.dart'; import 'package:hmg_patient_app_new/core/utils/calender_utils_new.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
@ -17,6 +18,8 @@ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_queue_details_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_queue_details_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart'; import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart';
import 'package:hmg_patient_app_new/features/my_invoices/models/get_invoices_list_response_model.dart';
import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_repo.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart';
@ -25,6 +28,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:open_filex/open_filex.dart';
class MyAppointmentsViewModel extends ChangeNotifier { class MyAppointmentsViewModel extends ChangeNotifier {
int selectedTabIndex = 0; int selectedTabIndex = 0;
@ -1519,4 +1523,74 @@ class MyAppointmentsViewModel extends ChangeNotifier {
); );
} }
} }
Future<void> getAppointmentInvoice({required num appointmentNum, Function(dynamic)? onSuccess, Function(String)? onError}) async {
String? downloadInvoicePDFBase64 = "";
final result = await myAppointmentsRepo.getAppointmentInvoice(appointmentNum);
result.fold(
(failure) async {
if (onError != null) {
onError(failure.message ?? "Unknown error");
}
},
(apiResponse) async {
if (apiResponse.messageStatus == 2) {
if (onError != null) {
onError(apiResponse.errorMessage ?? "Unknown error");
}
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
if (apiResponse.data!.isNotEmpty) {
GetInvoicesListResponseModel getInvoicesListResponseModel = apiResponse.data!.first;
final result = await getIt
.get<MyInvoicesRepo>()
.downloadInvoice(setupId: getInvoicesListResponseModel.setupId!, invoiceNo: getInvoicesListResponseModel.invoiceNo!, projectID: getInvoicesListResponseModel.projectId!);
result.fold(
(failure) async {
if (onError != null) {
onError(failure.message);
}
},
(apiResponse) async {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
if (apiResponse.data != null && apiResponse.data!.isNotEmpty) {
downloadInvoicePDFBase64 = apiResponse.data!;
String path = await Utils.createFileFromString(downloadInvoicePDFBase64!, "pdf");
try {
OpenFilex.open(path);
} catch (ex) {
showCommonBottomSheetWithoutHeight(
GetIt.instance<NavigationService>().navigatorKey.currentContext!,
child: Utils.getErrorWidget(loadingText: "Cannot open file"),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
} else {
downloadInvoicePDFBase64 = "";
}
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
} else {
showCommonBottomSheetWithoutHeight(
GetIt.instance<NavigationService>().navigatorKey.currentContext!,
child: Utils.getErrorWidget(loadingText: "Cannot open invoice"),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
}
},
);
}
} }

@ -61,10 +61,18 @@ class RadiologyViewModel extends ChangeNotifier {
} }
Future<void> getPatientRadiologyOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientRadiologyOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async {
patientRadiologyOrders.clear();
notifyListeners();
final result = await radiologyRepo.getPatientRadiologyOrders(); final result = await radiologyRepo.getPatientRadiologyOrders();
result.fold( result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure), // (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) {
isRadiologyOrdersLoading = false;
filterSuggestions();
notifyListeners();
},
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});

@ -1831,7 +1831,8 @@ abstract class LocaleKeys {
static const generateAiAnalysisPrescription = 'generateAiAnalysisPrescription'; static const generateAiAnalysisPrescription = 'generateAiAnalysisPrescription';
static const loadingAIAnalysisPrescription = 'loadingAIAnalysisPrescription'; static const loadingAIAnalysisPrescription = 'loadingAIAnalysisPrescription';
static const thisAboveInfoPrescription = 'thisAboveInfoPrescription'; static const thisAboveInfoPrescription = 'thisAboveInfoPrescription';
static const weatherIndicators = 'weatherIndicators';
static const liveCareNotificationPermissionsMessage = 'liveCareNotificationPermissionsMessage'; static const liveCareNotificationPermissionsMessage = 'liveCareNotificationPermissionsMessage';
static const weatherIndicators = 'weatherIndicators';
static const submitRating = 'submitRating';
} }

@ -802,6 +802,32 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
), ),
); );
}), }),
MedicalFileCard(
label: LocaleKeys.viewInvoiceDetails.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.invoices_list_icon,
isLargeText: true,
iconSize: 36.w,
).onPress(() {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingInvoiceDetails.tr(context: context));
myAppointmentsViewModel.getAppointmentInvoice(
appointmentNum: widget.patientAppointmentHistoryResponseModel.appointmentNo,
onSuccess: (val) {
LoaderBottomSheet.hideLoader();
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
}),
myAppointmentsVM.isAppointmentRatedResponseLoading myAppointmentsVM.isAppointmentRatedResponseLoading
? MedicalFileCard( ? MedicalFileCard(
label: "Medical File", label: "Medical File",

@ -294,7 +294,7 @@ class _AppointmentCardState extends State<AppointmentCard> {
SizedBox(height: 8.h), SizedBox(height: 8.h),
Wrap( Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
spacing: 3.h, spacing: 6.h,
runSpacing: 4.h, runSpacing: 4.h,
children: [ children: [
AppCustomChipWidget( AppCustomChipWidget(

@ -9,6 +9,7 @@ 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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
@ -31,6 +32,7 @@ class _ImmediateLiveCarePendingRequestPageState extends State<ImmediateLiveCareP
late ImmediateLiveCareViewModel immediateLiveCareViewModel; late ImmediateLiveCareViewModel immediateLiveCareViewModel;
late AppState appState; late AppState appState;
String chatURL = "";
@override @override
void initState() { void initState() {
@ -157,7 +159,12 @@ class _ImmediateLiveCarePendingRequestPageState extends State<ImmediateLiveCareP
CustomButton( CustomButton(
text: LocaleKeys.startLiveChat.tr(), text: LocaleKeys.startLiveChat.tr(),
onPressed: () async { onPressed: () async {
launchUrl(Uri.parse("https://chat.hmg.com/geneysChat/Index.aspx?RequestedId=44A515DF-E668-46B5-BD76-D7747D9C7308")); // launchUrl(Uri.parse("https://chat.hmg.com/geneysChat/Index.aspx?RequestedId=44A515DF-E668-46B5-BD76-D7747D9C7308"));
_getChatRequestID(
context,
name: appState.getAuthenticatedUser()!.firstName ?? '',
mobileNo: appState.getAuthenticatedUser()!.mobileNumber ?? '',
);
}, },
backgroundColor: AppColors.primaryRedColor, backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor,
@ -216,6 +223,24 @@ class _ImmediateLiveCarePendingRequestPageState extends State<ImmediateLiveCareP
); );
} }
void _getChatRequestID(BuildContext context, {required String name, required String mobileNo}) {
getIt.get<ContactUsViewModel>().getChatRequestID(
name: name,
mobileNo: mobileNo,
workGroup: "Live_Care_Chat",
onSuccess: (response) {
debugPrint("Chat Request ID received: ${getIt.get<ContactUsViewModel>().chatRequestID}");
chatURL = "https://chat.hmg.com/Index.aspx?RequestedId=${getIt.get<ContactUsViewModel>().chatRequestID}";
debugPrint("Chat URL: $chatURL");
Uri uri = Uri.parse(chatURL);
launchUrl(uri, mode: LaunchMode.platformDefault, webOnlyWindowName: "");
},
onError: (error) {
debugPrint("Error getting chat request ID: $error");
},
);
}
// void startTimer() { // void startTimer() {
// // timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime()); // // timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime());
// // setState(() {}); // // setState(() {});

@ -110,9 +110,9 @@ class WeatherWidget extends StatelessWidget {
); );
}, },
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
backgroundColor: AppColors.secondaryLightRedColor, backgroundColor: AppColors.infoColor.withValues(alpha: 0.12),
borderColor: AppColors.secondaryLightRedColor, borderColor: AppColors.infoColor.withValues(alpha: 0.01),
textColor: AppColors.primaryRedColor, textColor: AppColors.infoColor,
fontSize: 14.f, fontSize: 14.f,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
borderRadius: 10.r, borderRadius: 10.r,

@ -146,15 +146,10 @@ class _LandingPageState extends State<LandingPage> {
notificationsViewModel.initNotificationsViewModel(); notificationsViewModel.initNotificationsViewModel();
insuranceViewModel.initInsuranceProvider(); insuranceViewModel.initInsuranceProvider();
// Commented as per new requirement to remove rating popup from the app
if(!appState.isRatedVisible) { if(!appState.isRatedVisible) {
appointmentRatingViewModel.getLastRatingAppointment( appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) {
onSuccess: (response) {
if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) { if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) {
appointmentRatingViewModel.getAppointmentDetails( appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!,
appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!,
appointmentRatingViewModel.appointmentRatedList.last.projectID!,
onSuccess: ((response) { onSuccess: ((response) {
appointmentRatingViewModel.setClinicOrDoctor(false); appointmentRatingViewModel.setClinicOrDoctor(false);
appointmentRatingViewModel.setTitle(LocaleKeys.rateDoctor.tr(context: context)); appointmentRatingViewModel.setTitle(LocaleKeys.rateDoctor.tr(context: context));
@ -1455,10 +1450,6 @@ class _DirectionalDotPaginationBuilder extends SwiperPlugin {
@override @override
Widget build(BuildContext context, SwiperPluginConfig config) { Widget build(BuildContext context, SwiperPluginConfig config) {
final int activeIndex = isRTL ? (totalCount - 1 - config.activeIndex) : config.activeIndex;
print("the index is $activeIndex");
print("the config.activeIndex is ${config.activeIndex}");
return Row( return Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
textDirection: ui.TextDirection.ltr, // always lay out dots left-to-right textDirection: ui.TextDirection.ltr, // always lay out dots left-to-right

@ -25,9 +25,8 @@ class _SearchLabResultsContentState extends State<SearchLabResultsContent> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
filteredSuggestions = List.from(widget.labSuggestionsList); filteredSuggestions = List.from(widget.labSuggestionsList)..sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase()));
// Listen for changes in the search field
searchEditingController.addListener(() { searchEditingController.addListener(() {
filterSuggestions(); filterSuggestions();
}); });
@ -44,11 +43,11 @@ class _SearchLabResultsContentState extends State<SearchLabResultsContent> {
if (query.isEmpty) { if (query.isEmpty) {
setState(() { setState(() {
filteredSuggestions = List.from(widget.labSuggestionsList); filteredSuggestions = List.from(widget.labSuggestionsList)..sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase()));
}); });
} else { } else {
setState(() { setState(() {
filteredSuggestions = widget.labSuggestionsList.where((suggestion) => suggestion.toLowerCase().contains(query)).toList(); filteredSuggestions = widget.labSuggestionsList.where((suggestion) => suggestion.toLowerCase().contains(query)).toList()..sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase()));
}); });
} }
} }

@ -40,6 +40,9 @@ import 'package:hmg_patient_app_new/presentation/appointments/widgets/ask_doctor
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart';
import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_approvals_page.dart'; import 'package:hmg_patient_app_new/presentation/insurance/insurance_approvals_page.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/presentation/insurance/widgets/insurance_update_details_card.dart'; import 'package:hmg_patient_app_new/presentation/insurance/widgets/insurance_update_details_card.dart';

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -318,20 +319,21 @@ class ProfileSettingsState extends State<ProfileSettings> {
// actionItem(AppAssets.permission, LocaleKeys.permissions.tr(context: context), () {}, trailingLabel: "Location, Camera"), // actionItem(AppAssets.permission, LocaleKeys.permissions.tr(context: context), () {}, trailingLabel: "Location, Camera"),
// 1.divider, // 1.divider,
actionItem(AppAssets.rate, LocaleKeys.rateApp.tr(context: context), () async { actionItem(AppAssets.rate, LocaleKeys.rateApp.tr(context: context), () async {
if (await inAppReview.isAvailable()) { // if (await inAppReview.isAvailable()) {
inAppReview.requestReview(); // inAppReview.requestReview();
} else { // } else {
// inAppReview.openStoreListing(appStoreId: '6758851027');
// }
if (Platform.isAndroid) {
inAppReview.openStoreListing(appStoreId: '6758851027'); inAppReview.openStoreListing(appStoreId: '6758851027');
}
// if (Platform.isAndroid) {
// Utils.openWebView( // Utils.openWebView(
// url: 'https://play.google.com/store/apps/details?id=com.ejada.hmg', // url: 'https://play.google.com/store/apps/details?id=com.ejada.hmg',
// ); // );
// } else { } else {
// Utils.openWebView( if (await inAppReview.isAvailable()) {
// url: 'https://itunes.apple.com/app/id733503978', inAppReview.requestReview();
// ); }
// } }
// }, isExternalLink: true), // }, isExternalLink: true),
}), }),
1.divider, 1.divider,

@ -192,7 +192,7 @@ class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
SizedBox(width: 10), SizedBox(width: 10),
Expanded( Expanded(
child: CustomButton( child: CustomButton(
text: LocaleKeys.submit.tr(context: context), text: LocaleKeys.submitRating.tr(context: context),
onPressed: rating > 0 onPressed: rating > 0
? () { ? () {
LoaderBottomSheet.showLoader(); LoaderBottomSheet.showLoader();

@ -18,6 +18,7 @@ import 'package:hmg_patient_app_new/presentation/home/app_update_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_procedures_page.dart'; import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_procedures_page.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/presentation/my_family/my_family.dart';
import 'package:hmg_patient_app_new/presentation/servicesPriceList/services_price_list_page.dart'; import 'package:hmg_patient_app_new/presentation/servicesPriceList/services_price_list_page.dart';
import 'package:hmg_patient_app_new/presentation/smartwatches/smartwatch_home_page.dart'; import 'package:hmg_patient_app_new/presentation/smartwatches/smartwatch_home_page.dart';
import 'package:hmg_patient_app_new/presentation/symptoms_checker/organ_selector_screen.dart'; import 'package:hmg_patient_app_new/presentation/symptoms_checker/organ_selector_screen.dart';
@ -97,6 +98,9 @@ class AppRoutes {
// Weather // Weather
static const String weatherDetailsPage = '/weatherDetailsPage'; static const String weatherDetailsPage = '/weatherDetailsPage';
// Weather
static const String familyFilesPage = '/familyFilesPage';
static Map<String, WidgetBuilder> get routes => { static Map<String, WidgetBuilder> get routes => {
initialRoute: (context) => SplashPage(), initialRoute: (context) => SplashPage(),
loginScreen: (context) => LoginScreen(), loginScreen: (context) => LoginScreen(),
@ -134,6 +138,7 @@ class AppRoutes {
emergencyServicesPage: (context) => EmergencyServicesPage(), emergencyServicesPage: (context) => EmergencyServicesPage(),
servicesPriceListPage: (context) => ServicesPriceListPage(), servicesPriceListPage: (context) => ServicesPriceListPage(),
weatherDetailsPage: (context) => WeatherDetailsPage(), weatherDetailsPage: (context) => WeatherDetailsPage(),
familyFilesPage: (context) => FamilyMedicalScreen(),
addHealthTrackerEntryPage: (context) { addHealthTrackerEntryPage: (context) {
final args = ModalRoute.of(context)?.settings.arguments as HealthTrackerTypeEnum?; final args = ModalRoute.of(context)?.settings.arguments as HealthTrackerTypeEnum?;
return AddHealthTrackerEntryPage( return AddHealthTrackerEntryPage(

@ -25,6 +25,7 @@ class NavigationService {
navigatorKey.currentState?.popUntil(ModalRoute.withName(routeName)); navigatorKey.currentState?.popUntil(ModalRoute.withName(routeName));
} }
void replaceAllRoutesAndNavigateToLanding() { void replaceAllRoutesAndNavigateToLanding() {
AppState appState = getIt.get<AppState>(); AppState appState = getIt.get<AppState>();
appState.isAuthenticated = false; appState.isAuthenticated = false;
@ -34,6 +35,13 @@ class NavigationService {
); );
} }
Future replaceAllRoutesAndNavigateToLandingWithMedicalAuth() async {
navigatorKey.currentState?.pushNamedAndRemoveUntil(
AppRoutes.landingScreen,
(Route<dynamic> route) => false,
);
}
void replaceAllRoutesAndNavigateToLandingAuth() { void replaceAllRoutesAndNavigateToLandingAuth() {
navigatorKey.currentState?.pushNamedAndRemoveUntil( navigatorKey.currentState?.pushNamedAndRemoveUntil(
AppRoutes.landingScreen, AppRoutes.landingScreen,
@ -50,17 +58,10 @@ class NavigationService {
} }
Future<T?> pushToOtpScreen<T>( Future<T?> pushToOtpScreen<T>(
{required String phoneNumber, {required String phoneNumber, required Function(int code) checkActivationCode, required Function(String phoneNumber) onResendOTPPressed, bool isFormFamilyFile = false}) {
required Function(int code) checkActivationCode,
required Function(String phoneNumber) onResendOTPPressed,
bool isFormFamilyFile = false}) {
return navigatorKey.currentState!.push( return navigatorKey.currentState!.push(
MaterialPageRoute( MaterialPageRoute(
builder: (_) => OTPVerificationScreen( builder: (_) => OTPVerificationScreen(phoneNumber: phoneNumber, checkActivationCode: checkActivationCode, onResendOTPPressed: onResendOTPPressed, isFormFamilyFile: isFormFamilyFile)),
phoneNumber: phoneNumber,
checkActivationCode: checkActivationCode,
onResendOTPPressed: onResendOTPPressed,
isFormFamilyFile: isFormFamilyFile)),
); );
} }

@ -57,7 +57,7 @@ class _FamilyFileAddWidgetState extends State<FamilyFileAddWidget> {
isAllowRadius: true, isAllowRadius: true,
isBorderAllowed: false, isBorderAllowed: false,
isAllowLeadingIcon: true, isAllowLeadingIcon: true,
autoFocus: true, autoFocus: false,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
fontFamily: "Poppins", fontFamily: "Poppins",
padding: EdgeInsets.symmetric(vertical: 8.h), padding: EdgeInsets.symmetric(vertical: 8.h),
@ -76,7 +76,7 @@ class _FamilyFileAddWidgetState extends State<FamilyFileAddWidget> {
isAllowRadius: true, isAllowRadius: true,
isBorderAllowed: false, isBorderAllowed: false,
isAllowLeadingIcon: true, isAllowLeadingIcon: true,
autoFocus: true, autoFocus: false,
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
fontFamily: "Poppins", fontFamily: "Poppins",
padding: EdgeInsets.symmetric(vertical: 8.h), padding: EdgeInsets.symmetric(vertical: 8.h),
@ -91,7 +91,9 @@ class _FamilyFileAddWidgetState extends State<FamilyFileAddWidget> {
CustomButton( CustomButton(
text: LocaleKeys.add.tr(context: context), text: LocaleKeys.add.tr(context: context),
onPressed: () { onPressed: () {
FocusScope.of(context).unfocus(); // Unfocus all text fields and dismiss keyboard
FocusManager.instance.primaryFocus?.unfocus();
if (ValidationUtils.isValidatedIdAndPhoneWithCountryValidation( if (ValidationUtils.isValidatedIdAndPhoneWithCountryValidation(
nationalId: authVm.nationalIdController.text, nationalId: authVm.nationalIdController.text,
selectedCountry: authVm.selectedCountrySignup, selectedCountry: authVm.selectedCountrySignup,

@ -2,8 +2,8 @@ name: hmg_patient_app_new
description: "New HMG Patient App" description: "New HMG Patient App"
publish_to: 'none' # Remove this line if you wish to publish to pub.dev publish_to: 'none' # Remove this line if you wish to publish to pub.dev
#version: 0.0.22+21 version: 0.0.25+24
version: 0.0.1+20 #version: 0.0.1+23
environment: environment:
sdk: ">=3.6.0 <4.0.0" sdk: ">=3.6.0 <4.0.0"

Loading…
Cancel
Save