add invite users to tenant popup and confirm user invite component
This commit is contained in:
parent
b945205ea4
commit
7a9a338062
|
@ -84,3 +84,12 @@ export interface UserMergeRequestPersist {
|
|||
export interface RemoveCredentialRequestPersist {
|
||||
credentialId: Guid;
|
||||
}
|
||||
|
||||
export interface UserTenantUsersInviteRequest {
|
||||
users: UserInviteToTenantRequest[];
|
||||
}
|
||||
|
||||
export interface UserInviteToTenantRequest {
|
||||
email: string;
|
||||
roles: string[];
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
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 { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-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)));
|
||||
}
|
||||
|
||||
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
|
||||
//
|
||||
|
|
|
@ -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);
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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() { }
|
||||
}
|
|
@ -6,6 +6,11 @@
|
|||
<app-navigation-breadcrumb />
|
||||
|
||||
</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">
|
||||
<button mat-raised-button class="create-btn" (click)="export()" *ngIf="authService.hasPermission(authService.permissionEnum.ExportUsers)">
|
||||
<mat-icon>download</mat-icon>
|
||||
|
|
|
@ -25,6 +25,7 @@ import { Observable } from 'rxjs';
|
|||
import { takeUntil } from 'rxjs/operators';
|
||||
import { nameof } from 'ts-simple-nameof';
|
||||
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({
|
||||
templateUrl: './user-listing.component.html',
|
||||
|
@ -216,4 +217,17 @@ export class UserListingComponent extends BaseListingComponent<User, UserLookup>
|
|||
public setDefaultAvatar(ev: Event) {
|
||||
(ev.target as HTMLImageElement).src = 'assets/images/profile-placeholder.png';
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Invite
|
||||
//
|
||||
|
||||
invite() {
|
||||
const dialogRef = this.dialog.open(UserInviteToTenantDialogComponent, {
|
||||
autoFocus: false,
|
||||
restoreFocus: false,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { UserListingComponent } from './listing/user-listing.component';
|
|||
import { UsersRoutingModule } from './user.routing';
|
||||
import { UserRoleEditorComponent } from './listing/role-editor/user-role-editor.component';
|
||||
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({
|
||||
declarations: [
|
||||
|
@ -28,7 +29,8 @@ import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.mod
|
|||
HybridListingModule,
|
||||
AutoCompleteModule,
|
||||
TextFilterModule,
|
||||
UserSettingsModule
|
||||
UserSettingsModule,
|
||||
UserInviteToTenantDialogModule
|
||||
]
|
||||
})
|
||||
export class UsersModule { }
|
||||
|
|
|
@ -6,6 +6,7 @@ import { CommonFormsModule } from '@common/forms/common-forms.module';
|
|||
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||
import { MergeEmailConfirmation } from './merge-email-confirmation/merge-email-confirmation.component';
|
||||
import { UnlinkEmailConfirmation } from './unlink-email-confirmation/unlink-email-confirmation.component';
|
||||
import { UserInviteConfirmation } from './user-invite-confirmation/user-invite-confirmation.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -16,7 +17,8 @@ import { UnlinkEmailConfirmation } from './unlink-email-confirmation/unlink-emai
|
|||
declarations: [
|
||||
LoginComponent,
|
||||
MergeEmailConfirmation,
|
||||
UnlinkEmailConfirmation
|
||||
UnlinkEmailConfirmation,
|
||||
UserInviteConfirmation
|
||||
],
|
||||
exports: [
|
||||
LoginComponent
|
||||
|
|
|
@ -4,6 +4,7 @@ import { LoginComponent } from './login.component';
|
|||
import { MergeEmailConfirmation } from './merge-email-confirmation/merge-email-confirmation.component';
|
||||
import { UnlinkEmailConfirmation } from './unlink-email-confirmation/unlink-email-confirmation.component';
|
||||
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';
|
||||
|
||||
const routes: Routes = [
|
||||
|
@ -18,6 +19,8 @@ const routes: Routes = [
|
|||
// component: PostLoginComponent
|
||||
// },
|
||||
{ path: 'unlink/confirmation/:token', component: UnlinkEmailConfirmation },
|
||||
{ path: 'invitation/confirmation/:token', component: UserInviteConfirmation },
|
||||
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
|
@ -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')]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1768,7 +1768,8 @@
|
|||
"ACTIONS": {
|
||||
"EDIT": "Edit",
|
||||
"SAVE": "Save",
|
||||
"EXPORT": "Export users"
|
||||
"EXPORT": "Export users",
|
||||
"INVITE": "Invite users"
|
||||
}
|
||||
},
|
||||
"REFERENCE-FIELD-COMPONENT": {
|
||||
|
@ -2100,6 +2101,22 @@
|
|||
"USER-PROFILE-SETTINGS": "Nire Profilaren Ezarpenak...",
|
||||
"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": {
|
||||
"MERGING-EMAILS-DIALOG": {
|
||||
"TITLE": "Lotutako kontua egiaztatu",
|
||||
|
|
|
@ -1768,7 +1768,8 @@
|
|||
"ACTIONS": {
|
||||
"EDIT": "Edit",
|
||||
"SAVE": "Save",
|
||||
"EXPORT": "Export users"
|
||||
"EXPORT": "Export users",
|
||||
"INVITE": "Invite users"
|
||||
}
|
||||
},
|
||||
"REFERENCE-FIELD-COMPONENT": {
|
||||
|
@ -2100,6 +2101,22 @@
|
|||
"USER-PROFILE-SETTINGS": "My Profile Settings...",
|
||||
"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": {
|
||||
"MERGING-EMAILS-DIALOG": {
|
||||
"TITLE": "Verify linked account",
|
||||
|
|
|
@ -1768,7 +1768,8 @@
|
|||
"ACTIONS": {
|
||||
"EDIT": "Edit",
|
||||
"SAVE": "Save",
|
||||
"EXPORT": "Export users"
|
||||
"EXPORT": "Export users",
|
||||
"INVITE": "Invite users"
|
||||
}
|
||||
},
|
||||
"REFERENCE-FIELD-COMPONENT": {
|
||||
|
@ -2100,6 +2101,22 @@
|
|||
"USER-PROFILE-SETTINGS": "My 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": {
|
||||
"MERGING-EMAILS-DIALOG": {
|
||||
"TITLE": "Verify linked account",
|
||||
|
|
|
@ -1768,7 +1768,8 @@
|
|||
"ACTIONS": {
|
||||
"EDIT": "Edit",
|
||||
"SAVE": "Save",
|
||||
"EXPORT": "Export users"
|
||||
"EXPORT": "Export users",
|
||||
"INVITE": "Invite users"
|
||||
}
|
||||
},
|
||||
"REFERENCE-FIELD-COMPONENT": {
|
||||
|
@ -2100,6 +2101,22 @@
|
|||
"USER-PROFILE-SETTINGS": "Configuración de mi perfil...",
|
||||
"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": {
|
||||
"MERGING-EMAILS-DIALOG": {
|
||||
"TITLE": "Verify linked account",
|
||||
|
|
|
@ -1768,7 +1768,8 @@
|
|||
"ACTIONS": {
|
||||
"EDIT": "Edit",
|
||||
"SAVE": "Save",
|
||||
"EXPORT": "Export users"
|
||||
"EXPORT": "Export users",
|
||||
"INVITE": "Invite users"
|
||||
}
|
||||
},
|
||||
"REFERENCE-FIELD-COMPONENT": {
|
||||
|
@ -2100,6 +2101,22 @@
|
|||
"USER-PROFILE-SETTINGS": "Οι ρυθμίσεις του προφίλ μου...",
|
||||
"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": {
|
||||
"MERGING-EMAILS-DIALOG": {
|
||||
"TITLE": "Verify linked account",
|
||||
|
|
|
@ -1768,7 +1768,8 @@
|
|||
"ACTIONS": {
|
||||
"EDIT": "Edit",
|
||||
"SAVE": "Save",
|
||||
"EXPORT": "Export users"
|
||||
"EXPORT": "Export users",
|
||||
"INVITE": "Invite users"
|
||||
}
|
||||
},
|
||||
"REFERENCE-FIELD-COMPONENT": {
|
||||
|
@ -2100,6 +2101,22 @@
|
|||
"USER-PROFILE-SETTINGS": "Postavke profila...",
|
||||
"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": {
|
||||
"MERGING-EMAILS-DIALOG": {
|
||||
"TITLE": "Potvrdi povezani korisnički račun",
|
||||
|
|
|
@ -1768,7 +1768,8 @@
|
|||
"ACTIONS": {
|
||||
"EDIT": "Edit",
|
||||
"SAVE": "Save",
|
||||
"EXPORT": "Export users"
|
||||
"EXPORT": "Export users",
|
||||
"INVITE": "Invite users"
|
||||
}
|
||||
},
|
||||
"REFERENCE-FIELD-COMPONENT": {
|
||||
|
@ -2100,6 +2101,22 @@
|
|||
"USER-PROFILE-SETTINGS": "Ustawienia mojego Profilu...",
|
||||
"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": {
|
||||
"MERGING-EMAILS-DIALOG": {
|
||||
"TITLE": "Zweryfikuj połączone konto",
|
||||
|
|
|
@ -1768,7 +1768,8 @@
|
|||
"ACTIONS": {
|
||||
"EDIT": "Edit",
|
||||
"SAVE": "Save",
|
||||
"EXPORT": "Export users"
|
||||
"EXPORT": "Export users",
|
||||
"INVITE": "Invite users"
|
||||
}
|
||||
},
|
||||
"REFERENCE-FIELD-COMPONENT": {
|
||||
|
@ -2100,6 +2101,22 @@
|
|||
"USER-PROFILE-SETTINGS": "Definições do Meu Perfil...",
|
||||
"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": {
|
||||
"MERGING-EMAILS-DIALOG": {
|
||||
"TITLE": "Verify linked account",
|
||||
|
|
|
@ -1768,7 +1768,8 @@
|
|||
"ACTIONS": {
|
||||
"EDIT": "Edit",
|
||||
"SAVE": "Save",
|
||||
"EXPORT": "Export users"
|
||||
"EXPORT": "Export users",
|
||||
"INVITE": "Invite users"
|
||||
}
|
||||
},
|
||||
"REFERENCE-FIELD-COMPONENT": {
|
||||
|
@ -2100,6 +2101,22 @@
|
|||
"USER-PROFILE-SETTINGS": "Nastavenie profilu...",
|
||||
"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": {
|
||||
"MERGING-EMAILS-DIALOG": {
|
||||
"TITLE": "Verify linked account",
|
||||
|
|
|
@ -1768,7 +1768,8 @@
|
|||
"ACTIONS": {
|
||||
"EDIT": "Edit",
|
||||
"SAVE": "Save",
|
||||
"EXPORT": "Export users"
|
||||
"EXPORT": "Export users",
|
||||
"INVITE": "Invite users"
|
||||
}
|
||||
},
|
||||
"REFERENCE-FIELD-COMPONENT": {
|
||||
|
@ -2100,6 +2101,22 @@
|
|||
"USER-PROFILE-SETTINGS": "Podešavanja mog profila...",
|
||||
"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": {
|
||||
"MERGING-EMAILS-DIALOG": {
|
||||
"TITLE": "Verify linked account",
|
||||
|
|
|
@ -1768,7 +1768,8 @@
|
|||
"ACTIONS": {
|
||||
"EDIT": "Edit",
|
||||
"SAVE": "Save",
|
||||
"EXPORT": "Export users"
|
||||
"EXPORT": "Export users",
|
||||
"INVITE": "Invite users"
|
||||
}
|
||||
},
|
||||
"REFERENCE-FIELD-COMPONENT": {
|
||||
|
@ -2100,6 +2101,22 @@
|
|||
"USER-PROFILE-SETTINGS": "Profil Ayarlarım...",
|
||||
"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": {
|
||||
"MERGING-EMAILS-DIALOG": {
|
||||
"TITLE": "Verify linked account",
|
||||
|
|
Loading…
Reference in New Issue