Merge pull request 'dev_sultan' (#197) from dev_sultan into master

Reviewed-on: https://34.17.182.140/Haroon6138/HMG_Patient_App_New/pulls/197
dev_sikander
Haroon6138 3 weeks ago
commit 24fa8e18f3

@ -814,6 +814,7 @@
"ready": "جاهز", "ready": "جاهز",
"enterValidNationalId": "الرجاء إدخال رقم الهوية الوطنية أو رقم الملف الصحيح", "enterValidNationalId": "الرجاء إدخال رقم الهوية الوطنية أو رقم الملف الصحيح",
"enterValidPhoneNumber": "الرجاء إدخال رقم هاتف صالح", "enterValidPhoneNumber": "الرجاء إدخال رقم هاتف صالح",
"cannotEnterSaudiOrUAENumber": "لا يمكنك إدخال أرقام هواتف السعودية (00966) أو الإمارات (00971) عند اختيار دولة 'أخرى'",
"medicalCentersWithCount": "{count} مراكز طبية", "medicalCentersWithCount": "{count} مراكز طبية",
"medicalCenters": "مراكز طبية", "medicalCenters": "مراكز طبية",
"hospitalsWithCount": "{count} مستشفيات", "hospitalsWithCount": "{count} مستشفيات",

@ -804,6 +804,7 @@
"awaitingApproval": "Awaiting Approval", "awaitingApproval": "Awaiting Approval",
"enterValidNationalId": "Please enter a valid national ID or file number", "enterValidNationalId": "Please enter a valid national ID or file number",
"enterValidPhoneNumber": "Please enter a valid phone number", "enterValidPhoneNumber": "Please enter a valid phone number",
"cannotEnterSaudiOrUAENumber": "You cannot enter Saudi Arabia (00966) or UAE (00971) phone numbers when 'Others' country is selected",
"ready": "Ready", "ready": "Ready",
"medicalCentersWithCount": "{count} Medical Centers", "medicalCentersWithCount": "{count} Medical Centers",
"medicalCenters": " Medical Centers", "medicalCenters": " Medical Centers",

@ -194,6 +194,11 @@ class ApiClientImp implements ApiClient {
body['TokenID'] = "@dm!n"; body['TokenID'] = "@dm!n";
} }
if (url == 'https://uat.hmgwebservices.com/Services/NHIC.svc/REST/GetPatientInfo') {
url = "https://hmgwebservices.com/Services/NHIC.svc/REST/GetPatientInfo";
body['TokenID'] = "@dm!n";
}
// body['TokenID'] = "@dm!n"; // body['TokenID'] = "@dm!n";
// body['PatientID'] = 809289; // body['PatientID'] = 809289;
// body['PatientTypeID'] = 1; // body['PatientTypeID'] = 1;
@ -207,7 +212,7 @@ class ApiClientImp implements ApiClient {
// body['PatientID'] = 809289; // body['PatientID'] = 809289;
} }
if(!url.contains("/paymentApi")) { if (!url.contains("/paymentApi")) {
body['IsNewFlutterApp'] = true; body['IsNewFlutterApp'] = true;
} }
@ -261,10 +266,9 @@ class ApiClientImp implements ApiClient {
ApiClient._navigationService.pushAndRemoveUntil( ApiClient._navigationService.pushAndRemoveUntil(
CustomPageRoute( CustomPageRoute(
page: AppUpdatePage( page: AppUpdatePage(
appUpdateMessage: parsed['ErrorEndUserMessage'], appUpdateMessage: parsed['ErrorEndUserMessage'],
)), )),
ModalRoute.withName("/appUpdate") ModalRoute.withName("/appUpdate"));
);
logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode); logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
} }
if (parsed['ErrorType'] == 2) { if (parsed['ErrorType'] == 2) {
@ -272,8 +276,7 @@ class ApiClientImp implements ApiClient {
onFailure( onFailure(
parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
statusCode, statusCode,
failureType: failureType: UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url),
UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url),
); );
// logApiEndpointError(endPoint, "session logged out", statusCode); // logApiEndpointError(endPoint, "session logged out", statusCode);
} }

