[Library | Trunk]: Fix server errors. Input add validators for chips. Subscriber-invite: Change To field to chips, add short view

git-svn-id: https://svn.driver.research-infrastructures.eu/driver/dnet40/modules/uoa-services-library/trunk/ng-openaire-library/src/app@60968 d315682c-612b-4755-9ff5-7f18f6832af3
This commit is contained in:
k.triantafyllou 2021-04-23 16:25:52 +00:00
parent 1bd9eb8ec2
commit 336f85aa97
5 changed files with 79 additions and 89 deletions

View File

@ -11,7 +11,7 @@ import {
SimpleChanges, SimpleChanges,
ViewChild ViewChild
} from "@angular/core"; } from "@angular/core";
import {AbstractControl, FormArray, FormControl} from "@angular/forms"; import {AbstractControl, FormArray, FormControl, ValidatorFn} from "@angular/forms";
import {HelperFunctions} from "../../utils/HelperFunctions.class"; import {HelperFunctions} from "../../utils/HelperFunctions.class";
import {Observable, of, Subscription} from "rxjs"; import {Observable, of, Subscription} from "rxjs";
import {MatSelect} from "@angular/material/select"; import {MatSelect} from "@angular/material/select";
@ -100,7 +100,7 @@ export interface Option {
</span> </span>
</mat-chip> </mat-chip>
<div [class.uk-hidden]="formControl.value" class="uk-width-expand uk-position-relative chip-input"> <div [class.uk-hidden]="formControl.value" class="uk-width-expand uk-position-relative chip-input">
<input #searchInput class="uk-width-1-1" [formControl]="searchControl" [matAutocomplete]="auto" <input #searchInput [formControl]="searchControl" [matAutocomplete]="auto"
[matChipInputFor]="chipList" [matAutocompleteConnectedTo]="origin"> [matChipInputFor]="chipList" [matAutocompleteConnectedTo]="origin">
<div *ngIf="placeholder && !searchInput.value" class="placeholder uk-width-1-1" <div *ngIf="placeholder && !searchInput.value" class="placeholder uk-width-1-1"
(click)="searchInput.focus()">{{placeholder}}</div> (click)="searchInput.focus()">{{placeholder}}</div>
@ -119,7 +119,7 @@ export interface Option {
<div [ngClass]="inputClass" <div [ngClass]="inputClass"
[attr.uk-tooltip]="formControl.disabled?'title: This field is not editable; pos: bottom-left':null" [attr.uk-tooltip]="formControl.disabled?'title: This field is not editable; pos: bottom-left':null"
[class.clickable]="formControl.enabled" [class.clickable]="formControl.enabled"
[class.uk-form-danger]="formControl.invalid && formControl.touched" (click)="openSelect()"> [class.uk-form-danger]="formControl.invalid && searchControl.invalid && searchControl.touched" (click)="openSelect()">
<mat-form-field class="uk-width-1-1"> <mat-form-field class="uk-width-1-1">
<mat-chip-list #chipList> <mat-chip-list #chipList>
<mat-chip *ngFor="let chip of formAsArray.controls; let i=index" [selectable]="false" <mat-chip *ngFor="let chip of formAsArray.controls; let i=index" [selectable]="false"
@ -130,7 +130,7 @@ export interface Option {
</span> </span>
</mat-chip> </mat-chip>
<div class="uk-width-expand uk-position-relative chip-input"> <div class="uk-width-expand uk-position-relative chip-input">
<input #searchInput class="uk-width-1-1" [formControl]="searchControl" [matAutocomplete]="auto" <input #searchInput style="width: calc(100% - 8px) !important;" [formControl]="searchControl" [matAutocomplete]="auto"
[matChipInputFor]="chipList" [matAutocompleteConnectedTo]="origin" [matChipInputFor]="chipList" [matAutocompleteConnectedTo]="origin"
[matChipInputAddOnBlur]="addExtraChips && searchControl.value" [matChipInputAddOnBlur]="addExtraChips && searchControl.value"
(matChipInputTokenEnd)="add($event)"> (matChipInputTokenEnd)="add($event)">
@ -181,7 +181,7 @@ export class InputComponent implements OnInit, OnDestroy, OnChanges {
/** Textarea options */ /** Textarea options */
@Input('rows') rows: number = 3; @Input('rows') rows: number = 3;
/** Select | chips available options */ /** Select | chips available options */
@Input('options') options: Option[]; @Input('options') options: Option[] = [];
@Input('hint') hint = null; @Input('hint') hint = null;
@Input('placeholder') placeholder = ''; @Input('placeholder') placeholder = '';
@Input() inputClass: string = 'input-box'; @Input() inputClass: string = 'input-box';
@ -200,6 +200,7 @@ export class InputComponent implements OnInit, OnDestroy, OnChanges {
@Input() panelWidth: number = 300; @Input() panelWidth: number = 300;
@Input() panelClass: string = null; @Input() panelClass: string = null;
@Input() showOptionsOnEmpty: boolean = true; @Input() showOptionsOnEmpty: boolean = true;
@Input() validators: ValidatorFn[];
@Output() focusEmitter: EventEmitter<boolean> = new EventEmitter<boolean>(); @Output() focusEmitter: EventEmitter<boolean> = new EventEmitter<boolean>();
/** LogoUrl information */ /** LogoUrl information */
public secure: boolean = true; public secure: boolean = true;
@ -248,11 +249,9 @@ export class InputComponent implements OnInit, OnDestroy, OnChanges {
this.secure = (!this.initValue || this.initValue.includes('https://')); this.secure = (!this.initValue || this.initValue.includes('https://'));
} }
if (this.type === 'chips' || this.type === 'autocomplete') { if (this.type === 'chips' || this.type === 'autocomplete') {
if(!this.options) { if(this.options) {
console.error('Please provide options to continue');
} else {
this.filteredOptions = of(this.options); this.filteredOptions = of(this.options);
this.searchControl = new FormControl(''); this.searchControl = new FormControl('', this.validators);
this.subscriptions.push(this.searchControl.valueChanges.subscribe(value => { this.subscriptions.push(this.searchControl.valueChanges.subscribe(value => {
setTimeout(() => { setTimeout(() => {
this.searchInput.nativeElement.focus(); this.searchInput.nativeElement.focus();
@ -333,9 +332,9 @@ export class InputComponent implements OnInit, OnDestroy, OnChanges {
} }
add(event: MatChipInputEvent) { add(event: MatChipInputEvent) {
if (this.addExtraChips && event.value) { if (this.addExtraChips && event.value && this.searchControl.valid) {
this.stopPropagation(); this.stopPropagation();
this.formAsArray.push(new FormControl(event.value)); this.formAsArray.push(new FormControl(event.value, this.validators));
this.formAsArray.markAsDirty(); this.formAsArray.markAsDirty();
this.searchControl.setValue(''); this.searchControl.setValue('');
this.searchInput.nativeElement.value = ''; this.searchInput.nativeElement.value = '';

View File

@ -1,5 +1,5 @@
import {Component, Input, OnDestroy, OnInit} from "@angular/core"; import {Component, Input, OnDestroy, OnInit} from "@angular/core";
import {AbstractControl, FormBuilder, FormGroup, ValidationErrors, 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";
import {Email} from "../../utils/email/email"; import {Email} from "../../utils/email/email";
@ -16,17 +16,16 @@ declare var UIkit;
@Component({ @Component({
selector: 'subscriber-invite', selector: 'subscriber-invite',
template: ` template: `
<div class="uk-grid uk-child-width-1-1" uk-grid [formGroup]="inviteForm"> <div *ngIf="longView" class="uk-grid uk-child-width-1-1" uk-grid [formGroup]="inviteForm">
<div dashboard-input [formInput]="inviteForm.get('name')" [gridSmall]="true"> <div dashboard-input [formInput]="inviteForm.get('name')" [gridSmall]="true">
<div class="uk-text-bold field-label"> <div class="uk-text-bold field-label">
From: From:
</div> </div>
</div> </div>
<div dashboard-input [formInput]="inviteForm.get('recipients')" [gridSmall]="true"> <div dashboard-input [formInput]="inviteForm.get('recipients')" type="chips" placeholder="Write email(s)" [addExtraChips]="true" [validators]="validators" [gridSmall]="true">
<div class="uk-text-bold field-label uk-margin-bottom"> <div class="uk-text-bold field-label">
To *: To *:
</div> </div>
<span note>Separate multiple emails with a comma</span>
</div> </div>
<div class="uk-grid uk-grid-small" uk-grid> <div class="uk-grid uk-grid-small" uk-grid>
<div class="uk-text-bold field-label"> <div class="uk-text-bold field-label">
@ -34,7 +33,7 @@ declare var UIkit;
</div> </div>
<div class="uk-width-expand"> <div class="uk-width-expand">
<ckeditor *ngIf="isManager" class="form-control" formControlName="message" id="message" <ckeditor *ngIf="isManager" class="form-control" formControlName="message" id="message"
debounce="400" debounce="400" (ready)="loading = false"
[config]="{ extraAllowedContent: '* [uk-*](*) ; span', disallowedContent: 'script; *[on*]', removeButtons: 'Save,NewPage,DocProps,Preview,Print', [config]="{ extraAllowedContent: '* [uk-*](*) ; span', disallowedContent: 'script; *[on*]', removeButtons: 'Save,NewPage,DocProps,Preview,Print',
extraPlugins: 'divarea'}"></ckeditor> extraPlugins: 'divarea'}"></ckeditor>
<div *ngIf="!isManager" [innerHTML]="body.paragraphs"></div> <div *ngIf="!isManager" [innerHTML]="body.paragraphs"></div>
@ -52,17 +51,23 @@ declare var UIkit;
</div> </div>
</div> </div>
</div> </div>
<div *ngIf="!longView">
<div dashboard-input [formInput]="inviteForm.get('recipients')" type="chips" placeholder="Write email(s)" [addExtraChips]="true" [validators]="validators" [gridSmall]="true"></div>
</div>
`, `,
styleUrls: ['subscriber-invite.component.css'] styleUrls: ['subscriber-invite.component.css']
}) })
export class SubscriberInviteComponent implements OnInit, OnDestroy { export class SubscriberInviteComponent implements OnInit, OnDestroy {
@Input() @Input()
public user: User; public user: User;
public community: CommunityInfo @Input()
public longView: boolean = true;
public community: CommunityInfo;
public inviteForm: FormGroup; public inviteForm: FormGroup;
public email: Email; public email: Email;
public body: Body; public body: Body;
public loading: boolean = false; public validators: ValidatorFn[] = [Validators.email, Validators.required];
public loading: boolean = true;
private subscriptions: any[] = []; private subscriptions: any[] = [];
constructor(private fb: FormBuilder, constructor(private fb: FormBuilder,
@ -71,6 +76,7 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
} }
ngOnInit() { ngOnInit() {
this.loading = this.longView;
this.reset(); this.reset();
} }
@ -90,7 +96,7 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
this.unsubscribe(); this.unsubscribe();
this.inviteForm = this.fb.group({ this.inviteForm = this.fb.group({
name: this.fb.control('', Validators.required), name: this.fb.control('', Validators.required),
recipients: this.fb.control('', [Validators.required, this.emailsValidator]), recipients: this.fb.array([], Validators.required),
message: this.fb.control('', Validators.required) message: this.fb.control('', Validators.required)
}); });
this.subscriptions.push(this.communityService.getCommunityAsObservable().subscribe(community => { this.subscriptions.push(this.communityService.getCommunityAsObservable().subscribe(community => {
@ -102,20 +108,16 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
this.email = Composer.initializeInvitationsEmail(community.title); this.email = Composer.initializeInvitationsEmail(community.title);
this.inviteForm.get('message').setValue(this.body.paragraphs); this.inviteForm.get('message').setValue(this.body.paragraphs);
})); }));
} if(!this.isManager) {
this.loading = false;
emailsValidator(control: AbstractControl): ValidationErrors | null {
if (control.value === '' || !control.value || StringUtils.validateEmails(control.value)) {
return null;
} }
return { emails: true };
} }
invite() { invite() {
this.loading = true; this.loading = true;
this.parseRecipients();
this.body.paragraphs = this.inviteForm.value.message; this.body.paragraphs = this.inviteForm.value.message;
this.email.body = Composer.formatEmailBodyForInvitation(this.body); this.email.body = Composer.formatEmailBodyForInvitation(this.body);
this.email.recipients = this.inviteForm.get('recipients').value;
this.subscriptions.push(this.emailService.sendEmail(properties, this.email).subscribe(res => { this.subscriptions.push(this.emailService.sendEmail(properties, this.email).subscribe(res => {
if(res['success']) { if(res['success']) {
UIkit.notification('Invitation to subscribe has been <b>sent</b>', { UIkit.notification('Invitation to subscribe has been <b>sent</b>', {
@ -130,6 +132,7 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
pos: 'bottom-right' pos: 'bottom-right'
}); });
} }
this.reset();
this.loading = false; this.loading = false;
},error => { },error => {
UIkit.notification('An error has occurred. Please try again later', { UIkit.notification('An error has occurred. Please try again later', {
@ -137,29 +140,16 @@ export class SubscriberInviteComponent implements OnInit, OnDestroy {
timeout: 6000, timeout: 6000,
pos: 'bottom-right' pos: 'bottom-right'
}); });
this.reset();
this.loading = false; this.loading = false;
})); }));
} }
public parseRecipients() {
// remove spaces
let emails = this.inviteForm.get('recipients').value.replace(/\s/g, '');
// remove commas
emails = emails.split(",");
// remove empty fields
for (let i = 0; i < emails.length; i++) {
if (!(emails[i] == "")) {
this.email.recipients.push(emails[i]);
}
}
}
get isManager() { get isManager() {
return Session.isPortalAdministrator(this.user) || Session.isCurator('community', this.user) || Session.isManager('community', this.community.communityId, this.user); return Session.isPortalAdministrator(this.user) || Session.isCurator('community', this.user) || Session.isManager('community', this.community.communityId, this.user);
} }
get valid() { get valid() {
return this.inviteForm && this.inviteForm.valid; return !this.loading && this.inviteForm && this.inviteForm.valid;
} }
} }

View File

@ -118,18 +118,12 @@ export class ConfigurationService{
filtering(page_route: string) { filtering(page_route: string) {
let community: Portal = this.communityInformation.getValue(); let community: Portal = this.communityInformation.getValue();
let pages: Page[] = <Page[]>community.pages; let pages: Page[] = <Page[]>community.pages;
pages = pages.filter(function (page: Page) {
return page.route == page_route;
});
if (pages) { if (pages) {
let result = false; let page = pages.find((page: Page) => page.route == page_route);
if (pages['length'] > 0 && pages[0].route == page_route) { return page && page.isEnabled;
result = pages[0].isEnabled; } else {
} return false;
return result;
} }
} }

View File

@ -138,3 +138,8 @@ export const reset = {
name: 'reset', name: 'reset',
data: '<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><polyline fill="#000" points="1 2 2 2 2 6 6 6 6 7 1 7 1 2"></polyline><path fill="none" stroke="#000" stroke-width="1.1" d="M2.1,6.548 C3.391,3.29 6.746,1 10.5,1 C15.5,1 19.5,5 19.5,10 C19.5,15 15.5,19 10.5,19 C5.5,19 1.5,15 1.5,10"></path></svg>' data: '<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><polyline fill="#000" points="1 2 2 2 2 6 6 6 6 7 1 7 1 2"></polyline><path fill="none" stroke="#000" stroke-width="1.1" d="M2.1,6.548 C3.391,3.29 6.746,1 10.5,1 C15.5,1 19.5,5 19.5,10 C19.5,15 15.5,19 10.5,19 C5.5,19 1.5,15 1.5,10"></path></svg>'
} }
export const send = {
name: 'send',
data: '<svg xmlns="http://www.w3.org/2000/svg" height="20" viewBox="0 0 24 24" width="20" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>'
}

View File

@ -11,46 +11,48 @@ export class SmoothScroll {
private lastRoute; private lastRoute;
constructor(private router: Router) { constructor(private router: Router) {
this.sub = router.events.subscribe(event => { if(typeof window !== "undefined") {
if (event instanceof NavigationEnd) { this.sub = router.events.subscribe(event => {
if (this.interval) { if (event instanceof NavigationEnd) {
clearInterval(this.interval); if (this.interval) {
} clearInterval(this.interval);
const fragment = router.parseUrl(router.url).fragment; }
if (this.lastRoute !== this.getUrl(event.url)) { const fragment = router.parseUrl(router.url).fragment;
window.scrollTo({top: 0}); if (this.lastRoute !== this.getUrl(event.url)) {
} window.scrollTo({top: 0});
if (fragment) { }
let i = 0; if (fragment) {
this.interval = setInterval(() => { let i = 0;
i++; this.interval = setInterval(() => {
const element = document.getElementById(fragment); i++;
if (element) { const element = document.getElementById(fragment);
if (this.interval) { if (element) {
if (this.interval) {
clearInterval(this.interval);
}
const yOffset = -100;
let position = 0;
let interval = setInterval(() => {
if (position !== element.getBoundingClientRect().top) {
position = element.getBoundingClientRect().top;
} else {
clearInterval(interval);
const y = element.getBoundingClientRect().top + window.pageYOffset + yOffset;
window.scrollTo({top: y, behavior: 'smooth'});
}
}, 50);
}
if (i > 4 && this.interval) {
clearInterval(this.interval); clearInterval(this.interval);
} }
const yOffset = -100; }, 100);
let position = 0; } else {
let interval = setInterval(() => { window.scrollTo({top: 0, behavior: 'smooth'});
if (position !== element.getBoundingClientRect().top) { }
position = element.getBoundingClientRect().top; this.lastRoute = this.getUrl(event.url);
} else {
clearInterval(interval);
const y = element.getBoundingClientRect().top + window.pageYOffset + yOffset;
window.scrollTo({top: y, behavior: 'smooth'});
}
}, 50);
}
if (i > 4 && this.interval) {
clearInterval(this.interval);
}
}, 100);
} else {
window.scrollTo({top: 0, behavior: 'smooth'});
} }
this.lastRoute = this.getUrl(event.url); });
} }
});
} }
private getUrl(url: string) { private getUrl(url: string) {