diff --git a/console/package.json b/console/package.json index 8a4cfd4ed..dd020d8ae 100644 --- a/console/package.json +++ b/console/package.json @@ -83,4 +83,4 @@ "protractor": "~7.0.0", "typescript": "^4.9.5" } -} +} \ No newline at end of file diff --git a/console/src/app/modules/form-field/error.directive.ts b/console/src/app/modules/form-field/error/error.directive.ts similarity index 100% rename from console/src/app/modules/form-field/error.directive.ts rename to console/src/app/modules/form-field/error/error.directive.ts diff --git a/console/src/app/modules/form-field/animations.ts b/console/src/app/modules/form-field/field/animations.ts similarity index 100% rename from console/src/app/modules/form-field/animations.ts rename to console/src/app/modules/form-field/field/animations.ts diff --git a/console/src/app/modules/form-field/field/form-field.component.html b/console/src/app/modules/form-field/field/form-field.component.html new file mode 100644 index 000000000..376dbc229 --- /dev/null +++ b/console/src/app/modules/form-field/field/form-field.component.html @@ -0,0 +1,20 @@ +
+ +
+ + +
+ +
+
+ +
+
+ + {{ err.i18nKey | translate : err.params }} + + +
+
+
+
diff --git a/console/src/app/modules/form-field/form-field.component.scss b/console/src/app/modules/form-field/field/form-field.component.scss similarity index 100% rename from console/src/app/modules/form-field/form-field.component.scss rename to console/src/app/modules/form-field/field/form-field.component.scss diff --git a/console/src/app/modules/form-field/form-field.component.spec.ts b/console/src/app/modules/form-field/field/form-field.component.spec.ts similarity index 100% rename from console/src/app/modules/form-field/form-field.component.spec.ts rename to console/src/app/modules/form-field/field/form-field.component.spec.ts diff --git a/console/src/app/modules/form-field/form-field.component.ts b/console/src/app/modules/form-field/field/form-field.component.ts similarity index 66% rename from console/src/app/modules/form-field/form-field.component.ts rename to console/src/app/modules/form-field/field/form-field.component.ts index eec448053..5250a1247 100644 --- a/console/src/app/modules/form-field/form-field.component.ts +++ b/console/src/app/modules/form-field/field/form-field.component.ts @@ -10,6 +10,7 @@ import { HostListener, Inject, InjectionToken, + Input, OnDestroy, QueryList, ViewChild, @@ -17,11 +18,10 @@ import { } from '@angular/core'; import { NgControl } from '@angular/forms'; import { MatLegacyFormFieldControl as MatFormFieldControl } from '@angular/material/legacy-form-field'; -import { Subject } from 'rxjs'; -import { startWith, takeUntil } from 'rxjs/operators'; +import { combineLatest, map, Observable, of, startWith, Subject, takeUntil } from 'rxjs'; +import { CnslErrorDirective, CNSL_ERROR } from '../error/error.directive'; import { cnslFormFieldAnimations } from './animations'; -import { CnslErrorDirective, CNSL_ERROR } from './error.directive'; export const CNSL_FORM_FIELD = new InjectionToken('CnslFormFieldComponent'); @@ -29,6 +29,15 @@ class CnslFormFieldBase { constructor(public _elementRef: ElementRef) {} } +interface Help { + type: 'hints' | 'errors'; + validationErrors?: Array; +} +interface ValidationError { + i18nKey: string; + params: any; +} + @Component({ selector: 'cnsl-form-field', templateUrl: './form-field.component.html', @@ -59,19 +68,24 @@ export class CnslFormFieldComponent extends CnslFormFieldBase implements OnDestr @ViewChild('inputContainer') _inputContainerRef!: ElementRef; @ContentChild(MatFormFieldControl) _controlNonStatic!: MatFormFieldControl; @ContentChild(MatFormFieldControl, { static: true }) _controlStatic!: MatFormFieldControl; + @Input() public disableValidationErrors = false; + get _control(): MatFormFieldControl { return this._explicitFormFieldControl || this._controlNonStatic || this._controlStatic; } set _control(value: MatFormFieldControl) { this._explicitFormFieldControl = value; } + private _explicitFormFieldControl!: MatFormFieldControl; readonly stateChanges: Subject = new Subject(); + public help$?: Observable; _subscriptAnimationState: string = ''; @ContentChildren(CNSL_ERROR as any, { descendants: true }) _errorChildren!: QueryList; + // TODO: Remove? @HostListener('blur', ['false']) _focusChanged(isFocused: boolean): void { if (isFocused !== this.focused && !isFocused) { @@ -91,8 +105,6 @@ export class CnslFormFieldComponent extends CnslFormFieldBase implements OnDestr } public ngAfterViewInit(): void { - // Avoid animations on load. - this._subscriptAnimationState = 'enter'; this._changeDetectorRef.detectChanges(); } @@ -103,25 +115,21 @@ export class CnslFormFieldComponent extends CnslFormFieldBase implements OnDestr public ngAfterContentInit(): void { this._validateControlChild(); + this.mapHelp(); const control = this._control; - control.stateChanges.pipe(startWith(null)).subscribe(() => { + control.stateChanges.pipe(startWith(null), takeUntil(this._destroyed)).subscribe(() => { this._syncDescribedByIds(); this._changeDetectorRef.markForCheck(); }); // Run change detection if the value changes. + // TODO: Is that not redundant (see immediately above)? if (control.ngControl && control.ngControl.valueChanges) { control.ngControl.valueChanges .pipe(takeUntil(this._destroyed)) .subscribe(() => this._changeDetectorRef.markForCheck()); } - - // Update the aria-described by when the number of errors changes. - this._errorChildren.changes.pipe(startWith(null)).subscribe(() => { - this._syncDescribedByIds(); - this._changeDetectorRef.markForCheck(); - }); } /** Throws an error if the form field's control is missing. */ @@ -147,14 +155,68 @@ export class CnslFormFieldComponent extends CnslFormFieldBase implements OnDestr } } + private mapHelp(): void { + const validationErrors$: Observable> = this.disableValidationErrors + ? of([]) + : this._control.stateChanges?.pipe( + map(() => this.currentValidationErrors()), + startWith([]), + ) || of([]); + + const childrenErrors$: Observable = this._errorChildren.changes.pipe( + map(() => { + return this._errorChildren.length > 0; + }), + startWith(false), + ); + + this.help$ = combineLatest([validationErrors$, childrenErrors$]).pipe( + map((combined) => { + return combined[0].length >= 1 + ? { + type: 'errors', + validationErrors: combined[0], + } + : combined[1] + ? { + type: 'errors', + } + : { + type: 'hints', + validationErrors: undefined, + }; + }), + ); + this._changeDetectorRef.markForCheck(); + } + + private currentValidationErrors(): Array { + return ( + Object.entries(this._control.ngControl?.control?.errors || []) + ?.filter(this.filterErrorsProperties) + .map(this.mapToValidationError) + .filter(this.distinctFilter) || [] + ); + } + + private filterErrorsProperties(kv: [string, any]): boolean { + return typeof kv[1] == 'object' && (kv[1] as { valid: boolean }).valid === false; + } + + private mapToValidationError(kv: [string, any]): ValidationError { + return { + i18nKey: 'ERRORS.INVALID_FORMAT', + ...(kv[1] as ValidationError | any), + }; + } + + private distinctFilter(_: ValidationError, index: number, arr: Array): boolean { + return arr.findIndex((item) => item.i18nKey) === index; + } + /** Determines whether a class from the NgControl should be forwarded to the host element. */ _shouldForward(prop: keyof NgControl): boolean { const ngControl: any = this._control ? this._control.ngControl : null; return ngControl && ngControl[prop]; } - - /** Determines whether to display hints or errors. */ - _getDisplayedMessages(): 'error' | 'hint' { - return this._errorChildren && this._errorChildren.length > 0 ? 'error' : 'hint'; - } } diff --git a/console/src/app/modules/form-field/form-field.component.html b/console/src/app/modules/form-field/form-field.component.html deleted file mode 100644 index 5de3d9e64..000000000 --- a/console/src/app/modules/form-field/form-field.component.html +++ /dev/null @@ -1,15 +0,0 @@ -
- -
- - -
-
-
- -
-
- -
-
-
diff --git a/console/src/app/modules/form-field/form-field.module.ts b/console/src/app/modules/form-field/form-field.module.ts index e085fc35d..48d2fb7af 100644 --- a/console/src/app/modules/form-field/form-field.module.ts +++ b/console/src/app/modules/form-field/form-field.module.ts @@ -1,15 +1,16 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { MatRippleModule } from '@angular/material/core'; +import { TranslateModule } from '@ngx-translate/core'; import { LabelModule } from 'src/app/modules/label/label.module'; import { LabelComponent } from '../label/label.component'; -import { CnslErrorDirective } from './error.directive'; -import { CnslFormFieldComponent } from './form-field.component'; +import { CnslErrorDirective } from './error/error.directive'; +import { CnslFormFieldComponent } from './field/form-field.component'; @NgModule({ declarations: [CnslFormFieldComponent, CnslErrorDirective], - imports: [CommonModule, MatRippleModule, LabelModule], + imports: [CommonModule, MatRippleModule, LabelModule, TranslateModule], exports: [CnslFormFieldComponent, LabelComponent, CnslErrorDirective], }) export class FormFieldModule {} diff --git a/console/src/app/modules/form-field/validators/validators.ts b/console/src/app/modules/form-field/validators/validators.ts new file mode 100644 index 000000000..9c89699f4 --- /dev/null +++ b/console/src/app/modules/form-field/validators/validators.ts @@ -0,0 +1,73 @@ +import { AbstractControl, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; + +export function containsSymbolValidator(c: AbstractControl): ValidationErrors | null { + return regexpValidator(c, /[^a-z0-9]/gi, 'ERRORS.SYMBOLERROR'); +} + +export function containsNumberValidator(c: AbstractControl): ValidationErrors | null { + return regexpValidator(c, /[0-9]/g, 'ERRORS.NUMBERERROR'); +} + +export function containsUpperCaseValidator(c: AbstractControl): ValidationErrors | null { + return regexpValidator(c, /[A-Z]/g, 'ERRORS.UPPERCASEMISSING'); +} + +export function containsLowerCaseValidator(c: AbstractControl): ValidationErrors | null { + return regexpValidator(c, /[a-z]/g, 'ERRORS.LOWERCASEMISSING'); +} + +export function phoneValidator(c: AbstractControl): ValidationErrors | null { + return regexpValidator(c, /^($|(\+|00)[0-9 ]+$)/, 'ERRORS.PHONE'); +} + +export function requiredValidator(c: AbstractControl): ValidationErrors | null { + return i18nErr(Validators.required(c), 'ERRORS.REQUIRED'); +} + +export function emailValidator(c: AbstractControl): ValidationErrors | null { + return i18nErr(Validators.email(c), 'ERRORS.NOTANEMAIL'); +} + +export function minLengthValidator(minLength: number): ValidatorFn { + return (c: AbstractControl): ValidationErrors | null => { + return i18nErr(Validators.minLength(minLength)(c), 'ERRORS.MINLENGTH', { requiredLength: minLength }); + }; +} + +export function passwordConfirmValidator(passwordControlName: string = 'password') { + return (c: AbstractControl): ValidationErrors | null => { + if (!c.parent || !c) { + return null; + } + const pwd = c.parent.get(passwordControlName); + const cpwd = c; + + if (!pwd || !cpwd) { + return null; + } + if (pwd.value !== cpwd.value) { + return i18nErr(undefined, 'ERRORS.PWNOTEQUAL'); + } + return null; + }; +} + +function regexpValidator(c: AbstractControl, regexp: RegExp, i18nKey: string): ValidationErrors | null { + return !c.value || regexp.test(c.value) ? null : i18nErr({ invalid: true }, i18nKey, { regexp: regexp }); +} + +function i18nErr(err: ValidationErrors | null | undefined, i18nKey: string, params?: any): ValidationErrors | null { + if (err === null) { + return null; + } else { + return { + ...err, + invalid: true, + [i18nKey.toLowerCase().replaceAll('.', '')]: { + valid: false, + i18nKey: i18nKey, + params: params, + }, + }; + } +} diff --git a/console/src/app/modules/password-complexity-view/password-complexity-view.component.html b/console/src/app/modules/password-complexity-view/password-complexity-view.component.html index fff5887ec..73edd8cf0 100644 --- a/console/src/app/modules/password-complexity-view/password-complexity-view.component.html +++ b/console/src/app/modules/password-complexity-view/password-complexity-view.component.html @@ -28,23 +28,23 @@
- - - {{ 'USER.VALIDATION.SYMBOLERROR' | translate }} + + + {{ 'ERRORS.SYMBOLERROR' | translate }}
- - - {{ 'USER.VALIDATION.NUMBERERROR' | translate }} + + + {{ 'ERRORS.NUMBERERROR' | translate }}
- - - {{ 'USER.VALIDATION.UPPERCASEMISSING' | translate }} + + + {{ 'ERRORS.UPPERCASEMISSING' | translate }}
- - - {{ 'USER.VALIDATION.LOWERCASEMISSING' | translate }} + + + {{ 'ERRORS.LOWERCASEMISSING' | translate }}
diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.ts b/console/src/app/modules/policies/login-policy/login-policy.component.ts index 866df410f..b05ad5905 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.ts +++ b/console/src/app/modules/policies/login-policy/login-policy.component.ts @@ -1,5 +1,5 @@ import { Component, Injector, Input, OnInit, Type } from '@angular/core'; -import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { AbstractControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog'; import { Duration } from 'google-protobuf/google/protobuf/duration_pb'; import { take } from 'rxjs'; @@ -18,6 +18,7 @@ import { AdminService } from 'src/app/services/admin.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; +import { requiredValidator } from '../../form-field/validators/validators'; import { InfoSectionType } from '../../info-section/info-section.component'; import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component'; @@ -45,11 +46,11 @@ export class LoginPolicyComponent implements OnInit { public InfoSectionType: any = InfoSectionType; public PasswordlessType: any = PasswordlessType; public lifetimeForm: UntypedFormGroup = this.fb.group({ - passwordCheckLifetime: [{ disabled: true }, [Validators.required]], - externalLoginCheckLifetime: [{ disabled: true }, [Validators.required]], - mfaInitSkipLifetime: [{ disabled: true }, [Validators.required]], - secondFactorCheckLifetime: [{ disabled: true }, [Validators.required]], - multiFactorCheckLifetime: [{ disabled: true }, [Validators.required]], + passwordCheckLifetime: [{ disabled: true }, [requiredValidator]], + externalLoginCheckLifetime: [{ disabled: true }, [requiredValidator]], + mfaInitSkipLifetime: [{ disabled: true }, [requiredValidator]], + secondFactorCheckLifetime: [{ disabled: true }, [requiredValidator]], + multiFactorCheckLifetime: [{ disabled: true }, [requiredValidator]], }); constructor( private toast: ToastService, diff --git a/console/src/app/modules/policies/notification-settings/dialog-add-sms-provider/dialog-add-sms-provider.component.ts b/console/src/app/modules/policies/notification-settings/dialog-add-sms-provider/dialog-add-sms-provider.component.ts index 75c22add5..18ed54e5e 100644 --- a/console/src/app/modules/policies/notification-settings/dialog-add-sms-provider/dialog-add-sms-provider.component.ts +++ b/console/src/app/modules/policies/notification-settings/dialog-add-sms-provider/dialog-add-sms-provider.component.ts @@ -1,10 +1,11 @@ import { Component, Inject } from '@angular/core'; -import { AbstractControl, FormControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { AbstractControl, FormControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { MatLegacyDialog as MatDialog, MatLegacyDialogRef as MatDialogRef, MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA, } from '@angular/material/legacy-dialog'; +import { requiredValidator } from 'src/app/modules/form-field/validators/validators'; import { AddSMSProviderTwilioRequest, UpdateSMSProviderTwilioRequest, @@ -44,15 +45,15 @@ export class DialogAddSMSProviderComponent { @Inject(MAT_DIALOG_DATA) public data: any, ) { this.twilioForm = this.fb.group({ - sid: ['', [Validators.required]], - senderNumber: ['', [Validators.required]], + sid: ['', [requiredValidator]], + senderNumber: ['', [requiredValidator]], }); this.smsProviders = data.smsProviders; if (!!this.twilio) { this.twilioForm.patchValue(this.twilio); } else { - this.twilioForm.addControl('token', new FormControl('', Validators.required)); + this.twilioForm.addControl('token', new FormControl('', requiredValidator)); } } diff --git a/console/src/app/modules/policies/notification-settings/notification-settings.component.ts b/console/src/app/modules/policies/notification-settings/notification-settings.component.ts index 2cf94dbec..59fde5858 100644 --- a/console/src/app/modules/policies/notification-settings/notification-settings.component.ts +++ b/console/src/app/modules/policies/notification-settings/notification-settings.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnInit } from '@angular/core'; -import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { AbstractControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog'; import { take } from 'rxjs'; import { @@ -15,6 +15,7 @@ import { DebugNotificationProvider, SMSProvider, SMSProviderConfigState } from ' import { AdminService } from 'src/app/services/admin.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { ToastService } from 'src/app/services/toast.service'; +import { requiredValidator } from '../../form-field/validators/validators'; import { InfoSectionType } from '../../info-section/info-section.component'; import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component'; @@ -55,11 +56,11 @@ export class NotificationSettingsComponent implements OnInit { private authService: GrpcAuthService, ) { this.form = this.fb.group({ - senderAddress: [{ disabled: true, value: '' }, [Validators.required]], - senderName: [{ disabled: true, value: '' }, [Validators.required]], - tls: [{ disabled: true, value: true }, [Validators.required]], - hostAndPort: [{ disabled: true, value: '' }, [Validators.required]], - user: [{ disabled: true, value: '' }, [Validators.required]], + senderAddress: [{ disabled: true, value: '' }, [requiredValidator]], + senderName: [{ disabled: true, value: '' }, [requiredValidator]], + tls: [{ disabled: true, value: true }, [requiredValidator]], + hostAndPort: [{ disabled: true, value: '' }, [requiredValidator]], + user: [{ disabled: true, value: '' }, [requiredValidator]], }); } diff --git a/console/src/app/modules/policies/oidc-configuration/oidc-configuration.component.ts b/console/src/app/modules/policies/oidc-configuration/oidc-configuration.component.ts index 974133bb6..abf5bc94e 100644 --- a/console/src/app/modules/policies/oidc-configuration/oidc-configuration.component.ts +++ b/console/src/app/modules/policies/oidc-configuration/oidc-configuration.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { AbstractControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Duration } from 'google-protobuf/google/protobuf/duration_pb'; import { take } from 'rxjs'; import { @@ -12,6 +12,7 @@ import { OIDCSettings } from 'src/app/proto/generated/zitadel/settings_pb'; import { AdminService } from 'src/app/services/admin.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { ToastService } from 'src/app/services/toast.service'; +import { requiredValidator } from '../../form-field/validators/validators'; @Component({ selector: 'cnsl-oidc-configuration', @@ -31,10 +32,10 @@ export class OIDCConfigurationComponent implements OnInit { private authService: GrpcAuthService, ) { this.form = this.fb.group({ - accessTokenLifetime: [{ disabled: true }, [Validators.required]], - idTokenLifetime: [{ disabled: true }, [Validators.required]], - refreshTokenExpiration: [{ disabled: true }, [Validators.required]], - refreshTokenIdleExpiration: [{ disabled: true }, [Validators.required]], + accessTokenLifetime: [{ disabled: true }, [requiredValidator]], + idTokenLifetime: [{ disabled: true }, [requiredValidator]], + refreshTokenExpiration: [{ disabled: true }, [requiredValidator]], + refreshTokenIdleExpiration: [{ disabled: true }, [requiredValidator]], }); } diff --git a/console/src/app/modules/policies/secret-generator/dialog-add-secret-generator/dialog-add-secret-generator.component.ts b/console/src/app/modules/policies/secret-generator/dialog-add-secret-generator/dialog-add-secret-generator.component.ts index 06dcd5a42..c8d888f91 100644 --- a/console/src/app/modules/policies/secret-generator/dialog-add-secret-generator/dialog-add-secret-generator.component.ts +++ b/console/src/app/modules/policies/secret-generator/dialog-add-secret-generator/dialog-add-secret-generator.component.ts @@ -1,10 +1,11 @@ import { Component, Inject } from '@angular/core'; -import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { AbstractControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { MatLegacyDialogRef as MatDialogRef, MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA, } from '@angular/material/legacy-dialog'; import { Duration } from 'google-protobuf/google/protobuf/duration_pb'; +import { requiredValidator } from 'src/app/modules/form-field/validators/validators'; import { UpdateSecretGeneratorRequest } from 'src/app/proto/generated/zitadel/admin_pb'; import { SecretGeneratorType } from 'src/app/proto/generated/zitadel/settings_pb'; @@ -33,13 +34,13 @@ export class DialogAddSecretGeneratorComponent { @Inject(MAT_DIALOG_DATA) public data: any, ) { this.specsForm = this.fb.group({ - generatorType: [SecretGeneratorType.SECRET_GENERATOR_TYPE_APP_SECRET, [Validators.required]], - expiry: [1, [Validators.required]], - includeDigits: [true, [Validators.required]], - includeLowerLetters: [true, [Validators.required]], - includeSymbols: [true, [Validators.required]], - includeUpperLetters: [true, [Validators.required]], - length: [6, [Validators.required]], + generatorType: [SecretGeneratorType.SECRET_GENERATOR_TYPE_APP_SECRET, [requiredValidator]], + expiry: [1, [requiredValidator]], + includeDigits: [true, [requiredValidator]], + includeLowerLetters: [true, [requiredValidator]], + includeSymbols: [true, [requiredValidator]], + includeUpperLetters: [true, [requiredValidator]], + length: [6, [requiredValidator]], }); this.generatorType?.setValue(data.type); diff --git a/console/src/app/modules/project-role-detail-dialog/project-role-detail-dialog.component.ts b/console/src/app/modules/project-role-detail-dialog/project-role-detail-dialog.component.ts index 55845500e..c30099fc5 100644 --- a/console/src/app/modules/project-role-detail-dialog/project-role-detail-dialog.component.ts +++ b/console/src/app/modules/project-role-detail-dialog/project-role-detail-dialog.component.ts @@ -1,11 +1,12 @@ import { Component, Inject } from '@angular/core'; -import { AbstractControl, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; +import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { MatLegacyDialogRef as MatDialogRef, MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA, } from '@angular/material/legacy-dialog'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; +import { requiredValidator } from '../form-field/validators/validators'; @Component({ selector: 'cnsl-project-role-detail-dialog', @@ -24,7 +25,7 @@ export class ProjectRoleDetailDialogComponent { ) { this.projectId = data.projectId; this.formGroup = new UntypedFormGroup({ - key: new UntypedFormControl({ value: '', disabled: true }, [Validators.required]), + key: new UntypedFormControl({ value: '', disabled: true }, [requiredValidator]), displayName: new UntypedFormControl(''), group: new UntypedFormControl(''), }); diff --git a/console/src/app/modules/providers/provider-github-es/provider-github-es.component.ts b/console/src/app/modules/providers/provider-github-es/provider-github-es.component.ts index 740b4b99e..e57a80be4 100644 --- a/console/src/app/modules/providers/provider-github-es/provider-github-es.component.ts +++ b/console/src/app/modules/providers/provider-github-es/provider-github-es.component.ts @@ -1,7 +1,7 @@ import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes'; import { Location } from '@angular/common'; import { Component, Injector, Type } from '@angular/core'; -import { AbstractControl, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; +import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { MatLegacyChipInputEvent as MatChipInputEvent } from '@angular/material/legacy-chips'; import { ActivatedRoute } from '@angular/router'; import { take } from 'rxjs'; @@ -20,6 +20,7 @@ import { AdminService } from 'src/app/services/admin.service'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; +import { requiredValidator } from '../../form-field/validators/validators'; import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum'; @@ -51,12 +52,12 @@ export class ProviderGithubESComponent { breadcrumbService: BreadcrumbService, ) { this.form = new UntypedFormGroup({ - name: new UntypedFormControl('', [Validators.required]), - clientId: new UntypedFormControl('', [Validators.required]), - clientSecret: new UntypedFormControl('', [Validators.required]), - authorizationEndpoint: new UntypedFormControl('', [Validators.required]), - tokenEndpoint: new UntypedFormControl('', [Validators.required]), - userEndpoint: new UntypedFormControl('', [Validators.required]), + name: new UntypedFormControl('', [requiredValidator]), + clientId: new UntypedFormControl('', [requiredValidator]), + clientSecret: new UntypedFormControl('', [requiredValidator]), + authorizationEndpoint: new UntypedFormControl('', [requiredValidator]), + tokenEndpoint: new UntypedFormControl('', [requiredValidator]), + userEndpoint: new UntypedFormControl('', [requiredValidator]), scopesList: new UntypedFormControl(['openid', 'profile', 'email'], []), }); diff --git a/console/src/app/modules/providers/provider-github/provider-github.component.ts b/console/src/app/modules/providers/provider-github/provider-github.component.ts index fa2742941..511b47657 100644 --- a/console/src/app/modules/providers/provider-github/provider-github.component.ts +++ b/console/src/app/modules/providers/provider-github/provider-github.component.ts @@ -1,7 +1,7 @@ import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes'; import { Location } from '@angular/common'; import { Component, Injector, Type } from '@angular/core'; -import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms'; +import { AbstractControl, FormControl, FormGroup } from '@angular/forms'; import { MatLegacyChipInputEvent as MatChipInputEvent } from '@angular/material/legacy-chips'; import { ActivatedRoute } from '@angular/router'; import { take } from 'rxjs'; @@ -20,6 +20,7 @@ import { AdminService } from 'src/app/services/admin.service'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; +import { requiredValidator } from '../../form-field/validators/validators'; import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum'; @@ -53,8 +54,8 @@ export class ProviderGithubComponent { ) { this.form = new FormGroup({ name: new FormControl('', []), - clientId: new FormControl('', [Validators.required]), - clientSecret: new FormControl('', [Validators.required]), + clientId: new FormControl('', [requiredValidator]), + clientSecret: new FormControl('', [requiredValidator]), scopesList: new FormControl(['openid', 'profile', 'email'], []), }); diff --git a/console/src/app/modules/providers/provider-google/provider-google.component.ts b/console/src/app/modules/providers/provider-google/provider-google.component.ts index 5866ab77d..221c621b3 100644 --- a/console/src/app/modules/providers/provider-google/provider-google.component.ts +++ b/console/src/app/modules/providers/provider-google/provider-google.component.ts @@ -1,7 +1,7 @@ import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes'; import { Location } from '@angular/common'; import { Component, Injector, Type } from '@angular/core'; -import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms'; +import { AbstractControl, FormControl, FormGroup } from '@angular/forms'; import { MatLegacyChipInputEvent as MatChipInputEvent } from '@angular/material/legacy-chips'; import { ActivatedRoute } from '@angular/router'; import { take } from 'rxjs'; @@ -20,6 +20,7 @@ import { AdminService } from 'src/app/services/admin.service'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; +import { requiredValidator } from '../../form-field/validators/validators'; import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum'; @@ -53,8 +54,8 @@ export class ProviderGoogleComponent { ) { this.form = new FormGroup({ name: new FormControl('', []), - clientId: new FormControl('', [Validators.required]), - clientSecret: new FormControl('', [Validators.required]), + clientId: new FormControl('', [requiredValidator]), + clientSecret: new FormControl('', [requiredValidator]), scopesList: new FormControl(['openid', 'profile', 'email'], []), }); diff --git a/console/src/app/modules/providers/provider-jwt/provider-jwt.component.ts b/console/src/app/modules/providers/provider-jwt/provider-jwt.component.ts index a6c1d7556..29ea834c0 100644 --- a/console/src/app/modules/providers/provider-jwt/provider-jwt.component.ts +++ b/console/src/app/modules/providers/provider-jwt/provider-jwt.component.ts @@ -1,7 +1,7 @@ import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes'; import { Location } from '@angular/common'; import { Component, Injector, Type } from '@angular/core'; -import { AbstractControl, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; +import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; import { take } from 'rxjs/operators'; import { @@ -19,6 +19,7 @@ import { AdminService } from 'src/app/services/admin.service'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; +import { requiredValidator } from '../../form-field/validators/validators'; import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum'; @@ -82,11 +83,11 @@ export class ProviderJWTComponent { }); this.form = new UntypedFormGroup({ - name: new UntypedFormControl('', [Validators.required]), - headerName: new UntypedFormControl('', [Validators.required]), - issuer: new UntypedFormControl('', [Validators.required]), - jwtEndpoint: new UntypedFormControl('', [Validators.required]), - keysEndpoint: new UntypedFormControl('', [Validators.required]), + name: new UntypedFormControl('', [requiredValidator]), + headerName: new UntypedFormControl('', [requiredValidator]), + issuer: new UntypedFormControl('', [requiredValidator]), + jwtEndpoint: new UntypedFormControl('', [requiredValidator]), + keysEndpoint: new UntypedFormControl('', [requiredValidator]), }); } diff --git a/console/src/app/modules/providers/provider-oauth/provider-oauth.component.ts b/console/src/app/modules/providers/provider-oauth/provider-oauth.component.ts index a1958e1d6..d4331be27 100644 --- a/console/src/app/modules/providers/provider-oauth/provider-oauth.component.ts +++ b/console/src/app/modules/providers/provider-oauth/provider-oauth.component.ts @@ -1,7 +1,7 @@ import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes'; import { Location } from '@angular/common'; import { Component, Injector, Type } from '@angular/core'; -import { AbstractControl, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; +import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { MatLegacyChipInputEvent as MatChipInputEvent } from '@angular/material/legacy-chips'; import { ActivatedRoute, Router } from '@angular/router'; import { take } from 'rxjs'; @@ -20,6 +20,7 @@ import { AdminService } from 'src/app/services/admin.service'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; +import { requiredValidator } from '../../form-field/validators/validators'; import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum'; @@ -52,13 +53,13 @@ export class ProviderOAuthComponent { breadcrumbService: BreadcrumbService, ) { this.form = new UntypedFormGroup({ - name: new UntypedFormControl('', [Validators.required]), - clientId: new UntypedFormControl('', [Validators.required]), - clientSecret: new UntypedFormControl('', [Validators.required]), - authorizationEndpoint: new UntypedFormControl('', [Validators.required]), - tokenEndpoint: new UntypedFormControl('', [Validators.required]), - userEndpoint: new UntypedFormControl('', [Validators.required]), - idAttribute: new UntypedFormControl('', [Validators.required]), + name: new UntypedFormControl('', [requiredValidator]), + clientId: new UntypedFormControl('', [requiredValidator]), + clientSecret: new UntypedFormControl('', [requiredValidator]), + authorizationEndpoint: new UntypedFormControl('', [requiredValidator]), + tokenEndpoint: new UntypedFormControl('', [requiredValidator]), + userEndpoint: new UntypedFormControl('', [requiredValidator]), + idAttribute: new UntypedFormControl('', [requiredValidator]), scopesList: new UntypedFormControl(['openid', 'profile', 'email'], []), }); diff --git a/console/src/app/modules/providers/provider-oidc/provider-oidc.component.ts b/console/src/app/modules/providers/provider-oidc/provider-oidc.component.ts index 39e8d28e9..7c4a3ff99 100644 --- a/console/src/app/modules/providers/provider-oidc/provider-oidc.component.ts +++ b/console/src/app/modules/providers/provider-oidc/provider-oidc.component.ts @@ -1,7 +1,7 @@ import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes'; import { Location } from '@angular/common'; import { Component, Injector, Type } from '@angular/core'; -import { AbstractControl, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; +import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { MatLegacyChipInputEvent as MatChipInputEvent } from '@angular/material/legacy-chips'; import { ActivatedRoute } from '@angular/router'; import { take } from 'rxjs'; @@ -20,6 +20,7 @@ import { AdminService } from 'src/app/services/admin.service'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; +import { requiredValidator } from '../../form-field/validators/validators'; import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum'; @@ -51,10 +52,10 @@ export class ProviderOIDCComponent { breadcrumbService: BreadcrumbService, ) { this.oidcFormGroup = new UntypedFormGroup({ - name: new UntypedFormControl('', [Validators.required]), - clientId: new UntypedFormControl('', [Validators.required]), - clientSecret: new UntypedFormControl('', [Validators.required]), - issuer: new UntypedFormControl('', [Validators.required]), + name: new UntypedFormControl('', [requiredValidator]), + clientId: new UntypedFormControl('', [requiredValidator]), + clientSecret: new UntypedFormControl('', [requiredValidator]), + issuer: new UntypedFormControl('', [requiredValidator]), scopesList: new UntypedFormControl(['openid', 'profile', 'email'], []), }); diff --git a/console/src/app/pages/actions/add-flow-dialog/add-flow-dialog.component.ts b/console/src/app/pages/actions/add-flow-dialog/add-flow-dialog.component.ts index 9f34b02fd..49f4a98d1 100644 --- a/console/src/app/pages/actions/add-flow-dialog/add-flow-dialog.component.ts +++ b/console/src/app/pages/actions/add-flow-dialog/add-flow-dialog.component.ts @@ -1,9 +1,10 @@ import { Component, Inject } from '@angular/core'; -import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { MatLegacyDialogRef as MatDialogRef, MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA, } from '@angular/material/legacy-dialog'; +import { requiredValidator } from 'src/app/modules/form-field/validators/validators'; import { Action, FlowType, TriggerType } from 'src/app/proto/generated/zitadel/action_pb'; import { SetTriggerActionsRequest } from 'src/app/proto/generated/zitadel/management_pb'; import { ManagementService } from 'src/app/services/mgmt.service'; @@ -34,8 +35,8 @@ export class AddFlowDialogComponent { } this.form = this.fb.group({ - triggerType: [data.triggerType ? data.triggerType : '', [Validators.required]], - actionIdsList: [data.actions ? (data.actions as Action.AsObject[]).map((a) => a.id) : [], [Validators.required]], + triggerType: [data.triggerType ? data.triggerType : '', [requiredValidator]], + actionIdsList: [data.actions ? (data.actions as Action.AsObject[]).map((a) => a.id) : [], [requiredValidator]], }); this.getActionIds(); diff --git a/console/src/app/pages/org-create/org-create.component.html b/console/src/app/pages/org-create/org-create.component.html index c51220946..5dafb0142 100644 --- a/console/src/app/pages/org-create/org-create.component.html +++ b/console/src/app/pages/org-create/org-create.component.html @@ -57,37 +57,22 @@ {{ 'USER.PROFILE.USERNAME' | translate }} - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - {{ 'USER.PROFILE.EMAIL' | translate }} - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - {{ 'USER.PROFILE.FIRSTNAME' | translate }} - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - {{ 'USER.PROFILE.LASTNAME' | translate }} - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - {{ 'USER.PROFILE.NICKNAME' | translate }} - - {{ 'USER.VALIDATION.REQUIRED' | translate }} -

{{ 'USER.CREATE.GENDERLANGSECTION' | translate }}

@@ -99,9 +84,6 @@ {{ 'GENDERS.' + gender | translate }} - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - {{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }} @@ -109,9 +91,6 @@ {{ 'LANGUAGES.' + language | translate }} - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - @@ -148,10 +127,6 @@ {{ 'USER.PASSWORD.NEW' | translate }} - - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - {{ 'USER.PASSWORD.CONFIRM' | translate }} @@ -162,13 +137,6 @@ formControlName="confirmPassword" type="password" /> - - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - - - {{ 'USER.PASSWORD.NOTEQUAL' | translate }} - diff --git a/console/src/app/pages/org-create/org-create.component.ts b/console/src/app/pages/org-create/org-create.component.ts index 3f7ebeb6e..ce97569fa 100644 --- a/console/src/app/pages/org-create/org-create.component.ts +++ b/console/src/app/pages/org-create/org-create.component.ts @@ -4,7 +4,15 @@ import { Component } from '@angular/core'; import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, ValidatorFn, Validators } from '@angular/forms'; import { MatLegacySlideToggleChange as MatSlideToggleChange } from '@angular/material/legacy-slide-toggle'; import { Router } from '@angular/router'; -import { lowerCaseValidator, numberValidator, symbolValidator, upperCaseValidator } from 'src/app/pages/validators'; +import { + containsLowerCaseValidator, + containsNumberValidator, + containsSymbolValidator, + containsUpperCaseValidator, + minLengthValidator, + passwordConfirmValidator, + requiredValidator, +} from 'src/app/modules/form-field/validators/validators'; import { SetUpOrgRequest } from 'src/app/proto/generated/zitadel/admin_pb'; import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb'; import { Gender } from 'src/app/proto/generated/zitadel/user_pb'; @@ -13,26 +21,6 @@ import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/ import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; -function passwordConfirmValidator(c: AbstractControl): any { - if (!c.parent || !c) { - return; - } - const pwd = c.parent.get('password'); - const cpwd = c.parent.get('confirmPassword'); - - if (!pwd || !cpwd) { - return; - } - if (pwd.value !== cpwd.value) { - return { - invalid: true, - notequal: { - valid: false, - }, - }; - } -} - @Component({ selector: 'cnsl-org-create', templateUrl: './org-create.component.html', @@ -49,7 +37,7 @@ function passwordConfirmValidator(c: AbstractControl): any { }) export class OrgCreateComponent { public orgForm: UntypedFormGroup = this.fb.group({ - name: ['', [Validators.required]], + name: ['', [requiredValidator]], domain: [''], }); @@ -133,10 +121,10 @@ export class OrgCreateComponent { private initForm(): void { this.userForm = this.fb.group({ - userName: ['', [Validators.required]], - firstName: ['', [Validators.required]], - lastName: ['', [Validators.required]], - email: ['', [Validators.required]], + userName: ['', [requiredValidator]], + firstName: ['', [requiredValidator]], + lastName: ['', [requiredValidator]], + email: ['', [requiredValidator]], isVerified: [false, []], gender: [''], nickName: [''], @@ -145,7 +133,7 @@ export class OrgCreateComponent { } public initPwdValidators(): void { - const validators: Validators[] = [Validators.required]; + const validators: Validators[] = [requiredValidator]; if (this.usePassword) { this.mgmtService.getDefaultPasswordComplexityPolicy().then((data) => { @@ -153,23 +141,23 @@ export class OrgCreateComponent { this.policy = data.policy; if (this.policy.minLength) { - validators.push(Validators.minLength(this.policy.minLength)); + validators.push(minLengthValidator(this.policy.minLength)); } if (this.policy.hasLowercase) { - validators.push(lowerCaseValidator); + validators.push(containsLowerCaseValidator); } if (this.policy.hasUppercase) { - validators.push(upperCaseValidator); + validators.push(containsUpperCaseValidator); } if (this.policy.hasNumber) { - validators.push(numberValidator); + validators.push(containsNumberValidator); } if (this.policy.hasSymbol) { - validators.push(symbolValidator); + validators.push(containsSymbolValidator); } const pwdValidators = [...validators] as ValidatorFn[]; - const confirmPwdValidators = [...validators, passwordConfirmValidator] as ValidatorFn[]; + const confirmPwdValidators = [requiredValidator, passwordConfirmValidator()] as ValidatorFn[]; this.pwdForm = this.fb.group({ password: ['', pwdValidators], confirmPassword: ['', confirmPwdValidators], @@ -189,13 +177,13 @@ export class OrgCreateComponent { this.createSteps = 1; this.orgForm = this.fb.group({ - name: ['', [Validators.required]], + name: ['', [requiredValidator]], }); } else { this.createSteps = 2; this.orgForm = this.fb.group({ - name: ['', [Validators.required]], + name: ['', [requiredValidator]], domain: [''], }); } diff --git a/console/src/app/pages/projects/apps/app-create/app-create.component.html b/console/src/app/pages/projects/apps/app-create/app-create.component.html index 6a8091cc7..a31a937dd 100644 --- a/console/src/app/pages/projects/apps/app-create/app-create.component.html +++ b/console/src/app/pages/projects/apps/app-create/app-create.component.html @@ -37,7 +37,6 @@ {{ 'APP.NAME' | translate }} - {{ 'PROJECT.APP.NAMEREQUIRED' | translate }}

{{ 'APP.TYPETITLE' | translate }}

diff --git a/console/src/app/pages/projects/apps/app-create/app-create.component.ts b/console/src/app/pages/projects/apps/app-create/app-create.component.ts index 41adaf793..9baf56949 100644 --- a/console/src/app/pages/projects/apps/app-create/app-create.component.ts +++ b/console/src/app/pages/projects/apps/app-create/app-create.component.ts @@ -2,13 +2,14 @@ import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes'; import { StepperSelectionEvent } from '@angular/cdk/stepper'; import { Location } from '@angular/common'; import { Component, OnDestroy, OnInit } from '@angular/core'; -import { AbstractControl, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; +import { AbstractControl, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { Buffer } from 'buffer'; import { Subject, Subscription } from 'rxjs'; import { debounceTime, takeUntil } from 'rxjs/operators'; import { RadioItemAuthType } from 'src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component'; +import { requiredValidator } from 'src/app/modules/form-field/validators/validators'; import { APIAuthMethodType, OIDCAppType, @@ -127,8 +128,8 @@ export class AppCreateComponent implements OnInit, OnDestroy { private breadcrumbService: BreadcrumbService, ) { this.form = this.fb.group({ - name: ['', [Validators.required]], - appType: ['', [Validators.required]], + name: ['', [requiredValidator]], + appType: ['', [requiredValidator]], // apptype OIDC responseTypesList: ['', []], grantTypesList: ['', []], @@ -140,8 +141,8 @@ export class AppCreateComponent implements OnInit, OnDestroy { this.initForm(); this.firstFormGroup = this.fb.group({ - name: ['', [Validators.required]], - appType: [WEB_TYPE, [Validators.required]], + name: ['', [requiredValidator]], + appType: [WEB_TYPE, [requiredValidator]], }); this.samlConfigForm = this.fb.group({ @@ -192,7 +193,7 @@ export class AppCreateComponent implements OnInit, OnDestroy { }); this.secondFormGroup = this.fb.group({ - authMethod: [this.authMethods[0].key, [Validators.required]], + authMethod: [this.authMethods[0].key, [requiredValidator]], }); this.secondFormGroup.valueChanges.subscribe((form) => { @@ -276,8 +277,8 @@ export class AppCreateComponent implements OnInit, OnDestroy { public setDevFormValidators(): void { if (this.isDevOIDC) { - const grantTypesControl = new UntypedFormControl('', [Validators.required]); - const responseTypesControl = new UntypedFormControl('', [Validators.required]); + const grantTypesControl = new UntypedFormControl('', [requiredValidator]); + const responseTypesControl = new UntypedFormControl('', [requiredValidator]); this.form.addControl('grantTypesList', grantTypesControl); this.form.addControl('responseTypesList', responseTypesControl); diff --git a/console/src/app/pages/projects/owned-projects/project-role-create/project-role-create.component.ts b/console/src/app/pages/projects/owned-projects/project-role-create/project-role-create.component.ts index cc7ed140c..374902a43 100644 --- a/console/src/app/pages/projects/owned-projects/project-role-create/project-role-create.component.ts +++ b/console/src/app/pages/projects/owned-projects/project-role-create/project-role-create.component.ts @@ -1,9 +1,10 @@ import { animate, animateChild, query, stagger, style, transition, trigger } from '@angular/animations'; import { Location } from '@angular/common'; import { Component, OnDestroy, OnInit } from '@angular/core'; -import { UntypedFormArray, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; +import { UntypedFormArray, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { Subscription } from 'rxjs'; +import { requiredValidator } from 'src/app/modules/form-field/validators/validators'; import { BulkAddProjectRolesRequest } from 'src/app/proto/generated/zitadel/management_pb'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; @@ -32,7 +33,7 @@ export class ProjectRoleCreateComponent implements OnInit, OnDestroy { public formArray!: UntypedFormArray; public formGroup: UntypedFormGroup = this.fb.group({ - key: new UntypedFormControl('', [Validators.required]), + key: new UntypedFormControl('', [requiredValidator]), displayName: new UntypedFormControl(''), group: new UntypedFormControl(''), }); @@ -50,7 +51,7 @@ export class ProjectRoleCreateComponent implements OnInit, OnDestroy { public addEntry(): void { const newGroup = this.fb.group({ - key: new UntypedFormControl('', [Validators.required]), + key: new UntypedFormControl('', [requiredValidator]), displayName: new UntypedFormControl(''), group: new UntypedFormControl(''), }); diff --git a/console/src/app/pages/users/user-create-machine/user-create-machine.component.html b/console/src/app/pages/users/user-create-machine/user-create-machine.component.html index d9e10c369..9685f9066 100644 --- a/console/src/app/pages/users/user-create-machine/user-create-machine.component.html +++ b/console/src/app/pages/users/user-create-machine/user-create-machine.component.html @@ -12,19 +12,10 @@ {{ 'USER.MACHINE.USERNAME' | translate }}* - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - - - {{ 'USER.VALIDATION.NOEMAIL' | translate }} - {{ 'USER.MACHINE.NAME' | translate }}* - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - {{ 'USER.MACHINE.DESCRIPTION' | translate }} diff --git a/console/src/app/pages/users/user-create-machine/user-create-machine.component.ts b/console/src/app/pages/users/user-create-machine/user-create-machine.component.ts index 3b8bcccf6..a402a7f1d 100644 --- a/console/src/app/pages/users/user-create-machine/user-create-machine.component.ts +++ b/console/src/app/pages/users/user-create-machine/user-create-machine.component.ts @@ -1,8 +1,9 @@ import { Location } from '@angular/common'; import { Component, OnDestroy } from '@angular/core'; -import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { AbstractControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Router } from '@angular/router'; import { Subscription } from 'rxjs'; +import { minLengthValidator, requiredValidator } from 'src/app/modules/form-field/validators/validators'; import { AddMachineUserRequest } from 'src/app/proto/generated/zitadel/management_pb'; import { AccessTokenType } from 'src/app/proto/generated/zitadel/user_pb'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; @@ -45,8 +46,8 @@ export class UserCreateMachineComponent implements OnDestroy { private initForm(): void { this.userForm = this.fb.group({ - userName: ['', [Validators.required, Validators.minLength(2)]], - name: ['', [Validators.required]], + userName: ['', [requiredValidator, minLengthValidator(2)]], + name: ['', [requiredValidator]], description: ['', []], accessTokenType: [AccessTokenType.ACCESS_TOKEN_TYPE_BEARER, []], }); diff --git a/console/src/app/pages/users/user-create/user-create.component.html b/console/src/app/pages/users/user-create/user-create.component.html index 4705f2c68..acd3a8e10 100644 --- a/console/src/app/pages/users/user-create/user-create.component.html +++ b/console/src/app/pages/users/user-create/user-create.component.html @@ -15,12 +15,6 @@ {{ 'USER.PROFILE.EMAIL' | translate }}* - - {{ 'USER.VALIDATION.NOTANEMAIL' | translate }} - - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - {{ 'USER.PROFILE.USERNAME' | translate }}* @@ -31,35 +25,19 @@ [ngStyle]="{ 'padding-right': suffixPadding ? suffixPadding : '10px' }" /> {{ envSuffix }} - - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - - - {{ 'USER.VALIDATION.NOEMAIL' | translate }} - {{ 'USER.PROFILE.FIRSTNAME' | translate }}* - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - {{ 'USER.PROFILE.LASTNAME' | translate }}* - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - {{ 'USER.PROFILE.NICKNAME' | translate }} - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - @@ -84,10 +62,6 @@ {{ 'USER.PASSWORD.NEWINITIAL' | translate }} - - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - {{ 'USER.PASSWORD.CONFIRMINITIAL' | translate }} @@ -98,13 +72,6 @@ formControlName="confirmPassword" type="password" /> - - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - - - {{ 'USER.PASSWORD.NOTEQUAL' | translate }} - @@ -120,9 +87,6 @@ {{ 'GENDERS.' + gender | translate }} - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - {{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }} @@ -130,9 +94,6 @@ {{ 'LANGUAGES.' + language | translate }} - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - @@ -154,9 +115,6 @@ {{ 'USER.PROFILE.PHONE' | translate }} - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - diff --git a/console/src/app/pages/users/user-create/user-create.component.ts b/console/src/app/pages/users/user-create/user-create.component.ts index 811e57c17..c7e9976e5 100644 --- a/console/src/app/pages/users/user-create/user-create.component.ts +++ b/console/src/app/pages/users/user-create/user-create.component.ts @@ -13,27 +13,17 @@ import { ToastService } from 'src/app/services/toast.service'; import { CountryCallingCodesService, CountryPhoneCode } from 'src/app/services/country-calling-codes.service'; import { formatPhone } from 'src/app/utils/formatPhone'; -import { lowerCaseValidator, numberValidator, symbolValidator, upperCaseValidator } from '../../validators'; - -function passwordConfirmValidator(c: AbstractControl): any { - if (!c.parent || !c) { - return; - } - const pwd = c.parent.get('password'); - const cpwd = c.parent.get('confirmPassword'); - - if (!pwd || !cpwd) { - return; - } - if (pwd.value !== cpwd.value) { - return { - invalid: true, - notequal: { - valid: false, - }, - }; - } -} +import { + containsLowerCaseValidator, + containsNumberValidator, + containsSymbolValidator, + containsUpperCaseValidator, + emailValidator, + minLengthValidator, + passwordConfirmValidator, + phoneValidator, + requiredValidator, +} from '../../../modules/form-field/validators/validators'; @Component({ selector: 'cnsl-user-create', @@ -113,40 +103,40 @@ export class UserCreateComponent implements OnInit, OnDestroy { private initForm(): void { this.userForm = this.fb.group({ - email: ['', [Validators.required, Validators.email]], - userName: ['', [Validators.required, Validators.minLength(2)]], - firstName: ['', Validators.required], - lastName: ['', Validators.required], + email: ['', [requiredValidator, emailValidator]], + userName: ['', [requiredValidator, minLengthValidator(2)]], + firstName: ['', requiredValidator], + lastName: ['', requiredValidator], nickName: [''], gender: [], preferredLanguage: [''], - phone: [''], + phone: ['', phoneValidator], isVerified: [false, []], }); - const validators: Validators[] = [Validators.required]; + const validators: Validators[] = [requiredValidator]; this.mgmtService.getPasswordComplexityPolicy().then((data) => { if (data.policy) { this.policy = data.policy; if (this.policy.minLength) { - validators.push(Validators.minLength(this.policy.minLength)); + validators.push(minLengthValidator(this.policy.minLength)); } if (this.policy.hasLowercase) { - validators.push(lowerCaseValidator); + validators.push(containsLowerCaseValidator); } if (this.policy.hasUppercase) { - validators.push(upperCaseValidator); + validators.push(containsUpperCaseValidator); } if (this.policy.hasNumber) { - validators.push(numberValidator); + validators.push(containsNumberValidator); } if (this.policy.hasSymbol) { - validators.push(symbolValidator); + validators.push(containsSymbolValidator); } const pwdValidators = [...validators] as ValidatorFn[]; - const confirmPwdValidators = [...validators, passwordConfirmValidator] as ValidatorFn[]; + const confirmPwdValidators = [requiredValidator, passwordConfirmValidator()] as ValidatorFn[]; this.pwdForm = this.fb.group({ password: ['', pwdValidators], @@ -203,14 +193,13 @@ export class UserCreateComponent implements OnInit, OnDestroy { public setCountryCallingCode(): void { let value = (this.phone?.value as string) || ''; - this.phone?.setValue('+' + this.selected?.countryCallingCode + ' ' + value.replace(/\+[0-9]*\s/, '')); + this.countryPhoneCodes.forEach((code) => (value = value.replace(`+${code.countryCallingCode}`, ''))); + value = value.trim(); + this.phone?.setValue('+' + this.selected?.countryCallingCode + ' ' + value); } ngOnInit(): void { - // Set default selected country for phone numbers - const defaultCountryCallingCode = 'CH'; this.countryPhoneCodes = this.countryCallingCodesService.getCountryCallingCodes(); - this.selected = this.countryPhoneCodes.find((code) => code.countryCode === defaultCountryCallingCode); } ngOnDestroy(): void { diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts index 7eb7446cd..af50666e2 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts @@ -1,12 +1,14 @@ import { MediaMatcher } from '@angular/cdk/layout'; import { Location } from '@angular/common'; import { Component, EventEmitter, OnDestroy } from '@angular/core'; +import { Validators } from '@angular/forms'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog'; import { ActivatedRoute, Params } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { Buffer } from 'buffer'; import { Subscription, take } from 'rxjs'; import { ChangeType } from 'src/app/modules/changes/changes.component'; +import { phoneValidator, requiredValidator } from 'src/app/modules/form-field/validators/validators'; import { MetadataDialogComponent } from 'src/app/modules/metadata/metadata-dialog/metadata-dialog.component'; import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum'; import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component'; @@ -304,6 +306,7 @@ export class AuthUserDetailComponent implements OnDestroy { descriptionKey: 'USER.LOGINMETHODS.PHONE.EDITDESC', value: this.user?.human?.phone?.phone, type: type, + validator: Validators.compose([phoneValidator, requiredValidator]), }, width: '400px', }); diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html index 5b4b21f14..2aa944959 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html +++ b/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html @@ -3,40 +3,39 @@

{{ data.descriptionKey | translate }}

-
- - {{ 'USER.PROFILE.COUNTRY' | translate }} - - - - - {{ country.countryName }} - +{{ country.countryCallingCode }} - - - - - {{ data.labelKey | translate }} - - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - - -
+
+
+ + {{ 'USER.PROFILE.COUNTRY' | translate }} + + + + + {{ country.countryName }} + +{{ country.countryCallingCode }} + + + + + {{ data.labelKey | translate }} + + +
- - - {{ data.isVerifiedTextKey | translate }} - - - {{ data.isVerifiedTextDescKey | translate }} - - + + + {{ data.isVerifiedTextKey | translate }} + + + {{ data.isVerifiedTextDescKey | translate }} + + +
@@ -58,9 +51,6 @@ {{ 'USER.PASSWORD.OLD' | translate }} - - {{ 'USER.PASSWORD.REQUIRED' | translate }} -
@@ -71,10 +61,6 @@ {{ 'USER.PASSWORD.NEW' | translate }} - - - {{ 'USER.VALIDATION.REQUIRED' | translate }} - {{ 'USER.PASSWORD.CONFIRM' | translate }} @@ -85,9 +71,6 @@ type="password" formControlName="confirmPassword" /> - - {{ 'USER.PASSWORD.NOTEQUAL' | translate }} -
diff --git a/console/src/app/pages/users/user-detail/password/password.component.ts b/console/src/app/pages/users/user-detail/password/password.component.ts index ef32a1478..904981047 100644 --- a/console/src/app/pages/users/user-detail/password/password.component.ts +++ b/console/src/app/pages/users/user-detail/password/password.component.ts @@ -2,28 +2,21 @@ import { Component, OnDestroy } from '@angular/core'; import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; import { Subject, Subscription, take, takeUntil } from 'rxjs'; -import { lowerCaseValidator, numberValidator, symbolValidator, upperCaseValidator } from 'src/app/pages/validators'; +import { + containsLowerCaseValidator, + containsNumberValidator, + containsSymbolValidator, + containsUpperCaseValidator, + minLengthValidator, + passwordConfirmValidator, + requiredValidator, +} from 'src/app/modules/form-field/validators/validators'; import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; -function passwordConfirmValidator(c: AbstractControl): any { - if (!c.parent || !c) { - return; - } - const pwd = c.parent.get('password'); - const cpwd = c.parent.get('confirmPassword'); - - if (!pwd || !cpwd) { - return; - } - if (pwd.value !== cpwd.value) { - return { invalid: true, notequal: 'Password is not equal' }; - } -} - @Component({ selector: 'cnsl-password', templateUrl: './password.component.html', @@ -76,7 +69,7 @@ export class PasswordComponent implements OnDestroy { }); } - const validators: Validators[] = [Validators.required]; + const validators: Validators[] = [requiredValidator]; this.authService .getMyPasswordComplexityPolicy() .then((resp) => { @@ -84,19 +77,19 @@ export class PasswordComponent implements OnDestroy { this.policy = resp.policy; } if (this.policy.minLength) { - validators.push(Validators.minLength(this.policy.minLength)); + validators.push(minLengthValidator(this.policy.minLength)); } if (this.policy.hasLowercase) { - validators.push(lowerCaseValidator); + validators.push(containsLowerCaseValidator); } if (this.policy.hasUppercase) { - validators.push(upperCaseValidator); + validators.push(containsUpperCaseValidator); } if (this.policy.hasNumber) { - validators.push(numberValidator); + validators.push(containsNumberValidator); } if (this.policy.hasSymbol) { - validators.push(symbolValidator); + validators.push(containsSymbolValidator); } this.setupForm(validators); @@ -117,13 +110,13 @@ export class PasswordComponent implements OnDestroy { if (this.userId) { this.passwordForm = this.fb.group({ password: ['', validators], - confirmPassword: ['', [...validators, passwordConfirmValidator]], + confirmPassword: ['', [requiredValidator, passwordConfirmValidator()]], }); } else { this.passwordForm = this.fb.group({ - currentPassword: ['', Validators.required], + currentPassword: ['', requiredValidator], newPassword: ['', validators], - confirmPassword: ['', [...validators, passwordConfirmValidator]], + confirmPassword: ['', [requiredValidator, passwordConfirmValidator()]], }); } } diff --git a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts index a5788dde2..5ce365ae5 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts +++ b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts @@ -1,12 +1,14 @@ import { MediaMatcher } from '@angular/cdk/layout'; import { Location } from '@angular/common'; import { Component, EventEmitter, OnInit } from '@angular/core'; +import { Validators } from '@angular/forms'; import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { Buffer } from 'buffer'; import { take } from 'rxjs/operators'; import { ChangeType } from 'src/app/modules/changes/changes.component'; +import { phoneValidator, requiredValidator } from 'src/app/modules/form-field/validators/validators'; import { InfoSectionType } from 'src/app/modules/info-section/info-section.component'; import { MetadataDialogComponent } from 'src/app/modules/metadata/metadata-dialog/metadata-dialog.component'; import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component'; @@ -461,6 +463,7 @@ export class UserDetailComponent implements OnInit { descriptionKey: 'USER.LOGINMETHODS.PHONE.EDITDESC', value: this.user.human?.phone?.phone, type: EditDialogType.PHONE, + validator: Validators.compose([phoneValidator, requiredValidator]), }, width: '400px', }); diff --git a/console/src/app/pages/validators.ts b/console/src/app/pages/validators.ts deleted file mode 100644 index 13236d232..000000000 --- a/console/src/app/pages/validators.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { UntypedFormControl } from '@angular/forms'; - -export function symbolValidator(c: UntypedFormControl): any { - const REGEXP: RegExp = /[^a-z0-9]/gi; - - return REGEXP.test(c.value) - ? null - : { - invalid: true, - symbolValidator: { - valid: false, - }, - }; -} - -export function numberValidator(c: UntypedFormControl): any { - const REGEXP = /[0-9]/g; - - return REGEXP.test(c.value) - ? null - : { - invalid: true, - numberValidator: { - valid: false, - }, - }; -} - -export function upperCaseValidator(c: UntypedFormControl): any { - const REGEXP = /[A-Z]/g; - - return REGEXP.test(c.value) - ? null - : { - invalid: true, - upperCaseValidator: { - valid: false, - }, - }; -} - -export function lowerCaseValidator(c: UntypedFormControl): any { - const REGEXP = /[a-z]/g; - - return REGEXP.test(c.value) - ? null - : { - invalid: true, - lowerCaseValidator: { - valid: false, - }, - }; -} diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index b0b9fd725..085af409a 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -239,11 +239,20 @@ "NOROWS": "Keine Daten" }, "ERRORS": { - "REQUIRED": "Bitte fülle alle benötigten Felder aus.", + "REQUIRED": "Bitte fülle dieses Feld aus.", "TOKENINVALID": { "TITLE": "Du bist abgemeldet", "DESCRIPTION": "Klicke auf \"Einloggen\", um Dich erneut anzumelden." - } + }, + "INVALID_FORMAT": "Das Format is ungültig.", + "NOTANEMAIL": "Der eingegebene Wert ist keine E-Mail Adresse.", + "MINLENGTH": "Muss mindestens {{requiredLength}} Zeichen lang sein.", + "UPPERCASEMISSING": "Muss einen Grossbuchstaben beinhalten.", + "LOWERCASEMISSING": "Muss einen Kleinbuchstaben beinhalten.", + "SYMBOLERROR": "Muss ein Symbol/Satzzeichen beinhalten.", + "NUMBERERROR": "Muss eine Ziffer beinhalten.", + "PWNOTEQUAL": "Die Passwörter stimmen nicht überein.", + "PHONE": "Die Telefonnummer muss mit 00 oder + starten." }, "USER": { "SETTINGS": { @@ -524,8 +533,7 @@ "SET": "Passwort neu setzen", "RESENDNOTIFICATION": "Email zum Zurücksetzen senden", "REQUIRED": "Bitte prüfe, dass alle notwendigen Felder ausgefüllt sind.", - "MINLENGTHERROR": "Muss mindestens {{value}} Zeichen lang sein.", - "NOTEQUAL": "Die Passwörter stimmen nicht überein." + "MINLENGTHERROR": "Muss mindestens {{value}} Zeichen lang sein." }, "ID": "ID", "EMAIL": "E-Mail", @@ -579,17 +587,6 @@ "3": "Nach Rolle filtern" } }, - "VALIDATION": { - "INVALIDPATTERN": "Das Passwort erfüllt nicht die vorgegebenen Regeln.", - "NOTANEMAIL": "Der eingegebene Wert ist keine E-Mail Adresse.", - "REQUIRED": "Das Eingabefeld ist leer.", - "MINLENGTH": "Das Passwort muss mindestens {{requiredLength}} Zeichen lang sein.", - "NOEMAIL": "Benutzername darf keine E-Mail-Adresse sein.", - "UPPERCASEMISSING": "Muss einen Grossbuchstaben beinhalten.", - "LOWERCASEMISSING": "Muss einen Kleinbuchstaben beinhalten.", - "SYMBOLERROR": "Muss ein Symbol/Satzzeichen beinhalten.", - "NUMBERERROR": "Muss eine Ziffer beinhalten." - }, "STATE": { "0": "Unbekannt", "1": "Aktiv", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index a30577cb1..76bafca94 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -240,11 +240,20 @@ "NOROWS": "No data" }, "ERRORS": { - "REQUIRED": "Some required fields are missing.", + "REQUIRED": "Please fill in this field.", "TOKENINVALID": { "TITLE": "Your authorization token has expired.", "DESCRIPTION": "Click the button below to log in again." - } + }, + "INVALID_FORMAT": "The formatting is invalid.", + "NOTANEMAIL": "The given value is not an e-mail address.", + "MINLENGTH": "Must be at least {{requiredLength}} characters long.", + "UPPERCASEMISSING": "Must include an uppercase character.", + "LOWERCASEMISSING": "Must include a lowercase character.", + "SYMBOLERROR": "Must include a symbol or punctuation mark.", + "NUMBERERROR": "Must include a digit.", + "PWNOTEQUAL": "The passwords provided do not match.", + "PHONE": "The phone number must start with 00 or +." }, "USER": { "SETTINGS": { @@ -525,8 +534,7 @@ "SET": "Set New Password", "RESENDNOTIFICATION": "Send Password Reset Link", "REQUIRED": "Some required fields are missing.", - "MINLENGTHERROR": "Has to be at least {{value}} characters long.", - "NOTEQUAL": "The passwords provided do not match." + "MINLENGTHERROR": "Has to be at least {{value}} characters long." }, "ID": "ID", "EMAIL": "E-mail", @@ -580,17 +588,6 @@ "3": "filter for rolename" } }, - "VALIDATION": { - "INVALIDPATTERN": "The password does not fulfil the defined rules.", - "NOTANEMAIL": "The given value is not an e-mail address", - "REQUIRED": "The input field is empty.", - "MINLENGTH": "The password has to be at least {{requiredLength}} characters long.", - "NOEMAIL": "The user name cannot be an e-mail address.", - "UPPERCASEMISSING": "Must include an uppercase character.", - "LOWERCASEMISSING": "Must include a lowercase character.", - "SYMBOLERROR": "Must include a symbol or punctuation mark.", - "NUMBERERROR": "Must include a digit." - }, "STATE": { "0": "Unknown", "1": "Active", diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json index 1be1c6b7a..aafc1f7b7 100644 --- a/console/src/assets/i18n/fr.json +++ b/console/src/assets/i18n/fr.json @@ -239,11 +239,20 @@ "NOROWS": "Pas de données" }, "ERRORS": { - "REQUIRED": "Certains champs obligatoires sont manquants.", + "REQUIRED": "Remplis ce champ s'il te plaît.", "TOKENINVALID": { "TITLE": "Votre jeton d'autorisation a expiré.", "DESCRIPTION": "Cliquez sur le bouton ci-dessous pour vous reconnecter." - } + }, + "INVALID_FORMAT": "Le format n'est pas valide", + "NOTANEMAIL": "La valeur donnée n'est pas une adresse e-mail", + "MINLENGTH": "Doit comporter au moins {{length}} caractères.", + "UPPERCASEMISSING": "Doit inclure un caractère majuscule.", + "LOWERCASEMISSING": "Doit inclure un caractère minuscule.", + "SYMBOLERROR": "Doit inclure un symbole ou un signe de ponctuation.", + "NUMBERERROR": "Doit inclure un chiffre.", + "PWNOTEQUAL": "Les mots de passe fournis ne correspondent pas.", + "PHONE": "Le numéro de téléphone doit commencer par 00 ou +." }, "USER": { "SETTINGS": { @@ -524,8 +533,7 @@ "SET": "Définir un nouveau mot de passe", "RESENDNOTIFICATION": "Envoyer le lien de réinitialisation du mot de passe", "REQUIRED": "Certains champs obligatoires sont manquants.", - "MINLENGTHERROR": "Doit comporter au moins {{value}} caractères.", - "NOTEQUAL": "Les mots de passe fournis ne correspondent pas." + "MINLENGTHERROR": "Doit comporter au moins {{value}} caractères." }, "ID": "ID", "EMAIL": "E-mail", @@ -579,17 +587,6 @@ "3": "filtre pour nom de rôle" } }, - "VALIDATION": { - "INVALIDPATTERN": "Le mot de passe ne répond pas aux règles définies.", - "NOTANEMAIL": "La valeur donnée n'est pas une adresse e-mail", - "REQUIRED": "Le champ de saisie est vide.", - "MINLENGTH": "Le mot de passe doit comporter au moins{{length}} caractères.", - "NOEMAIL": "Le nom d'utilisateur ne peut pas être une adresse électronique.", - "UPPERCASEMISSING": "Doit inclure un caractère majuscule.", - "LOWERCASEMISSING": "Doit inclure un caractère minuscule.", - "SYMBOLERROR": "Doit inclure un symbole ou un signe de ponctuation.", - "NUMBERERROR": "Doit inclure un chiffre." - }, "STATE": { "0": "Inconnu", "1": "Actif", diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index 0ab2621b5..7651422d2 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -239,11 +239,20 @@ "NOROWS": "Nessun dato" }, "ERRORS": { - "REQUIRED": "Mancano alcuni campi obbligatori.", + "REQUIRED": "Compilare questo campo.", "TOKENINVALID": { "TITLE": "Il tuo Access Token \u00e8 scaduto.", "DESCRIPTION": "Clicca il pulsante per richiedere una nuova sessione." - } + }, + "INVALID_FORMAT": "Il formato non è valido.", + "NOTANEMAIL": "Il valore dato non \u00e8 un indirizzo e-mail.", + "MINLENGTH": "Deve essere lunga almeno {{requiredLength}} caratteri.", + "UPPERCASEMISSING": "Deve includere un carattere maiuscolo.", + "LOWERCASEMISSING": "Deve includere un carattere minuscolo.", + "SYMBOLERROR": "Deve includere un simbolo o un segno di punteggiatura.", + "NUMBERERROR": "Deve includere una cifra.", + "PWNOTEQUAL": "Le password fornite non corrispondono.", + "PHONE": "Il numero di telefono deve iniziare con 00 o +." }, "USER": { "SETTINGS": { @@ -524,8 +533,7 @@ "SET": "Imposta nuova password", "RESENDNOTIFICATION": "Invia email per la reimpostazione", "REQUIRED": "Mancano alcuni campi obbligatori.", - "MINLENGTHERROR": "Deve essere lunga almeno {{valore}} caratteri.", - "NOTEQUAL": "Le password fornite non corrispondono." + "MINLENGTHERROR": "Deve essere lunga almeno {{valore}} caratteri." }, "ID": "ID", "EMAIL": "E-mail", @@ -579,17 +587,6 @@ "3": "filtro per rolename" } }, - "VALIDATION": { - "INVALIDPATTERN": "La password non soddisfa le regole definite.", - "NOTANEMAIL": "Il valore dato non \u00e8 un indirizzo e-mail", - "REQUIRED": "Il campo di input \u00e8 vuoto.", - "MINLENGTH": "Deve essere lunga almeno {{requiredLength}} caratteri.", - "NOEMAIL": "Il nome utente non pu\u00f2 essere un indirizzo e-mail.", - "UPPERCASEMISSING": "Deve includere un carattere maiuscolo.", - "LOWERCASEMISSING": "Deve includere un carattere minuscolo.", - "SYMBOLERROR": "Deve includere un simbolo o un segno di punteggiatura.", - "NUMBERERROR": "Deve includere una cifra." - }, "STATE": { "0": "Sconosciuto", "1": "Attivo", diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json index dca5fefd4..527179beb 100644 --- a/console/src/assets/i18n/pl.json +++ b/console/src/assets/i18n/pl.json @@ -239,11 +239,20 @@ "NOROWS": "Brak danych" }, "ERRORS": { - "REQUIRED": "Brakuje niektórych wymaganych pól.", + "REQUIRED": "Proszę wypełnić to pole.", "TOKENINVALID": { "TITLE": "Twój token autoryzacji wygasł.", "DESCRIPTION": "Kliknij przycisk poniżej, aby ponownie się zalogować." - } + }, + "INVALID_FORMAT": "Format jest nieprawidłowy.", + "NOTANEMAIL": "Podana wartość nie jest adresem e-mail.", + "MINLENGTH": "Musi mieć co najmniej {{requiredLength}} znaków.", + "UPPERCASEMISSING": "Musi zawierać wielką literę.", + "LOWERCASEMISSING": "Musi zawierać małą literę.", + "SYMBOLERROR": "Musi zawierać symbol lub znak interpunkcyjny.", + "NUMBERERROR": "Musi zawierać cyfrę.", + "PWNOTEQUAL": "Podane hasła nie są identyczne.", + "PHONE": "Numer telefonu musi zaczynać się od 00 lub +." }, "USER": { "SETTINGS": { @@ -524,8 +533,7 @@ "SET": "Ustaw nowe hasło", "RESENDNOTIFICATION": "Wyślij link resetowania hasła", "REQUIRED": "Brakuje niektórych wymaganych pól.", - "MINLENGTHERROR": "Musi mieć co najmniej {{value}} znaków.", - "NOTEQUAL": "Podane hasła nie są identyczne." + "MINLENGTHERROR": "Musi mieć co najmniej {{value}} znaków." }, "ID": "ID", "EMAIL": "E-mail", @@ -579,17 +587,6 @@ "3": "filtruj po roli" } }, - "VALIDATION": { - "INVALIDPATTERN": "Hasło nie spełnia określonych reguł.", - "NOTANEMAIL": "Podana wartość nie jest adresem e-mail", - "REQUIRED": "Pole jest puste.", - "MINLENGTH": "Hasło musi mieć co najmniej {{requiredLength}} znaków.", - "NOEMAIL": "Nazwa użytkownika nie może być adresem e-mail.", - "UPPERCASEMISSING": "Musi zawierać wielką literę.", - "LOWERCASEMISSING": "Musi zawierać małą literę.", - "SYMBOLERROR": "Musi zawierać symbol lub znak interpunkcyjny.", - "NUMBERERROR": "Musi zawierać cyfrę." - }, "STATE": { "0": "Nieznany", "1": "Aktywny", diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json index 05e484cf8..2ee33ed92 100644 --- a/console/src/assets/i18n/zh.json +++ b/console/src/assets/i18n/zh.json @@ -239,11 +239,20 @@ "NOROWS": "没有数据" }, "ERRORS": { - "REQUIRED": "缺少一些必填字段。", + "REQUIRED": "请填写此栏", "TOKENINVALID": { "TITLE": "您的授权令牌已过期。", "DESCRIPTION": "点击下方按钮再次登录。" - } + }, + "INVALID_FORMAT": "格式是无效的。", + "NOTANEMAIL": "给定的值不是合法电子邮件地址。", + "MINLENGTH": "长度必须至少是{{requiredLength}}字符。", + "UPPERCASEMISSING": "密码必须包含大写字符。", + "LOWERCASEMISSING": "密码必须包含小写字符。", + "SYMBOLERROR": "密码必须包含符号或标点符号。", + "NUMBERERROR": "密码必须包含数字。", + "PWNOTEQUAL": "提供的密码不匹配。", + "PHONE": "电话号码必须以00或+开头。" }, "USER": { "SETTINGS": { @@ -524,8 +533,7 @@ "SET": "设置新密码", "RESENDNOTIFICATION": "发送重置密码链接", "REQUIRED": "缺少必填字段。", - "MINLENGTHERROR": "密码长度必须至少为 {{value}} 个字符。", - "NOTEQUAL": "提供的密码不匹配。" + "MINLENGTHERROR": "密码长度必须至少为 {{value}} 个字符。" }, "ID": "ID", "EMAIL": "电子邮件", @@ -579,17 +587,6 @@ "3": "过滤角色名称" } }, - "VALIDATION": { - "INVALIDPATTERN": "密码不符合定义的规则。", - "NOTANEMAIL": "给定的值不是合法电子邮件地址", - "REQUIRED": "输入字段为空。", - "MINLENGTH": "密码长度必须至少为 {{requiredLength}} 个字符。", - "NOEMAIL": "用户名不能是电子邮件地址。", - "UPPERCASEMISSING": "密码必须包含大写字符。", - "LOWERCASEMISSING": "密码必须包含小写字符。", - "SYMBOLERROR": "密码必须包含符号或标点符号。", - "NUMBERERROR": "密码必须包含数字。" - }, "STATE": { "0": "未知", "1": "启用", diff --git a/console/src/component-themes.scss b/console/src/component-themes.scss index c278dca29..e3f67772c 100644 --- a/console/src/component-themes.scss +++ b/console/src/component-themes.scss @@ -41,7 +41,7 @@ @import './styles/color.scss'; @import 'src/app/pages/instance/instance.component.scss'; @import 'src/app/modules/refresh-table/refresh-table.component.scss'; -@import 'src/app/modules/form-field/form-field.component.scss'; +@import 'src/app/modules/form-field/field/form-field.component.scss'; @import 'src/app/modules/label/label.component.scss'; @import 'src/app/modules/meta-layout/meta.scss'; @import 'src/app/pages/projects/owned-projects/project-grant-detail/project-grant-illustration/project-grant-illustration.component';