Fix an undefined error in searchInput of Input component. Add multi emails in invitation of manager and members and optimize subscriber invite.

This commit is contained in:
Konstantinos Triantafyllou 2023-06-26 15:09:37 +03:00
parent 7d2eb68914
commit 0face4b57f
4 changed files with 104 additions and 38 deletions

View File

@ -95,13 +95,16 @@
</div> </div>
</div> </div>
<modal-alert #inviteModal (alertOutput)="invite()" [overflowBody]="false" classTitle="uk-background-primary uk-light" <modal-alert #inviteModal (alertOutput)="invite()" [overflowBody]="false" classTitle="uk-background-primary uk-light"
[okDisabled]="invited && invited.invalid"> [okDisabled]="!valid">
<div *ngIf="message" class="uk-margin-medium-bottom uk-text-small"> <div *ngIf="message" class="uk-margin-medium-bottom uk-text-small">
<div [innerHTML]="message | safeHtml"></div> <div [innerHTML]="message | safeHtml"></div>
</div> </div>
<div *ngIf="invited"> <div *ngIf="emailsForm">
<div input [formInput]="invited" <div #emailInput input [formInput]="emailsForm" type="chips"
placeholder="Email"></div> placeholder="Recipients" hint="Add a recipient" [visibleChips]="3"
[addExtraChips]="true" [validators]="validators" [separators]="[',']">
<div note>Separate emails with commas</div>
</div>
</div> </div>
</modal-alert> </modal-alert>
<modal-alert #deleteModal [overflowBody]="false" (alertOutput)="deleteActive()" classTitle="uk-background-primary uk-light"> <modal-alert #deleteModal [overflowBody]="false" (alertOutput)="deleteActive()" classTitle="uk-background-primary uk-light">

View File

