mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-08 07:05:12 +00:00
feat: Use V2 API's in Console (#9312)
# Which Problems Are Solved Solves #8976 # Additional Changes I have done some intensive refactorings and we are using the new @zitadel/client package for GRPC access. # Additional Context - Closes #8976 --------- Co-authored-by: Max Peintner <[email protected]>
This commit is contained in:
@@ -1,10 +1,19 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, ValidatorFn, Validators } from '@angular/forms';
|
||||
import { Component, DestroyRef, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||
import { FormBuilder, FormControl, ValidatorFn } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { Subject, debounceTime, Observable } from 'rxjs';
|
||||
import { AddHumanUserRequest } from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { Domain } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import {
|
||||
debounceTime,
|
||||
defer,
|
||||
of,
|
||||
Observable,
|
||||
shareReplay,
|
||||
firstValueFrom,
|
||||
forkJoin,
|
||||
ObservedValueOf,
|
||||
EMPTY,
|
||||
ReplaySubject,
|
||||
} from 'rxjs';
|
||||
import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
import { Gender } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
|
||||
@@ -23,249 +32,354 @@ import {
|
||||
passwordConfirmValidator,
|
||||
phoneValidator,
|
||||
requiredValidator,
|
||||
} from '../../../modules/form-field/validators/validators';
|
||||
import { LanguagesService } from '../../../services/languages.service';
|
||||
} from 'src/app/modules/form-field/validators/validators';
|
||||
import { LanguagesService } from 'src/app/services/languages.service';
|
||||
import { UserService } from 'src/app/services/user.service';
|
||||
import { AddHumanUserRequest } from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { AddHumanUserRequestSchema } from '@zitadel/proto/zitadel/user/v2/user_service_pb';
|
||||
import { create } from '@bufbuild/protobuf';
|
||||
import { SetHumanPhoneSchema } from '@zitadel/proto/zitadel/user/v2/phone_pb';
|
||||
import { PasswordSchema } from '@zitadel/proto/zitadel/user/v2/password_pb';
|
||||
import { SetHumanEmailSchema } from '@zitadel/proto/zitadel/user/v2/email_pb';
|
||||
import { FeatureService } from 'src/app/services/feature.service';
|
||||
import { catchError, filter, map, startWith, timeout } from 'rxjs/operators';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-user-create',
|
||||
templateUrl: './user-create.component.html',
|
||||
styleUrls: ['./user-create.component.scss'],
|
||||
})
|
||||
export class UserCreateComponent implements OnInit, OnDestroy {
|
||||
public user: AddHumanUserRequest.AsObject = new AddHumanUserRequest().toObject();
|
||||
public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED];
|
||||
export class UserCreateComponent implements OnInit {
|
||||
public readonly genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED];
|
||||
public selected: CountryPhoneCode | undefined = {
|
||||
countryCallingCode: '1',
|
||||
countryCode: 'US',
|
||||
countryName: 'United States of America',
|
||||
};
|
||||
public countryPhoneCodes: CountryPhoneCode[] = [];
|
||||
public userForm!: UntypedFormGroup;
|
||||
public pwdForm!: UntypedFormGroup;
|
||||
private destroyed$: Subject<void> = new Subject();
|
||||
public readonly countryPhoneCodes: CountryPhoneCode[];
|
||||
|
||||
public userLoginMustBeDomain: boolean = false;
|
||||
public loading: boolean = false;
|
||||
public loading = false;
|
||||
|
||||
private readonly suffix$ = new ReplaySubject<HTMLSpanElement>(1);
|
||||
@ViewChild('suffix') public set suffix(suffix: ElementRef<HTMLSpanElement>) {
|
||||
this.suffix$.next(suffix.nativeElement);
|
||||
}
|
||||
|
||||
@ViewChild('suffix') public suffix!: any;
|
||||
private primaryDomain!: Domain.AsObject;
|
||||
public usePassword: boolean = false;
|
||||
public policy!: PasswordComplexityPolicy.AsObject;
|
||||
protected readonly useV2Api$: Observable<boolean>;
|
||||
protected readonly envSuffix$: Observable<string>;
|
||||
protected readonly userForm: ReturnType<typeof this.buildUserForm>;
|
||||
protected readonly pwdForm$: ReturnType<typeof this.buildPwdForm>;
|
||||
protected readonly passwordComplexityPolicy$: Observable<PasswordComplexityPolicy.AsObject>;
|
||||
protected readonly suffixPadding$: Observable<string>;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private toast: ToastService,
|
||||
private fb: UntypedFormBuilder,
|
||||
private mgmtService: ManagementService,
|
||||
private changeDetRef: ChangeDetectorRef,
|
||||
private _location: Location,
|
||||
private countryCallingCodesService: CountryCallingCodesService,
|
||||
public langSvc: LanguagesService,
|
||||
breadcrumbService: BreadcrumbService,
|
||||
private readonly router: Router,
|
||||
private readonly toast: ToastService,
|
||||
private readonly fb: FormBuilder,
|
||||
private readonly mgmtService: ManagementService,
|
||||
private readonly userService: UserService,
|
||||
public readonly langSvc: LanguagesService,
|
||||
private readonly featureService: FeatureService,
|
||||
private readonly destroyRef: DestroyRef,
|
||||
private readonly breadcrumbService: BreadcrumbService,
|
||||
protected readonly location: Location,
|
||||
countryCallingCodesService: CountryCallingCodesService,
|
||||
) {
|
||||
breadcrumbService.setBreadcrumb([
|
||||
this.useV2Api$ = this.getUseV2Api().pipe(shareReplay({ refCount: true, bufferSize: 1 }));
|
||||
this.envSuffix$ = this.getEnvSuffix();
|
||||
this.suffixPadding$ = this.getSuffixPadding();
|
||||
this.passwordComplexityPolicy$ = this.getPasswordComplexityPolicy().pipe(shareReplay({ refCount: true, bufferSize: 1 }));
|
||||
|
||||
this.userForm = this.buildUserForm();
|
||||
this.pwdForm$ = this.buildPwdForm(this.passwordComplexityPolicy$);
|
||||
|
||||
this.countryPhoneCodes = countryCallingCodesService.getCountryCallingCodes();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
// already start loading if we should use v2 api
|
||||
this.useV2Api$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe();
|
||||
this.watchPhoneChanges();
|
||||
|
||||
this.breadcrumbService.setBreadcrumb([
|
||||
new Breadcrumb({
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
}),
|
||||
]);
|
||||
this.loading = true;
|
||||
this.loadOrg();
|
||||
this.mgmtService
|
||||
.getDomainPolicy()
|
||||
.then((resp) => {
|
||||
if (resp.policy?.userLoginMustBeDomain) {
|
||||
this.userLoginMustBeDomain = resp.policy.userLoginMustBeDomain;
|
||||
}
|
||||
|
||||
private getUseV2Api(): Observable<boolean> {
|
||||
return defer(() => this.featureService.getInstanceFeatures(true)).pipe(
|
||||
map((features) => !!features.getConsoleUseV2UserApi()?.getEnabled()),
|
||||
timeout(1000),
|
||||
catchError(() => of(false)),
|
||||
);
|
||||
}
|
||||
|
||||
private getEnvSuffix() {
|
||||
const domainPolicy$ = defer(() => this.mgmtService.getDomainPolicy());
|
||||
const orgDomains$ = defer(() => this.mgmtService.listOrgDomains());
|
||||
|
||||
return forkJoin([domainPolicy$, orgDomains$]).pipe(
|
||||
map(([policy, domains]) => {
|
||||
const userLoginMustBeDomain = policy.policy?.userLoginMustBeDomain;
|
||||
const primaryDomain = domains.resultList.find((resp) => resp.isPrimary);
|
||||
if (userLoginMustBeDomain && primaryDomain) {
|
||||
return `@${primaryDomain.domainName}`;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
this.initForm();
|
||||
this.loading = false;
|
||||
this.changeDetRef.detectChanges();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
this.initForm();
|
||||
this.loading = false;
|
||||
this.changeDetRef.detectChanges();
|
||||
});
|
||||
}),
|
||||
catchError(() => of('')),
|
||||
);
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this._location.back();
|
||||
private getSuffixPadding() {
|
||||
return this.suffix$.pipe(
|
||||
map((suffix) => `${suffix.offsetWidth + 10}px`),
|
||||
startWith('10px'),
|
||||
);
|
||||
}
|
||||
|
||||
private async loadOrg(): Promise<void> {
|
||||
const domains = await this.mgmtService.listOrgDomains();
|
||||
const found = domains.resultList.find((resp) => resp.isPrimary);
|
||||
if (found) {
|
||||
this.primaryDomain = found;
|
||||
}
|
||||
private getPasswordComplexityPolicy() {
|
||||
return defer(() => this.mgmtService.getPasswordComplexityPolicy()).pipe(
|
||||
map(({ policy }) => policy),
|
||||
filter(Boolean),
|
||||
catchError((error) => {
|
||||
this.toast.showError(error);
|
||||
return EMPTY;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private initForm(): void {
|
||||
this.userForm = this.fb.group({
|
||||
email: ['', [requiredValidator, emailValidator]],
|
||||
userName: ['', [requiredValidator, minLengthValidator(2)]],
|
||||
firstName: ['', requiredValidator],
|
||||
lastName: ['', requiredValidator],
|
||||
nickName: [''],
|
||||
gender: [],
|
||||
preferredLanguage: [''],
|
||||
phone: ['', phoneValidator],
|
||||
isVerified: [false, []],
|
||||
public buildUserForm() {
|
||||
return this.fb.group({
|
||||
email: new FormControl('', { nonNullable: true, validators: [requiredValidator, emailValidator] }),
|
||||
userName: new FormControl('', { nonNullable: true, validators: [requiredValidator, minLengthValidator(2)] }),
|
||||
firstName: new FormControl('', { nonNullable: true, validators: [requiredValidator] }),
|
||||
lastName: new FormControl('', { nonNullable: true, validators: [requiredValidator] }),
|
||||
nickName: new FormControl('', { nonNullable: true }),
|
||||
gender: new FormControl(Gender.GENDER_UNSPECIFIED, { nonNullable: true, validators: [requiredValidator] }),
|
||||
preferredLanguage: new FormControl('', { nonNullable: true }),
|
||||
phone: new FormControl('', { nonNullable: true, validators: [phoneValidator] }),
|
||||
emailVerified: new FormControl(false, { nonNullable: true }),
|
||||
sendEmail: new FormControl(true, { nonNullable: true }),
|
||||
phoneVerified: new FormControl(false, { nonNullable: true }),
|
||||
sendSms: new FormControl(true, { nonNullable: true }),
|
||||
});
|
||||
}
|
||||
|
||||
const validators: Validators[] = [requiredValidator];
|
||||
|
||||
this.mgmtService.getPasswordComplexityPolicy().then((data) => {
|
||||
if (data.policy) {
|
||||
this.policy = data.policy;
|
||||
|
||||
if (this.policy.minLength) {
|
||||
validators.push(minLengthValidator(this.policy.minLength));
|
||||
public buildPwdForm(passwordComplexityPolicy$: Observable<PasswordComplexityPolicy.AsObject>) {
|
||||
return passwordComplexityPolicy$.pipe(
|
||||
map((policy) => {
|
||||
const validators: [ValidatorFn] = [requiredValidator];
|
||||
if (policy.minLength) {
|
||||
validators.push(minLengthValidator(policy.minLength));
|
||||
}
|
||||
if (this.policy.hasLowercase) {
|
||||
if (policy.hasLowercase) {
|
||||
validators.push(containsLowerCaseValidator);
|
||||
}
|
||||
if (this.policy.hasUppercase) {
|
||||
if (policy.hasUppercase) {
|
||||
validators.push(containsUpperCaseValidator);
|
||||
}
|
||||
if (this.policy.hasNumber) {
|
||||
if (policy.hasNumber) {
|
||||
validators.push(containsNumberValidator);
|
||||
}
|
||||
if (this.policy.hasSymbol) {
|
||||
if (policy.hasSymbol) {
|
||||
validators.push(containsSymbolValidator);
|
||||
}
|
||||
const pwdValidators = [...validators] as ValidatorFn[];
|
||||
const confirmPwdValidators = [requiredValidator, passwordConfirmValidator()] as ValidatorFn[];
|
||||
|
||||
this.pwdForm = this.fb.group({
|
||||
password: ['', pwdValidators],
|
||||
confirmPassword: ['', confirmPwdValidators],
|
||||
return this.fb.group({
|
||||
password: new FormControl('', { nonNullable: true, validators }),
|
||||
confirmPassword: new FormControl('', {
|
||||
nonNullable: true,
|
||||
validators: [requiredValidator, passwordConfirmValidator()],
|
||||
}),
|
||||
});
|
||||
}
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
this.phone?.valueChanges.pipe(debounceTime(200)).subscribe((value: string) => {
|
||||
private watchPhoneChanges(): void {
|
||||
const phone = this.userForm.controls.phone;
|
||||
|
||||
phone.valueChanges.pipe(debounceTime(200), takeUntilDestroyed(this.destroyRef)).subscribe((value) => {
|
||||
const phoneNumber = formatPhone(value);
|
||||
if (phoneNumber) {
|
||||
this.selected = this.countryPhoneCodes.find((code) => code.countryCode === phoneNumber.country);
|
||||
this.phone?.setValue(phoneNumber.phone);
|
||||
phone.setValue(phoneNumber.phone);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public createUser(): void {
|
||||
this.user = this.userForm.value;
|
||||
public async createUser(pwdForm: ObservedValueOf<typeof this.pwdForm$>): Promise<void> {
|
||||
if (await firstValueFrom(this.useV2Api$)) {
|
||||
await this.createUserV2(pwdForm);
|
||||
} else {
|
||||
await this.createUserV1(pwdForm);
|
||||
}
|
||||
}
|
||||
|
||||
public async createUserV1(pwdForm: ObservedValueOf<typeof this.pwdForm$>): Promise<void> {
|
||||
this.loading = true;
|
||||
|
||||
const controls = this.userForm.controls;
|
||||
const profileReq = new AddHumanUserRequest.Profile();
|
||||
profileReq.setFirstName(this.firstName?.value);
|
||||
profileReq.setLastName(this.lastName?.value);
|
||||
profileReq.setNickName(this.nickName?.value);
|
||||
profileReq.setPreferredLanguage(this.preferredLanguage?.value);
|
||||
profileReq.setGender(this.gender?.value);
|
||||
profileReq.setFirstName(controls.firstName.value);
|
||||
profileReq.setLastName(controls.lastName.value);
|
||||
profileReq.setNickName(controls.nickName.value);
|
||||
profileReq.setPreferredLanguage(controls.preferredLanguage.value);
|
||||
profileReq.setGender(controls.gender.value);
|
||||
|
||||
const humanReq = new AddHumanUserRequest();
|
||||
humanReq.setUserName(this.userName?.value);
|
||||
humanReq.setUserName(controls.userName.value);
|
||||
humanReq.setProfile(profileReq);
|
||||
|
||||
const emailreq = new AddHumanUserRequest.Email();
|
||||
emailreq.setEmail(this.email?.value);
|
||||
emailreq.setIsEmailVerified(this.isVerified?.value);
|
||||
emailreq.setEmail(controls.email.value);
|
||||
emailreq.setIsEmailVerified(controls.emailVerified.value);
|
||||
humanReq.setEmail(emailreq);
|
||||
|
||||
if (this.usePassword && this.password?.value) {
|
||||
humanReq.setInitialPassword(this.password.value);
|
||||
if (this.usePassword) {
|
||||
humanReq.setInitialPassword(pwdForm.controls.password.value);
|
||||
}
|
||||
|
||||
if (this.phone && this.phone.value) {
|
||||
if (controls.phone.value) {
|
||||
// Try to parse number and format it according to country
|
||||
const phoneNumber = formatPhone(this.phone.value);
|
||||
const phoneNumber = formatPhone(controls.phone.value);
|
||||
if (phoneNumber) {
|
||||
this.selected = this.countryPhoneCodes.find((code) => code.countryCode === phoneNumber.country);
|
||||
humanReq.setPhone(new AddHumanUserRequest.Phone().setPhone(phoneNumber.phone));
|
||||
}
|
||||
}
|
||||
|
||||
this.mgmtService
|
||||
.addHumanUser(humanReq)
|
||||
.then((data) => {
|
||||
this.loading = false;
|
||||
this.toast.showInfo('USER.TOAST.CREATED', true);
|
||||
this.router.navigate(['users', data.userId], { queryParams: { new: true } });
|
||||
})
|
||||
.catch((error) => {
|
||||
this.loading = false;
|
||||
this.toast.showError(error);
|
||||
try {
|
||||
const data = await this.mgmtService.addHumanUser(humanReq);
|
||||
this.toast.showInfo('USER.TOAST.CREATED', true);
|
||||
this.router.navigate(['users', data.userId], { queryParams: { new: true } }).then();
|
||||
} catch (error) {
|
||||
this.toast.showError(error);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async createUserV2(pwdForm: ObservedValueOf<typeof this.pwdForm$>): Promise<void> {
|
||||
this.loading = true;
|
||||
|
||||
const controls = this.userForm.controls;
|
||||
const humanReq = create(AddHumanUserRequestSchema, {
|
||||
username: controls.userName.value,
|
||||
profile: {
|
||||
givenName: controls.firstName.value,
|
||||
familyName: controls.lastName.value,
|
||||
nickName: controls.nickName.value,
|
||||
preferredLanguage: controls.preferredLanguage.value,
|
||||
// the enum numbers of v1 gender are the same as v2 gender
|
||||
gender: controls.gender.value as unknown as any,
|
||||
},
|
||||
});
|
||||
|
||||
if (this.usePassword) {
|
||||
const password = create(PasswordSchema, { password: pwdForm.controls.password.value });
|
||||
humanReq.passwordType = { case: 'password', value: password };
|
||||
}
|
||||
if (controls.emailVerified.value) {
|
||||
humanReq.email = create(SetHumanEmailSchema, {
|
||||
email: controls.email.value,
|
||||
verification: {
|
||||
value: true,
|
||||
case: 'isVerified',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
if (controls.sendEmail.value) {
|
||||
humanReq.email = create(SetHumanEmailSchema, {
|
||||
email: controls.email.value,
|
||||
verification: {
|
||||
case: 'sendCode',
|
||||
value: {},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
humanReq.email = create(SetHumanEmailSchema, {
|
||||
email: controls.email.value,
|
||||
verification: {
|
||||
value: false,
|
||||
case: 'isVerified',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const phoneNumber = formatPhone(controls.phone.value);
|
||||
if (phoneNumber) {
|
||||
const country = phoneNumber.country;
|
||||
this.selected = this.countryPhoneCodes.find((code) => code.countryCode === country);
|
||||
if (controls.phoneVerified.value) {
|
||||
humanReq.phone = create(SetHumanPhoneSchema, {
|
||||
phone: phoneNumber.phone,
|
||||
verification: {
|
||||
case: 'isVerified',
|
||||
value: true,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
if (controls.sendSms.value) {
|
||||
humanReq.phone = create(SetHumanPhoneSchema, {
|
||||
phone: phoneNumber.phone,
|
||||
verification: {
|
||||
case: 'sendCode',
|
||||
value: {},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
humanReq.phone = create(SetHumanPhoneSchema, {
|
||||
phone: phoneNumber.phone,
|
||||
verification: {
|
||||
case: 'isVerified',
|
||||
value: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await this.userService.addHumanUser(humanReq);
|
||||
if (this.sendEmailAfterCreation) {
|
||||
await this.userService.passwordReset({
|
||||
userId: data.userId,
|
||||
medium: {
|
||||
case: 'sendLink',
|
||||
value: {},
|
||||
},
|
||||
});
|
||||
}
|
||||
this.toast.showInfo('USER.TOAST.CREATED', true);
|
||||
await this.router.navigate(['users', data.userId], { queryParams: { new: true } });
|
||||
} catch (error) {
|
||||
this.toast.showError(error);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
public get sendEmailAfterCreation() {
|
||||
const controls = this.userForm.controls;
|
||||
|
||||
const sendEmailAfterCreationIsAOption = controls.emailVerified.value && !this.usePassword;
|
||||
return sendEmailAfterCreationIsAOption && controls.sendEmail.value;
|
||||
}
|
||||
|
||||
public setCountryCallingCode(): void {
|
||||
let value = (this.phone?.value as string) || '';
|
||||
let value = this.userForm.controls.phone.value;
|
||||
this.countryPhoneCodes.forEach((code) => (value = value.replace(`+${code.countryCallingCode}`, '')));
|
||||
value = value.trim();
|
||||
this.phone?.setValue('+' + this.selected?.countryCallingCode + ' ' + value);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.countryPhoneCodes = this.countryCallingCodesService.getCountryCallingCodes();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroyed$.next();
|
||||
this.destroyed$.complete();
|
||||
}
|
||||
|
||||
public get email(): AbstractControl | null {
|
||||
return this.userForm.get('email');
|
||||
}
|
||||
public get isVerified(): AbstractControl | null {
|
||||
return this.userForm.get('isVerified');
|
||||
}
|
||||
public get userName(): AbstractControl | null {
|
||||
return this.userForm.get('userName');
|
||||
}
|
||||
public get firstName(): AbstractControl | null {
|
||||
return this.userForm.get('firstName');
|
||||
}
|
||||
public get lastName(): AbstractControl | null {
|
||||
return this.userForm.get('lastName');
|
||||
}
|
||||
public get nickName(): AbstractControl | null {
|
||||
return this.userForm.get('nickName');
|
||||
}
|
||||
public get gender(): AbstractControl | null {
|
||||
return this.userForm.get('gender');
|
||||
}
|
||||
public get preferredLanguage(): AbstractControl | null {
|
||||
return this.userForm.get('preferredLanguage');
|
||||
}
|
||||
public get phone(): AbstractControl | null {
|
||||
return this.userForm.get('phone');
|
||||
}
|
||||
|
||||
public get password(): AbstractControl | null {
|
||||
return this.pwdForm.get('password');
|
||||
}
|
||||
public get confirmPassword(): AbstractControl | null {
|
||||
return this.pwdForm.get('confirmPassword');
|
||||
}
|
||||
|
||||
public get envSuffix(): string {
|
||||
if (this.userLoginMustBeDomain && this.primaryDomain?.domainName) {
|
||||
return `@${this.primaryDomain.domainName}`;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
public get suffixPadding(): string | undefined {
|
||||
if (this.suffix?.nativeElement.offsetWidth) {
|
||||
return `${(this.suffix.nativeElement as HTMLElement).offsetWidth + 10}px`;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
this.userForm.controls.phone.setValue('+' + this.selected?.countryCallingCode + ' ' + value);
|
||||
}
|
||||
|
||||
public compareCountries(i1: CountryPhoneCode, i2: CountryPhoneCode) {
|
||||
|
||||
Reference in New Issue
Block a user