add invite users to tenant popup and confirm user invite component

This commit is contained in:
amentis 2024-06-21 14:35:24 +03:00
parent b945205ea4
commit 7a9a338062
26 changed files with 817 additions and 14 deletions

View File

@ -84,3 +84,12 @@ export interface UserMergeRequestPersist {
export interface RemoveCredentialRequestPersist { export interface RemoveCredentialRequestPersist {
credentialId: Guid; credentialId: Guid;
} }
export interface UserTenantUsersInviteRequest {
users: UserInviteToTenantRequest[];
}
export interface UserInviteToTenantRequest {
email: string;
roles: string[];
}

View File

@ -1,7 +1,7 @@
import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http'; import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { IsActive } from '@app/core/common/enum/is-active.enum'; import { IsActive } from '@app/core/common/enum/is-active.enum';
import { DmpAssociatedUser, RemoveCredentialRequestPersist, User, UserMergeRequestPersist, UserPersist, UserRolePatchPersist } from '@app/core/model/user/user'; import { DmpAssociatedUser, RemoveCredentialRequestPersist, User, UserMergeRequestPersist, UserPersist, UserRolePatchPersist, UserTenantUsersInviteRequest } from '@app/core/model/user/user';
import { UserLookup } from '@app/core/query/user.lookup'; import { UserLookup } from '@app/core/query/user.lookup';
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
@ -139,6 +139,22 @@ export class UserService {
catchError((error: any) => throwError(error))); catchError((error: any) => throwError(error)));
} }
inviteUsersToTenant(item: UserTenantUsersInviteRequest): Observable<boolean> {
const url = `${this.apiBase}/invite-users-to-tenant`;
return this.http
.post<boolean>(url, item).pipe(
catchError((error: any) => throwError(error)));
}
confirmInviteUser(token: Guid): Observable<boolean> {
const url = `${this.apiBase}/confirm-invite-user-to-tenant/token/${token}`;
return this.http
.get<boolean>(url).pipe(
catchError((error: any) => throwError(error)));
}
// //
// Autocomplete Commons // Autocomplete Commons
// //

View File

@ -0,0 +1,117 @@
import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
import { UserInviteToTenantRequest, UserTenantUsersInviteRequest } from "@app/core/model/user/user";
import { BackendErrorValidator } from '@common/forms/validation/custom-validator';
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
import { Validation, ValidationContext } from '@common/forms/validation/validation-context';
export class UserTenantUsersInviteRequestEditorModel implements UserTenantUsersInviteRequest {
users: UserInviteToTenantRequestEditorModel[] = [];
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
constructor() { }
buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup {
if (context == null) { context = this.createValidationContext(); }
return this.formBuilder.group({
users: this.formBuilder.array(
(this.users ?? []).map(
(item, index) => item.buildForm({
rootPath: `userInviteToTenantRequest[${index}].`,
disabled: disabled
})
), context.getValidation('users').validators
),
});
}
createValidationContext(): ValidationContext {
const baseContext: ValidationContext = new ValidationContext();
const baseValidationArray: Validation[] = new Array<Validation>();
baseValidationArray.push({ key: 'users', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'userInviteToTenantRequest')] });
baseContext.validation = baseValidationArray;
return baseContext;
}
static reapplyValidators(params: {
formArray: UntypedFormArray,
validationErrorModel: ValidationErrorModel,
}): void {
const { validationErrorModel, formArray } = params;
formArray?.controls?.forEach(
(control, index) => UserInviteToTenantRequestEditorModel.reapplyValidators({
formGroup: control as UntypedFormGroup,
rootPath: `userInviteToTenantRequest[${index}].`,
validationErrorModel: validationErrorModel
})
);
}
}
export class UserInviteToTenantRequestEditorModel implements UserInviteToTenantRequest {
email: string;
roles: string[];
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
constructor(
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
) { }
buildForm(params?: {
context?: ValidationContext,
disabled?: boolean,
rootPath?: string
}): UntypedFormGroup {
let { context = null, disabled = false, rootPath } = params ?? {}
if (context == null) {
context = UserInviteToTenantRequestEditorModel.createValidationContext({
validationErrorModel: this.validationErrorModel,
rootPath
});
}
return this.formBuilder.group({
email: [{ value: this.email, disabled: disabled }, context.getValidation('email').validators],
roles: [{ value: this.roles, disabled: disabled }, context.getValidation('roles').validators],
});
}
static createValidationContext(params: {
rootPath?: string,
validationErrorModel: ValidationErrorModel
}): ValidationContext {
const { rootPath = '', validationErrorModel } = params;
const baseContext: ValidationContext = new ValidationContext();
const baseValidationArray: Validation[] = new Array<Validation>();
baseValidationArray.push({ key: 'email', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}email`)] });
baseValidationArray.push({ key: 'roles', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}roles`)] });
baseContext.validation = baseValidationArray;
return baseContext;
}
static reapplyValidators(params: {
formGroup: UntypedFormGroup,
validationErrorModel: ValidationErrorModel,
rootPath: string
}): void {
const { formGroup, rootPath, validationErrorModel } = params;
const context = UserInviteToTenantRequestEditorModel.createValidationContext({
rootPath,
validationErrorModel
});
['email', 'roles'].forEach(keyField => {
const control = formGroup?.get(keyField);
control?.clearValidators();
control?.addValidators(context.getValidation(keyField).validators);
})
}
}

