otp screen & register Uae & resend Activation Code.

pull/35/head
aamir-csol 2 months ago
parent 87830e17ab
commit 21a9cc8c89

@ -1,5 +1,6 @@
import 'dart:developer'; import 'dart:developer';
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/utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
@ -33,7 +34,7 @@ class ValidationUtils {
if (nationalId != null && nationalId.isNotEmpty && selectedCountry != null) { if (nationalId != null && nationalId.isNotEmpty && selectedCountry != null) {
if (selectedCountry == CountryEnum.saudiArabia) { if (selectedCountry == CountryEnum.saudiArabia) {
if (!validateIqama(nationalId)) { if (!validateIqama(nationalId)) {
_dialogService.showExceptionBottomSheet(message: "Please enter a valid national ID", onOkPressed: onOkPress); _dialogService.showExceptionBottomSheet(message: "Please enter a valid Iqama ID", onOkPressed: onOkPress);
return false; return false;
} }
} }
@ -98,4 +99,44 @@ class ValidationUtils {
final regex = RegExp(r'^784\d{4}\d{7}\d{1}$'); final regex = RegExp(r'^784\d{4}\d{7}\d{1}$');
return regex.hasMatch(id); return regex.hasMatch(id);
} }
static bool validateUaeRegistration({String? name, GenderTypeEnum? gender, NationalityCountries? country, MaritalStatusTypeEnum? maritalStatus, required Function() onOkPress}) {
if (name == null || name.isEmpty) {
_dialogService.showExceptionBottomSheet(message: "Please enter a valid name", onOkPressed: onOkPress);
return false;
}
if (gender == null) {
_dialogService.showExceptionBottomSheet(message: "Please select a gender", onOkPressed: onOkPress);
return false;
}
if (maritalStatus == null) {
_dialogService.showExceptionBottomSheet(message: "Please select a marital status", onOkPressed: onOkPress);
return false;
}
if (country == null) {
_dialogService.showExceptionBottomSheet(message: "Please select a country", onOkPressed: onOkPress);
return false;
}
return true;
}
static bool isValidateEmail({String? email, required Function() onOkPress}) {
if (email == null || email.isEmpty) {
_dialogService.showExceptionBottomSheet(message: "Please enter email", onOkPressed: onOkPress);
return false;
}
final bool emailIsValid = RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}$").hasMatch(email);
if (!emailIsValid) {
_dialogService.showExceptionBottomSheet(message: "Please enter a valid email format", onOkPressed: onOkPress);
return false;
}
return true;
}
} }

