diff --git a/apps/proxy-auth/src/app/element.module.ts b/apps/proxy-auth/src/app/element.module.ts index 4f1d4515..da1a3280 100644 --- a/apps/proxy-auth/src/app/element.module.ts +++ b/apps/proxy-auth/src/app/element.module.ts @@ -43,6 +43,14 @@ function documentReady(fn: any) { window['initVerification'] = (config: any) => { documentReady(() => { + const urlParams = new URLSearchParams(window.location.search); + const isRegisterFormOnlyFromParams = urlParams.get('isRegisterFormOnly') === 'true'; + const paramsData = { + ...(urlParams.get('first_name') && { firstName: urlParams.get('first_name') }), + ...(urlParams.get('last_name') && { lastName: urlParams.get('last_name') }), + ...(urlParams.get('email') && { email: urlParams.get('email') }), + ...(urlParams.get('signup_service_id') && { signupServiceId: urlParams.get('signup_service_id') }), + }; if (config?.referenceId || config?.authToken || config?.showCompanyDetails) { const findOtpProvider = document.querySelector('proxy-auth'); if (findOtpProvider) { @@ -65,6 +73,7 @@ window['initVerification'] = (config: any) => { sendOtpElement.exclude_role_ids = config?.exclude_role_ids; sendOtpElement.include_role_ids = config?.include_role_ids; sendOtpElement.isHidden = config?.isHidden; + sendOtpElement.isRegisterFormOnly = config?.isRegisterFormOnly || isRegisterFormOnlyFromParams; sendOtpElement.target = config?.target ?? '_self'; sendOtpElement.css = config.style; if (!config.success || typeof config.success !== 'function') { @@ -73,8 +82,8 @@ window['initVerification'] = (config: any) => { sendOtpElement.successReturn = config.success; sendOtpElement.failureReturn = config.failure; - // omitting keys which are not required in API payload - sendOtpElement.otherData = omit(config, RESERVED_KEYS); + // omitting keys which are not required in API payload; query params fill in missing values + sendOtpElement.otherData = { ...paramsData, ...omit(config, RESERVED_KEYS) }; if (document.getElementById('proxyContainer') && config?.type !== 'user-management') { document.getElementById('proxyContainer').append(sendOtpElement); } else if (config?.type === 'user-management') { diff --git a/apps/proxy-auth/src/app/otp/component/login/login.component.html b/apps/proxy-auth/src/app/otp/component/login/login.component.html index 0862177c..42ec7708 100644 --- a/apps/proxy-auth/src/app/otp/component/login/login.component.html +++ b/apps/proxy-auth/src/app/otp/component/login/login.component.html @@ -1,4 +1,4 @@ -
+
-
+
Login
Email or Mobile @@ -47,15 +54,29 @@
- - + + + + + + + + +
@@ -72,7 +93,14 @@
-
+
Reset Password
Change Password
diff --git a/apps/proxy-auth/src/app/otp/component/login/login.component.scss b/apps/proxy-auth/src/app/otp/component/login/login.component.scss index cba8f3f8..63cea40e 100644 --- a/apps/proxy-auth/src/app/otp/component/login/login.component.scss +++ b/apps/proxy-auth/src/app/otp/component/login/login.component.scss @@ -47,6 +47,15 @@ p { .close-button { right: 16px; } + +.dark-theme { + .back-button svg { + fill: #ffffff; + } + .close-button svg path { + fill: #ffffff; + } +} .heading { font-size: 16px; line-height: 20px; @@ -91,3 +100,70 @@ p { justify-content: center; padding: 8px 0; } + +// Dark theme — mat-form-field overrides (mirrors register.component.scss pattern) +:host ::ng-deep .dark-theme-login { + .mat-mdc-text-field-wrapper, + .mdc-text-field--outlined { + background-color: transparent !important; + } + + .mdc-notched-outline__leading, + .mdc-notched-outline__notch, + .mdc-notched-outline__trailing { + border-color: #ffffff !important; + } + + .mat-mdc-form-field-flex { + background-color: transparent !important; + } + + .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-floating-label, + .mat-mdc-form-field .mdc-floating-label { + color: #ffffff !important; + } + + .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-text-field__input, + .mat-mdc-input-element { + color: #ffffff !important; + } + + // Legacy Material outline fields + .mat-form-field-appearance-outline { + .mat-form-field-outline, + .mat-form-field-outline-thick { + color: #ffffff !important; + } + + &.mat-form-field-invalid { + .mat-form-field-outline, + .mat-form-field-outline-thick { + color: red !important; + } + } + } + + .mat-form-field-flex { + background-color: transparent !important; + } + + .mat-input-element { + color: #ffffff !important; + } + + .mat-form-field-label { + color: #ffffff !important; + } + + // Hint text + .mat-hint, + .mat-mdc-form-field-hint, + .mat-mdc-form-field-hint-wrapper { + color: rgba(255, 255, 255, 0.7) !important; + } + + // Suffix icon (password toggle) + .mat-icon-button svg path { + fill: #ffffff; + } +} diff --git a/apps/proxy-auth/src/app/otp/component/login/login.component.ts b/apps/proxy-auth/src/app/otp/component/login/login.component.ts index 7d154538..3ad8a054 100644 --- a/apps/proxy-auth/src/app/otp/component/login/login.component.ts +++ b/apps/proxy-auth/src/app/otp/component/login/login.component.ts @@ -23,6 +23,7 @@ import { NgHcaptchaComponent } from 'ng-hcaptcha'; }) export class LoginComponent extends BaseComponent implements OnInit, OnDestroy { @Input() public loginServiceData: any; + @Input() public theme: string; @Output() public togglePopUp: EventEmitter = new EventEmitter(); @Output() public closePopUp: EventEmitter = new EventEmitter(); @Output() public openPopUp: EventEmitter = new EventEmitter(); diff --git a/apps/proxy-auth/src/app/otp/component/register/register.component.html b/apps/proxy-auth/src/app/otp/component/register/register.component.html index cf7ec272..a6fa3626 100644 --- a/apps/proxy-auth/src/app/otp/component/register/register.component.html +++ b/apps/proxy-auth/src/app/otp/component/register/register.component.html @@ -1,11 +1,11 @@
-
Register
- @@ -17,8 +17,11 @@ proxyMarkAllAsTouched [buttonRef]="submitBtn" (valid)="submit()" + [class.custom-radius]="!!borderRadiusValue" + [style.--field-border-radius]="borderRadiusValue || '4px'" + [class.dark-theme-login]="isDarkTheme" > -

User Details

+

User Details

{{ (selectGetOtpInProcess$ | async) ? 'Sending...' : 'Get OTP' }} @@ -170,12 +186,18 @@ }" >
-
+
Note: Password should contain atleast one Capital Letter, one Small Letter, one Digit and one Symbol
- -
Company Details (Optional)
+ +
+ Company Details (Optional) +
- +
diff --git a/apps/proxy-auth/src/app/otp/component/register/register.component.scss b/apps/proxy-auth/src/app/otp/component/register/register.component.scss index bf767ba2..bc72e341 100644 --- a/apps/proxy-auth/src/app/otp/component/register/register.component.scss +++ b/apps/proxy-auth/src/app/otp/component/register/register.component.scss @@ -263,3 +263,197 @@ input[matinput]::-webkit-inner-spin-button { .verify-otp-link { font-size: 12px; } + +.form-wrapper.custom-radius { + --mdc-outlined-text-field-container-shape: var(--field-border-radius, 4px); + --mdc-filled-text-field-container-shape: var(--field-border-radius, 4px); + --mat-form-field-container-shape: var(--field-border-radius, 4px); + --mdc-shape-small: var(--field-border-radius, 4px); +} + +:host ::ng-deep .form-wrapper.custom-radius { + .mat-mdc-text-field-wrapper, + .mdc-text-field--outlined { + border-radius: var(--field-border-radius, 4px) !important; + } + + .mdc-notched-outline__leading { + border-radius: var(--field-border-radius, 4px) 0 0 var(--field-border-radius, 4px) !important; + width: var(--field-border-radius, 4px) !important; + } + + .mdc-notched-outline__trailing { + border-radius: 0 var(--field-border-radius, 4px) var(--field-border-radius, 4px) 0 !important; + } + + .mat-form-field-outline-start { + border-radius: var(--field-border-radius, 4px) 0 0 var(--field-border-radius, 4px) !important; + width: var(--field-border-radius, 4px) !important; + } + + .mat-form-field-outline-end { + border-radius: 0 var(--field-border-radius, 4px) var(--field-border-radius, 4px) 0 !important; + } + + .mat-form-field-wrapper, + .mat-form-field-flex, + .mat-mdc-form-field-flex { + border-radius: var(--field-border-radius, 4px) !important; + } + + .mat-mdc-raised-button, + .mat-mdc-flat-button, + .mat-mdc-unelevated-button, + .mat-raised-button, + .mat-flat-button { + border-radius: var(--field-border-radius, 4px) !important; + } + + .register-user-details { + .iti { + border-radius: var(--field-border-radius, 4px) !important; + } + + input[type='tel'] { + border-radius: var(--field-border-radius, 4px) !important; + } + } +} +.register-user-details { + border-radius: var(--field-border-radius, 4px) !important; +} + +// Dark theme styles +.dark-text { + color: #ffffff !important; + + a { + color: #ffffff !important; + } +} + +.dark-divider { + border-top-color: #ffffff !important; +} + +:host ::ng-deep .form-wrapper.dark-theme-login { + .mat-mdc-text-field-wrapper, + .mdc-text-field--outlined { + background-color: transparent !important; + } + + .mdc-notched-outline__leading, + .mdc-notched-outline__notch, + .mdc-notched-outline__trailing { + border-color: #ffffff !important; + } + + .mat-mdc-form-field-flex { + background-color: transparent !important; + } + + .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-floating-label, + .mat-mdc-form-field .mdc-floating-label { + color: #ffffff !important; + } + + .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-text-field__input, + .mat-mdc-input-element { + color: #ffffff !important; + } + + // Legacy Material — use `color` on outline containers so children + // inherit via `currentColor`, preserving the gap's transparent top border + .mat-form-field-appearance-outline { + .mat-form-field-outline, + .mat-form-field-outline-thick { + color: #ffffff !important; + } + + &.mat-form-field-invalid { + .mat-form-field-outline, + .mat-form-field-outline-thick { + color: red !important; + } + } + } + + .mat-form-field-flex { + background-color: transparent !important; + } + + .mat-input-element { + color: #ffffff !important; + } + + .mat-form-field-label { + color: #ffffff !important; + } + + .register-user-details { + input[type='tel'], + [id^='init-contact'] { + border-color: #ffffff !important; + color: #ffffff !important; + background-color: #616161 !important; + + &::placeholder { + color: rgba(255, 255, 255, 0.7) !important; + } + } + + .iti__selected-flag { + background-color: #616161 !important; + } + + .iti__arrow { + border-top-color: #ffffff; + } + + .iti__dial-code { + color: #ffffff !important; + } + + .iti--allow-dropdown { + .iti__flag-container { + .iti__country-list { + background-color: #616161 !important; + + .iti__country:hover, + .iti__country.iti__highlight { + background-color: #757575 !important; + } + + .iti__country { + .iti__country-name { + color: #ffffff !important; + } + } + } + } + } + } +} +:host ::ng-deep .form-wrapper { + .register-user-details { + .iti--allow-dropdown { + .iti__flag-container { + .iti__selected-flag { + border-top-left-radius: var(--field-border-radius, 4px) !important; + border-bottom-left-radius: var(--field-border-radius, 4px) !important; + } + } + } + } +} + +// Custom button hover color applied via --btn-hover-color CSS property. +// .has-hover-color is only added when buttonHoverColor is truthy. +.has-hover-color:hover:not([disabled]) { + background-color: var(--btn-hover-color) !important; + + .mdc-button__ripple::before, + .mat-mdc-button-ripple::before { + opacity: 0 !important; + } +} diff --git a/apps/proxy-auth/src/app/otp/component/register/register.component.ts b/apps/proxy-auth/src/app/otp/component/register/register.component.ts index 72831308..61064c23 100644 --- a/apps/proxy-auth/src/app/otp/component/register/register.component.ts +++ b/apps/proxy-auth/src/app/otp/component/register/register.component.ts @@ -34,6 +34,7 @@ import { selectVerifyOtpV2InProcess, selectVerifyOtpV2Success, selectApiErrorResponse, + selectWidgetTheme, } from '../../store/selectors'; import { IGetOtpRes } from '../../model/otp'; @@ -49,6 +50,13 @@ export class RegisterComponent extends BaseComponent implements AfterViewInit, O @Input() public registrationViaLogin: boolean; @Input() public prefillDetails; @Input() public showCompanyDetails: boolean = true; + @Input() public version: string = 'v1'; + @Input() public theme: string; + @Input() public firstName: string; + @Input() public lastName: string; + @Input() public email: string; + @Input() public signupServiceId: string | number; + @Input() public isRegisterFormOnly: boolean = false; public showPassword: boolean = false; public showConfirmPassword: boolean = false; @Output() public togglePopUp: EventEmitter = new EventEmitter(); @@ -114,6 +122,7 @@ export class RegisterComponent extends BaseComponent implements AfterViewInit, O public isOtpSent: boolean = false; public isNumberChanged: boolean = false; public otpError: string = ''; + public otpVerificationToken: string = ''; // Resend OTP timer properties public resendTimer: number = 0; @@ -121,6 +130,9 @@ export class RegisterComponent extends BaseComponent implements AfterViewInit, O public lastSentMobileNumber: string = ''; private timerSubscription: Subscription; + public selectWidgetTheme$: Observable; + public uiPreferences: any = {}; + @ViewChild('otp1', { static: false }) otp1Ref: ElementRef; @ViewChild('otp2', { static: false }) otp2Ref: ElementRef; @ViewChild('otp3', { static: false }) otp3Ref: ElementRef; @@ -168,9 +180,20 @@ export class RegisterComponent extends BaseComponent implements AfterViewInit, O distinctUntilChanged(_.isEqual), takeUntil(this.destroy$) ); + this.selectWidgetTheme$ = this.store.pipe( + select(selectWidgetTheme), + distinctUntilChanged(_.isEqual), + takeUntil(this.destroy$) + ); } ngOnInit(): void { + this.selectWidgetTheme$.pipe(takeUntil(this.destroy$)).subscribe((theme) => { + this.uiPreferences = theme?.ui_preferences || {}; + }); + if (this.isRegisterFormOnly) { + this.registrationForm.get('user.email').disable(); + } this.registrationForm .get('user.mobile') .valueChanges.pipe(takeUntil(this.destroy$)) @@ -194,6 +217,9 @@ export class RegisterComponent extends BaseComponent implements AfterViewInit, O this.otpError = ''; // Clear error on successful verification } }); + this.selectVerifyOtpV2Data$.pipe(takeUntil(this.destroy$)).subscribe((res) => { + this.otpVerificationToken = res?.data?.otp_verification_token; + }); this.selectGetOtpSuccess$.pipe(takeUntil(this.destroy$)).subscribe((res) => { this.isOtpSent = res; if (res) { @@ -219,6 +245,15 @@ export class RegisterComponent extends BaseComponent implements AfterViewInit, O if (changes?.prefillDetails?.currentValue) { this.checkPrefillDetails(); } + if (changes?.firstName?.currentValue) { + this.registrationForm.get('user.firstName').setValue(changes.firstName.currentValue); + } + if (changes?.lastName?.currentValue) { + this.registrationForm.get('user.lastName').setValue(changes.lastName.currentValue); + } + if (changes?.email?.currentValue) { + this.registrationForm.get('user.email').setValue(changes.email.currentValue); + } } checkPrefillDetails() { if (isNaN(Number(this.prefillDetails))) { @@ -406,7 +441,7 @@ export class RegisterComponent extends BaseComponent implements AfterViewInit, O this.registrationForm.get('user.mobile').setErrors({ otpVerificationFailed: true }); return; } - const formData = removeEmptyKeys(cloneDeep(this.registrationForm.value), true); + const formData = removeEmptyKeys(cloneDeep(this.registrationForm.getRawValue()), true); const state = JSON.parse( this.otpUtilityService.aesDecrypt( this.registrationViaLogin ? this.loginServiceData.state : this.serviceData?.state ?? '', @@ -431,6 +466,7 @@ export class RegisterComponent extends BaseComponent implements AfterViewInit, O service_id: this.registrationViaLogin ? this.loginServiceData.service_id : this.serviceData.service_id, url_unique_id: state?.url_unique_id, request_data: formData, + ...(this.signupServiceId && { signup_service_id: this.signupServiceId }), }; const encodedData = this.otpUtilityService.aesEncrypt( JSON.stringify(payload), @@ -439,14 +475,20 @@ export class RegisterComponent extends BaseComponent implements AfterViewInit, O true ); const registrationState = this.registrationViaLogin ? this.loginServiceData.state : this.serviceData.state; - this.otpService.register({ proxy_state: encodedData, state: registrationState }).subscribe( - (response) => { - window.location.href = response.data.redirect_url; - }, - (err) => { - this.apiError.next(errorResolver(err?.error.errors)); - } - ); + this.otpService + .register({ + proxy_state: encodedData, + state: registrationState, + otp_verification_token: this.otpVerificationToken, + }) + .subscribe( + (response) => { + window.location.href = response.data.redirect_url; + }, + (err) => { + this.apiError.next(errorResolver(err?.error.errors)); + } + ); } public getOtp() { @@ -601,4 +643,51 @@ export class RegisterComponent extends BaseComponent implements AfterViewInit, O this.otpForm.reset(); this.cdr.detectChanges(); } + + public get primaryColor(): string | null { + if (this.version !== 'v2') { + return null; + } + const isDark = this.theme === 'dark'; + return isDark + ? this.uiPreferences?.dark_theme_primary_color || null + : this.uiPreferences?.light_theme_primary_color || null; + } + + public get borderRadiusValue(): string | null { + if (this.version !== 'v2') { + return null; + } + switch (this.uiPreferences?.border_radius) { + case 'none': + return '0'; + case 'small': + return '4px'; + case 'medium': + return '8px'; + case 'large': + return '12px'; + default: + return null; + } + } + + public get buttonColor(): string | null { + if (this.version !== 'v2') return null; + return this.uiPreferences?.button_color || null; + } + + public get buttonHoverColor(): string | null { + if (this.version !== 'v2') return null; + return this.uiPreferences?.button_hover_color || null; + } + + public get buttonTextColor(): string | null { + if (this.version !== 'v2') return null; + return this.uiPreferences?.button_text_color || null; + } + + public get isDarkTheme(): boolean { + return this.theme === 'dark'; + } } diff --git a/apps/proxy-auth/src/app/otp/component/send-otp-center/send-otp-center.component.html b/apps/proxy-auth/src/app/otp/component/send-otp-center/send-otp-center.component.html index bbed09e7..698b6836 100644 --- a/apps/proxy-auth/src/app/otp/component/send-otp-center/send-otp-center.component.html +++ b/apps/proxy-auth/src/app/otp/component/send-otp-center/send-otp-center.component.html @@ -3,13 +3,13 @@
- + -
Login
+ +
+ Logo +
+
+
{{ titleText }}
@@ -60,9 +70,9 @@ - @@ -116,7 +126,9 @@ >
- OR + Or continue with +
@@ -129,7 +141,14 @@ - -