View File

@ -0,0 +1,57 @@
<div class="user-invite-to-tenant-dialog container-fluid" *ngIf="formGroup">
<div class="row">
<div class="col">
<h1 class="title">{{'USER-INVITE-TO-TENANT-DIALOG.TITLE' | translate}}</h1>
</div>
<div class="col-auto" (click)="closeDialog()">
<mat-icon class="close-icon">close</mat-icon>
</div>
</div>
<div mat-dialog-content class="row">
<div *ngFor="let user of formGroup.get('users').controls; let userIndex=index;" class="row mb-3">
<div class="col-12 col-xl mt-3">
<mat-form-field class="w-100">
<mat-label>{{'USER-INVITE-TO-TENANT-DIALOG.FIELDS.EMAIL' | translate}}</mat-label>
<input matInput type="text" name="email" [formControl]="user.get('email')">
<mat-error *ngIf="user.get('email').hasError('backendError')">{{user.get('email').getError('backendError').message}}</mat-error>
<mat-error *ngIf="user.get('email').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-12 col-xl mt-3">
<mat-form-field class="w-100">
<mat-label>{{'USER-INVITE-TO-TENANT-DIALOG.FIELDS.ROLES' | translate}}</mat-label>
<mat-select multiple [formControl]="user.get('roles')">
<mat-option [value]="appRoleEnum.TenantAdmin">{{enumUtils.toAppRoleString(appRoleEnum.TenantAdmin)}}</mat-option>
<mat-option [value]="appRoleEnum.TenantPlanManager">{{enumUtils.toAppRoleString(appRoleEnum.TenantPlanManager)}}</mat-option>
<mat-option [value]="appRoleEnum.TenantConfigManager">{{enumUtils.toAppRoleString(appRoleEnum.TenantConfigManager)}}</mat-option>
<mat-option [value]="appRoleEnum.TenantUser">{{enumUtils.toAppRoleString(appRoleEnum.TenantUser)}}</mat-option>
</mat-select>
<mat-error *ngIf="user.get('roles').hasError('backendError')">{{user.get('roles').getError('backendError').message}}</mat-error>
<mat-error *ngIf="user.get('roles').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="col-12 col-xl-auto">
<button mat-icon-button class="action-list-icon" matTooltip="{{'USER-INVITE-TO-TENANT-DIALOG.ACTIONS.REMOVE-USER' | translate}}" (click)="removeUser(userIndex)" [disabled]="formGroup.get('users').controls.length == 1">
<mat-icon>delete</mat-icon>
</button>
</div>
</div>
</div>
<mat-error *ngIf="formGroup.get('users').dirty && formGroup.get('users').hasError('required')">{{'USER-INVITE-TO-TENANT-DIALOG.USERS-REQUIRED' | translate}}</mat-error>
<mat-error *ngIf="formGroup.get('users').hasError('backendError')">{{formGroup.get('users').getError('backendError').message}}</mat-error>
<div class="p-2">
<div class="row">
<div class="col">
<button mat-icon-button (click)="addUser()">
<mat-icon>add</mat-icon>
</button>
</div>
</div>
</div>
<div class="col mt-2">
<button mat-raised-button [disabled]="inProgressSendButton" (click)="send()" type="button" class="invite-btn">{{'USER-INVITE-TO-TENANT-DIALOG.ACTIONS.INVITE' | translate}}</button>
<mat-error *ngIf="formGroup.get('users').hasError('backendError')">{{formGroup.get('users').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('users').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</div>
</div>

View File

@ -0,0 +1,150 @@
.user-invite-to-tenant-dialog {
padding: 2.5rem;
.form-container {
width: 33em;
min-height: 14em;
padding: 0.28em 0.34em 0em 1.125em;
}
.close-icon {
cursor: pointer;
// margin-right: 20px;
padding: .4rem;
width: auto !important;
height: auto !important;
}
.close-icon:hover {
background-color: #ECECED !important;
border-radius: 50%;
}
.title {
font-size: 2.375em;
font-weight: lighter;
color: #000000;
opacity: 0.8;
margin-bottom: 0.842em;
}
.content {
width: 31em;
margin: 0px;
padding: 0px;
}
// .mat-form-field {
// background: #fafafa;
// border: 1px solid #d1d1d1;
// border-radius: 4px;
// }
.hint {
font-size: 0.875rem;
font-weight: 500;
color: #212121;
opacity: 0.81;
}
::ng-deep .mat-dialog-container {
border-radius: 8px;
}
.search {
padding: 2px !important;
}
::ng-deep .search {
.mat-form-field-infix {
font-size: 1rem;
padding: 0.6em 0 1em 0 !important;
}
// .mat-form-field-underline {
// display: none;
// }
// .mat-form-field-flex {
// padding: 0em;
// }
// .align-arrow-right {
// display: none;
// }
}
.select-role {
width: 50% !important;
font-size: 14px;
color: #848484;
height: min-content;
margin-right: 2rem;
border: none;
background-color: transparent;
}
::ng-deep .select-role {
.mat-form-field-outline-start,
.mat-form-field-outline-gap,
.mat-form-field-outline-end {
border: none !important;
}
.mat-select-arrow-wrapper {
transform: none !important;
}
}
::ng-deep .select-role .mat-form-field-wrapper {
padding-bottom: 0 !important;
}
.invite-btn {
background: #ffffff 0% 0% no-repeat padding-box;
border: 1px solid var(--primary-color);
border-radius: 30px;
opacity: 1;
min-width: 101px;
height: 43px;
color: var(--primary-color);
font-weight: 500;
}
.invite-btn-disabled {
min-width: 6.64em;
height: 2.93em;
background: #ffffff;
border: 1px solid #b5b5b5;
border-radius: 30px;
font-weight: bold;
letter-spacing: -0.35px;
color: #b5b5b5;
margin-bottom: 0.25em;
cursor: default;
}
.invite-btn:hover {
background: var(--primary-color);
color: #ffffff;
}
@keyframes blink {
0% {
border: 1px solid rgba(255, 0, 0, 0.2);
}
50% {
border: 1px solid rgba(255, 0, 0, 0.5);
}
100% {
border: 1px solid rgba(255, 0, 0, 0.2);
}
}
:host ::ng-deep .invalid-email {
border: 1px solid red;
// animation: blink 1.4s infinite;
// animation-fill-mode: both;
}
}

View File

@ -0,0 +1,110 @@
import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { HttpErrorResponse } from '@angular/common/http';
import { Component, Inject, OnInit } from '@angular/core';
import { UntypedFormArray, UntypedFormGroup } from '@angular/forms';
import { MatDialogRef } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { BaseComponent } from '@common/base/base.component';
import { FormService } from '@common/forms/form-service';
import { HttpError, HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { TranslateService } from '@ngx-translate/core';
import { takeUntil } from 'rxjs/operators';
import { UserInviteToTenantRequestEditorModel, UserTenantUsersInviteRequestEditorModel } from './user-invite-to-tenant-dialog-editor.model';
import { UserTenantUsersInviteRequest } from '@app/core/model/user/user';
import { UserService } from '@app/core/services/user/user.service';
import { AppRole } from '@app/core/common/enum/app-role';
@Component({
selector: 'app-user-invite-to-tenant-dialog.component',
templateUrl: 'user-invite-to-tenant-dialog.component.html',
styleUrls: ['./user-invite-to-tenant-dialog.component.scss']
})
export class UserInviteToTenantDialogComponent extends BaseComponent implements OnInit {
editorModel: UserTenantUsersInviteRequestEditorModel;
formGroup: any;
inProgressSendButton = false;
appRoleEnum = AppRole;
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
constructor(
public enumUtils: EnumUtils,
public route: ActivatedRoute,
public router: Router,
private language: TranslateService,
public dialogRef: MatDialogRef<UserInviteToTenantDialogComponent>,
private uiNotificationService: UiNotificationService,
private httpErrorHandlingService: HttpErrorHandlingService,
private userService: UserService,
private formService: FormService,
) {
super();
}
ngOnInit() {
this.editorModel = new UserTenantUsersInviteRequestEditorModel();
this.formGroup = this.editorModel.buildForm();
this.addUser();
}
addUser(): void {
const formArray = this.formGroup.get("users") as UntypedFormArray;
const user: UserInviteToTenantRequestEditorModel = new UserInviteToTenantRequestEditorModel(this.editorModel.validationErrorModel);
formArray.push(user.buildForm({ rootPath: "userInviteToTenantRequest[" + formArray.length + "]." }));
}
removeUser(userIndex: number): void {
(this.formGroup.get("users") as UntypedFormArray).removeAt(userIndex);
UserTenantUsersInviteRequestEditorModel.reapplyValidators(
{
formArray: this.formGroup.get("users") as UntypedFormArray,
validationErrorModel: this.editorModel.validationErrorModel,
}
);
(this.formGroup.get("users") as UntypedFormArray).markAsDirty();
}
send() {
this.formService.removeAllBackEndErrors(this.formGroup);
this.formService.touchAllFormFields(this.formGroup);
if (!this.formGroup.valid) { return; }
this.inProgressSendButton = true;
const userFormData = this.formGroup.value as UserTenantUsersInviteRequest;
this.userService.inviteUsersToTenant(userFormData)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => {
this.dialogRef.close();
this.onCallbackSuccess();
},
error => this.onCallbackError(error)
);
}
closeDialog(): void {
this.dialogRef.close();
}
onCallbackSuccess(): void {
this.uiNotificationService.snackBarNotification(this.language.instant('DMP-USER-INVITATION-DIALOG.SUCCESS'), SnackBarNotificationLevel.Success);
}
onCallbackError(errorResponse: HttpErrorResponse) {
this.inProgressSendButton = false;
let errorOverrides = new Map<number, string>();
errorOverrides.set(-1, this.language.instant('DMP-USER-INVITATION-DIALOG.ERROR'));
this.httpErrorHandlingService.handleBackedRequestError(errorResponse, errorOverrides, SnackBarNotificationLevel.Error);
const error: HttpError = this.httpErrorHandlingService.getError(errorResponse);
if (error.statusCode === 400) {
this.editorModel.validationErrorModel.fromJSONObject(errorResponse.error);
this.formService.validateAllFormFields(this.formGroup);
}
}
}

View File

@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CommonUiModule } from '@common/ui/common-ui.module';
import { UserInviteToTenantDialogComponent } from './user-invite-to-tenant-dialog.component';
@NgModule({
imports: [CommonUiModule, FormsModule, ReactiveFormsModule],
declarations: [UserInviteToTenantDialogComponent],
exports: [UserInviteToTenantDialogComponent]
})
export class UserInviteToTenantDialogModule {
constructor() { }
}

View File

@ -6,6 +6,11 @@
<app-navigation-breadcrumb /> <app-navigation-breadcrumb />
</div> </div>
<div class="col-auto">
<button mat-raised-button class="create-btn" (click)="invite()" *ngIf="authService.hasPermission(authService.permissionEnum.ExportUsers)">
{{'USER-LISTING.ACTIONS.INVITE' | translate}}
</button>
</div>
<div class="col-auto"> <div class="col-auto">
<button mat-raised-button class="create-btn" (click)="export()" *ngIf="authService.hasPermission(authService.permissionEnum.ExportUsers)"> <button mat-raised-button class="create-btn" (click)="export()" *ngIf="authService.hasPermission(authService.permissionEnum.ExportUsers)">
<mat-icon>download</mat-icon> <mat-icon>download</mat-icon>

View File

@ -25,6 +25,7 @@ import { Observable } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof'; import { nameof } from 'ts-simple-nameof';
import { RouterUtilsService } from '@app/core/services/router/router-utils.service'; import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
import { UserInviteToTenantDialogComponent } from './user-invite-to-tenant-dialog/user-invite-to-tenant-dialog.component';
@Component({ @Component({
templateUrl: './user-listing.component.html', templateUrl: './user-listing.component.html',
@ -216,4 +217,17 @@ export class UserListingComponent extends BaseListingComponent<User, UserLookup>
public setDefaultAvatar(ev: Event) { public setDefaultAvatar(ev: Event) {
(ev.target as HTMLImageElement).src = 'assets/images/profile-placeholder.png'; (ev.target as HTMLImageElement).src = 'assets/images/profile-placeholder.png';
} }
//
// Invite
//
invite() {
const dialogRef = this.dialog.open(UserInviteToTenantDialogComponent, {
autoFocus: false,
restoreFocus: false,
});
}
} }

View File

@ -11,6 +11,7 @@ import { UserListingComponent } from './listing/user-listing.component';
import { UsersRoutingModule } from './user.routing'; import { UsersRoutingModule } from './user.routing';
import { UserRoleEditorComponent } from './listing/role-editor/user-role-editor.component'; import { UserRoleEditorComponent } from './listing/role-editor/user-role-editor.component';
import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module'; import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module';
import { UserInviteToTenantDialogModule } from './listing/user-invite-to-tenant-dialog/user-invite-to-tenant-dialog.module';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -28,7 +29,8 @@ import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.mod
HybridListingModule, HybridListingModule,
AutoCompleteModule, AutoCompleteModule,
TextFilterModule, TextFilterModule,
UserSettingsModule UserSettingsModule,
UserInviteToTenantDialogModule
] ]
}) })
export class UsersModule { } export class UsersModule { }

