From 217b423308194c8dcf7d587ed31aae132ab354c4 Mon Sep 17 00:00:00 2001 From: "Aamir.Muhammad" Date: Tue, 19 May 2026 15:29:24 +0300 Subject: [PATCH] OTP Changes & Drop Down Fixes. --- lib/core/api_consts.dart | 2 +- .../authentication_view_model.dart | 113 +++-- .../widgets/otp_verification_screen.dart | 17 +- .../hmg_services/hmg_services_view_model.dart | 3 +- .../authentication/register_step2.dart | 473 +++++++++--------- .../authentication/saved_login_screen.dart | 35 +- lib/services/navigation_service.dart | 4 +- lib/widgets/dropdown/dropdown_widget.dart | 105 ++-- 8 files changed, 380 insertions(+), 372 deletions(-) diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 8dbc9ce3..bf4ebbed 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -4,7 +4,7 @@ import 'package:hmg_patient_app_new/core/enums.dart'; class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.preProd; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index eaaa6848..c0e55a4d 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -99,29 +99,32 @@ class AuthenticationViewModel extends ChangeNotifier { // Login screen errors String? _nationalIdError; + String? get nationalIdError => _nationalIdError; // Phone number errors (used in multiple screens) String? _phoneNumberError; + String? get phoneNumberError => _phoneNumberError; // Registration screen errors String? _nameError; + String? get nameError => _nameError; String? _emailError; + String? get emailError => _emailError; String? _dobError; + String? get dobError => _dobError; // Check if registration form has any errors (for container border) - bool get hasRegistrationFormError => - _nationalIdError != null || _dobError != null; + bool get hasRegistrationFormError => _nationalIdError != null || _dobError != null; // Check if ID and phone have errors (for family file container) - bool get hasIdAndPhoneError => - _nationalIdError != null || _phoneNumberError != null; + bool get hasIdAndPhoneError => _nationalIdError != null || _phoneNumberError != null; // Additional field errors for UAE registration step 2 String? _genderError; @@ -130,15 +133,13 @@ class AuthenticationViewModel extends ChangeNotifier { // Getters for step 2 field errors (nameError and emailError already exist above) String? get genderError => _genderError; + String? get maritalStatusError => _maritalStatusError; + String? get countryError => _countryError; // Check if registration step 2 form has any errors (for container border) - bool get hasRegistrationStep2FormError => - _nameError != null || - _genderError != null || - _maritalStatusError != null || - _countryError != null; + bool get hasRegistrationStep2FormError => _nameError != null || _genderError != null || _maritalStatusError != null || _countryError != null; // Clear all step 2 field errors void clearAllStep2FieldErrors() { @@ -349,7 +350,7 @@ class AuthenticationViewModel extends ChangeNotifier { if (nationalIdController.text.isEmpty) { _nationalIdError = LocaleKeys.pleaseEnterAnationalID.tr(); notifyListeners(); - return false; // Stop here, don't check phone yet + return false; // Stop here, don't check phone yet } // Step 2: Validate National ID format @@ -359,7 +360,7 @@ class AuthenticationViewModel extends ChangeNotifier { if (!Utils.isSAUDIIDValid(cleanedId)) { _nationalIdError = LocaleKeys.enterValidNationalId.tr(); notifyListeners(); - return false; // Stop here + return false; // Stop here } } @@ -368,13 +369,13 @@ class AuthenticationViewModel extends ChangeNotifier { if (!ValidationUtils.validateIqama(nationalIdController.text)) { _nationalIdError = LocaleKeys.pleaseEnterAValidIqamaID.tr(); notifyListeners(); - return false; // Stop here + return false; // Stop here } } else if (selectedCountrySignup == CountryEnum.unitedArabEmirates) { if (!ValidationUtils.validateUaeNationalId(nationalIdController.text)) { _nationalIdError = LocaleKeys.pleaseEnterAValidNationalID.tr(); notifyListeners(); - return false; // Stop here + return false; // Stop here } } @@ -382,7 +383,7 @@ class AuthenticationViewModel extends ChangeNotifier { if (phoneNumberController.text.isEmpty) { _phoneNumberError = LocaleKeys.enterValidPhoneNumber.tr(); notifyListeners(); - return false; // Stop here + return false; // Stop here } // Step 5: Validate phone number format based on country @@ -412,7 +413,7 @@ class AuthenticationViewModel extends ChangeNotifier { if (nationalIdController.text.isEmpty) { _nationalIdError = LocaleKeys.pleaseEnterAnationalID.tr(); notifyListeners(); - return false; // Stop here, don't check other fields + return false; // Stop here, don't check other fields } // Step 2: Validate National ID format @@ -422,7 +423,7 @@ class AuthenticationViewModel extends ChangeNotifier { if (!Utils.isSAUDIIDValid(cleanedId)) { _nationalIdError = LocaleKeys.enterValidNationalId.tr(); notifyListeners(); - return false; // Stop here + return false; // Stop here } } @@ -431,13 +432,13 @@ class AuthenticationViewModel extends ChangeNotifier { if (!ValidationUtils.validateIqama(nationalIdController.text)) { _nationalIdError = LocaleKeys.pleaseEnterAValidIqamaID.tr(); notifyListeners(); - return false; // Stop here + return false; // Stop here } } else if (selectedCountrySignup == CountryEnum.unitedArabEmirates) { if (!ValidationUtils.validateUaeNationalId(nationalIdController.text)) { _nationalIdError = LocaleKeys.pleaseEnterAValidNationalID.tr(); notifyListeners(); - return false; // Stop here + return false; // Stop here } } @@ -445,7 +446,7 @@ class AuthenticationViewModel extends ChangeNotifier { if (dobController.text.isEmpty || dob == null || dob!.isEmpty) { _dobError = LocaleKeys.pleaseEnterAValidDateOfBirth.tr(); notifyListeners(); - return false; // Stop here + return false; // Stop here } // Step 5: Only validate Terms if both National ID and DOB are valid @@ -458,7 +459,7 @@ class AuthenticationViewModel extends ChangeNotifier { }, ); notifyListeners(); - return false; // Stop here + return false; // Stop here } // All validations passed @@ -481,11 +482,27 @@ class AuthenticationViewModel extends ChangeNotifier { _countryError = null; // Step 1: Validate name (only for UAE users) + // if (isUserFromUAE()) { + // if (nameController.text.trim().isEmpty) { + // _nameError = isArabic ? "الرجاء إدخال الاسم الكامل" : "Please enter full name"; + // notifyListeners(); + // return false; // Stop here + // } + // } + if (isUserFromUAE()) { - if (nameController.text.trim().isEmpty) { + // Remove extra spaces between words + final fullName = nameController.text.trim().replaceAll(RegExp(r'\s+'), ' '); + + // Split into words + final nameParts = fullName.split(' '); + + // Require at least 2 words + if (nameParts.length < 2) { _nameError = isArabic ? "الرجاء إدخال الاسم الكامل" : "Please enter full name"; + notifyListeners(); - return false; // Stop here + return false; } } @@ -603,7 +620,6 @@ class AuthenticationViewModel extends ChangeNotifier { // Format for display (use Hijri) dobController.text = Utils.formatHijriDateToDisplay(hijriDateTimeForController.toIso8601String()); - } else { // Gregorian calendar mode // Validate the date can be parsed @@ -624,7 +640,6 @@ class AuthenticationViewModel extends ChangeNotifier { clearDobError(); notifyListeners(); - } catch (e, stackTrace) { debugPrint('onDobChange: Unexpected error processing date "$date" - $e'); debugPrint('Stack trace: $stackTrace'); @@ -768,7 +783,7 @@ class AuthenticationViewModel extends ChangeNotifier { }, (apiResponse) { // LoadingUtils.hideFullScreenLoader(); - log("apiResponse: ${apiResponse.data.toString()}"); + log("apiResponse: ${apiResponse.data?.toJson().toString()}"); log("messageStatus: ${apiResponse.messageStatus.toString()}"); if (apiResponse.messageStatus == 1) { onSuccess(apiResponse.data); @@ -856,6 +871,9 @@ class AuthenticationViewModel extends ChangeNotifier { nationId: nationalIdController.text, isForRegister: false, patientOutSA: false, + // patientOutSA: selectedCountrySignup == CountryEnum.others + // ? false + // : (_appState.getSelectDeviceByImeiRespModelElement != null && _appState.getSelectDeviceByImeiRespModelElement!.outSa == true ? true : false), otpTypeEnum: otpTypeEnum, patientId: 0, zipCode: selectedCountrySignup == CountryEnum.others @@ -889,7 +907,10 @@ class AuthenticationViewModel extends ChangeNotifier { } else if (apiResponse.messageStatus == 1) { if (apiResponse.data['isSMSSent']) { _appState.setAppAuthToken = apiResponse.data['LogInTokenID']; - await sendActivationCode(otpTypeEnum: otpTypeEnum, phoneNumber: phoneNumberController.text, nationalIdOrFileNumber: nationalIdController.text, isForRegister: false); + print("============================"); + var zipcode = getZipCode(); + print("======== Zip Code =========== $zipcode ========"); + await sendActivationCode(otpTypeEnum: otpTypeEnum, phoneNumber: phoneNumberController.text, nationalIdOrFileNumber: nationalIdController.text, isForRegister: false, zipCode: zipcode); } else { if (apiResponse.data['IsAuthenticated']) { await checkActivationCode( @@ -911,6 +932,14 @@ class AuthenticationViewModel extends ChangeNotifier { ); } + String getZipCode() { + return selectedCountrySignup == CountryEnum.others + ? "0" + : (_appState.getSelectDeviceByImeiRespModelElement != null && _appState.getSelectDeviceByImeiRespModelElement!.outSa == true + ? CountryEnum.unitedArabEmirates.countryCode.toString() + : selectedCountrySignup.countryCode.toString()); + } + Future sendActivationCode( {required OTPTypeEnum otpTypeEnum, required String nationalIdOrFileNumber, @@ -921,12 +950,13 @@ class AuthenticationViewModel extends ChangeNotifier { bool isExcludedUser = false, bool isFormFamilyFile = false, bool isNeedLoading = false, - int? responseID}) async { + int? responseID, + String? zipCode}) async { var request = RequestUtils.getCommonRequestSendActivationCode( otpTypeEnum: otpTypeEnum, mobileNumber: phoneNumber, selectedLoginType: otpTypeEnum.toInt(), - zipCode: selectedCountrySignup.countryCode, + zipCode: zipCode ?? selectedCountrySignup.countryCode, nationalId: nationalIdOrFileNumber, isFileNo: isForRegister ? isPatientHasFile(request: payload) : false, patientId: isFormFamilyFile ? _appState.getAuthenticatedUser()!.patientId : 0, @@ -983,6 +1013,7 @@ class AuthenticationViewModel extends ChangeNotifier { navigateToOTPScreen( otpTypeEnum: otpTypeEnum, phoneNumber: phoneNumber, + zipCode: zipCode ?? "", isComingFromRegister: checkIsUserComingForRegister(request: payload), payload: payload, isFormFamilyFile: isFormFamilyFile, @@ -1308,10 +1339,12 @@ class AuthenticationViewModel extends ChangeNotifier { bool isFormFamilyFile = false, bool isExcludedUser = false, int? responseID, - int? patientShareRequestID}) async { + int? patientShareRequestID, + required String zipCode}) async { _navigationService.pushToOtpScreen( phoneNumber: phoneNumber, isFormFamilyFile: isFormFamilyFile, + zipCode: zipCode, checkActivationCode: (int activationCode) async { await checkActivationCode( activationCode: activationCode.toString(), @@ -1324,18 +1357,18 @@ class AuthenticationViewModel extends ChangeNotifier { }, ); }, - onResendOTPPressed: (String phoneNumber) async { + onResendOTPPressed: (String phoneNumber, String zipCode) async { await sendActivationCode( - otpTypeEnum: otpTypeEnum, - phoneNumber: phoneNumberController.text, - nationalIdOrFileNumber: nationalIdController.text, - isForRegister: isComingFromRegister, - isComingFromResendOTP: true, - payload: payload, - isFormFamilyFile: isFormFamilyFile, - isExcludedUser: isExcludedUser, - responseID: responseID, - ); + otpTypeEnum: otpTypeEnum, + phoneNumber: phoneNumberController.text, + nationalIdOrFileNumber: nationalIdController.text, + isForRegister: isComingFromRegister, + isComingFromResendOTP: true, + payload: payload, + isFormFamilyFile: isFormFamilyFile, + isExcludedUser: isExcludedUser, + responseID: responseID, + zipCode: zipCode); }, ); } diff --git a/lib/features/authentication/widgets/otp_verification_screen.dart b/lib/features/authentication/widgets/otp_verification_screen.dart index 41ca087a..00eb768f 100644 --- a/lib/features/authentication/widgets/otp_verification_screen.dart +++ b/lib/features/authentication/widgets/otp_verification_screen.dart @@ -427,12 +427,12 @@ class OTPWidgetState extends State with SingleTickerProviderStateMixi class OTPVerificationScreen extends StatefulWidget { final String phoneNumber; + final String zipCode; final Function(int code) checkActivationCode; - final Function(String phoneNumber) onResendOTPPressed; + final Function(String phoneNumber, String zipCode) onResendOTPPressed; final bool isFormFamilyFile; - const OTPVerificationScreen( - {super.key, required this.phoneNumber, required this.checkActivationCode, required this.onResendOTPPressed, required this.isFormFamilyFile}); + const OTPVerificationScreen({super.key, required this.phoneNumber, required this.zipCode, required this.checkActivationCode, required this.onResendOTPPressed, required this.isFormFamilyFile}); @override State createState() => _OTPVerificationScreenState(); @@ -452,7 +452,7 @@ class _OTPVerificationScreenState extends State { super.initState(); _otpController = TextEditingController(); _startResendTimer(); - if(Platform.isAndroid) { + if (Platform.isAndroid) { checkSignature(); } } @@ -521,7 +521,7 @@ class _OTPVerificationScreenState extends State { }); _otpController.clear(); _startResendTimer(); - widget.onResendOTPPressed(widget.phoneNumber); + widget.onResendOTPPressed(widget.phoneNumber, widget.zipCode); } } @@ -584,12 +584,7 @@ class _OTPVerificationScreenState extends State { pinBoxColor: AppColors.whiteColor, autoFocus: true, onTextChanged: _onOtpChanged, - pinTextStyle: TextStyle( - fontSize: 40.f, - fontWeight: FontWeight.bold, - color: AppColors.whiteColor, - fontFamily: "Poppins" - ), + pinTextStyle: TextStyle(fontSize: 40.f, fontWeight: FontWeight.bold, color: AppColors.whiteColor, fontFamily: "Poppins"), ), ), ), diff --git a/lib/features/hmg_services/hmg_services_view_model.dart b/lib/features/hmg_services/hmg_services_view_model.dart index d76814ec..da1ae80d 100644 --- a/lib/features/hmg_services/hmg_services_view_model.dart +++ b/lib/features/hmg_services/hmg_services_view_model.dart @@ -781,6 +781,7 @@ class HmgServicesViewModel extends ChangeNotifier { navigationService.pushToOtpScreen( phoneNumber: phoneNumber, isFormFamilyFile: false, + zipCode: "", checkActivationCode: (int activationCode) async { checkEReferralActivationCode( requestModel: CheckActivationCodeForEReferralRequestModel( @@ -795,7 +796,7 @@ class HmgServicesViewModel extends ChangeNotifier { }, ); }, - onResendOTPPressed: (String phoneNumber) async { + onResendOTPPressed: (String phoneNumber, String zipCode) async { // await sendActivationCode( // otpTypeEnum: otpTypeEnum, // phoneNumber: phoneNumberController.text, diff --git a/lib/presentation/authentication/register_step2.dart b/lib/presentation/authentication/register_step2.dart index c17d9ba7..cb96e63d 100644 --- a/lib/presentation/authentication/register_step2.dart +++ b/lib/presentation/authentication/register_step2.dart @@ -42,15 +42,12 @@ class _RegisterNew extends State { WidgetsBinding.instance.addPostFrameCallback((_) { authVM?.clearAllStep2FieldErrors(); }); - - // 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"); - }); + if (!authVM!.isUserFromUAE()) { + WidgetsBinding.instance.addPostFrameCallback((_) { + insuranceVM?.setIsInsuranceDataToBeLoaded(true); + insuranceVM?.initInsuranceProvider(); + }); + } } @override @@ -188,254 +185,256 @@ class _RegisterNew extends State { ), padding: EdgeInsets.only(left: 16.h, right: 16.h), child: Column( - children: [ - TextInputWidget( - labelText: authVM!.isUserFromUAE() ? LocaleKeys.fullName.tr(context: context) : LocaleKeys.name.tr(context: context), - hintText: authVM!.isUserFromUAE() ? LocaleKeys.enterNameHere.tr(context: context) : (name), - controller: authVM!.isUserFromUAE() ? authVM!.nameController : null, - isEnable: true, - prefix: null, - isAllowRadius: false, - isBorderAllowed: false, - keyboardType: TextInputType.text, - // textInputAction: TextInputAction.done, - onSubmitted: (value) { - FocusScope.of(context).unfocus(); - }, - onChange: (value) { - // Clear error when user starts typing - authVM!.clearNameError(); - }, - isAllowLeadingIcon: true, - isReadOnly: authVM!.isUserFromUAE() ? false : true, - leadingIcon: AppAssets.user_circle, - labelColor: AppColors.textColor, - ).paddingSymmetrical(0.h, 8.h), - // Show error message if exists - if (authVM!.isUserFromUAE() && authVM!.nameError != null) - Padding( - padding: EdgeInsets.only(left: 52.w, top: 4.h, bottom: 4.h, right: 16.w), - child: Text( - authVM!.nameError!, - style: TextStyle( - color: AppColors.primaryRedColor, - fontSize: 12.f, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + TextInputWidget( + labelText: authVM!.isUserFromUAE() ? LocaleKeys.fullName.tr(context: context) : LocaleKeys.name.tr(context: context), + hintText: authVM!.isUserFromUAE() ? LocaleKeys.enterNameHere.tr(context: context) : (name), + controller: authVM!.isUserFromUAE() ? authVM!.nameController : null, + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + // hintColor: Color(0xff898A8D), + keyboardType: TextInputType.text, + onSubmitted: (value) { + FocusScope.of(context).unfocus(); + }, + onChange: (value) { + authVM!.clearNameError(); + }, + isAllowLeadingIcon: true, + isReadOnly: authVM!.isUserFromUAE() ? false : true, + leadingIcon: AppAssets.user_circle, + labelColor: AppColors.textColor, + ).paddingSymmetrical(0.h, 8.h), + // Show error message if exists + if (authVM!.isUserFromUAE() && authVM!.nameError != null) + Padding( + padding: EdgeInsets.only(left: 0.w, top: 4.h, bottom: 4.h, right: 16.w), + child: Text( + authVM!.nameError!, + style: TextStyle( + color: AppColors.primaryRedColor, + fontSize: 12.f, + ), + ), ), - ), - ), - Divider(height: 1.h, color: AppColors.greyColor), - TextInputWidget( - labelText: LocaleKeys.nationalIdNumber.tr(context: context), - hintText: authVM!.isUserFromUAE() ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : (appState.getNHICUserData.idNumber ?? ""), - controller: null, - isEnable: true, - prefix: null, - isAllowRadius: false, - isBorderAllowed: false, - isAllowLeadingIcon: true, - isReadOnly: true, - labelColor: AppColors.textColor, - leadingIcon: AppAssets.student_card) - .paddingSymmetrical(0.h, 8.h), - Divider(height: 1, color: AppColors.greyColor), - authVM!.isUserFromUAE() - ? Selector( - selector: (_, authViewModel) => authViewModel.genderType, - shouldRebuild: (previous, next) => previous != next, - builder: (context, genderType, child) { - final authVM = context.read(); - return DropdownWidget( - labelText: LocaleKeys.gender.tr(context: context), - hintText: LocaleKeys.malE.tr(context: context), - isEnable: true, - dropdownItems: GenderTypeEnum.values.map((e) => appState.isArabic() ? e.typeAr : e.type).toList(), - selectedValue: genderType != null ? (appState.isArabic() ? genderType.typeAr : genderType.type) : "", - onChange: authVM.onGenderChange, - isBorderAllowed: false, - hasSelectionCustomIcon: true, - isAllowRadius: false, - labelColor: AppColors.textColor, - padding: EdgeInsets.only(top: 8.h, bottom: 8.h, left: 0, right: 0), - selectionCustomIcon: AppAssets.arrow_down, - leadingIcon: AppAssets.user_full, - ).withVerticalPadding(8); - }) - : TextInputWidget( - labelText: LocaleKeys.gender.tr(context: context), - hintText: (appState.getNHICUserData.gender ?? ""), + Divider(height: 1.h, color: AppColors.greyColor), + TextInputWidget( + labelText: LocaleKeys.nationalIdNumber.tr(context: context), + hintText: authVM!.isUserFromUAE() ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : (appState.getNHICUserData.idNumber ?? ""), controller: null, isEnable: true, prefix: null, isAllowRadius: false, isBorderAllowed: false, isAllowLeadingIcon: true, - isReadOnly: authVM!.isUserFromUAE() ? false : true, - leadingIcon: AppAssets.user_full, - labelColor: AppColors.textColor, - onChange: (value) {}) - .paddingSymmetrical(0.h, 8.h), - // Show gender error message if exists (for UAE users) - if (authVM!.isUserFromUAE() && authVM!.genderError != null) - Padding( - padding: EdgeInsets.only(left: 52.w, top: 4.h, bottom: 4.h, right: 16.w), - child: Text( - authVM!.genderError!, - style: TextStyle( - color: AppColors.primaryRedColor, - fontSize: 12.f, - ), - ), - ), - Divider(height: 1, color: AppColors.greyColor), - authVM!.isUserFromUAE() - ? Selector( - selector: (_, authViewModel) => authViewModel.maritalStatus, - shouldRebuild: (previous, next) => previous != next, - builder: (context, maritalStatus, child) { - final authVM = context.read(); // For onChange - return DropdownWidget( - labelText: LocaleKeys.maritalStatus.tr(context: context), - hintText: LocaleKeys.married.tr(context: context), - isEnable: true, - dropdownItems: MaritalStatusTypeEnum.values.map((e) => appState.isArabic() ? e.typeAr : e.type).toList(), - selectedValue: maritalStatus != null ? (appState.isArabic() ? maritalStatus.typeAr : maritalStatus.type) : "", - onChange: authVM.onMaritalStatusChange, - isBorderAllowed: false, - hasSelectionCustomIcon: true, - isAllowRadius: false, - labelColor: AppColors.textColor, - padding: EdgeInsets.only(top: 8.h, bottom: 8.h, left: 0, right: 0), - selectionCustomIcon: AppAssets.arrow_down, - leadingIcon: AppAssets.smart_phone, - ).withVerticalPadding(8); - }, - ) - : TextInputWidget( - labelText: LocaleKeys.maritalStatus.tr(context: context), - hintText: appState.isArabic() - ? (MaritalStatusTypeExtension.fromValue(appState.getNHICUserData.maritalStatusCode)?.typeAr ?? '') - : (MaritalStatusTypeExtension.fromValue(appState.getNHICUserData.maritalStatusCode)?.type ?? ''), - isEnable: true, - prefix: null, - isAllowRadius: false, - isBorderAllowed: false, - isAllowLeadingIcon: true, isReadOnly: true, labelColor: AppColors.textColor, - leadingIcon: AppAssets.smart_phone, - onChange: (value) {}) + leadingIcon: AppAssets.student_card) .paddingSymmetrical(0.h, 8.h), - // Show marital status error message if exists (for UAE users) - if (authVM!.isUserFromUAE() && authVM!.maritalStatusError != null) - Padding( - padding: EdgeInsets.only(left: 52.w, top: 4.h, bottom: 4.h, right: 16.w), - child: Text( - authVM!.maritalStatusError!, - style: TextStyle( - color: AppColors.primaryRedColor, - fontSize: 12.f, + Divider(height: 1, color: AppColors.greyColor), + authVM!.isUserFromUAE() + ? Selector( + selector: (_, authViewModel) => authViewModel.genderType, + shouldRebuild: (previous, next) => previous != next, + builder: (context, genderType, child) { + final authVM = context.read(); + return DropdownWidget( + labelText: LocaleKeys.gender.tr(context: context), + hintText: LocaleKeys.malE.tr(context: context), + isEnable: true, + dropdownItems: GenderTypeEnum.values.map((e) => appState.isArabic() ? e.typeAr : e.type).toList(), + selectedValue: genderType != null ? (appState.isArabic() ? genderType.typeAr : genderType.type) : "", + onChange: authVM.onGenderChange, + isBorderAllowed: false, + hasSelectionCustomIcon: true, + isAllowRadius: false, + labelColor: AppColors.textColor, + padding: EdgeInsets.only(top: 8.h, bottom: 8.h, left: 0, right: 0), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.user_full, + ).withVerticalPadding(8); + }) + : TextInputWidget( + labelText: LocaleKeys.gender.tr(context: context), + hintText: (appState.getNHICUserData.gender ?? ""), + controller: null, + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: authVM!.isUserFromUAE() ? false : true, + leadingIcon: AppAssets.user_full, + labelColor: AppColors.textColor, + onChange: (value) {}) + .paddingSymmetrical(0.h, 8.h), + // Show gender error message if exists (for UAE users) + if (authVM!.isUserFromUAE() && authVM!.genderError != null) + Padding( + padding: EdgeInsets.only(left: 0.w, top: 4.h, bottom: 4.h, right: 16.w), + child: Text( + authVM!.genderError!, + style: TextStyle( + color: AppColors.primaryRedColor, + fontSize: 12.f, + ), + ), + ), + Divider(height: 1, color: AppColors.greyColor), + authVM!.isUserFromUAE() + ? Selector( + selector: (_, authViewModel) => authViewModel.maritalStatus, + shouldRebuild: (previous, next) => previous != next, + builder: (context, maritalStatus, child) { + final authVM = context.read(); // For onChange + return DropdownWidget( + labelText: LocaleKeys.maritalStatus.tr(context: context), + hintText: LocaleKeys.married.tr(context: context), + isEnable: true, + dropdownItems: MaritalStatusTypeEnum.values.map((e) => appState.isArabic() ? e.typeAr : e.type).toList(), + selectedValue: maritalStatus != null ? (appState.isArabic() ? maritalStatus.typeAr : maritalStatus.type) : "", + onChange: authVM.onMaritalStatusChange, + isBorderAllowed: false, + hasSelectionCustomIcon: true, + isAllowRadius: false, + labelColor: AppColors.textColor, + padding: EdgeInsets.only(top: 8.h, bottom: 8.h, left: 0, right: 0), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.smart_phone, + ).withVerticalPadding(8); + }, + ) + : TextInputWidget( + labelText: LocaleKeys.maritalStatus.tr(context: context), + hintText: appState.isArabic() + ? (MaritalStatusTypeExtension.fromValue(appState.getNHICUserData.maritalStatusCode)?.typeAr ?? '') + : (MaritalStatusTypeExtension.fromValue(appState.getNHICUserData.maritalStatusCode)?.type ?? ''), + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + labelColor: AppColors.textColor, + leadingIcon: AppAssets.smart_phone, + onChange: (value) {}) + .paddingSymmetrical(0.h, 8.h), + // Show marital status error message if exists (for UAE users) + if (authVM!.isUserFromUAE() && authVM!.maritalStatusError != null) + Padding( + padding: EdgeInsets.only(left: 0.w, top: 4.h, bottom: 4.h, right: 16.w), + child: Text( + authVM!.maritalStatusError!, + style: TextStyle( + color: AppColors.primaryRedColor, + fontSize: 12.f, + ), + ), ), + Divider(height: 1.h, color: AppColors.greyColor), + authVM!.isUserFromUAE() + ? Selector? countriesList, NationalityCountries? selectedCountry, bool isArabic})>( + selector: (context, authViewModel) { + final appState = getIt.get(); + return ( + countriesList: authViewModel.countriesList, + selectedCountry: authViewModel.pickedCountryByUAEUser, + isArabic: appState.isArabic(), + ); + }, + shouldRebuild: (previous, next) => + previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic, + builder: (context, data, child) { + final authVM = context.read(); + return DropdownWidget( + labelText: LocaleKeys.country.tr(context: context), + hintText: LocaleKeys.uae.tr(context: context), + isEnable: true, + dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(), + selectedValue: data.selectedCountry != null + ? data.isArabic + ? data.selectedCountry!.nameN ?? "" + : data.selectedCountry!.name ?? "" + : "", + onChange: authVM.onUAEUserCountrySelection, + isBorderAllowed: false, + hasSelectionCustomIcon: true, + labelColor: AppColors.textColor, + isAllowRadius: false, + padding: EdgeInsets.only(top: 8.h, bottom: 8.h, left: 0, right: 0), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.globe, + ).withVerticalPadding(8.h); + }, + ) + : TextInputWidget( + labelText: LocaleKeys.nationality.tr(context: context), + hintText: appState.isArabic() + ? (authVM!.countriesList!.firstWhere((e) => e.id == (appState.getNHICUserData.nationalityCode ?? ""), orElse: () => NationalityCountries()).nameN ?? "") + : (authVM!.countriesList!.firstWhere((e) => e.id == (appState.getNHICUserData.nationalityCode ?? ""), orElse: () => NationalityCountries()).name ?? ""), + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + labelColor: AppColors.textColor, + leadingIcon: AppAssets.globe, + onChange: (value) {}) + .paddingSymmetrical(0.h, 8.h), + // Show country error message if exists (for UAE users) + if (authVM!.isUserFromUAE() && authVM!.countryError != null) + Padding( + padding: EdgeInsets.only(left: 0.w, top: 4.h, bottom: 4.h, right: 16.w), + child: Text( + authVM!.countryError!, + style: TextStyle( + color: AppColors.primaryRedColor, + fontSize: 12.f, + ), + ), + ), + Divider( + height: 1, + color: AppColors.greyColor, ), - ), - Divider(height: 1.h, color: AppColors.greyColor), - authVM!.isUserFromUAE() - ? Selector? countriesList, NationalityCountries? selectedCountry, bool isArabic})>( - selector: (context, authViewModel) { - final appState = getIt.get(); - return ( - countriesList: authViewModel.countriesList, - selectedCountry: authViewModel.pickedCountryByUAEUser, - isArabic: appState.isArabic(), - ); - }, - shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic, - builder: (context, data, child) { - final authVM = context.read(); - return DropdownWidget( - labelText: LocaleKeys.country.tr(context: context), - hintText: LocaleKeys.uae.tr(context: context), - isEnable: true, - dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(), - selectedValue: data.selectedCountry != null - ? data.isArabic - ? data.selectedCountry!.nameN ?? "" - : data.selectedCountry!.name ?? "" - : "", - onChange: authVM.onUAEUserCountrySelection, - isBorderAllowed: false, - hasSelectionCustomIcon: true, - labelColor: AppColors.textColor, - isAllowRadius: false, - padding: EdgeInsets.only(top: 8.h, bottom: 8.h, left: 0, right: 0), - selectionCustomIcon: AppAssets.arrow_down, - leadingIcon: AppAssets.globe, - ).withVerticalPadding(8.h); - }, - ) - : TextInputWidget( - labelText: LocaleKeys.nationality.tr(context: context), - hintText: appState.isArabic() - ? (authVM!.countriesList!.firstWhere((e) => e.id == (appState.getNHICUserData.nationalityCode ?? ""), orElse: () => NationalityCountries()).nameN ?? "") - : (authVM!.countriesList!.firstWhere((e) => e.id == (appState.getNHICUserData.nationalityCode ?? ""), orElse: () => NationalityCountries()).name ?? ""), - isEnable: true, + TextInputWidget( + labelText: LocaleKeys.mobileNumber.tr(context: context), + hintText: appState.getUserRegistrationPayload.patientMobileNumber.toString(), + controller: null, + isEnable: false, prefix: null, isAllowRadius: false, isBorderAllowed: false, isAllowLeadingIcon: true, - isReadOnly: true, labelColor: AppColors.textColor, - leadingIcon: AppAssets.globe, - onChange: (value) {}) + isReadOnly: true, + leadingIcon: AppAssets.call) .paddingSymmetrical(0.h, 8.h), - // Show country error message if exists (for UAE users) - if (authVM!.isUserFromUAE() && authVM!.countryError != null) - Padding( - padding: EdgeInsets.only(left: 52.w, top: 4.h, bottom: 4.h, right: 16.w), - child: Text( - authVM!.countryError!, - style: TextStyle( - color: AppColors.primaryRedColor, - fontSize: 12.f, - ), + Divider( + height: 1.h, + color: AppColors.greyColor, ), - ), - Divider( - height: 1, - color: AppColors.greyColor, - ), - TextInputWidget( - labelText: LocaleKeys.mobileNumber.tr(context: context), - hintText: appState.getUserRegistrationPayload.patientMobileNumber.toString(), - controller: null, - isEnable: false, - prefix: null, - isAllowRadius: false, - isBorderAllowed: false, - isAllowLeadingIcon: true, - labelColor: AppColors.textColor, - isReadOnly: true, - leadingIcon: AppAssets.call) - .paddingSymmetrical(0.h, 8.h), - Divider( - height: 1.h, - color: AppColors.greyColor, + TextInputWidget( + labelText: LocaleKeys.dob.tr(context: context), + hintText: authVM!.isUserFromUAE() ? (appState.getUserRegistrationPayload.dob ?? '') : (appState.getNHICUserData.dateOfBirth ?? ""), + controller: authVM!.isUserFromUAE() ? authVM!.dobController : null, + isEnable: false, + prefix: null, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + labelColor: AppColors.textColor, + leadingIcon: AppAssets.birthday_cake, + selectionType: null, + ).paddingSymmetrical(0.h, 8.h), + ], ), - TextInputWidget( - labelText: LocaleKeys.dob.tr(context: context), - hintText: authVM!.isUserFromUAE() ? (appState.getUserRegistrationPayload.dob ?? '') : (appState.getNHICUserData.dateOfBirth ?? ""), - controller: authVM!.isUserFromUAE() ? authVM!.dobController : null, - isEnable: false, - prefix: null, - isBorderAllowed: false, - isAllowLeadingIcon: true, - isReadOnly: true, - labelColor: AppColors.textColor, - leadingIcon: AppAssets.birthday_cake, - selectionType: null, - ).paddingSymmetrical(0.h, 8.h), - ], - ), - ); + ); }, ), SizedBox(height: 50.h), diff --git a/lib/presentation/authentication/saved_login_screen.dart b/lib/presentation/authentication/saved_login_screen.dart index 64afff0b..7f0cdc34 100644 --- a/lib/presentation/authentication/saved_login_screen.dart +++ b/lib/presentation/authentication/saved_login_screen.dart @@ -87,14 +87,12 @@ class _SavedLogin extends State { LocaleKeys.welcomeBack.tr().toText16(color: AppColors.inputLabelTextColor), SizedBox(height: 16.h), appState.getSelectDeviceByImeiRespModelElement != null - ? appState.getSelectDeviceByImeiRespModelElement!.name!.toCamelCase - .toText26(isBold: true, height: 26 / 36, color: AppColors.textColor, isEnglishOnly: true) + ? appState.getSelectDeviceByImeiRespModelElement!.name!.toCamelCase.toText26(isBold: true, height: 26 / 36, color: AppColors.textColor, isEnglishOnly: true) : SizedBox(), SizedBox(height: 24.h), Container( padding: EdgeInsets.all(16.h), - decoration: RoundedRectangleBorder() - .toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: false, isCustomShadow: [ + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: false, isCustomShadow: [ BoxShadow(color: Color(0x0D000000), blurRadius: 16.h, offset: Offset(0, 0), spreadRadius: 5.h), ]), child: Column( @@ -106,9 +104,7 @@ class _SavedLogin extends State { textDirection: ui.TextDirection.ltr, child: appState.getSelectDeviceByImeiRespModelElement != null ? (appState.getSelectDeviceByImeiRespModelElement!.createdOn != null - ? DateUtil.getFormattedDate( - DateUtil.convertStringToDate(appState.getSelectDeviceByImeiRespModelElement!.createdOn!), - "d MMMM, y 'at' HH:mm") + ? DateUtil.getFormattedDate(DateUtil.convertStringToDate(appState.getSelectDeviceByImeiRespModelElement!.createdOn!), "d MMMM, y 'at' HH:mm") : '--') .toText16(isBold: true, color: AppColors.textColor, isEnglishOnly: true) : SizedBox(), @@ -118,14 +114,10 @@ class _SavedLogin extends State { ? Container( margin: EdgeInsets.all(16.h), child: Utils.buildSvgWithAssets( - icon: (isOther == true && loginType == LoginTypeEnum.sms) - ? AppAssets.whatsapp - : getTypeIcons(appState.getSelectDeviceByImeiRespModelElement!.logInType!), + icon: (isOther == true && loginType == LoginTypeEnum.sms) ? AppAssets.whatsapp : getTypeIcons(appState.getSelectDeviceByImeiRespModelElement!.logInType!), height: 54.h, width: 54.w, - iconColor: (isOther == true && loginType == LoginTypeEnum.sms) || loginType.toInt == 4 - ? null - : AppColors.primaryRedColor)) + iconColor: (isOther == true && loginType == LoginTypeEnum.sms) || loginType.toInt == 4 ? null : AppColors.primaryRedColor)) : SizedBox(), // Main login button - for isOther with SMS, show WhatsApp, otherwise keep original login type CustomButton( @@ -138,9 +130,7 @@ class _SavedLogin extends State { } else { // 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), + otpTypeEnum: (isOther == true && loginType == LoginTypeEnum.sms) ? OTPTypeEnum.whatsapp : (loginType == LoginTypeEnum.sms ? OTPTypeEnum.sms : OTPTypeEnum.whatsapp), ); } }, @@ -153,8 +143,7 @@ class _SavedLogin extends State { height: 44.h, padding: EdgeInsets.symmetric(vertical: 10.h), icon: (isOther == true && loginType == LoginTypeEnum.sms) ? AppAssets.whatsapp : getTypeIcons(loginType.toInt), - iconColor: - (isOther == true && loginType == LoginTypeEnum.sms) || loginType == LoginTypeEnum.whatsapp ? null : Colors.white, + iconColor: (isOther == true && loginType == LoginTypeEnum.sms) || loginType == LoginTypeEnum.whatsapp ? null : Colors.white, ), ], ), @@ -189,13 +178,13 @@ class _SavedLogin extends State { backgroundColor: Colors.transparent, enableDrag: false, // Prevent dragging to avoid focus conflicts - builder: (bottomSheetContext) => - StatefulBuilder(builder: (BuildContext context, StateSetter setModalState) { + builder: (bottomSheetContext) => StatefulBuilder(builder: (BuildContext context, StateSetter setModalState) { return Padding( padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom), child: SingleChildScrollView( child: GenericBottomSheet( - countryCode: "966", + countryCode: appState.getSelectDeviceByImeiRespModelElement!.outSa == true ? "971" : "966", + // countryCode: "966", initialPhoneNumber: "", textController: TextEditingController(), isFromSavedLogin: true, @@ -221,9 +210,7 @@ class _SavedLogin extends State { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - Padding( - padding: EdgeInsets.symmetric(horizontal: 8.h), - child: (LocaleKeys.oR.tr()).toText16(color: AppColors.textColor)), + Padding(padding: EdgeInsets.symmetric(horizontal: 8.h), child: (LocaleKeys.oR.tr()).toText16(color: AppColors.textColor)), ], ), Padding( diff --git a/lib/services/navigation_service.dart b/lib/services/navigation_service.dart index 3f0fb2a1..a83d4e3d 100644 --- a/lib/services/navigation_service.dart +++ b/lib/services/navigation_service.dart @@ -58,10 +58,10 @@ class NavigationService { } Future pushToOtpScreen( - {required String phoneNumber, required Function(int code) checkActivationCode, required Function(String phoneNumber) onResendOTPPressed, bool isFormFamilyFile = false}) { + {required String phoneNumber, required String zipCode, required Function(int code) checkActivationCode, required Function(String phoneNumber, String zipCode) onResendOTPPressed, bool isFormFamilyFile = false}) { return navigatorKey.currentState!.push( MaterialPageRoute( - builder: (_) => OTPVerificationScreen(phoneNumber: phoneNumber, checkActivationCode: checkActivationCode, onResendOTPPressed: onResendOTPPressed, isFormFamilyFile: isFormFamilyFile)), + builder: (_) => OTPVerificationScreen(phoneNumber: phoneNumber, zipCode: zipCode, checkActivationCode: checkActivationCode, onResendOTPPressed: onResendOTPPressed, isFormFamilyFile: isFormFamilyFile)), ); } diff --git a/lib/widgets/dropdown/dropdown_widget.dart b/lib/widgets/dropdown/dropdown_widget.dart index 7f76d8a6..32f6b3f1 100644 --- a/lib/widgets/dropdown/dropdown_widget.dart +++ b/lib/widgets/dropdown/dropdown_widget.dart @@ -21,6 +21,7 @@ class DropdownWidget extends StatelessWidget { final Color? labelColor; final String? errorMessage; final bool? hasError; + const DropdownWidget( {Key? key, required this.labelText, @@ -37,8 +38,7 @@ class DropdownWidget extends StatelessWidget { this.leadingIcon, this.labelColor, this.errorMessage, - this.hasError =false - }) + this.hasError = false}) : super(key: key); @override @@ -46,32 +46,33 @@ class DropdownWidget extends StatelessWidget { Widget content = Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, - children: [_buildLabelText(labelColor), _buildDropdown(context),], + children: [ + _buildLabelText(labelColor), + _buildDropdown(context), + ], ); - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [Container( - padding: padding, - alignment: Alignment.center, // This might need adjustment based on layout - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Container( + padding: padding, + alignment: Alignment.center, // This might need adjustment based on layout + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: isAllowRadius ? 15.h : null, - side: isBorderAllowed ? BorderSide(color: hasError! ? Colors.red: const Color(0xffefefef), width: 1) : null, - ), - child: Row( - // Wrap with a Row - crossAxisAlignment: CrossAxisAlignment.center, // Align items vertically in the center - children: [ - if (leadingIcon != null) ...[ - _buildLeadingIcon(), - SizedBox(width: 3.h), + side: isBorderAllowed ? BorderSide(color: hasError! ? Colors.red : const Color(0xffefefef), width: 1) : null, + ), + child: Row( + // Wrap with a Row + crossAxisAlignment: CrossAxisAlignment.center, // Align items vertically in the center + children: [ + if (leadingIcon != null) ...[ + _buildLeadingIcon(), + SizedBox(width: 3.h), + ], + Expanded(child: content), ], - Expanded(child: content), - - ], + ), ), - ), if (hasError! && errorMessage != null) Padding( padding: EdgeInsets.only(top: 4.h, left: 12.h), // Adjust padding as needed @@ -82,16 +83,17 @@ class DropdownWidget extends StatelessWidget { fontSize: 12.f, ), ), - )]); + ) + ]); } Widget _buildLeadingIcon() { return Container( - height: 40.h, - width: 40.h, - margin: EdgeInsets.only(right: 10.h), - padding: EdgeInsets.all(8.h), - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 10.h, color: AppColors.greyColor), + height: 40.h, + width: 40.h, + margin: EdgeInsets.only(right: 10.h), + padding: EdgeInsets.all(8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 10.h, color: AppColors.greyColor), child: Utils.buildSvgWithAssets(icon: leadingIcon!), ); } @@ -116,35 +118,26 @@ class DropdownWidget extends StatelessWidget { final renderBox = context.findRenderObject() as RenderBox; final offset = renderBox.localToGlobal(Offset.zero); final selected = await showMenu( - context: context, - position: RelativeRect.fromLTRB( - offset.dx, - offset.dy + renderBox.size.height, - offset.dx + renderBox.size.width, - 0, - ), - items: dropdownItems - .map( - (e) => PopupMenuItem( - value: e, - child: Text( - e, - style: TextStyle( - fontSize: 14.f, - height: 21 / 14, - fontWeight: FontWeight.w600, - letterSpacing: -0.2, + context: context, + position: RelativeRect.fromLTRB( + offset.dx, + offset.dy + renderBox.size.height, + offset.dx + renderBox.size.width, + 0, + ), + items: dropdownItems + .map( + (e) => PopupMenuItem( + value: e, + child: Text( + e, + style: TextStyle(color: AppColors.textColor, fontSize: 14.f, height: 21 / 14, fontWeight: FontWeight.w600, letterSpacing: -0.2), ), ), - ), - ) - .toList(), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - - ), - color: Colors.black - ); + ) + .toList(), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + color: AppColors.whiteColor); if (selected != null && onChange != null) { onChange!(selected); @@ -165,7 +158,7 @@ class DropdownWidget extends StatelessWidget { height: 21 / 14, fontWeight: FontWeight.w600, // color: (selectedValue != null && selectedValue!.isNotEmpty) ? const Color(0xff2E3039) : const Color(0xffB0B0B0), - color: AppColors.textColor, + color: (selectedValue == null || selectedValue!.isEmpty) ? AppColors.inputLabelTextColor :AppColors.textColor, letterSpacing: -0.2, ), ),