@ -1,5 +1,21 @@
import {Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild} from '@angular/core'; import {
import {UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators} from '@angular/forms'; ChangeDetectorRef,
Component,
Input,
OnChanges,
OnDestroy,
OnInit,
SimpleChanges,
ViewChild
} from '@angular/core';
import {
UntypedFormArray,
UntypedFormBuilder,
UntypedFormControl,
UntypedFormGroup,
ValidatorFn,
Validators
} from '@angular/forms';
import {AlertModal} from "../../../utils/modal/alert"; import {AlertModal} from "../../../utils/modal/alert";
import {UserRegistryService} from "../../../services/user-registry.service"; import {UserRegistryService} from "../../../services/user-registry.service";
import {EnvProperties} from "../../../utils/properties/env-properties"; import {EnvProperties} from "../../../utils/properties/env-properties";
@ -9,9 +25,22 @@ import {UserManagementService} from "../../../services/user-management.service";
import {Router} from "@angular/router"; import {Router} from "@angular/router";
import {StringUtils} from "../../../utils/string-utils.class"; import {StringUtils} from "../../../utils/string-utils.class";
import {NotificationService} from "../../../notifications/notification.service"; import {NotificationService} from "../../../notifications/notification.service";
import {Subscription} from "rxjs"; import {forkJoin, of, Subscription} from "rxjs";
import {NotificationHandler} from "../../../utils/notification-handler"; import {NotificationHandler} from "../../../utils/notification-handler";
import {ClearCacheService} from "../../../services/clear-cache.service"; import {ClearCacheService} from "../../../services/clear-cache.service";
import {catchError, map, tap} from "rxjs/operators";
import notification = CKEDITOR.plugins.notification;
import {InputComponent} from "../../../sharedComponents/input/input.component";
class InvitationResponse {
email: string;
invitation: any;
constructor(email, invitation) {
this.email = email;
this.invitation = invitation;
}
}
@Component({ @Component({
selector: 'role-users', selector: 'role-users',
@ -49,7 +78,8 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
public loadActive: boolean = true; public loadActive: boolean = true;
public loadPending: boolean = true; public loadPending: boolean = true;
public selectedUser: string = null; public selectedUser: string = null;
public invited: UntypedFormControl; public emailsForm: UntypedFormArray;
public validators: ValidatorFn[] = [Validators.email];
public properties: EnvProperties = properties; public properties: EnvProperties = properties;
public exists: boolean = true; public exists: boolean = true;
public roleFb: UntypedFormGroup; public roleFb: UntypedFormGroup;
@ -60,6 +90,7 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
/** Search */ /** Search */
filterForm: UntypedFormGroup; filterForm: UntypedFormGroup;
@ViewChild('inviteModal') inviteModal: AlertModal; @ViewChild('inviteModal') inviteModal: AlertModal;
@ViewChild('emailInput') emailInput: InputComponent;
@ViewChild('deleteModal') deleteModal: AlertModal; @ViewChild('deleteModal') deleteModal: AlertModal;
@ViewChild('deletePendingModal') deletePendingModal: AlertModal; @ViewChild('deletePendingModal') deletePendingModal: AlertModal;
@ViewChild('createRoleModal') createRoleModal: AlertModal; @ViewChild('createRoleModal') createRoleModal: AlertModal;
@ -69,6 +100,7 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
private clearCacheService: ClearCacheService, private clearCacheService: ClearCacheService,
private notificationService: NotificationService, private notificationService: NotificationService,
private router: Router, private router: Router,
private cdr: ChangeDetectorRef,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder) {
} }
@ -172,7 +204,8 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
this.inviteModal.alertTitle = 'Invite ' + this.role; this.inviteModal.alertTitle = 'Invite ' + this.role;
this.inviteModal.okButtonLeft = false; this.inviteModal.okButtonLeft = false;
this.inviteModal.okButtonText = 'Send'; this.inviteModal.okButtonText = 'Send';
this.invited = this.fb.control('', [Validators.required, Validators.email]); this.emailsForm = this.fb.array([], Validators.required);
this.cdr.detectChanges();
this.inviteModal.open(); this.inviteModal.open();
} }
@ -225,32 +258,55 @@ export class RoleUsersComponent implements OnInit, OnDestroy, OnChanges {
})); }));
} }
get valid() {
return this.emailsForm && this.emailsForm.valid || (this.emailInput && this.emailInput.searchControl.getRawValue() && this.emailInput.searchControl.valid);
}
invite() { invite() {
if(this.emailInput.searchControl.getRawValue() && this.emailInput.searchControl.valid) {
this.emailInput.add(null,true);
}
this.showCurrent = false; this.showCurrent = false;
this.loadPending = true; this.loadPending = true;
this.selectedUser = this.invited.value; let current = null;
let details = { let invitations = (<Array<any>>this.emailsForm.value).map(email => {
current = email;
return this.userRegistryService.invite(this.type, this.id, {
link: this.link, link: this.link,
email: this.emailComposer(this.name, this.invited.value, this.role) email: this.emailComposer(this.name, email, this.role)
} }, this.role).pipe(map(invitation => new InvitationResponse(email, invitation), catchError(error => {
this.subs.push(this.userRegistryService.invite(this.type, this.id, details, this.role).subscribe(invitation => { return of(new InvitationResponse(current, null));
if (!this.pending.includes(this.invited.value)) { })));
this.pending.push(this.invited.value); });
this.pendingPage = Math.ceil(this.pending.length / this.pageSize); this.subs.push(forkJoin(invitations).subscribe(responses => {
this.filterPendingBySearch(this.filterForm.value.pending); let notifications = responses.map(response => {
if(response.invitation) {
NotificationHandler.rise(StringUtils.capitalize(this.role) + ' invitation to ' + response.email + ' has been <b>sent</b>');
if (!this.pending.includes(response.email)) {
this.pending.push(response.email);
} }
if(this.notificationFn) { if(this.notificationFn) {
this.subs.push(this.notificationService.sendNotification(this.notificationFn(this.name, this.invited.value, this.role, invitation)).subscribe(notification => { return this.notificationService.sendNotification(this.notificationFn(this.name, response.email, this.role, response.invitation)).pipe(map(notification => {
NotificationHandler.rise('A notification has been <b>sent</b> successfully'); return notification;
}, error => { }), tap(() => {
NotificationHandler.rise('An error has occurred. Please try again later', 'danger'); NotificationHandler.rise('A notification has been <b>sent</b> successfully to ' + response.email);
}), catchError( error => {
NotificationHandler.rise('An error has occurred while sending a notification to ' + response.email + '. Please try again later', 'danger');
return of(null);
})); }));
} else {
return of(null);
} }
NotificationHandler.rise(StringUtils.capitalize(this.role) + ' invitation to ' + this.selectedUser + ' has been <b>sent</b>'); } else {
this.loadPending = false; NotificationHandler.rise('An error has occurred while sending the invitation mail to ' + response.email + '.Please try again later', 'danger');
}, error => { return of(null);
NotificationHandler.rise('An error has occurred. Please try again later', 'danger'); }
});
this.subs.push(forkJoin(notifications).subscribe(() => {
this.pendingPage = Math.ceil(this.pending.length / this.pageSize);
this.filterPendingBySearch(this.filterForm.value.pending);
this.loadPending = false; this.loadPending = false;
}))
})); }));
} }