View File

@ -6,6 +6,7 @@ import { CommonFormsModule } from '@common/forms/common-forms.module';
import { CommonUiModule } from '@common/ui/common-ui.module'; import { CommonUiModule } from '@common/ui/common-ui.module';
import { MergeEmailConfirmation } from './merge-email-confirmation/merge-email-confirmation.component'; import { MergeEmailConfirmation } from './merge-email-confirmation/merge-email-confirmation.component';
import { UnlinkEmailConfirmation } from './unlink-email-confirmation/unlink-email-confirmation.component'; import { UnlinkEmailConfirmation } from './unlink-email-confirmation/unlink-email-confirmation.component';
import { UserInviteConfirmation } from './user-invite-confirmation/user-invite-confirmation.component';
@NgModule({ @NgModule({
imports: [ imports: [
@ -16,7 +17,8 @@ import { UnlinkEmailConfirmation } from './unlink-email-confirmation/unlink-emai
declarations: [ declarations: [
LoginComponent, LoginComponent,
MergeEmailConfirmation, MergeEmailConfirmation,
UnlinkEmailConfirmation UnlinkEmailConfirmation,
UserInviteConfirmation
], ],
exports: [ exports: [
LoginComponent LoginComponent

View File

@ -4,6 +4,7 @@ import { LoginComponent } from './login.component';
import { MergeEmailConfirmation } from './merge-email-confirmation/merge-email-confirmation.component'; import { MergeEmailConfirmation } from './merge-email-confirmation/merge-email-confirmation.component';
import { UnlinkEmailConfirmation } from './unlink-email-confirmation/unlink-email-confirmation.component'; import { UnlinkEmailConfirmation } from './unlink-email-confirmation/unlink-email-confirmation.component';
import { AuthGuard } from '@app/core/auth-guard.service'; import { AuthGuard } from '@app/core/auth-guard.service';
import { UserInviteConfirmation } from './user-invite-confirmation/user-invite-confirmation.component';
// import { PostLoginComponent } from './post-login/post-login.component'; // import { PostLoginComponent } from './post-login/post-login.component';
const routes: Routes = [ const routes: Routes = [
@ -18,6 +19,8 @@ const routes: Routes = [
// component: PostLoginComponent // component: PostLoginComponent
// }, // },
{ path: 'unlink/confirmation/:token', component: UnlinkEmailConfirmation }, { path: 'unlink/confirmation/:token', component: UnlinkEmailConfirmation },
{ path: 'invitation/confirmation/:token', component: UserInviteConfirmation },
]; ];
@NgModule({ @NgModule({

View File

@ -0,0 +1,21 @@
<div class="user-invite">
<div class="container-fluid">
<div class="row">
<div class="col user-invite-title">{{'USER-INVITE.TITLE' | translate}}</div>
</div>
<div *ngIf="showForm" class="row user-invite-content">
<div class="col">
<div class="row justify-content-center">
<div class="col-auto">
<span>
{{ 'USER-INVITE.IN-PROGRESS' | translate }}
</span>
</div>
</div>
</div>
</div>
<ng-template #loading>
</ng-template>
</div>
</div>

View File

@ -0,0 +1,19 @@
.user-invite {
height: fit-content;
//margin-top: 80px;
min-height: 100vh;
background-color: #ffffff;
}
.user-invite-title {
font-size: 1.25rem;
color: #212121;
padding-top: 4.1875rem;
padding-left: 3.75rem;
padding-bottom: 4.0625rem;
}
.user-invite-content {
margin-left: 9rem;
margin-right: 11rem;
}

View File

@ -0,0 +1,78 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
import { UserService } from '@app/core/services/user/user.service';
import { BaseComponent } from '@common/base/base.component';
import { HttpError, HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { Guid } from '@common/types/guid';
import { TranslateService } from '@ngx-translate/core';
import { takeUntil } from "rxjs/operators";
@Component({
selector: 'app-user-invite-confirmation-component',
templateUrl: './user-invite-confirmation.component.html'
})
export class UserInviteConfirmation extends BaseComponent implements OnInit {
private token: Guid;
get showForm(): boolean {
return this.token != null;
}
constructor(
private userService: UserService,
private route: ActivatedRoute,
private router: Router,
private language: TranslateService,
private routerUtils: RouterUtilsService,
private httpErrorHandlingService: HttpErrorHandlingService
) { super(); }
ngOnInit() {
this.route.params
.pipe(takeUntil(this._destroyed))
.subscribe(params => {
const token = params['token']
if (token != null) {
this.token = token;
this.onConfirm();
}
});
}
onConfirm(): void {
if (this.showForm === false) return;
this.userService.confirmInviteUser(this.token)
.subscribe(result => {
if (result) {
this.onCallbackConfirmationSuccess();
}
},
error => this.onCallbackError(error));
}
onCallbackConfirmationSuccess() {
this.router.navigate([this.routerUtils.generateUrl('home')])
.then(() => {
localStorage.setItem('refreshPage', null);
localStorage.setItem('refreshPage', 'true');
window.location.reload();
});
}
onCallbackError(errorResponse: HttpErrorResponse) {
let errorOverrides = new Map<number, string>();
errorOverrides.set(302, this.language.instant('EMAIL-CONFIRMATION.EMAIL-FOUND'));
errorOverrides.set(-1, this.language.instant('EMAIL-CONFIRMATION.EXPIRED-EMAIL'));
this.httpErrorHandlingService.handleBackedRequestError(errorResponse, errorOverrides)
const error: HttpError = this.httpErrorHandlingService.getError(errorResponse);
if (error.statusCode === 302) {
this.router.navigate([this.routerUtils.generateUrl('home')]);
}
}
}

View File

@ -1768,7 +1768,8 @@
"ACTIONS": { "ACTIONS": {
"EDIT": "Edit", "EDIT": "Edit",
"SAVE": "Save", "SAVE": "Save",
"EXPORT": "Export users" "EXPORT": "Export users",
"INVITE": "Invite users"
} }
}, },
"REFERENCE-FIELD-COMPONENT": { "REFERENCE-FIELD-COMPONENT": {
@ -2100,6 +2101,22 @@
"USER-PROFILE-SETTINGS": "Nire Profilaren Ezarpenak...", "USER-PROFILE-SETTINGS": "Nire Profilaren Ezarpenak...",
"LOG-OUT": "Saioa itxi" "LOG-OUT": "Saioa itxi"
}, },
"USER-INVITE": {
"IN-PROGRESS": "User Invitation in progress..",
"TITLE": "User Invitation"
},
"USER-INVITE-TO-TENANT-DIALOG": {
"TITLE": "Invite Users",
"FIELDS": {
"EMAIL": "Email",
"ROLES": "Roles"
},
"ACTIONS": {
"REMOVE-USER":"Remove User",
"INVITE":"Invite"
},
"USERS-REQUIRED": "Required at leat on user"
},
"USER-PROFILE": { "USER-PROFILE": {
"MERGING-EMAILS-DIALOG": { "MERGING-EMAILS-DIALOG": {
"TITLE": "Lotutako kontua egiaztatu", "TITLE": "Lotutako kontua egiaztatu",

View File

@ -1768,7 +1768,8 @@
"ACTIONS": { "ACTIONS": {
"EDIT": "Edit", "EDIT": "Edit",
"SAVE": "Save", "SAVE": "Save",
"EXPORT": "Export users" "EXPORT": "Export users",
"INVITE": "Invite users"
} }
}, },
"REFERENCE-FIELD-COMPONENT": { "REFERENCE-FIELD-COMPONENT": {
@ -2100,6 +2101,22 @@
"USER-PROFILE-SETTINGS": "My Profile Settings...", "USER-PROFILE-SETTINGS": "My Profile Settings...",
"LOG-OUT": "Abmeldung" "LOG-OUT": "Abmeldung"
}, },
"USER-INVITE": {
"IN-PROGRESS": "User Invitation in progress..",
"TITLE": "User Invitation"
},
"USER-INVITE-TO-TENANT-DIALOG": {
"TITLE": "Invite Users",
"FIELDS": {
"EMAIL": "Email",
"ROLES": "Roles"
},
"ACTIONS": {
"REMOVE-USER":"Remove User",
"INVITE":"Invite"
},
"USERS-REQUIRED": "Required at leat on user"
},
"USER-PROFILE": { "USER-PROFILE": {
"MERGING-EMAILS-DIALOG": { "MERGING-EMAILS-DIALOG": {
"TITLE": "Verify linked account", "TITLE": "Verify linked account",

View File

@ -1768,7 +1768,8 @@
"ACTIONS": { "ACTIONS": {
"EDIT": "Edit", "EDIT": "Edit",
"SAVE": "Save", "SAVE": "Save",
"EXPORT": "Export users" "EXPORT": "Export users",
"INVITE": "Invite users"
} }
}, },
"REFERENCE-FIELD-COMPONENT": { "REFERENCE-FIELD-COMPONENT": {
@ -2100,6 +2101,22 @@
"USER-PROFILE-SETTINGS": "My Profile Settings...", "USER-PROFILE-SETTINGS": "My Profile Settings...",
"LOG-OUT": "Log Out" "LOG-OUT": "Log Out"
}, },
"USER-INVITE": {
"IN-PROGRESS": "User Invitation in progress..",
"TITLE": "User Invitation"
},
"USER-INVITE-TO-TENANT-DIALOG": {
"TITLE": "Invite Users",
"FIELDS": {
"EMAIL": "Email",
"ROLES": "Roles"
},
"ACTIONS": {
"REMOVE-USER":"Remove User",
"INVITE":"Invite"
},
"USERS-REQUIRED": "Required at leat on user"
},
"USER-PROFILE": { "USER-PROFILE": {
"MERGING-EMAILS-DIALOG": { "MERGING-EMAILS-DIALOG": {
"TITLE": "Verify linked account", "TITLE": "Verify linked account",

View File

@ -1768,7 +1768,8 @@
"ACTIONS": { "ACTIONS": {
"EDIT": "Edit", "EDIT": "Edit",
"SAVE": "Save", "SAVE": "Save",
"EXPORT": "Export users" "EXPORT": "Export users",
"INVITE": "Invite users"
} }
}, },
"REFERENCE-FIELD-COMPONENT": { "REFERENCE-FIELD-COMPONENT": {
@ -2100,6 +2101,22 @@
"USER-PROFILE-SETTINGS": "Configuración de mi perfil...", "USER-PROFILE-SETTINGS": "Configuración de mi perfil...",
"LOG-OUT": "Cerrar la sesión" "LOG-OUT": "Cerrar la sesión"
}, },
"USER-INVITE": {
"IN-PROGRESS": "User Invitation in progress..",
"TITLE": "User Invitation"
},
"USER-INVITE-TO-TENANT-DIALOG": {
"TITLE": "Invite Users",
"FIELDS": {
"EMAIL": "Email",
"ROLES": "Roles"
},
"ACTIONS": {
"REMOVE-USER":"Remove User",
"INVITE":"Invite"
},
"USERS-REQUIRED": "Required at leat on user"
},
"USER-PROFILE": { "USER-PROFILE": {
"MERGING-EMAILS-DIALOG": { "MERGING-EMAILS-DIALOG": {
"TITLE": "Verify linked account", "TITLE": "Verify linked account",

View File

@ -1768,7 +1768,8 @@
"ACTIONS": { "ACTIONS": {
"EDIT": "Edit", "EDIT": "Edit",
"SAVE": "Save", "SAVE": "Save",
"EXPORT": "Export users" "EXPORT": "Export users",
"INVITE": "Invite users"
} }
}, },
"REFERENCE-FIELD-COMPONENT": { "REFERENCE-FIELD-COMPONENT": {
@ -2100,6 +2101,22 @@
"USER-PROFILE-SETTINGS": "Οι ρυθμίσεις του προφίλ μου...", "USER-PROFILE-SETTINGS": "Οι ρυθμίσεις του προφίλ μου...",
"LOG-OUT": "Αποσύνδεση" "LOG-OUT": "Αποσύνδεση"
}, },
"USER-INVITE": {
"IN-PROGRESS": "User Invitation in progress..",
"TITLE": "User Invitation"
},
"USER-INVITE-TO-TENANT-DIALOG": {
"TITLE": "Invite Users",
"FIELDS": {
"EMAIL": "Email",
"ROLES": "Roles"
},
"ACTIONS": {
"REMOVE-USER":"Remove User",
"INVITE":"Invite"
},
"USERS-REQUIRED": "Required at leat on user"
},
"USER-PROFILE": { "USER-PROFILE": {
"MERGING-EMAILS-DIALOG": { "MERGING-EMAILS-DIALOG": {
"TITLE": "Verify linked account", "TITLE": "Verify linked account",

View File

@ -1768,7 +1768,8 @@
"ACTIONS": { "ACTIONS": {
"EDIT": "Edit", "EDIT": "Edit",
"SAVE": "Save", "SAVE": "Save",
"EXPORT": "Export users" "EXPORT": "Export users",
"INVITE": "Invite users"
} }
}, },
"REFERENCE-FIELD-COMPONENT": { "REFERENCE-FIELD-COMPONENT": {
@ -2100,6 +2101,22 @@
"USER-PROFILE-SETTINGS": "Postavke profila...", "USER-PROFILE-SETTINGS": "Postavke profila...",
"LOG-OUT": "Odjava" "LOG-OUT": "Odjava"
}, },
"USER-INVITE": {
"IN-PROGRESS": "User Invitation in progress..",
"TITLE": "User Invitation"
},
"USER-INVITE-TO-TENANT-DIALOG": {
"TITLE": "Invite Users",
"FIELDS": {
"EMAIL": "Email",
"ROLES": "Roles"
},
"ACTIONS": {
"REMOVE-USER":"Remove User",
"INVITE":"Invite"
},
"USERS-REQUIRED": "Required at leat on user"
},
"USER-PROFILE": { "USER-PROFILE": {
"MERGING-EMAILS-DIALOG": { "MERGING-EMAILS-DIALOG": {
"TITLE": "Potvrdi povezani korisnički račun", "TITLE": "Potvrdi povezani korisnički račun",

View File

@ -1768,7 +1768,8 @@
"ACTIONS": { "ACTIONS": {
"EDIT": "Edit", "EDIT": "Edit",
"SAVE": "Save", "SAVE": "Save",
"EXPORT": "Export users" "EXPORT": "Export users",
"INVITE": "Invite users"
} }
}, },
"REFERENCE-FIELD-COMPONENT": { "REFERENCE-FIELD-COMPONENT": {
@ -2100,6 +2101,22 @@
"USER-PROFILE-SETTINGS": "Ustawienia mojego Profilu...", "USER-PROFILE-SETTINGS": "Ustawienia mojego Profilu...",
"LOG-OUT": "Wyloguj" "LOG-OUT": "Wyloguj"
}, },
"USER-INVITE": {
"IN-PROGRESS": "User Invitation in progress..",
"TITLE": "User Invitation"
},
"USER-INVITE-TO-TENANT-DIALOG": {
"TITLE": "Invite Users",
"FIELDS": {
"EMAIL": "Email",
"ROLES": "Roles"
},
"ACTIONS": {
"REMOVE-USER":"Remove User",
"INVITE":"Invite"
},
"USERS-REQUIRED": "Required at leat on user"
},
"USER-PROFILE": { "USER-PROFILE": {
"MERGING-EMAILS-DIALOG": { "MERGING-EMAILS-DIALOG": {
"TITLE": "Zweryfikuj połączone konto", "TITLE": "Zweryfikuj połączone konto",

View File

@ -1768,7 +1768,8 @@
"ACTIONS": { "ACTIONS": {
"EDIT": "Edit", "EDIT": "Edit",
"SAVE": "Save", "SAVE": "Save",
"EXPORT": "Export users" "EXPORT": "Export users",
"INVITE": "Invite users"
} }
}, },
"REFERENCE-FIELD-COMPONENT": { "REFERENCE-FIELD-COMPONENT": {
@ -2100,6 +2101,22 @@
"USER-PROFILE-SETTINGS": "Definições do Meu Perfil...", "USER-PROFILE-SETTINGS": "Definições do Meu Perfil...",
"LOG-OUT": "Terminar Sessão" "LOG-OUT": "Terminar Sessão"
}, },
"USER-INVITE": {
"IN-PROGRESS": "User Invitation in progress..",
"TITLE": "User Invitation"
},
"USER-INVITE-TO-TENANT-DIALOG": {
"TITLE": "Invite Users",
"FIELDS": {
"EMAIL": "Email",
"ROLES": "Roles"
},
"ACTIONS": {
"REMOVE-USER":"Remove User",
"INVITE":"Invite"
},
"USERS-REQUIRED": "Required at leat on user"
},
"USER-PROFILE": { "USER-PROFILE": {
"MERGING-EMAILS-DIALOG": { "MERGING-EMAILS-DIALOG": {
"TITLE": "Verify linked account", "TITLE": "Verify linked account",

View File

@ -1768,7 +1768,8 @@
"ACTIONS": { "ACTIONS": {
"EDIT": "Edit", "EDIT": "Edit",
"SAVE": "Save", "SAVE": "Save",
"EXPORT": "Export users" "EXPORT": "Export users",
"INVITE": "Invite users"
} }
}, },
"REFERENCE-FIELD-COMPONENT": { "REFERENCE-FIELD-COMPONENT": {
@ -2100,6 +2101,22 @@
"USER-PROFILE-SETTINGS": "Nastavenie profilu...", "USER-PROFILE-SETTINGS": "Nastavenie profilu...",
"LOG-OUT": "Odhlásiť sa" "LOG-OUT": "Odhlásiť sa"
}, },
"USER-INVITE": {
"IN-PROGRESS": "User Invitation in progress..",
"TITLE": "User Invitation"
},
"USER-INVITE-TO-TENANT-DIALOG": {
"TITLE": "Invite Users",
"FIELDS": {
"EMAIL": "Email",
"ROLES": "Roles"
},
"ACTIONS": {
"REMOVE-USER":"Remove User",
"INVITE":"Invite"
},
"USERS-REQUIRED": "Required at leat on user"
},
"USER-PROFILE": { "USER-PROFILE": {
"MERGING-EMAILS-DIALOG": { "MERGING-EMAILS-DIALOG": {
"TITLE": "Verify linked account", "TITLE": "Verify linked account",

View File

@ -1768,7 +1768,8 @@
"ACTIONS": { "ACTIONS": {
"EDIT": "Edit", "EDIT": "Edit",
"SAVE": "Save", "SAVE": "Save",
"EXPORT": "Export users" "EXPORT": "Export users",
"INVITE": "Invite users"
} }
}, },
"REFERENCE-FIELD-COMPONENT": { "REFERENCE-FIELD-COMPONENT": {
@ -2100,6 +2101,22 @@
"USER-PROFILE-SETTINGS": "Podešavanja mog profila...", "USER-PROFILE-SETTINGS": "Podešavanja mog profila...",
"LOG-OUT": "Odjavite se" "LOG-OUT": "Odjavite se"
}, },
"USER-INVITE": {
"IN-PROGRESS": "User Invitation in progress..",
"TITLE": "User Invitation"
},
"USER-INVITE-TO-TENANT-DIALOG": {
"TITLE": "Invite Users",
"FIELDS": {
"EMAIL": "Email",
"ROLES": "Roles"
},
"ACTIONS": {
"REMOVE-USER":"Remove User",
"INVITE":"Invite"
},
"USERS-REQUIRED": "Required at leat on user"
},
"USER-PROFILE": { "USER-PROFILE": {
"MERGING-EMAILS-DIALOG": { "MERGING-EMAILS-DIALOG": {
"TITLE": "Verify linked account", "TITLE": "Verify linked account",

View File

@ -1768,7 +1768,8 @@
"ACTIONS": { "ACTIONS": {
"EDIT": "Edit", "EDIT": "Edit",
"SAVE": "Save", "SAVE": "Save",
"EXPORT": "Export users" "EXPORT": "Export users",
"INVITE": "Invite users"
} }
}, },
"REFERENCE-FIELD-COMPONENT": { "REFERENCE-FIELD-COMPONENT": {
@ -2100,6 +2101,22 @@
"USER-PROFILE-SETTINGS": "Profil Ayarlarım...", "USER-PROFILE-SETTINGS": "Profil Ayarlarım...",
"LOG-OUT": ıkış Yap" "LOG-OUT": ıkış Yap"
}, },
"USER-INVITE": {
"IN-PROGRESS": "User Invitation in progress..",
"TITLE": "User Invitation"
},
"USER-INVITE-TO-TENANT-DIALOG": {
"TITLE": "Invite Users",
"FIELDS": {
"EMAIL": "Email",
"ROLES": "Roles"
},
"ACTIONS": {
"REMOVE-USER":"Remove User",
"INVITE":"Invite"
},
"USERS-REQUIRED": "Required at leat on user"
},
"USER-PROFILE": { "USER-PROFILE": {
"MERGING-EMAILS-DIALOG": { "MERGING-EMAILS-DIALOG": {
"TITLE": "Verify linked account", "TITLE": "Verify linked account",