@ -112,10 +112,13 @@ class ApiConsts {
static final String selectDeviceImei = 'Services/Patients.svc/REST/Patient_SELECTDeviceIMEIbyIMEI'; static final String selectDeviceImei = 'Services/Patients.svc/REST/Patient_SELECTDeviceIMEIbyIMEI';
static final String checkPatientAuth = 'Services/Authentication.svc/REST/CheckPatientAuthentication'; static final String checkPatientAuth = 'Services/Authentication.svc/REST/CheckPatientAuthentication';
static final String checkPatientAuthOthers = 'Services/Authentication.svc/REST/CheckPatientAuthentication_Others';
static final String sendActivationCode = 'Services/Authentication.svc/REST/SendActivationCodebyOTPNotificationType'; static final String sendActivationCode = 'Services/Authentication.svc/REST/SendActivationCodebyOTPNotificationType';
static final String sendActivationCodeOthers = 'Services/Authentication.svc/REST/SendActivationCodebyOTPNotificationType_Others';
static final String sendActivationCodeRegister = 'Services/Authentication.svc/REST/SendActivationCodebyOTPNotificationTypeForRegistration'; static final String sendActivationCodeRegister = 'Services/Authentication.svc/REST/SendActivationCodebyOTPNotificationTypeForRegistration';
static final String checkActivationCode = 'Services/Authentication.svc/REST/CheckActivationCode'; static final String checkActivationCode = 'Services/Authentication.svc/REST/CheckActivationCode';
static final String checkActivationCodeOthers = 'Services/Authentication.svc/REST/CheckActivationCode_Others';
static final String checkActivationCodeRegister = 'Services/Authentication.svc/REST/CheckActivationCodeForRegistration'; static final String checkActivationCodeRegister = 'Services/Authentication.svc/REST/CheckActivationCodeForRegistration';
static final String checkUsageAgreement = 'Services/Patients.svc/REST/CheckForUsageAgreement'; static final String checkUsageAgreement = 'Services/Patients.svc/REST/CheckForUsageAgreement';
static final String getUserAgreementContent = 'Services/Patients.svc/REST/GetUsageAgreementText'; static final String getUserAgreementContent = 'Services/Patients.svc/REST/GetUsageAgreementText';
@ -124,6 +127,7 @@ class ApiConsts {
static final String checkUserStatus = 'Services/NHIC.svc/REST/GetPatientInfo'; static final String checkUserStatus = 'Services/NHIC.svc/REST/GetPatientInfo';
static final String insertPatientDeviceIMEIData = 'Services/Patients.svc/REST/Patient_INSERTDeviceIMEI'; static final String insertPatientDeviceIMEIData = 'Services/Patients.svc/REST/Patient_INSERTDeviceIMEI';
static final String insertPatientDeviceIMEIDataOthers = 'Services/Patients.svc/REST/Patient_INSERTDeviceIMEI_Others';
static final String insertPatientMobileData = 'Services/MobileNotifications.svc/REST/Insert_PatientMobileDeviceInfo'; static final String insertPatientMobileData = 'Services/MobileNotifications.svc/REST/Insert_PatientMobileDeviceInfo';
static final String getPatientMobileData = '/Services/Authentication.svc/REST/GetMobileLoginInfo'; static final String getPatientMobileData = '/Services/Authentication.svc/REST/GetMobileLoginInfo';
static final String getPrivileges = 'Services/Patients.svc/REST/Service_Privilege'; static final String getPrivileges = 'Services/Patients.svc/REST/Service_Privilege';

@ -16,6 +16,7 @@ class AppAssets {
static const String call = '$svgBasePath/call.svg'; static const String call = '$svgBasePath/call.svg';
static const String email = '$svgBasePath/email.svg'; static const String email = '$svgBasePath/email.svg';
static const String globe = '$svgBasePath/globe.svg'; static const String globe = '$svgBasePath/globe.svg';
static const String globeOther = '$svgBasePath/globe_other.svg';
static const String cancel = '$svgBasePath/cancel.svg'; static const String cancel = '$svgBasePath/cancel.svg';
static const String bell = '$svgBasePath/bell.svg'; static const String bell = '$svgBasePath/bell.svg';
static const String login1 = '$svgBasePath/login1.svg'; static const String login1 = '$svgBasePath/login1.svg';

@ -116,6 +116,7 @@ class AppState {
set setIsAuthenticated(v) => isAuthenticated = v; set setIsAuthenticated(v) => isAuthenticated = v;
String deviceTypeID = ""; String deviceTypeID = "";
set setDeviceTypeID(v) => deviceTypeID = v; set setDeviceTypeID(v) => deviceTypeID = v;

@ -12,7 +12,7 @@ enum AuthMethodTypesEnum { sms, whatsApp, fingerPrint, faceID, moreOptions }
enum ViewStateEnum { hide, idle, busy, error, busyLocal, errorLocal } enum ViewStateEnum { hide, idle, busy, error, busyLocal, errorLocal }
enum CountryEnum { saudiArabia, unitedArabEmirates } enum CountryEnum { saudiArabia, unitedArabEmirates, others }
enum CalenderEnum { gregorian, hijri } enum CalenderEnum { gregorian, hijri }

@ -27,14 +27,21 @@ class RequestUtils {
}) { }) {
bool fileNo = false; bool fileNo = false;
if (nationId.isNotEmpty) { if (nationId.isNotEmpty) {
fileNo = nationId.length < 10; final numericRegex = RegExp(r'^[0-9]+$');
fileNo = nationId.length < 10 && numericRegex.hasMatch(nationId);
//fileNo = nationId.length < 10 && nationId.isNumericOnly() ;
if (fileNo) { if (fileNo) {
patientId = int.tryParse(nationId); patientId = int.tryParse(nationId);
} }
} }
var request = SendActivationRequest(); var request = SendActivationRequest();
if (phoneNumber.isNotEmpty) { if (phoneNumber.isNotEmpty) {
request.patientMobileNumber = int.parse(phoneNumber); // If zipCode is "0", it means "Others" country is selected
if (zipCode == "0") {
request.patientMobileNumberOthers = phoneNumber;
} else {
request.patientMobileNumber = int.parse(phoneNumber);
}
} }
request.oTPSendType = otpTypeEnum.toInt(); // could map OTPTypeEnum if needed request.oTPSendType = otpTypeEnum.toInt(); // could map OTPTypeEnum if needed
request.zipCode = zipCode; // or countryCode if defined elsewhere request.zipCode = zipCode; // or countryCode if defined elsewhere
@ -53,9 +60,9 @@ class RequestUtils {
request.patientIdentificationID = request.nationalID; request.patientIdentificationID = request.nationalID;
request.searchType = 2; request.searchType = 2;
} else { } else {
request.patientID = 0; request.patientID = 0;
request.searchType = 1; request.searchType = 1;
request.patientIdentificationID = request.nationalID = nationId.isNotEmpty ? int.parse(nationId) : 0; request.patientIdentificationID = request.nationalID = nationId.isNotEmpty ? nationId : '0';
} }
request.isRegister = false; request.isRegister = false;
} }
@ -75,11 +82,18 @@ class RequestUtils {
required int loginType}) { required int loginType}) {
bool fileNo = false; bool fileNo = false;
if (nationIdText.isNotEmpty) { if (nationIdText.isNotEmpty) {
fileNo = nationIdText.length < 10; final numericRegex = RegExp(r'^[0-9]+$');
fileNo = nationIdText.length < 10 && numericRegex.hasMatch(nationIdText);
} }
var request = SendActivationRequest(); var request = SendActivationRequest();
request.patientMobileNumber = int.parse(phoneNumber); // If countryCode is "0", it means "Others" country is selected
request.mobileNo = '0$phoneNumber'; if (countryCode == "0") {
request.patientMobileNumberOthers = phoneNumber;
request.mobileNo = phoneNumber;
} else {
request.patientMobileNumber = int.parse(phoneNumber);
request.mobileNo = '0$phoneNumber';
}
request.deviceToken = deviceToken; request.deviceToken = deviceToken;
request.projectOutSA = patientOutSA; request.projectOutSA = patientOutSA;
request.loginType = loginType; request.loginType = loginType;
@ -102,14 +116,14 @@ class RequestUtils {
} else { } else {
if (fileNo) { if (fileNo) {
request.patientID = patientId ?? int.parse(nationIdText); request.patientID = patientId ?? int.parse(nationIdText);
request.patientIdentificationID = request.nationalID = 0; request.patientIdentificationID = request.nationalID = '0';
request.searchType = 2; request.searchType = 2;
//TODO: Issue HEre is Not Login //TODO: Issue HEre is Not Login
} else { } else {
request.patientID = 0; request.patientID = 0;
request.searchType = 1; request.searchType = 1;
//TODO: Issue HEre is Not Login //TODO: Issue HEre is Not Login
request.patientIdentificationID = request.nationalID = (nationIdText.isNotEmpty ? int.parse(nationIdText) : 0); request.patientIdentificationID = request.nationalID = (nationIdText.isNotEmpty ? nationIdText : '0');
} }
request.isRegister = false; request.isRegister = false;
} }
@ -123,7 +137,7 @@ class RequestUtils {
required String mobileNumber, required String mobileNumber,
required String zipCode, required String zipCode,
required int? patientId, required int? patientId,
required int? nationalId, required dynamic nationalId,
required bool patientOutSA, required bool patientOutSA,
required int selectedLoginType, required int selectedLoginType,
required bool isForRegister, required bool isForRegister,
@ -136,9 +150,15 @@ class RequestUtils {
AppState _appState = getIt.get<AppState>(); AppState _appState = getIt.get<AppState>();
var request = SendActivationRequest(); var request = SendActivationRequest();
if (mobileNumber.isNotEmpty) { if (mobileNumber.isNotEmpty) {
request.patientMobileNumber = int.parse(mobileNumber); // If zipCode is "0", it means "Others" country is selected
if (zipCode == "0") {
request.patientMobileNumberOthers = mobileNumber;
request.mobileNo = mobileNumber;
} else {
request.patientMobileNumber = int.parse(mobileNumber);
request.mobileNo = '0$mobileNumber';
}
} }
request.mobileNo = '0$mobileNumber';
request.projectOutSA = patientOutSA; request.projectOutSA = patientOutSA;
request.loginType = selectedLoginType; request.loginType = selectedLoginType;
request.oTPSendType = otpTypeEnum.toInt(); //this.selectedOption == 1 ? 1 : 2; request.oTPSendType = otpTypeEnum.toInt(); //this.selectedOption == 1 ? 1 : 2;
@ -159,7 +179,7 @@ class RequestUtils {
request.searchType = isFileNo ? 2 : 1; request.searchType = isFileNo ? 2 : 1;
request.patientID = patientId ?? 0; request.patientID = patientId ?? 0;
request.nationalID = nationalId ?? 0; request.nationalID = nationalId ?? 0;
request.patientIdentificationID = (nationalId ?? '0') as int?; request.patientIdentificationID = (nationalId ?? '0');
request.isRegister = false; request.isRegister = false;
} }
request.deviceTypeID = request.searchType; request.deviceTypeID = request.searchType;

@ -60,11 +60,23 @@ class ValidationUtils {
return isCorrectID; return isCorrectID;
} }
static bool isValidatePhone({String? phoneNumber, required Function() onOkPress}) { static bool isValidatePhone({String? phoneNumber, required Function() onOkPress, CountryEnum? selectedCountry}) {
if (phoneNumber == null || phoneNumber.isEmpty) { if (phoneNumber == null || phoneNumber.isEmpty) {
_dialogService.showExceptionBottomSheet(message: LocaleKeys.enterValidPhoneNumber.tr(), onOkPressed: onOkPress); _dialogService.showExceptionBottomSheet(message: LocaleKeys.enterValidPhoneNumber.tr(), onOkPressed: onOkPress);
return false; return false;
} }
// Check if "Others" country is selected and phone number starts with restricted codes
if (selectedCountry == CountryEnum.others) {
if (phoneNumber.startsWith('00966') || phoneNumber.startsWith('00971')) {
_dialogService.showExceptionBottomSheet(
message: LocaleKeys.cannotEnterSaudiOrUAENumber.tr(),
onOkPressed: onOkPress
);
return false;
}
}
return true; return true;
} }

@ -470,6 +470,8 @@ extension CountryExtension on CountryEnum {
return "Kingdom Of Saudi Arabia"; return "Kingdom Of Saudi Arabia";
case CountryEnum.unitedArabEmirates: case CountryEnum.unitedArabEmirates:
return "United Arab Emirates"; return "United Arab Emirates";
case CountryEnum.others:
return "Others";
} }
} }
@ -479,6 +481,8 @@ extension CountryExtension on CountryEnum {
return "المملكة العربية السعودية"; return "المملكة العربية السعودية";
case CountryEnum.unitedArabEmirates: case CountryEnum.unitedArabEmirates:
return "الإمارات العربية المتحدة"; return "الإمارات العربية المتحدة";
case CountryEnum.others:
return "آخرون";
} }
} }
@ -488,6 +492,8 @@ extension CountryExtension on CountryEnum {
return AppAssets.ksa; return AppAssets.ksa;
case CountryEnum.unitedArabEmirates: case CountryEnum.unitedArabEmirates:
return AppAssets.uae; return AppAssets.uae;
case CountryEnum.others:
return AppAssets.globeOther;
} }
} }
@ -497,6 +503,8 @@ extension CountryExtension on CountryEnum {
return "966"; return "966";
case CountryEnum.unitedArabEmirates: case CountryEnum.unitedArabEmirates:
return "971"; return "971";
case CountryEnum.others:
return "0";
} }
} }
@ -508,6 +516,9 @@ extension CountryExtension on CountryEnum {
case "United Arab Emirates": case "United Arab Emirates":
case "الإمارات العربية المتحدة": case "الإمارات العربية المتحدة":
return CountryEnum.unitedArabEmirates; return CountryEnum.unitedArabEmirates;
case "Others":
case "آخرون":
return CountryEnum.others;
default: default:
throw Exception("Invalid country name"); throw Exception("Invalid country name");
} }