View File

@ -405,7 +405,7 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
} }
click(event: ClickEvent) { click(event: ClickEvent) {
this.focus(!event.clicked, event); this.focus(!event.clicked);
} }
ngOnInit() { ngOnInit() {
@ -532,8 +532,10 @@ export class InputComponent implements OnInit, OnDestroy, AfterViewInit, OnChang
if (this.focused) { if (this.focused) {
this.open(true); this.open(true);
setTimeout(() => { setTimeout(() => {
if(this.searchInput) {
this.searchInput.nativeElement.focus(); this.searchInput.nativeElement.focus();
this.searchInput.nativeElement.value = value; this.searchInput.nativeElement.value = value;
}
}, 0); }, 0);
} }
})); }));

View File

@ -1,4 +1,4 @@
import {Component, Input, OnDestroy, OnInit} from "@angular/core"; import {Component, Input, OnDestroy, OnInit, ViewChild} from "@angular/core";
import {AbstractControl, FormBuilder, FormGroup, ValidationErrors, ValidatorFn, Validators} from "@angular/forms"; import {AbstractControl, FormBuilder, FormGroup, ValidationErrors, ValidatorFn, Validators} from "@angular/forms";
import {Subscriber} from "rxjs"; import {Subscriber} from "rxjs";
import {StringUtils} from "../../utils/string-utils.class"; import {StringUtils} from "../../utils/string-utils.class";
@ -11,13 +11,14 @@ import {EmailService} from "../../utils/email/email.service";
import {properties} from "../../../../environments/environment"; import {properties} from "../../../../environments/environment";
import {CommunityInfo} from "../../connect/community/communityInfo"; import {CommunityInfo} from "../../connect/community/communityInfo";
import {NotificationHandler} from "../../utils/notification-handler"; import {NotificationHandler} from "../../utils/notification-handler";
import {InputComponent} from "../input/input.component";
@Component({ @Component({
selector: 'subscriber-invite', selector: 'subscriber-invite',
template: ` template: `
<div *ngIf="body" class="uk-grid uk-child-width-1-1" uk-grid [formGroup]="inviteForm"> <div *ngIf="body" class="uk-grid uk-child-width-1-1" uk-grid [formGroup]="inviteForm">
<div input [formInput]="inviteForm.get('name')" placeholder="From"></div> <div input [formInput]="inviteForm.get('name')" placeholder="From"></div>
<div input [formInput]="inviteForm.get('recipients')" type="chips" <div #emailInput input [formInput]="inviteForm.get('recipients')" type="chips"
placeholder="Recipients" hint="Add a recipient" [visibleChips]="3" placeholder="Recipients" hint="Add a recipient" [visibleChips]="3"
[addExtraChips]="true" [validators]="validators" [separators]="[',']"> [addExtraChips]="true" [validators]="validators" [separators]="[',']">
<div note>Separate emails with commas</div> <div note>Separate emails with commas</div>
@ -61,6 +62,7 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
public validators: ValidatorFn[] = [Validators.email]; public validators: ValidatorFn[] = [Validators.email];
public loading: boolean = true; public loading: boolean = true;
private subscriptions: any[] = []; private subscriptions: any[] = [];
@ViewChild('emailInput') emailInput: InputComponent;
constructor(private fb: FormBuilder, constructor(private fb: FormBuilder,
private emailService: EmailService, private emailService: EmailService,
@ -105,6 +107,9 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
} }
invite() { invite() {
if(this.emailInput.searchControl.getRawValue() && this.emailInput.searchControl.valid) {
this.emailInput.add(null,true);
}
this.loading = true; this.loading = true;
this.body.paragraphs = this.inviteForm.getRawValue().message; this.body.paragraphs = this.inviteForm.getRawValue().message;
this.email.body = Composer.formatEmailBodyForInvitation(this.body); this.email.body = Composer.formatEmailBodyForInvitation(this.body);
@ -129,6 +134,6 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
} }
get valid() { get valid() {
return !this.loading && this.inviteForm && this.inviteForm.valid; return !this.loading && this.inviteForm && this.inviteForm.valid || (this.emailInput && this.emailInput.searchControl.getRawValue() && this.emailInput.searchControl.valid);
} }
} }