@ -108,6 +108,7 @@ class AuthenticationViewModel extends ChangeNotifier {
Future<void> clearDefaultInputValues() async { Future<void> clearDefaultInputValues() async {
nationalIdController.clear(); nationalIdController.clear();
phoneNumberController.clear(); phoneNumberController.clear();
emailController.clear();
dobController.clear(); dobController.clear();
maritalStatus = null; maritalStatus = null;
genderType = null; genderType = null;
@ -324,7 +325,8 @@ class AuthenticationViewModel extends ChangeNotifier {
); );
} }
Future<void> sendActivationCode({required OTPTypeEnum otpTypeEnum, required String nationalIdOrFileNumber, required String phoneNumber, required bool isForRegister, dynamic payload}) async { Future<void> sendActivationCode(
{required OTPTypeEnum otpTypeEnum, required String nationalIdOrFileNumber, required String phoneNumber, required bool isForRegister, dynamic payload, bool isComingFromResendOTP = false}) async {
var request = RequestUtils.getCommonRequestSendActivationCode( var request = RequestUtils.getCommonRequestSendActivationCode(
otpTypeEnum: otpTypeEnum, otpTypeEnum: otpTypeEnum,
mobileNumber: phoneNumber, mobileNumber: phoneNumber,
@ -364,7 +366,7 @@ class AuthenticationViewModel extends ChangeNotifier {
} else { } else {
if (apiResponse.data != null && apiResponse.data['isSMSSent'] == true) { if (apiResponse.data != null && apiResponse.data['isSMSSent'] == true) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
navigateToOTPScreen(otpTypeEnum: otpTypeEnum, phoneNumber: phoneNumber); if (!isComingFromResendOTP) navigateToOTPScreen(otpTypeEnum: otpTypeEnum, phoneNumber: phoneNumber, isComingFromRegister: checkIsUserComingForRegister(request: payload), payload: payload);
} else { } else {
// TODO: Handle isSMSSent false // TODO: Handle isSMSSent false
// navigateToOTPScreen(otpTypeEnum: otpTypeEnum, phoneNumber: phoneNumber); // navigateToOTPScreen(otpTypeEnum: otpTypeEnum, phoneNumber: phoneNumber);
@ -386,8 +388,9 @@ class AuthenticationViewModel extends ChangeNotifier {
required String? activationCode, required String? activationCode,
required OTPTypeEnum otpTypeEnum, required OTPTypeEnum otpTypeEnum,
required Function(String? message) onWrongActivationCode, required Function(String? message) onWrongActivationCode,
Function()? onResendActivation,
}) async { }) async {
bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true); bool isForRegister = (_appState.getUserRegistrationPayload.healthId != null || _appState.getUserRegistrationPayload.patientOutSa == true || _appState.getUserRegistrationPayload.patientOutSa == 1);
final request = RequestUtils.getCommonRequestWelcome( final request = RequestUtils.getCommonRequestWelcome(
phoneNumber: phoneNumberController.text, phoneNumber: phoneNumberController.text,
@ -553,7 +556,7 @@ class AuthenticationViewModel extends ChangeNotifier {
_navigationService.pushAndReplace(AppRoutes.landingScreen); _navigationService.pushAndReplace(AppRoutes.landingScreen);
} }
Future<void> navigateToOTPScreen({required OTPTypeEnum otpTypeEnum, required String phoneNumber}) async { Future<void> navigateToOTPScreen({required OTPTypeEnum otpTypeEnum, required String phoneNumber, required bool isComingFromRegister, dynamic payload}) async {
_navigationService.pushToOtpScreen( _navigationService.pushToOtpScreen(
phoneNumber: phoneNumber, phoneNumber: phoneNumber,
checkActivationCode: (int activationCode) async { checkActivationCode: (int activationCode) async {
@ -562,11 +565,18 @@ class AuthenticationViewModel extends ChangeNotifier {
otpTypeEnum: otpTypeEnum, otpTypeEnum: otpTypeEnum,
onWrongActivationCode: (String? value) { onWrongActivationCode: (String? value) {
onWrongActivationCode(message: value); onWrongActivationCode(message: value);
});
// Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => RegisterNewStep2(null, {"nationalID": "12345678654321"})));
}, },
onResendOTPPressed: (String phoneNumber) {}, );
},
onResendOTPPressed: (String phoneNumber) async {
await sendActivationCode(
otpTypeEnum: otpTypeEnum,
phoneNumber: phoneNumberController.text,
nationalIdOrFileNumber: nationalIdController.text,
isForRegister: isComingFromRegister,
isComingFromResendOTP: true,
payload: payload);
},
); );
} }
@ -649,19 +659,18 @@ class AuthenticationViewModel extends ChangeNotifier {
} }
Future<void> onRegistrationComplete() async { Future<void> onRegistrationComplete() async {
LoaderBottomSheet.showLoader();
var request = RequestUtils.getUserSignupCompletionRequest(fullName: nameController.text, emailAddress: emailController.text, gender: genderType, maritalStatus: maritalStatus); var request = RequestUtils.getUserSignupCompletionRequest(fullName: nameController.text, emailAddress: emailController.text, gender: genderType, maritalStatus: maritalStatus);
//
print("============= Final Payload ===============");
print(request);
print("=================== ====================");
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 {
if (apiResponse.data is String) { if (apiResponse.data is String) {
//TODO: This Section Need to Be Testing.
_dialogService.showExceptionBottomSheet(message: apiResponse.data, onOkPressed: () {}, onCancelPressed: () {});
//TODO: Here We Need to Show a Dialog Of Something in the case of Fail With OK and Cancel and the Display Variable WIll be result. //TODO: Here We Need to Show a Dialog Of Something in the case of Fail With OK and Cancel and the Display Variable WIll be result.
} else { } else {
print(apiResponse.data as Map<String, dynamic>); print(apiResponse.data as Map<String, dynamic>);
if (apiResponse.data["MessageStatus"] == 1) { if (apiResponse.data["MessageStatus"] == 1) {
LoaderBottomSheet.hideLoader();
//TODO: Here We Need to Show a Dialog Of Something in the case of Success. //TODO: Here We Need to Show a Dialog Of Something in the case of Success.
await clearDefaultInputValues(); // This will Clear All Default Values Of User. await clearDefaultInputValues(); // This will Clear All Default Values Of User.
_navigationService.pushAndReplace(AppRoutes.loginScreen); _navigationService.pushAndReplace(AppRoutes.loginScreen);
@ -722,8 +731,6 @@ class AuthenticationViewModel extends ChangeNotifier {
} else { } else {
isOutSideSa = false; isOutSideSa = false;
} }
print(isOutSideSa);
return isOutSideSa; return isOutSideSa;
} }

@ -407,7 +407,7 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
late final TextEditingController _otpController; late final TextEditingController _otpController;
Timer? _resendTimer; Timer? _resendTimer;
int _resendTime = 60; int _resendTime = 120;
bool _isOtpComplete = false; bool _isOtpComplete = false;
bool _isVerifying = false; // Flag to prevent multiple verification calls bool _isVerifying = false; // Flag to prevent multiple verification calls
@ -452,11 +452,11 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
void _resendOtp() { void _resendOtp() {
if (_resendTime == 0) { if (_resendTime == 0) {
setState(() { setState(() {
_resendTime = 60; _resendTime = 120;
_isVerifying = false; // Reset verification flag _isVerifying = false; // Reset verification flag
}); });
_startResendTimer(); _startResendTimer();
autoFillOtp("1234"); // autoFillOtp("1234");
widget.onResendOTPPressed(widget.phoneNumber); widget.onResendOTPPressed(widget.phoneNumber);
} }
} }
@ -526,9 +526,18 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
children: [ children: [
const Text("Didn't receive it? "), const Text("Didn't receive it? "),
if (_resendTime > 0) if (_resendTime > 0)
Text( Builder(
'resend in (${_resendTime.toString().padLeft(2, '0')}:00). ', // Use a Builder to easily calculate minutes and seconds inline
builder: (context) {
final minutes = (_resendTime ~/ 60)
.toString()
.padLeft(2, '0'); // Integer division for minutes final seconds = (_resendTime % 60).toString().padLeft(2, '0'); // Modulo for remaining seconds
final seconds = (_resendTime % 60).toString().padLeft(2, '0'); // Modulo for remaining seconds // <--- HERE IT IS
return Text(
'resend in ($minutes:$seconds). ',
style: const TextStyle(color: Colors.grey), style: const TextStyle(color: Colors.grey),
);
},
) )
else else
GestureDetector( GestureDetector(
@ -558,7 +567,7 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
/// Auto fill OTP into text fields /// Auto fill OTP into text fields
void autoFillOtp(String otp) { void autoFillOtp(String otp) {
if (otp.length != _otpLength) return; if (otp.length != _otpLength) return;
_isVerifying = false; // Reset flag before setting new OTP _isVerifying = false;
_otpController.text = otp; _otpController.text = otp;
} }
} }

@ -6,6 +6,7 @@ import 'package:hmg_patient_app_new/core/common_models/nationality_country_model
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/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/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';
@ -66,7 +67,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
children: [ children: [
TextInputWidget( TextInputWidget(
labelText: authVM!.isUserFromUAE() ? LocaleKeys.fullName.tr() : LocaleKeys.name.tr(), labelText: authVM!.isUserFromUAE() ? LocaleKeys.fullName.tr() : LocaleKeys.name.tr(),
hintText: authVM!.isUserFromUAE() ? "" : ("${appState.getNHICUserData.firstNameEn!.toUpperCase()} ${appState.getNHICUserData.lastNameEn!.toUpperCase()}"), hintText: authVM!.isUserFromUAE() ? "Enter your full name" : ("${appState.getNHICUserData.firstNameEn!.toUpperCase()} ${appState.getNHICUserData.lastNameEn!.toUpperCase()}"),
controller: authVM!.isUserFromUAE() ? authVM!.nameController : null, controller: authVM!.isUserFromUAE() ? authVM!.nameController : null,
isEnable: true, isEnable: true,
prefix: null, prefix: null,
@ -275,6 +276,20 @@ class _RegisterNew extends State<RegisterNewStep2> {
icon: AppAssets.confirm, icon: AppAssets.confirm,
iconColor: AppColors.textGreenColor, iconColor: AppColors.textGreenColor,
onPressed: () { 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);
}
// if (isFromDubai) { // if (isFromDubai) {
// if (name == null) { // if (name == null) {
// AppToast.showErrorToast(message: LocaleKeys.enterFullName); // AppToast.showErrorToast(message: LocaleKeys.enterFullName);
@ -293,8 +308,6 @@ class _RegisterNew extends State<RegisterNewStep2> {
// return; // return;
// } // }
// } // }
showModel(context: context);
}, },
), ),
) )
@ -324,7 +337,13 @@ class _RegisterNew extends State<RegisterNewStep2> {
child: CustomButton( child: CustomButton(
text: LocaleKeys.submit, text: LocaleKeys.submit,
onPressed: () { onPressed: () {
if (ValidationUtils.isValidateEmail(
email: authVM!.emailController.text,
onOkPress: () {
Navigator.of(context).pop();
})) {
authVM!.onRegistrationComplete(); authVM!.onRegistrationComplete();
}
}, },
backgroundColor: AppColors.bgGreenColor, backgroundColor: AppColors.bgGreenColor,
borderColor: AppColors.bgGreenColor, borderColor: AppColors.bgGreenColor,

@ -152,6 +152,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: 12.h,
isCountryDropDown: widget.isEnableCountryDropdown, isCountryDropDown: widget.isEnableCountryDropdown,
leadingIcon: widget.isForEmail ? AppAssets.email : AppAssets.smart_phone, leadingIcon: widget.isForEmail ? AppAssets.email : AppAssets.smart_phone,
) )

@ -76,9 +76,14 @@ class _CustomCountryDropdownState extends State<CustomCountryDropdown> {
_openDropdown(); _openDropdown();
} }
}, },
child: Utils.buildSvgWithAssets(icon: selectedCountry != null ? selectedCountry!.iconPath : AppAssets.ksa, width: 40.h, height: 40.h)), child: Row(
children: [
Utils.buildSvgWithAssets(icon: selectedCountry != null ? selectedCountry!.iconPath : AppAssets.ksa, width: 40.h, height: 40.h),
SizedBox(width: 8.h), SizedBox(width: 8.h),
Utils.buildSvgWithAssets(icon: _isDropdownOpen ? AppAssets.dropdow_icon : AppAssets.dropdow_icon), Utils.buildSvgWithAssets(icon: _isDropdownOpen ? AppAssets.dropdow_icon : AppAssets.dropdow_icon),
],
),
),
SizedBox(width: 4.h), SizedBox(width: 4.h),
if (widget.isFromBottomSheet) if (widget.isFromBottomSheet)
GestureDetector( GestureDetector(
@ -100,19 +105,23 @@ class _CustomCountryDropdownState extends State<CustomCountryDropdown> {
], ],
), ),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text( Text(
selectedCountry!.countryCode, selectedCountry!.countryCode,
style: TextStyle(fontSize: 12.fSize, height: 21 / 18, fontWeight: FontWeight.w600, letterSpacing: -0.2), style: TextStyle(fontSize: 12.fSize, height: 23 / 18, fontWeight: FontWeight.w600, letterSpacing: -1),
), ),
SizedBox(width: 4.h), SizedBox(width: 4.h),
if (widget.isEnableTextField) if (widget.isEnableTextField)
SizedBox( SizedBox(
height: 18, height: 20,
width: 200, width: 200,
// color: Colors.red,
child: TextField( child: TextField(
focusNode: textFocusNode, focusNode: textFocusNode,
decoration: InputDecoration(hintText: "", isDense: true, border: InputBorder.none), style: TextStyle(fontSize: 12.fSize, height: 23 / 18, fontWeight: FontWeight.w600, letterSpacing: -1),
decoration: InputDecoration(hintText: "", isDense: false, border: InputBorder.none),
keyboardType: TextInputType.phone, keyboardType: TextInputType.phone,
onChanged: widget.onPhoneNumberChanged), onChanged: widget.onPhoneNumberChanged),
), ),
@ -146,7 +155,6 @@ class _CustomCountryDropdownState extends State<CustomCountryDropdown> {
_overlayEntry = OverlayEntry( _overlayEntry = OverlayEntry(
builder: (context) => Stack( builder: (context) => Stack(
children: [ children: [
// Dismiss dropdown when tapping outside
Positioned.fill( Positioned.fill(
child: GestureDetector( child: GestureDetector(
onTap: _closeDropdown, onTap: _closeDropdown,
@ -157,7 +165,7 @@ class _CustomCountryDropdownState extends State<CustomCountryDropdown> {
Positioned( Positioned(
top: offset.dy + renderBox.size.height, top: offset.dy + renderBox.size.height,
left: widget.isRtl ? offset.dx + 6.h : offset.dx - 6.h, left: widget.isRtl ? offset.dx + 6.h : offset.dx - 6.h,
width: renderBox.size.width, width: !widget.isFromBottomSheet ? renderBox.size.width : 60.h,
child: Material( child: Material(
child: Container( child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.white, borderRadius: 12), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.white, borderRadius: 12),
@ -184,8 +192,7 @@ class _CustomCountryDropdownState extends State<CustomCountryDropdown> {
style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, letterSpacing: -0.2)), style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, letterSpacing: -0.2)),
], ],
), ),
), )),
),
) )
.toList(), .toList(),
), ),

@ -223,32 +223,21 @@ class TextInputWidget extends StatelessWidget {
keyboardType: keyboardType, keyboardType: keyboardType,
controller: controller, controller: controller,
readOnly: isReadOnly, readOnly: isReadOnly,
textAlignVertical: TextAlignVertical.top, textAlignVertical: TextAlignVertical.center,
textAlign: TextAlign.left, textAlign: TextAlign.left,
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
onChanged: onChange, onChanged: onChange,
focusNode: focusNode ?? _focusNode, focusNode: focusNode ?? _focusNode,
autofocus: autoFocus, autofocus: autoFocus,
textInputAction: TextInputAction.done, textInputAction: TextInputAction.done,
cursorHeight: isWalletAmountInput! ? 40.h : 18.h, cursorHeight: isWalletAmountInput! ? 40.h : 20.h,
style: TextStyle(fontSize: fontSize!.fSize, height: isWalletAmountInput! ? 1 / 4 : 21 / 14, fontWeight: FontWeight.w500, color: AppColors.textColor, letterSpacing: -0.2), style: TextStyle(fontSize: fontSize!.fSize, height: isWalletAmountInput! ? 1 / 4 : 16 / 14, fontWeight: FontWeight.w500, color: AppColors.textColor, letterSpacing: -1),
decoration: InputDecoration( decoration: InputDecoration(
isDense: true, isDense: true,
hintText: hintText, hintText: hintText,
hintStyle: TextStyle(fontSize: 14.fSize, height: 21 / 16, fontWeight: FontWeight.w500, color: Color(0xff898A8D), letterSpacing: -0.2), hintStyle: TextStyle(fontSize: 14.fSize, height: 21 / 16, fontWeight: FontWeight.w500, color: Color(0xff898A8D), letterSpacing: -1),
prefixIconConstraints: BoxConstraints(minWidth: 45.h), prefixIconConstraints: BoxConstraints(minWidth: 30.h),
prefixIcon: prefix == null prefixIcon: prefix == null ? null : "+${prefix!}".toText14(letterSpacing: -1, color: AppColors.textColor, weight: FontWeight.w500),
? null
: Text(
"+" + prefix!,
style: TextStyle(
fontSize: 14.fSize,
height: 21 / 14,
fontWeight: FontWeight.w500,
color: Color(0xff2E303A),
letterSpacing: -0.2,
),
),
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
border: InputBorder.none, border: InputBorder.none,
focusedBorder: InputBorder.none, focusedBorder: InputBorder.none,

Loading…
Cancel
Save