@ -102,11 +102,16 @@ class AuthenticationRepoImp implements AuthenticationRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> checkPatientAuthentication({required dynamic checkPatientAuthenticationReq, String? languageID}) async { Future<Either<Failure, GenericApiModel<dynamic>>> checkPatientAuthentication({required dynamic checkPatientAuthenticationReq, String? languageID}) async {
int isOutKsa = (checkPatientAuthenticationReq.zipCode == '966' || checkPatientAuthenticationReq.zipCode == '+966') ? 0 : 1; int isOutKsa = (checkPatientAuthenticationReq.zipCode == '966' || checkPatientAuthenticationReq.zipCode == '+966') ? 0 : 1;
checkPatientAuthenticationReq.patientOutSA = isOutKsa; checkPatientAuthenticationReq.patientOutSA = isOutKsa;
// Determine which API to use based on zipCode
final isOthersCountry = checkPatientAuthenticationReq.zipCode == '0';
final apiEndpoint = isOthersCountry ? ApiConsts.checkPatientAuthOthers : ApiConsts.checkPatientAuth;
try { try {
GenericApiModel<dynamic>? apiResponse; GenericApiModel<dynamic>? apiResponse;
Failure? failure; Failure? failure;
await apiClient.post( await apiClient.post(
ApiConsts.checkPatientAuth, apiEndpoint,
body: checkPatientAuthenticationReq.toJson(), body: checkPatientAuthenticationReq.toJson(),
onFailure: (error, statusCode, {messageStatus, failureType}) { onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType; failure = failureType;
@ -150,16 +155,25 @@ class AuthenticationRepoImp implements AuthenticationRepo {
// payload.remove("ResponseID"); // payload.remove("ResponseID");
} }
// Determine which API to use based on zipCode
final isOthersCountry = sendActivationCodeReq.zipCode == '0';
String apiEndpoint;
if (isFormFamilyFile) {
apiEndpoint = ApiConsts.sendFamilyFileActivation;
} else if (isRegister) {
apiEndpoint = ApiConsts.sendActivationCodeRegister;
} else if (isOthersCountry) {
apiEndpoint = ApiConsts.sendActivationCodeOthers;
} else {
apiEndpoint = ApiConsts.sendActivationCode;
}
try { try {
GenericApiModel<dynamic>? apiResponse; GenericApiModel<dynamic>? apiResponse;
Failure? failure; Failure? failure;
await apiClient.post( await apiClient.post(
isFormFamilyFile apiEndpoint,
? ApiConsts.sendFamilyFileActivation
: isRegister
? ApiConsts.sendActivationCodeRegister
: ApiConsts.sendActivationCode,
body: isFormFamilyFile ? payload : sendActivationCodeReq.toJson(), body: isFormFamilyFile ? payload : sendActivationCodeReq.toJson(),
onFailure: (error, statusCode, {messageStatus, failureType}) { onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType; failure = failureType;
@ -186,6 +200,8 @@ class AuthenticationRepoImp implements AuthenticationRepo {
} }
} }
@override @override
Future<Either<Failure, GenericApiModel<dynamic>>> checkActivationCodeRepo( Future<Either<Failure, GenericApiModel<dynamic>>> checkActivationCodeRepo(
{required dynamic newRequest, // could be CheckActivationCodeReq or CheckActivationCodeRegisterReq {required dynamic newRequest, // could be CheckActivationCodeReq or CheckActivationCodeRegisterReq
@ -304,11 +320,16 @@ class AuthenticationRepoImp implements AuthenticationRepo {
} }
} }
// Determine which API to use based on zipCode
final zipCode = _getZipCodeFromRequest(newRequest);
final isOthersCountry = zipCode == '0';
final endpoint = isFormFamilyFile final endpoint = isFormFamilyFile
? ApiConsts.checkActivationCodeForFamily ? ApiConsts.checkActivationCodeForFamily
: isRegister : isRegister
? ApiConsts.checkActivationCodeRegister ? ApiConsts.checkActivationCodeRegister
: ApiConsts.checkActivationCode; : isOthersCountry
? ApiConsts.checkActivationCodeOthers
: ApiConsts.checkActivationCode;
try { try {
GenericApiModel<dynamic>? apiResponse; GenericApiModel<dynamic>? apiResponse;
@ -521,8 +542,14 @@ class AuthenticationRepoImp implements AuthenticationRepo {
try { try {
GenericApiModel<dynamic>? apiResponse; GenericApiModel<dynamic>? apiResponse;
Failure? failure; Failure? failure;
// Determine which API to use based on zipCode
final zipCode = patientIMEIDataRequest['ZipCode'];
final isOthersCountry = zipCode == '0';
final apiEndpoint = isOthersCountry ? ApiConsts.insertPatientDeviceIMEIDataOthers : ApiConsts.insertPatientDeviceIMEIData;
return apiClient.post( return apiClient.post(
ApiConsts.insertPatientDeviceIMEIData, apiEndpoint,
body: patientIMEIDataRequest, body: patientIMEIDataRequest,
onFailure: (error, statusCode, {messageStatus, failureType}) { onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType; failure = failureType;
@ -648,4 +675,17 @@ class AuthenticationRepoImp implements AuthenticationRepo {
return Future.value(Left(UnknownFailure(e.toString()))); return Future.value(Left(UnknownFailure(e.toString())));
} }
} }
/// Helper method to safely get zipCode from dynamic request (Map or Object)
String _getZipCodeFromRequest(dynamic request) {
if (request is Map) {
return request['ZipCode']?.toString() ?? request['zipCode']?.toString() ?? '';
} else {
try {
return request.zipCode?.toString() ?? '';
} catch (e) {
return '';
}
}
}
} }

@ -70,7 +70,7 @@ class AuthenticationViewModel extends ChangeNotifier {
_errorHandlerService = errorHandlerService, _errorHandlerService = errorHandlerService,
_appState = appState, _appState = appState,
_authenticationRepo = authenticationRepo, _authenticationRepo = authenticationRepo,
_localAuthService = localAuthService; _localAuthService = localAuthService {}
final TextEditingController nationalIdController = TextEditingController(), final TextEditingController nationalIdController = TextEditingController(),
phoneNumberController = TextEditingController(), phoneNumberController = TextEditingController(),
dobController = TextEditingController(), dobController = TextEditingController(),
@ -182,6 +182,17 @@ class AuthenticationViewModel extends ChangeNotifier {
void onPhoneNumberChange(String? phoneNumber) { void onPhoneNumberChange(String? phoneNumber) {
phoneNumberController.text = phoneNumber!; phoneNumberController.text = phoneNumber!;
// Real-time validation for Others country - prevent entering Saudi/UAE numbers
if (selectedCountrySignup == CountryEnum.others) {
if (phoneNumber.startsWith('00966') || phoneNumber.startsWith('00971')) {
// Remove the restricted prefix
phoneNumberController.text = phoneNumber.replaceAll(RegExp(r'^(00966|00971)'), '');
phoneNumberController.selection = TextSelection.fromPosition(
TextPosition(offset: phoneNumberController.text.length),
);
}
}
} }
void onTermAccepted() { void onTermAccepted() {
@ -301,9 +312,11 @@ class AuthenticationViewModel extends ChangeNotifier {
patientOutSA: false, patientOutSA: false,
otpTypeEnum: otpTypeEnum, otpTypeEnum: otpTypeEnum,
patientId: 0, patientId: 0,
zipCode: _appState.getSelectDeviceByImeiRespModelElement != null && _appState.getSelectDeviceByImeiRespModelElement!.outSa == true zipCode: selectedCountrySignup == CountryEnum.others
? CountryEnum.unitedArabEmirates.countryCode ? "0"
: selectedCountrySignup.countryCode, : (_appState.getSelectDeviceByImeiRespModelElement != null && _appState.getSelectDeviceByImeiRespModelElement!.outSa == true
? CountryEnum.unitedArabEmirates.countryCode
: selectedCountrySignup.countryCode),
calenderType: calenderType); calenderType: calenderType);
final result = await _authenticationRepo.checkPatientAuthentication(checkPatientAuthenticationReq: checkPatientAuthenticationReq); final result = await _authenticationRepo.checkPatientAuthentication(checkPatientAuthenticationReq: checkPatientAuthenticationReq);
@ -368,7 +381,7 @@ class AuthenticationViewModel extends ChangeNotifier {
mobileNumber: phoneNumber, mobileNumber: phoneNumber,
selectedLoginType: otpTypeEnum.toInt(), selectedLoginType: otpTypeEnum.toInt(),
zipCode: selectedCountrySignup.countryCode, zipCode: selectedCountrySignup.countryCode,
nationalId: int.parse(nationalIdOrFileNumber), nationalId:nationalIdOrFileNumber,
isFileNo: isForRegister ? isPatientHasFile(request: payload) : false, isFileNo: isForRegister ? isPatientHasFile(request: payload) : false,
patientId: isFormFamilyFile ? _appState.getAuthenticatedUser()!.patientId : 0, patientId: isFormFamilyFile ? _appState.getAuthenticatedUser()!.patientId : 0,
isForRegister: isForRegister, isForRegister: isForRegister,
@ -607,6 +620,7 @@ class AuthenticationViewModel extends ChangeNotifier {
activation.list!.first.isParentUser = true; activation.list!.first.isParentUser = true;
} }
activation.list!.first.bloodGroup = activation.patientBlodType; activation.list!.first.bloodGroup = activation.patientBlodType;
activation.list!.first.zipCode = selectedCountrySignup == CountryEnum.others ? '0' : selectedCountrySignup.countryCode;
_appState.setAuthenticatedUser(activation.list!.first); _appState.setAuthenticatedUser(activation.list!.first);
_appState.setPrivilegeModelList(activation.list!.first.listPrivilege!); _appState.setPrivilegeModelList(activation.list!.first.listPrivilege!);
_appState.setUserBloodGroup = activation.patientBlodType ?? "N/A"; _appState.setUserBloodGroup = activation.patientBlodType ?? "N/A";
@ -854,7 +868,9 @@ class AuthenticationViewModel extends ChangeNotifier {
await clearDefaultInputValues(); // This will Clear All Default Values Of User. await clearDefaultInputValues(); // This will Clear All Default Values Of User.
Future.delayed(Duration(seconds: 1), () { Future.delayed(Duration(seconds: 1), () {
LoadingUtils.hideFullScreenLoader(); LoadingUtils.hideFullScreenLoader();
_navigationService.pushAndReplace(AppRoutes.loginScreen); // _navigationService.pushAndReplace(AppRoutes.loginScreen);
_navigationService.pushAndRemoveUntil(CustomPageRoute(page: LandingNavigation()), (r) => false);
_navigationService.push(CustomPageRoute(page: LoginScreen()));
}); });
} }
} }
@ -948,22 +964,24 @@ class AuthenticationViewModel extends ChangeNotifier {
Future<void> insertPatientIMEIData(int loginType) async { Future<void> insertPatientIMEIData(int loginType) async {
final resultEither = await _authenticationRepo.insertPatientIMEIData( final resultEither = await _authenticationRepo.insertPatientIMEIData(
patientIMEIDataRequest: PatientInsertDeviceImei( patientIMEIDataRequest: PatientInsertDeviceImei(
imei: _appState.deviceToken, imei: _appState.deviceToken,
deviceTypeId: _appState.getDeviceTypeID(), deviceTypeId: _appState.getDeviceTypeID(),
patientId: _appState.getAuthenticatedUser()!.patientId!, patientId: _appState.getAuthenticatedUser()!.patientId!,
patientIdentificationNo: _appState.getAuthenticatedUser()!.patientIdentificationNo!, patientIdentificationNo: _appState.getAuthenticatedUser()!.patientIdentificationNo!,
identificationNo: _appState.getAuthenticatedUser()!.patientIdentificationNo!, identificationNo: _appState.getAuthenticatedUser()!.patientIdentificationNo!,
firstName: _appState.getAuthenticatedUser()!.firstName!, firstName: _appState.getAuthenticatedUser()!.firstName!,
lastName: _appState.getAuthenticatedUser()!.lastName!, lastName: _appState.getAuthenticatedUser()!.lastName!,
patientTypeId: _appState.getAuthenticatedUser()!.patientType, patientTypeId: _appState.getAuthenticatedUser()!.patientType,
mobileNo: _appState.getAuthenticatedUser()!.mobileNumber!, mobileNo: _appState.getAuthenticatedUser()!.mobileNumber!,
logInTypeId: loginType, logInTypeId: loginType,
patientOutSa: _appState.getAuthenticatedUser()!.outSa!, patientOutSa: _appState.getAuthenticatedUser()!.outSa!,
outSa: _appState.getAuthenticatedUser()!.outSa == 1 ? true : false, outSa: _appState.getAuthenticatedUser()!.outSa == 1 ? true : false,
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,
).toJson()); zipCode: _appState.getAuthenticatedUser()!.zipCode //selectedCountrySignup == CountryEnum.others ? '0': selectedCountrySignup.countryCode,
)
.toJson());
resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async { resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async {
if (apiResponse.messageStatus == 1) { if (apiResponse.messageStatus == 1) {
log("Insert IMEI Success"); log("Insert IMEI Success");

@ -1,5 +1,6 @@
class CheckActivationCodeRegisterReq { class CheckActivationCodeRegisterReq {
int? patientMobileNumber; int? patientMobileNumber;
String? patientMobileNumberOthers;
String? mobileNo; String? mobileNo;
String? deviceToken; String? deviceToken;
bool? projectOutSA; bool? projectOutSA;
@ -9,8 +10,8 @@ class CheckActivationCodeRegisterReq {
String? logInTokenID; String? logInTokenID;
int? searchType; int? searchType;
int? patientID; int? patientID;
int? nationalID; dynamic nationalID;
int? patientIdentificationID; dynamic patientIdentificationID;
String? activationCode; String? activationCode;
bool? isSilentLogin; bool? isSilentLogin;
double? versionID; double? versionID;
@ -29,6 +30,7 @@ class CheckActivationCodeRegisterReq {
CheckActivationCodeRegisterReq({ CheckActivationCodeRegisterReq({
this.patientMobileNumber, this.patientMobileNumber,
this.patientMobileNumberOthers,
this.mobileNo, this.mobileNo,
this.deviceToken, this.deviceToken,
this.projectOutSA, this.projectOutSA,
@ -59,6 +61,7 @@ class CheckActivationCodeRegisterReq {
CheckActivationCodeRegisterReq.fromJson(Map<String, dynamic> json) { CheckActivationCodeRegisterReq.fromJson(Map<String, dynamic> json) {
patientMobileNumber = json['PatientMobileNumber']; patientMobileNumber = json['PatientMobileNumber'];
patientMobileNumberOthers = json['PatientMobileNumber_Others'];
mobileNo = json['MobileNo']; mobileNo = json['MobileNo'];
deviceToken = json['DeviceToken']; deviceToken = json['DeviceToken'];
projectOutSA = json['ProjectOutSA']; projectOutSA = json['ProjectOutSA'];
@ -89,9 +92,14 @@ class CheckActivationCodeRegisterReq {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{}; final Map<String, dynamic> data = <String, dynamic>{};
data['PatientMobileNumber'] = patientMobileNumber; // Only send PatientMobileNumber_Others if it's set, otherwise send PatientMobileNumber
data['MobileNo'] = mobileNo; if (patientMobileNumberOthers != null) {
data['PatientMobileNumber_Others'] = patientMobileNumberOthers;
} else if (patientMobileNumber != null) {
data['PatientMobileNumber'] = patientMobileNumber;
}
data['DeviceToken'] = deviceToken; data['DeviceToken'] = deviceToken;
data['MobileNo'] = mobileNo;
data['ProjectOutSA'] = projectOutSA; data['ProjectOutSA'] = projectOutSA;
data['LoginType'] = loginType; data['LoginType'] = loginType;
data['ZipCode'] = zipCode; data['ZipCode'] = zipCode;

@ -28,6 +28,7 @@ class PatientInsertDeviceImei {
int? patientOutSa; int? patientOutSa;
String? sessionId; String? sessionId;
String? lastName; String? lastName;
String? zipCode;
PatientInsertDeviceImei({ PatientInsertDeviceImei({
this.setupId, this.setupId,
@ -56,7 +57,8 @@ class PatientInsertDeviceImei {
this.patientTypeId, this.patientTypeId,
this.patientOutSa, this.patientOutSa,
this.sessionId, this.sessionId,
this.lastName this.lastName,
this.zipCode,
}); });
factory PatientInsertDeviceImei.fromRawJson(String str) => PatientInsertDeviceImei.fromJson(json.decode(str)); factory PatientInsertDeviceImei.fromRawJson(String str) => PatientInsertDeviceImei.fromJson(json.decode(str));
@ -91,6 +93,7 @@ class PatientInsertDeviceImei {
patientOutSa: json["PatientOutSA"], patientOutSa: json["PatientOutSA"],
sessionId: json["SessionID"], sessionId: json["SessionID"],
lastName: json["LastName"], lastName: json["LastName"],
zipCode: json["ZipCode"],
); );
@ -122,5 +125,6 @@ class PatientInsertDeviceImei {
"PatientOutSA": patientOutSa, "PatientOutSA": patientOutSa,
"SessionID": sessionId, "SessionID": sessionId,
"LastName": lastName, "LastName": lastName,
"ZipCode": zipCode,
}; };
} }

@ -1,5 +1,6 @@
class SendActivationRequest { class SendActivationRequest {
int? patientMobileNumber; int? patientMobileNumber;
String? patientMobileNumberOthers;
String? mobileNo; String? mobileNo;
String? deviceToken; String? deviceToken;
bool? projectOutSA; bool? projectOutSA;
@ -9,8 +10,8 @@ class SendActivationRequest {
String? logInTokenID; String? logInTokenID;
int? searchType; int? searchType;
int? patientID; int? patientID;
int? nationalID; dynamic nationalID;
int? patientIdentificationID; dynamic patientIdentificationID;
int? oTPSendType; int? oTPSendType;
int? languageID; int? languageID;
double? versionID; double? versionID;
@ -32,6 +33,7 @@ class SendActivationRequest {
SendActivationRequest( SendActivationRequest(
{this.patientMobileNumber, {this.patientMobileNumber,
this.patientMobileNumberOthers,
this.mobileNo, this.mobileNo,
this.deviceToken, this.deviceToken,
this.projectOutSA, this.projectOutSA,
@ -64,6 +66,7 @@ class SendActivationRequest {
SendActivationRequest.fromJson(Map<String, dynamic> json) { SendActivationRequest.fromJson(Map<String, dynamic> json) {
patientMobileNumber = json['PatientMobileNumber']; patientMobileNumber = json['PatientMobileNumber'];
patientMobileNumberOthers = json['PatientMobileNumber_Others'];
mobileNo = json['MobileNo']; mobileNo = json['MobileNo'];
deviceToken = json['DeviceToken']; deviceToken = json['DeviceToken'];
projectOutSA = json['ProjectOutSA']; projectOutSA = json['ProjectOutSA'];
@ -97,7 +100,12 @@ class SendActivationRequest {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>(); final Map<String, dynamic> data = new Map<String, dynamic>();
data['PatientMobileNumber'] = patientMobileNumber; // Only send PatientMobileNumber_Others if it's set, otherwise send PatientMobileNumber
if (patientMobileNumberOthers != null) {
data['PatientMobileNumber_Others'] = patientMobileNumberOthers;
} else if (patientMobileNumber != null) {
data['PatientMobileNumber'] = patientMobileNumber;
}
data['MobileNo'] = mobileNo; data['MobileNo'] = mobileNo;
data['DeviceToken'] = deviceToken; data['DeviceToken'] = deviceToken;
data['ProjectOutSA'] = projectOutSA; data['ProjectOutSA'] = projectOutSA;

@ -19,7 +19,7 @@ class SelectDeviceByImeiRespModelElement {
bool? biometricEnabled; bool? biometricEnabled;
int? patientType; int? patientType;
int? preferredLanguage; int? preferredLanguage;
bool? isOther;
SelectDeviceByImeiRespModelElement({ SelectDeviceByImeiRespModelElement({
this.id, this.id,
this.imei, this.imei,
@ -35,6 +35,7 @@ class SelectDeviceByImeiRespModelElement {
this.biometricEnabled, this.biometricEnabled,
this.patientType, this.patientType,
this.preferredLanguage, this.preferredLanguage,
this.isOther
}); });
factory SelectDeviceByImeiRespModelElement.fromJson(Map<String, dynamic> json) => SelectDeviceByImeiRespModelElement( factory SelectDeviceByImeiRespModelElement.fromJson(Map<String, dynamic> json) => SelectDeviceByImeiRespModelElement(
@ -52,6 +53,7 @@ class SelectDeviceByImeiRespModelElement {
biometricEnabled: json["BiometricEnabled"] as bool?, biometricEnabled: json["BiometricEnabled"] as bool?,
patientType: json["PatientType"] as int?, patientType: json["PatientType"] as int?,
preferredLanguage: json["PreferredLanguage"] as int?, preferredLanguage: json["PreferredLanguage"] as int?,
isOther: json["IsOther"] as bool?
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
@ -69,5 +71,6 @@ class SelectDeviceByImeiRespModelElement {
"BiometricEnabled": biometricEnabled, "BiometricEnabled": biometricEnabled,
"PatientType": patientType, "PatientType": patientType,
"PreferredLanguage": preferredLanguage, "PreferredLanguage": preferredLanguage,
"IsOther": isOther,
}; };
} }

@ -918,7 +918,10 @@ class HmgServicesRepoImp implements HmgServicesRepo {
@override @override
Future<Either<Failure, GenericApiModel<List<VitalSignResModel>>>> getPatientVitalSign() async { Future<Either<Failure, GenericApiModel<List<VitalSignResModel>>>> getPatientVitalSign() async {
Map<String, dynamic> requestBody = {}; Map<String, dynamic> requestBody = {
};
try { try {
GenericApiModel<List<VitalSignResModel>>? apiResponse; GenericApiModel<List<VitalSignResModel>>? apiResponse;
@ -942,12 +945,29 @@ class HmgServicesRepoImp implements HmgServicesRepo {
if (vitalSignJson is Map<String, dynamic>) { if (vitalSignJson is Map<String, dynamic>) {
final vitalSign = VitalSignResModel.fromJson(vitalSignJson); final vitalSign = VitalSignResModel.fromJson(vitalSignJson);
// Only add records where BOTH height AND weight are greater than 0 // Debug logging for blood pressure
print('=== Repository Blood Pressure Check ===');
print('bloodPressureHigher: ${vitalSign.bloodPressureHigher}');
print('bloodPressureLower: ${vitalSign.bloodPressureLower}');
print('weightKg: ${vitalSign.weightKg}');
print('heightCm: ${vitalSign.heightCm}');
// Check if the record has at least one valid vital sign measurement
final hasValidWeight = _isValidValue(vitalSign.weightKg); final hasValidWeight = _isValidValue(vitalSign.weightKg);
final hasValidHeight = _isValidValue(vitalSign.heightCm); final hasValidHeight = _isValidValue(vitalSign.heightCm);
final hasValidBMI = _isValidValue(vitalSign.bodyMassIndex);
// Only add if both height and weight are valid (> 0) final hasValidTemperature = _isValidValue(vitalSign.temperatureCelcius);
if (hasValidWeight && hasValidHeight) { final hasValidHeartRate = _isValidValue(vitalSign.heartRate) || _isValidValue(vitalSign.pulseBeatPerMinute);
final hasValidRespiration = _isValidValue(vitalSign.respirationBeatPerMinute);
final hasValidBloodPressure = _isValidValue(vitalSign.bloodPressureHigher) || _isValidValue(vitalSign.bloodPressureLower);
print('hasValidBloodPressure: $hasValidBloodPressure');
print('Will add to list: ${hasValidWeight || hasValidHeight || hasValidBMI || hasValidTemperature || hasValidHeartRate || hasValidRespiration || hasValidBloodPressure}');
print('=====================================');
// Add if ANY vital sign has valid data
if (hasValidWeight || hasValidHeight || hasValidBMI || hasValidTemperature ||
hasValidHeartRate || hasValidRespiration || hasValidBloodPressure) {
vitalSignList.add(vitalSign); vitalSignList.add(vitalSign);
} }
} }

@ -32,10 +32,14 @@ class InsuranceViewModel extends ChangeNotifier {
InsuranceViewModel({required this.insuranceRepo, required this.errorHandlerService}); InsuranceViewModel({required this.insuranceRepo, required this.errorHandlerService});
initInsuranceProvider() { initInsuranceProvider() {
debugPrint("InsuranceViewModel: initInsuranceProvider called, isInsuranceDataToBeLoaded=$isInsuranceDataToBeLoaded");
if (isInsuranceDataToBeLoaded) { if (isInsuranceDataToBeLoaded) {
patientInsuranceList.clear(); patientInsuranceList.clear();
isInsuranceLoading = true; isInsuranceLoading = true;
debugPrint("InsuranceViewModel: Calling getPatientInsuranceDetails API");
getPatientInsuranceDetails(); getPatientInsuranceDetails();
} else {
debugPrint("InsuranceViewModel: Skipping API call, isInsuranceDataToBeLoaded is false");
} }
patientInsuranceCardHistoryList.clear(); patientInsuranceCardHistoryList.clear();
isInsuranceHistoryLoading = true; isInsuranceHistoryLoading = true;
@ -71,13 +75,18 @@ class InsuranceViewModel extends ChangeNotifier {
} }
Future<void> getPatientInsuranceDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async { Future<void> getPatientInsuranceDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async {
if (!isInsuranceDataToBeLoaded) return; if (!isInsuranceDataToBeLoaded) {
debugPrint("InsuranceViewModel: getPatientInsuranceDetails - skipping, flag is false");
return;
}
debugPrint("InsuranceViewModel: getPatientInsuranceDetails - making API call");
final result = await insuranceRepo.getPatientInsuranceDetails(); final result = await insuranceRepo.getPatientInsuranceDetails();
result.fold( result.fold(
// (failure) async => await errorHandlerService.handleError(failure: failure), // (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) async { (failure) async {
debugPrint("InsuranceViewModel: API call failed - ${failure.toString()}");
isInsuranceLoading = false; isInsuranceLoading = false;
isInsuranceDataToBeLoaded = false; isInsuranceDataToBeLoaded = false;
isInsuranceExpired = false; isInsuranceExpired = false;
@ -86,16 +95,21 @@ class InsuranceViewModel extends ChangeNotifier {
}, },
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 2) { if (apiResponse.messageStatus == 2) {
debugPrint("InsuranceViewModel: API returned error status");
isInsuranceDataToBeLoaded = false; isInsuranceDataToBeLoaded = false;
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) { } else if (apiResponse.messageStatus == 1) {
debugPrint("InsuranceViewModel: API success - received ${apiResponse.data?.length ?? 0} insurance records");
patientInsuranceList = apiResponse.data!; patientInsuranceList = apiResponse.data!;
isInsuranceLoading = false; isInsuranceLoading = false;
isInsuranceDataToBeLoaded = false; isInsuranceDataToBeLoaded = false;
isInsuranceExpired = DateTime.now().isAfter( if (patientInsuranceList.isNotEmpty) {
DateUtil.convertStringToDate(patientInsuranceList.first.cardValidTo), isInsuranceExpired = DateTime.now().isAfter(
); DateUtil.convertStringToDate(patientInsuranceList.first.cardValidTo),
);
debugPrint("InsuranceViewModel: Insurance card expired: $isInsuranceExpired");
}
isInsuranceActive = patientInsuranceList.first.isActive ?? false; isInsuranceActive = patientInsuranceList.first.isActive ?? false;
// isInsuranceActive = true; // isInsuranceActive = true;

@ -812,6 +812,7 @@ abstract class LocaleKeys {
static const ready = 'ready'; static const ready = 'ready';
static const enterValidNationalId = 'enterValidNationalId'; static const enterValidNationalId = 'enterValidNationalId';
static const enterValidPhoneNumber = 'enterValidPhoneNumber'; static const enterValidPhoneNumber = 'enterValidPhoneNumber';
static const cannotEnterSaudiOrUAENumber = 'cannotEnterSaudiOrUAENumber';
static const medicalCentersWithCount = 'medicalCentersWithCount'; static const medicalCentersWithCount = 'medicalCentersWithCount';
static const medicalCenters = 'medicalCenters'; static const medicalCenters = 'medicalCenters';
static const hospitalsWithCount = 'hospitalsWithCount'; static const hospitalsWithCount = 'hospitalsWithCount';

@ -79,7 +79,7 @@ class LoginScreenState extends State<LoginScreen> {
hintText: "xxxxxxxxx", hintText: "xxxxxxxxx",
controller: authVm.nationalIdController, controller: authVm.nationalIdController,
focusNode: _nationalIdFocusNode, focusNode: _nationalIdFocusNode,
keyboardType: TextInputType.number, keyboardType: TextInputType.text,
isEnable: true, isEnable: true,
prefix: null, prefix: null,
autoFocus: true, autoFocus: true,
@ -168,41 +168,47 @@ class LoginScreenState extends State<LoginScreen> {
initialPhoneNumber: "", initialPhoneNumber: "",
textController: phoneNumberController, textController: phoneNumberController,
isEnableCountryDropdown: true, isEnableCountryDropdown: true,
onCountryChange: authViewModel.onCountryChange, onCountryChange: (country) {
authViewModel.onCountryChange(country);
setModalState(() {});
},
onChange: authViewModel.onPhoneNumberChange, onChange: authViewModel.onPhoneNumberChange,
buttons: [ buttons: [
Padding( if (authViewModel.selectedCountrySignup != CountryEnum.others)
padding: EdgeInsets.only(bottom: 10.h), Padding(
child: CustomButton( padding: EdgeInsets.only(bottom: 10.h),
text: LocaleKeys.sendOTPSMS.tr(context: context), child: CustomButton(
onPressed: () async { text: LocaleKeys.sendOTPSMS.tr(context: context),
if (ValidationUtils.isValidatePhone( onPressed: () async {
phoneNumber: phoneNumberController!.text, if (ValidationUtils.isValidatePhone(
onOkPress: () { phoneNumber: phoneNumberController!.text,
Navigator.of(context).pop(); selectedCountry: authViewModel.selectedCountrySignup,
})) { onOkPress: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
appState.setSelectDeviceByImeiRespModelElement(null); })) {
await authViewModel.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms); Navigator.of(context).pop();
} appState.setSelectDeviceByImeiRespModelElement(null);
}, await authViewModel.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms);
backgroundColor: AppColors.primaryRedColor, }
borderColor: AppColors.primaryRedBorderColor, },
textColor: Colors.white, backgroundColor: AppColors.primaryRedColor,
iconColor: Colors.white, borderColor: AppColors.primaryRedBorderColor,
icon: AppAssets.message, textColor: Colors.white,
), iconColor: Colors.white,
), icon: AppAssets.message,
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 8.h),
child: LocaleKeys.oR.tr(context: context).toText16(color: AppColors.textColor),
), ),
], ),
), if (authViewModel.selectedCountrySignup != CountryEnum.others)
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 8.h),
child: LocaleKeys.oR.tr(context: context).toText16(color: AppColors.textColor),
),
],
),
Padding( Padding(
padding: EdgeInsets.only(bottom: 10.h, top: 10.h), padding: EdgeInsets.only(bottom: 10.h, top: 10.h),
child: CustomButton( child: CustomButton(
@ -210,6 +216,7 @@ class LoginScreenState extends State<LoginScreen> {
onPressed: () async { onPressed: () async {
if (ValidationUtils.isValidatePhone( if (ValidationUtils.isValidatePhone(
phoneNumber: phoneNumberController!.text, phoneNumber: phoneNumberController!.text,
selectedCountry: authViewModel.selectedCountrySignup,
onOkPress: () { onOkPress: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
})) { })) {

@ -90,7 +90,7 @@ class _RegisterNew extends State<RegisterNew> {
child: Column( child: Column(
children: [ children: [
CustomCountryDropdown( CustomCountryDropdown(
countryList: CountryEnum.values, countryList: CountryEnum.values.where((c) => c != CountryEnum.others).toList(),
onCountryChange: authVm.onCountryChange, onCountryChange: authVm.onCountryChange,
// isRtl: Directionality.of(context) == TextDirection.LTR, // isRtl: Directionality.of(context) == TextDirection.LTR,
).withVerticalPadding(8.h), ).withVerticalPadding(8.h),

@ -5,12 +5,15 @@ import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart'; import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart';
import 'package:hmg_patient_app_new/core/dependencies.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/enums.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';
import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; import 'package:hmg_patient_app_new/core/utils/validation_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/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_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/presentation/insurance/widgets/patient_insurance_card.dart';
import 'package:hmg_patient_app_new/theme/colors.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/appbar/app_bar_widget.dart';
import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart';
@ -28,11 +31,22 @@ class RegisterNewStep2 extends StatefulWidget {
class _RegisterNew extends State<RegisterNewStep2> { class _RegisterNew extends State<RegisterNewStep2> {
AuthenticationViewModel? authVM; AuthenticationViewModel? authVM;
InsuranceViewModel? insuranceVM;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
authVM = context.read<AuthenticationViewModel>(); authVM = context.read<AuthenticationViewModel>();
insuranceVM = context.read<InsuranceViewModel>();
// Call insurance API to fetch data
WidgetsBinding.instance.addPostFrameCallback((_) {
debugPrint("Registration Step 2: Calling insurance API");
// Reset the flag to ensure API gets called
insuranceVM?.setIsInsuranceDataToBeLoaded(true);
insuranceVM?.initInsuranceProvider();
debugPrint("Registration Step 2: Insurance API call initiated");
});
} }
@override @override
@ -59,6 +73,59 @@ class _RegisterNew extends State<RegisterNewStep2> {
onLanguageChanged: (lang) {}, onLanguageChanged: (lang) {},
hideLogoAndLang: true, hideLogoAndLang: true,
), ),
bottomSheet: Container(
// height: 200.h,
width: MediaQuery.of(context).size.width,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
customBorder: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)),
hasShadow: true,
),
child: Row(
children: [
Expanded(
child: CustomButton(
text: LocaleKeys.cancel.tr(),
icon: AppAssets.cancel,
onPressed: () {
Navigator.of(context).pop();
// authVM!.clearDefaultInputValues();
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
iconColor: AppColors.primaryRedColor,
),
),
SizedBox(width: 16.w),
Expanded(
child: CustomButton(
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
text: LocaleKeys.confirm.tr(),
icon: AppAssets.confirm,
iconColor: AppColors.whiteColor,
onPressed: () {
if (appState.getUserRegistrationPayload.zipCode != CountryEnum.saudiArabia.countryCode) {
if (ValidationUtils.validateUaeRegistration(
name: authVM!.nameController.text,
gender: authVM!.genderType,
country: authVM!.pickedCountryByUAEUser,
maritalStatus: authVM!.maritalStatus,
onOkPress: () {
Navigator.of(context).pop();
})) {
showModel(context: context);
}
} else {
showModel(context: context);
}
},
),
)
],
).paddingAll(16.h),),
body: GestureDetector( body: GestureDetector(
onTap: () { onTap: () {
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
@ -75,6 +142,29 @@ class _RegisterNew extends State<RegisterNewStep2> {
children: <Widget>[ children: <Widget>[
LocaleKeys.personalDetailsVerification.tr(context: context).toText26(color: AppColors.textColor, weight: FontWeight.w600, letterSpacing: -2), LocaleKeys.personalDetailsVerification.tr(context: context).toText26(color: AppColors.textColor, weight: FontWeight.w600, letterSpacing: -2),
SizedBox(height: 24.h), SizedBox(height: 24.h),
// Insurance Card Section
Consumer<InsuranceViewModel>(
builder: (context, insuranceVM, child) {
// Only show if insurance data is available and not loading
if (!insuranceVM.isInsuranceLoading && insuranceVM.patientInsuranceList.isNotEmpty) {
return Column(
children: [
PatientInsuranceCard(
insuranceCardDetailsModel: insuranceVM.patientInsuranceList.first,
isInsuranceExpired: DateTime.now().isAfter(
DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo),
),
),
SizedBox(height: 24.h),
],
);
}
// Don't show anything if loading or no data
return SizedBox.shrink();
},
),
Container( Container(
decoration: BoxDecoration(color: AppColors.whiteColor, borderRadius: BorderRadius.circular(24)), decoration: BoxDecoration(color: AppColors.whiteColor, borderRadius: BorderRadius.circular(24)),
padding: EdgeInsets.only(left: 16.h, right: 16.h), padding: EdgeInsets.only(left: 16.h, right: 16.h),
@ -97,7 +187,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
isReadOnly: authVM!.isUserFromUAE() ? false : true, isReadOnly: authVM!.isUserFromUAE() ? false : true,
leadingIcon: AppAssets.user_circle, leadingIcon: AppAssets.user_circle,
labelColor: AppColors.textColor, labelColor: AppColors.textColor,
).paddingSymmetrical(0.h, 16.h), ).paddingSymmetrical(0.h, 8.h),
Divider(height: 1.h, color: AppColors.greyColor), Divider(height: 1.h, color: AppColors.greyColor),
TextInputWidget( TextInputWidget(
labelText: LocaleKeys.nationalIdNumber.tr(context: context), labelText: LocaleKeys.nationalIdNumber.tr(context: context),
@ -111,7 +201,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
isReadOnly: true, isReadOnly: true,
labelColor: AppColors.textColor, labelColor: AppColors.textColor,
leadingIcon: AppAssets.student_card) leadingIcon: AppAssets.student_card)
.paddingSymmetrical(0.h, 16.h), .paddingSymmetrical(0.h, 8.h),
Divider(height: 1, color: AppColors.greyColor), Divider(height: 1, color: AppColors.greyColor),
authVM!.isUserFromUAE() authVM!.isUserFromUAE()
? Selector<AuthenticationViewModel, GenderTypeEnum?>( ? Selector<AuthenticationViewModel, GenderTypeEnum?>(
@ -148,7 +238,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
leadingIcon: AppAssets.user_full, leadingIcon: AppAssets.user_full,
labelColor: AppColors.textColor, labelColor: AppColors.textColor,
onChange: (value) {}) onChange: (value) {})
.paddingSymmetrical(0.h, 16.h), .paddingSymmetrical(0.h, 8.h),
Divider(height: 1, color: AppColors.greyColor), Divider(height: 1, color: AppColors.greyColor),
authVM!.isUserFromUAE() authVM!.isUserFromUAE()
? Selector<AuthenticationViewModel, MaritalStatusTypeEnum?>( ? Selector<AuthenticationViewModel, MaritalStatusTypeEnum?>(
@ -187,7 +277,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
labelColor: AppColors.textColor, labelColor: AppColors.textColor,
leadingIcon: AppAssets.smart_phone, leadingIcon: AppAssets.smart_phone,
onChange: (value) {}) onChange: (value) {})
.paddingSymmetrical(0.h, 16.h), .paddingSymmetrical(0.h, 8.h),
Divider(height: 1.h, color: AppColors.greyColor), Divider(height: 1.h, color: AppColors.greyColor),
authVM!.isUserFromUAE() authVM!.isUserFromUAE()
? Selector<AuthenticationViewModel, ({List<NationalityCountries>? countriesList, NationalityCountries? selectedCountry, bool isArabic})>( ? Selector<AuthenticationViewModel, ({List<NationalityCountries>? countriesList, NationalityCountries? selectedCountry, bool isArabic})>(
@ -237,14 +327,14 @@ class _RegisterNew extends State<RegisterNewStep2> {
labelColor: AppColors.textColor, labelColor: AppColors.textColor,
leadingIcon: AppAssets.globe, leadingIcon: AppAssets.globe,
onChange: (value) {}) onChange: (value) {})
.paddingSymmetrical(0.h, 16.h), .paddingSymmetrical(0.h, 8.h),
Divider( Divider(
height: 1, height: 1,
color: AppColors.greyColor, color: AppColors.greyColor,
), ),
TextInputWidget( TextInputWidget(
labelText: LocaleKeys.mobileNumber.tr(context: context), labelText: LocaleKeys.mobileNumber.tr(context: context),
hintText: (appState.getUserRegistrationPayload.patientMobileNumber.toString() ?? ""), hintText: appState.getUserRegistrationPayload.patientMobileNumber.toString(),
controller: null, controller: null,
isEnable: false, isEnable: false,
prefix: null, prefix: null,
@ -254,7 +344,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
labelColor: AppColors.textColor, labelColor: AppColors.textColor,
isReadOnly: true, isReadOnly: true,
leadingIcon: AppAssets.call) leadingIcon: AppAssets.call)
.paddingSymmetrical(0.h, 16.h), .paddingSymmetrical(0.h, 8.h),
Divider( Divider(
height: 1.h, height: 1.h,
color: AppColors.greyColor, color: AppColors.greyColor,
@ -271,56 +361,56 @@ class _RegisterNew extends State<RegisterNewStep2> {
labelColor: AppColors.textColor, labelColor: AppColors.textColor,
leadingIcon: AppAssets.birthday_cake, leadingIcon: AppAssets.birthday_cake,
selectionType: null, selectionType: null,
).paddingSymmetrical(0.h, 16.h), ).paddingSymmetrical(0.h, 8.h),
], ],
), ),
), ),
SizedBox(height: 50.h), SizedBox(height: 50.h),
Row( // Row(
children: [ // children: [
Expanded( // Expanded(
child: CustomButton( // child: CustomButton(
text: LocaleKeys.cancel.tr(context: context), // text: LocaleKeys.cancel.tr(context: context),
icon: AppAssets.cancel, // icon: AppAssets.cancel,
onPressed: () { // onPressed: () {
Navigator.of(context).pop(); // Navigator.of(context).pop();
// authVM!.clearDefaultInputValues(); // // authVM!.clearDefaultInputValues();
}, // },
backgroundColor: AppColors.secondaryLightRedColor, // backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor, // borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor, // textColor: AppColors.primaryRedColor,
iconColor: AppColors.primaryRedColor, // iconColor: AppColors.primaryRedColor,
), // ),
), // ),
SizedBox(width: 16.w), // SizedBox(width: 16.w),
Expanded( // Expanded(
child: CustomButton( // child: CustomButton(
backgroundColor: AppColors.primaryRedColor, // backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor, // borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor, // textColor: AppColors.whiteColor,
text: LocaleKeys.confirm.tr(context: context), // text: LocaleKeys.confirm.tr(context: context),
icon: AppAssets.confirm, // icon: AppAssets.confirm,
iconColor: AppColors.whiteColor, // iconColor: AppColors.whiteColor,
onPressed: () { // onPressed: () {
if (appState.getUserRegistrationPayload.zipCode != CountryEnum.saudiArabia.countryCode) { // if (appState.getUserRegistrationPayload.zipCode != CountryEnum.saudiArabia.countryCode) {
if (ValidationUtils.validateUaeRegistration( // if (ValidationUtils.validateUaeRegistration(
name: authVM!.nameController.text, // name: authVM!.nameController.text,
gender: authVM!.genderType, // gender: authVM!.genderType,
country: authVM!.pickedCountryByUAEUser, // country: authVM!.pickedCountryByUAEUser,
maritalStatus: authVM!.maritalStatus, // maritalStatus: authVM!.maritalStatus,
onOkPress: () { // onOkPress: () {
Navigator.of(context).pop(); // Navigator.of(context).pop();
})) { // })) {
showModel(context: context); // showModel(context: context);
} // }
} else { // } else {
showModel(context: context); // showModel(context: context);
} // }
}, // },
), // ),
) // )
], // ],
), // ),
], ],
), ),
), ),

@ -31,7 +31,7 @@ class _SavedLogin extends State<SavedLogin> {
LoginTypeEnum loginType = LoginTypeEnum.sms; LoginTypeEnum loginType = LoginTypeEnum.sms;
late AuthenticationViewModel authVm; late AuthenticationViewModel authVm;
late AppState appState; late AppState appState;
bool? isOther;
@override @override
void initState() { void initState() {
authVm = context.read<AuthenticationViewModel>(); authVm = context.read<AuthenticationViewModel>();
@ -43,6 +43,15 @@ class _SavedLogin extends State<SavedLogin> {
: appState.getSelectDeviceByImeiRespModelElement!.mobile!; : appState.getSelectDeviceByImeiRespModelElement!.mobile!;
authVm.nationalIdController.text = appState.getSelectDeviceByImeiRespModelElement!.identificationNo!; authVm.nationalIdController.text = appState.getSelectDeviceByImeiRespModelElement!.identificationNo!;
// Check if this is an "Others" country user
isOther = appState.getSelectDeviceByImeiRespModelElement!.isOther == true;
// Set country to Others if isOther flag is true - this ensures _Others APIs are called
if (isOther!) {
authVm.phoneNumberController.text = appState.getSelectDeviceByImeiRespModelElement!.mobile!;
authVm.selectedCountrySignup = CountryEnum.others;
}
if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) { if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) {
authVm.loginWithFingerPrintFace(() {}); authVm.loginWithFingerPrintFace(() {});
} }
@ -90,9 +99,9 @@ class _SavedLogin extends State<SavedLogin> {
), ),
child: Column( child: Column(
children: [ children: [
// Last login info // Last login info - show WhatsApp only if isOther AND loginType is SMS
("${LocaleKeys.lastLoginBy.tr()} ${loginType.displayName}") ("${LocaleKeys.lastLoginBy.tr()} ${(isOther == true && loginType == LoginTypeEnum.sms) ? LoginTypeEnum.whatsapp.displayName : loginType.displayName}")
.toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -1), .toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -1),
Directionality( Directionality(
textDirection: ui.TextDirection.ltr, textDirection: ui.TextDirection.ltr,
@ -108,20 +117,27 @@ class _SavedLogin extends State<SavedLogin> {
? Container( ? Container(
margin: EdgeInsets.all(16.h), margin: EdgeInsets.all(16.h),
child: Utils.buildSvgWithAssets( child: Utils.buildSvgWithAssets(
icon: getTypeIcons(appState.getSelectDeviceByImeiRespModelElement!.logInType!), icon: (isOther == true && loginType == LoginTypeEnum.sms) ? AppAssets.whatsapp : getTypeIcons(appState.getSelectDeviceByImeiRespModelElement!.logInType!),
height: 54.h, height: 54.h,
width: 54.w, width: 54.w,
iconColor: loginType.toInt == 4 ? null : AppColors.primaryRedColor)) iconColor: (isOther == true && loginType == LoginTypeEnum.sms) || loginType.toInt == 4 ? null : AppColors.primaryRedColor))
: SizedBox(), : SizedBox(),
// Face ID login button // Main login button - for isOther with SMS, show WhatsApp, otherwise keep original login type
CustomButton( CustomButton(
text: "${LocaleKeys.loginBy.tr()} ${loginType.displayName}", text: (isOther == true && loginType == LoginTypeEnum.sms)
? "${LocaleKeys.loginBy.tr()} ${LoginTypeEnum.whatsapp.displayName}"
: "${LocaleKeys.loginBy.tr()} ${loginType.displayName}",
onPressed: () { onPressed: () {
if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) { if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) {
authVm.loginWithFingerPrintFace(() {}); authVm.loginWithFingerPrintFace(() {});
} else { } else {
authVm.checkUserAuthentication(otpTypeEnum: loginType == LoginTypeEnum.sms ? OTPTypeEnum.sms : OTPTypeEnum.whatsapp); // For isOther with SMS, use WhatsApp; otherwise use the original login type
authVm.checkUserAuthentication(
otpTypeEnum: (isOther == true && loginType == LoginTypeEnum.sms)
? OTPTypeEnum.whatsapp
: (loginType == LoginTypeEnum.sms ? OTPTypeEnum.sms : OTPTypeEnum.whatsapp),
);
} }
}, },
backgroundColor: AppColors.primaryRedColor, backgroundColor: AppColors.primaryRedColor,
@ -132,24 +148,25 @@ class _SavedLogin extends State<SavedLogin> {
borderRadius: 12.r, borderRadius: 12.r,
height: 40.h, height: 40.h,
padding: EdgeInsets.symmetric(vertical: 10.h), padding: EdgeInsets.symmetric(vertical: 10.h),
icon: getTypeIcons(loginType.toInt), icon: (isOther == true && loginType == LoginTypeEnum.sms) ? AppAssets.whatsapp : getTypeIcons(loginType.toInt),
//loginType == LoginTypeEnum.sms ? AppAssets.sms :AppAssets.whatsapp, iconColor: (isOther == true && loginType == LoginTypeEnum.sms) || loginType == LoginTypeEnum.whatsapp ? null : Colors.white,
iconColor: loginType != LoginTypeEnum.whatsapp ? Colors.white : null,
), ),
], ],
), ),
), ),
SizedBox(height: 24.h), SizedBox(height: 24.h),
Padding( // Hide OR + OTP login options entirely when isOther is true
padding: EdgeInsets.symmetric(horizontal: 16.w), if (isOther != true) ...[
child: Text( Padding(
LocaleKeys.oR.tr(), padding: EdgeInsets.symmetric(horizontal: 16.w),
style: context.dynamicTextStyle(fontSize: 16.f, fontWeight: FontWeight.w500), child: Text(
LocaleKeys.oR.tr(),
style: context.dynamicTextStyle(fontSize: 16.f, fontWeight: FontWeight.w500),
),
), ),
), SizedBox(height: 24.h),
SizedBox(height: 24.h), // OTP login button
// OTP login button loginType.toInt != 1
loginType.toInt != 1
? Column( ? Column(
children: [ children: [
loginType.toInt != 1 loginType.toInt != 1
@ -257,9 +274,10 @@ class _SavedLogin extends State<SavedLogin> {
padding: EdgeInsets.fromLTRB(0, 14.h, 0, 14.h), padding: EdgeInsets.fromLTRB(0, 14.h, 0, 14.h),
applyThemeColor: false, applyThemeColor: false,
), ),
],
const Spacer(flex: 2), const Spacer(flex: 2),
// OR divider // OR divider
SizedBox(height: 24.h), SizedBox(height: 24.h),
// Guest and Switch account // Guest and Switch account
Row( Row(

@ -10,6 +10,7 @@ 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/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.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/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.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';
import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
@ -1051,6 +1052,7 @@ class _LandingPageState extends State<LandingPage> {
isDone: isDone, isDone: isDone,
onPressed: () { onPressed: () {
// sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true); // sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true);
authVM.loginWithFingerPrintFace(() async { authVM.loginWithFingerPrintFace(() async {
isDone = true; isDone = true;
cacheService.saveBool(key: CacheConst.quickLoginEnabled, value: true); cacheService.saveBool(key: CacheConst.quickLoginEnabled, value: true);

@ -229,12 +229,21 @@ class _VitalSignDetailsPageState extends State<VitalSignDetailsPage> {
} }
Widget _historyCard(BuildContext context, {required List<DataPoint> history}) { Widget _historyCard(BuildContext context, {required List<DataPoint> history}) {
// For graph display, use only the last 5 data points to avoid performance issues
final graphHistory = _isGraphVisible && history.length > 5
? history.sublist(history.length - 5)
: history;
// For blood pressure, we need both systolic and diastolic series // For blood pressure, we need both systolic and diastolic series
List<DataPoint>? secondaryHistory; List<DataPoint>? secondaryHistory;
if (args.metric == VitalSignMetric.bloodPressure) { if (args.metric == VitalSignMetric.bloodPressure) {
secondaryHistory = _buildBloodPressureDiastolicSeries( final fullSecondaryHistory = _buildBloodPressureDiastolicSeries(
context.read<HmgServicesViewModel>().vitalSignList, context.read<HmgServicesViewModel>().vitalSignList,
); );
// Also limit secondary history to last 5 for graph
secondaryHistory = _isGraphVisible && fullSecondaryHistory.length > 5
? fullSecondaryHistory.sublist(fullSecondaryHistory.length - 5)
: fullSecondaryHistory;
} }
return Container( return Container(
@ -303,7 +312,7 @@ class _VitalSignDetailsPageState extends State<VitalSignDetailsPage> {
if (history.isEmpty) if (history.isEmpty)
Utils.getNoDataWidget(context, noDataText: LocaleKeys.noHistoryAvailable.tr(context: context), isSmallWidget: true) Utils.getNoDataWidget(context, noDataText: LocaleKeys.noHistoryAvailable.tr(context: context), isSmallWidget: true)
else if (_isGraphVisible) else if (_isGraphVisible)
_buildHistoryGraph(history, secondaryHistory: secondaryHistory) _buildHistoryGraph(graphHistory, secondaryHistory: secondaryHistory)
else else
_buildHistoryList(context, history), _buildHistoryList(context, history),
], ],
@ -604,8 +613,13 @@ class _VitalSignDetailsPageState extends State<VitalSignDetailsPage> {
double? _toDouble(dynamic v) { double? _toDouble(dynamic v) {
if (v == null) return null; if (v == null) return null;
if (v is num) return v.toDouble(); if (v is num) {
return double.tryParse(v.toString()); // Treat 0 as invalid for vital signs
return v == 0 ? null : v.toDouble();
}
final parsed = double.tryParse(v.toString());
// Treat 0 as invalid for vital signs
return (parsed == null || parsed == 0) ? null : parsed;
} }
String _latestValueText(VitalSignResModel? latest) { String _latestValueText(VitalSignResModel? latest) {

@ -28,6 +28,19 @@ class VitalSignPage extends StatefulWidget {
} }
class _VitalSignPageState extends State<VitalSignPage> { class _VitalSignPageState extends State<VitalSignPage> {
// Helper function to check if a value is valid (not null, not 0, not empty string)
bool _isValidValue(dynamic value) {
if (value == null) return false;
if (value is num) return value > 0;
if (value is String) {
if (value.trim().isEmpty) return false;
final parsed = double.tryParse(value);
return parsed != null && parsed > 0;
}
return false;
}
void _openDetails(VitalSignDetailsArgs args) { void _openDetails(VitalSignDetailsArgs args) {
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
@ -55,6 +68,18 @@ class _VitalSignPageState extends State<VitalSignPage> {
? viewModel.vitalSignList.first ? viewModel.vitalSignList.first
: null; : null;
// Debug logging for blood pressure
if (latestVitalSign != null) {
print('=== Blood Pressure Debug ===');
print('bloodPressureHigher: ${latestVitalSign.bloodPressureHigher}');
print('bloodPressureLower: ${latestVitalSign.bloodPressureLower}');
print('bloodPressureHigher type: ${latestVitalSign.bloodPressureHigher.runtimeType}');
print('bloodPressureLower type: ${latestVitalSign.bloodPressureLower.runtimeType}');
print('bloodPressureHigher == 0: ${latestVitalSign.bloodPressureHigher == 0}');
print('bloodPressureLower == 0: ${latestVitalSign.bloodPressureLower == 0}');
print('========================');
}
return SingleChildScrollView( return SingleChildScrollView(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -129,9 +154,9 @@ class _VitalSignPageState extends State<VitalSignPage> {
_buildVitalSignCard( _buildVitalSignCard(
icon: AppAssets.bloodPressure, icon: AppAssets.bloodPressure,
label: LocaleKeys.bloodPressure.tr(context: context), label: LocaleKeys.bloodPressure.tr(context: context),
value: latestVitalSign != null && value: (latestVitalSign != null &&
latestVitalSign.bloodPressureHigher != null && _isValidValue(latestVitalSign.bloodPressureHigher) &&
latestVitalSign.bloodPressureLower != null _isValidValue(latestVitalSign.bloodPressureLower))
? '${latestVitalSign.bloodPressureHigher}/${latestVitalSign.bloodPressureLower}' ? '${latestVitalSign.bloodPressureHigher}/${latestVitalSign.bloodPressureLower}'
: '--', : '--',
unit: '', unit: '',

@ -57,7 +57,7 @@ class _SplashScreenState extends State<SplashPage> {
await notificationService.initialize(onNotificationClick: (payload) { await notificationService.initialize(onNotificationClick: (payload) {
// Handle notification click here // Handle notification click here
}); });
ZoomService().initializeZoomSDK(); //ZoomService().initializeZoomSDK();
if (isAppOpenedFromCall) { if (isAppOpenedFromCall) {
navigateToTeleConsult(); navigateToTeleConsult();
} else { } else {

@ -158,7 +158,7 @@ class GenericBottomSheetState extends State<GenericBottomSheet> {
prefix: widget.isForEmail ? null : widget.countryCode, prefix: widget.isForEmail ? null : widget.countryCode,
isBorderAllowed: false, isBorderAllowed: false,
isAllowLeadingIcon: true, isAllowLeadingIcon: true,
fontSize: 18.f, fontSize: 14.f,
isCountryDropDown: widget.isEnableCountryDropdown, isCountryDropDown: widget.isEnableCountryDropdown,
leadingIcon: widget.isForEmail ? AppAssets.email : AppAssets.smart_phone, leadingIcon: widget.isForEmail ? AppAssets.email : AppAssets.smart_phone,
fontFamily: "Poppins", fontFamily: "Poppins",

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.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/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
@ -52,7 +53,7 @@ class DatePickerWidget extends StatelessWidget {
Widget _buildLabelText() { Widget _buildLabelText() {
return Text( return Text(
labelText, labelText,
style: const TextStyle( style: TextStyle(
fontSize: 12.f, fontSize: 12.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Color(0xff898A8D), color: Color(0xff898A8D),

@ -98,11 +98,12 @@ class CustomCountryDropdownState extends State<CustomCountryDropdown> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Text( if (selectedCountry != CountryEnum.others)
selectedCountry!.countryCode, Text(
style: TextStyle(fontSize: 12.f, fontWeight: FontWeight.w600, letterSpacing: -0.4, height: 1.5, fontFamily: "Poppins"), selectedCountry!.countryCode,
), style: TextStyle(fontSize: 12.f, fontWeight: FontWeight.w600, letterSpacing: -0.4, height: 1.5, fontFamily: "Poppins"),
SizedBox(width: 4.h), ),
if (selectedCountry != CountryEnum.others) SizedBox(width: 4.h),
if (widget.isEnableTextField) if (widget.isEnableTextField)
SizedBox( SizedBox(
height: 20.h, height: 20.h,
@ -112,7 +113,14 @@ class CustomCountryDropdownState extends State<CustomCountryDropdown> {
child: TextField( child: TextField(
focusNode: textFocusNode, 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: "", isDense: true, border: InputBorder.none, contentPadding: EdgeInsets.zero),
decoration: InputDecoration(
hintText: selectedCountry == CountryEnum.others ? "001*******" : "",
isDense: true,
border: InputBorder.none,
contentPadding: EdgeInsets.zero,
),
keyboardType: TextInputType.phone, keyboardType: TextInputType.phone,
onChanged: widget.onPhoneNumberChanged, onChanged: widget.onPhoneNumberChanged,
), ),

@ -44,7 +44,7 @@ class _FamilyFileAddWidgetState extends State<FamilyFileAddWidget> {
child: Column( child: Column(
children: [ children: [
CustomCountryDropdown( CustomCountryDropdown(
countryList: CountryEnum.values, countryList: CountryEnum.values.where((c) => c != CountryEnum.others).toList(),
onCountryChange: authVm.onCountryChange, onCountryChange: authVm.onCountryChange,
).paddingOnly(top: 8.h, bottom: 16.h), ).paddingOnly(top: 8.h, bottom: 16.h),
Divider(height: 1.h, color: AppColors.spacerLineColor), Divider(height: 1.h, color: AppColors.spacerLineColor),

@ -1,3 +1,4 @@
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/string_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -99,7 +100,7 @@ class _PhoneNumberInputState extends State<PhoneNumberInput> {
letterSpacing: 2, letterSpacing: 2,
), ),
), ),
style: const TextStyle( style: TextStyle(
fontSize: 18.f, fontSize: 18.f,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
letterSpacing: 2, letterSpacing: 2,

Loading…
Cancel
Save