connect-admin/src/app/pages/users/personal-info/personal-info.component.ts

480 lines
20 KiB
TypeScript
Raw Normal View History

import {Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
import {Title} from '@angular/platform-browser';
import {AlertModal} from "../../../openaireLibrary/utils/modal/alert";
import {CuratorService} from "../../../openaireLibrary/connect/curators/curator.service";
import {UtilitiesService} from "../../../openaireLibrary/services/utilities.service";
import {UserManagementService} from "../../../openaireLibrary/services/user-management.service";
import {FormArray, FormBuilder, FormGroup, Validators} from "@angular/forms";
import {Subscriber} from "rxjs";
import {EnvProperties} from "../../../openaireLibrary/utils/properties/env-properties";
import {properties} from "../../../../environments/environment";
import {User} from "../../../openaireLibrary/login/utils/helper.class";
import {Affiliation, Curator} from "../../../openaireLibrary/utils/entities/CuratorInfo";
import {HelpContentService} from "../../../services/help-content.service";
import {Page} from "../../../domain/page";
import {CommunityService} from "../../../openaireLibrary/connect/community/community.service";
import {StringUtils} from "../../../openaireLibrary/utils/string-utils.class";
declare var UIkit;
@Component({
selector: 'personal-info',
template: `
<div page-content>
<div header>
<users-tabs tab="personal"></users-tabs>
</div>
<div inner>
<div class="uk-card-header">
<div class="uk-flex uk-child-width-1-1 uk-child-width-1-2@m uk-grid" uk-grid>
<div>
<div class="uk-text-small title">
Personal Info & Affiliations
<span *ngIf="!loading && (curatorFb && curatorFb.dirty && !newCurator)"> (unsaved changes)</span>
</div>
</div>
<div class="uk-text-right@m uk-text-center">
<button class="uk-button uk-button-secondary outlined uk-margin-right"
(click)="reset()"
[disabled]="loading || !hasChanged">Reset
</button>
<button class="uk-button uk-button-secondary"
(click)="updateCurator()"
[disabled]="loading || !hasChanged || curatorFb.invalid">Save
</button>
</div>
</div>
</div>
<div class="uk-card uk-card-default uk-position-relative" style="min-height: 60vh">
<div [class.hidden]="loading" style="max-height: 60vh" class="uk-overflow-auto uk-padding-remove-bottom uk-padding-large">
<form *ngIf="curatorFb" [formGroup]="curatorFb">
<div class="uk-grid uk-margin-large-bottom" uk-grid>
<div class="uk-grid uk-width-1-1 uk-flex-middle" uk-grid>
<div>
<div class="image">
<img [src]="photo"/>
<input #fileInput id="photo" type="file" class="uk-hidden" (change)="fileChangeEvent($event)"/>
<icon class="uk-text-secondary clickable" name="photo" ratio="1.5"
(click)="$event.stopPropagation();uploadPhoto(fileInput);$event.preventDefault()"></icon>
<div *ngIf="curator.photo || file" #element
uk-dropdown="mode: click; pos: bottom-left; delay-hide: 0; flip: false">
<ul class="uk-nav uk-dropdown-nav">
<li><a (click)="fileInput.click();hide(element)">Upload a new photo</a></li>
<li><a (click)="removePhoto();hide(element)">Remove this photo</a></li>
</ul>
</div>
</div>
</div>
<div class="uk-width-expand">
<h5 class="uk-text-secondary uk-text-bold">{{user.firstname + ' ' + user.lastname}}</h5>
<!--<div>
<span class="connected">Connected with: </span>
<span class="uk-text-secondary uk-text-bold">{{user.email}}</span>
</div>-->
</div>
</div>
<div dashboard-input class="uk-width-1-1" label="Display Name" placeholder="Write your name"
[formInput]="curatorFb.get('name')"></div>
<div dashboard-input class="uk-width-1-1" label="Biography" placeholder="Write biography..."
[formInput]="curatorFb.get('bio')" type="textarea" rows="4"></div>
<div class="uk-width-1-1">
<h5 class="uk-margin-large uk-text-bold">My Affiliations</h5>
<div class="uk-flex uk-flex-center">
<a (click)="editAffiliationOpen()" class="uk-flex uk-flex-middle uk-text-uppercase">
<button class="large uk-icon-button uk-button-secondary">
<icon name="add"></icon>
</button>
<button class="uk-button uk-button-link uk-margin-small-left uk-text-secondary">
Add New Affiliation
</button>
</a>
</div>
<div class="uk-margin-medium">
<div *ngFor="let affiliation of affiliations.controls; let i=index"
class="uk-card uk-card-default uk-card-body uk-text-small uk-margin-bottom">
<div class="uk-grid uk-grid-divider uk-flex-middle" uk-grid>
<div class="uk-width-expand@m uk-width-1-1 uk-grid uk-flex-middle" uk-grid>
<div class="uk-width-small">
<img [src]="affiliation.value.logo_url | urlPrefix">
</div>
<div class="uk-width-auto">
<h6>{{affiliation.value.name}}</h6>
URL: <a [href]="affiliation.value.website_url | urlPrefix" target="_blank">{{affiliation.value.website_url}}</a>
</div>
</div>
<div class="uk-width-auto@m uk-width-1-1">
<div class="uk-width-1-1 uk-flex uk-flex-center">
<div class="uk-padding-small uk-padding-remove-horizontal">
<a (click)="editAffiliationOpen(i)" class="uk-button action uk-flex uk-flex-middle">
<icon name="edit" ratio="0.7"></icon>
<span class="uk-margin-small-left">Edit Affiliation</span>
</a>
<a (click)="deleteAffiliationOpen(i)" class="uk-button action uk-flex uk-flex-middle uk-margin-small-top">
<icon name="remove" ratio="0.7"></icon>
<span class="uk-margin-small-left">Delete Affiliation</span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="uk-width-1-1 uk-text-small">
Your personal info will be visible in the Curators page of your Community Gateway.
Read <a (click)="privacy()">privacy policy statement</a>.
</div>
</div>
</form>
</div>
<div *ngIf="loading" class="uk-position-center">
<loading></loading>
</div>
</div>
</div>
</div>
<modal-alert #privacyStatement (alertOutput)="privacyStatement.cancel()">
<div class="uk-text-small">
Your personal data and photo are processed by OpenAIRE in conformity with personal data protection legal
framework.
They will be stored safely in our system for as long as OpenAIRE exists. Since you press the "save" button,
you give us the consent to make them public in your Community Gateway to let users know who is
configuring the platform. You always have the right to exercise your rights and ask for access,
rectification, erasure and restriction of your data. Please contact <a href="mailto:rcd@openaire.eu">rcd@openaire.eu</a>
if you have any inquiries.
</div>
</modal-alert>
<modal-alert #affiliationModal [okDisabled]="affiliationFb && affiliationFb.invalid" (alertOutput)="editAffiliation()">
<form *ngIf="affiliationFb" [formGroup]="affiliationFb">
<div class="uk-grid uk-padding uk-padding-remove-horizontal uk-child-width-1-1" uk-grid>
<div dashboard-input label="Name" placeholder="Write affiliation's name" [formInput]="affiliationFb.get('name')"></div>
<div dashboard-input label="Logo URL" type="logoURL" placeholder="Write your affiliation's logo URL" [formInput]="affiliationFb.get('logo_url')"></div>
<div dashboard-input label="Website URL" type="URL" placeholder="Write your affiliation's website URL" [formInput]="affiliationFb.get('website_url')"></div>
</div>
</form>
</modal-alert>
<modal-alert #removeAffiliationModal (alertOutput)="removeAffiliation()">
</modal-alert>
<modal-alert #enableCuratorsModal (alertOutput)="enableCurators()">
<div class="uk-padding uk-padding-remove-horizontal">
Your personal information has been successfully saved.<br><br>
This information will be visible in <span class="uk-text-bold">Curators page</span> of Research Community Dashboard, which is <span class="uk-text-bold">disabled</span>.
Do you want to <span class="uk-text-bold">enable</span> it now?
</div>
</modal-alert>
`,
styleUrls: ['personal-info.component.css']
})
export class PersonalInfoComponent implements OnInit, OnDestroy {
/** Curator information */
public loading = false;
public user: User;
public curator: Curator;
public curatorFb: FormGroup;
public properties: EnvProperties = properties;
public curatorsPage: Page;
public newCurator = false;
public communityId: string;
/** Photo */
public photo: any = null;
private photoChanged: boolean = false;
public file: File = null;
private maxsize: number = 200 * 1024;
private deletePhoto = false;
private subs: any[] = [];
/** Affiliations */
public affiliationFb: FormGroup;
public index: number = -1;
@ViewChild('fileInput') fileInput: ElementRef;
@ViewChild('privacyStatement') privacyStatement: AlertModal;
@ViewChild('affiliationModal') affiliationModal: AlertModal;
@ViewChild('removeAffiliationModal') removeAffiliationModal: AlertModal;
@ViewChild('enableCuratorsModal') enableCuratorsModal: AlertModal;
constructor(private route: ActivatedRoute,
private title: Title,
private fb: FormBuilder,
private curatorService: CuratorService,
private utilitiesService: UtilitiesService,
private helpContentService: HelpContentService,
private communityService: CommunityService,
private userManagementService: UserManagementService) {
}
ngOnInit() {
this.subs.push(this.communityService.getCommunityAsObservable().subscribe(community => {
this.communityId = community.communityId;
this.subs.push(this.userManagementService.getUserInfo().subscribe(user => {
this.user = user;
if (this.user) {
this.title.setTitle(community.communityId.toUpperCase() + " | Personal Info");
this.loading = true;
this.subs.push(this.curatorService.getCurator(properties).subscribe(curator => {
this.initCurator(curator);
this.reset();
this.loading = false;
}, error => {
if (error.status === 404) {
this.initCurator(null);
this.reset();
} else {
console.error(error);
}
this.loading = false;
}));
}
}));
}));
}
ngOnDestroy() {
this.subs.forEach(subscription => {
if (subscription instanceof Subscriber) {
subscription.unsubscribe();
}
})
}
hide(element: any) {
UIkit.dropdown(element).hide();
}
initCurator(curator: Curator) {
if (curator) {
this.curator = curator;
this.curator.email = this.user.email;
} else {
this.newCurator = true;
this.curator = new Curator();
this.curator._id = this.user.id;
this.curator.email = this.user.email;
this.curator.name = this.user.fullname;
this.curator.affiliations = [];
this.curator.bio = '';
this.curator.photo = null;
}
this.curatorsPageStatus();
}
reset() {
this.photoChanged = false;
this.file = null;
if (this.fileInput) {
this.fileInput.nativeElement.value = null;
}
let affiliations: FormArray = this.fb.array([]);
this.curator.affiliations.forEach(affiliation => {
affiliations.push(this.fb.group({
id: this.fb.control(affiliation.id),
name: this.fb.control(affiliation.name, Validators.required),
logo_url: this.fb.control(affiliation.logo_url, [Validators.required, StringUtils.urlValidator()]),
website_url: this.fb.control(affiliation.website_url, [Validators.required, StringUtils.urlValidator()]),
}));
});
this.curatorFb = this.fb.group({
_id: this.fb.control(this.curator._id),
name: this.fb.control(this.curator.name, Validators.required),
bio: this.fb.control(this.curator.bio),
email: this.fb.control(this.curator.email),
photo: this.fb.control(this.curator.photo),
affiliations: affiliations
});
if (this.curator.photo) {
this.photo = this.properties.utilsService + '/download/' + this.curator.photo;
} else {
this.photo = 'assets/common-assets/curator-default.png';
}
}
get affiliations(): FormArray {
return this.curatorFb.get('affiliations') as FormArray;
}
saveCurator() {
this.curatorService.updateCurator(this.properties, this.curatorFb.value).subscribe((curator) => {
if (curator) {
UIkit.notification('Your data has been <b>saved successfully</b>', {
status: 'success',
timeout: 6000,
pos: 'bottom-right'
});
this.newCurator = false;
this.deletePhoto = false;
this.initCurator(curator);
this.reset();
if(!this.curatorsEnabled) {
this.curatorsEnabledOpen();
}
this.loading = false;
}
},
error => {
this.handleUpdateError('An error has occurred. Try again later!');
this.reset();
});
}
updateCurator() {
if (this.curatorFb.valid) {
this.loading = true;
if (this.file) {
this.utilitiesService.uploadPhoto(this.properties.utilsService + '/upload/' + this.curator._id, this.file).subscribe((res) => {
if (this.curator.photo) {
this.utilitiesService.deletePhoto(this.properties.utilsService + '/delete/' + this.curator.photo).subscribe();
}
this.curatorFb.get('photo').setValue(res.filename);
this.saveCurator();
}, error => {
this.handleUpdateError('An error has occurred during photo uploading.');
}
);
} else {
if (this.deletePhoto && this.curator.photo) {
this.utilitiesService.deletePhoto(this.properties.utilsService + '/delete/' + this.curator.photo).subscribe();
this.curatorFb.get('photo').setValue(null);
}
this.saveCurator();
}
}
}
private curatorsPageStatus() {
this.helpContentService.getCommunityPagesByRoute(this.communityId, '/curators', this.properties.adminToolsAPIURL).subscribe((page) => {
this.curatorsPage = page;
});
}
public get curatorsEnabled(): boolean {
return !this.curatorsPage || this.curatorsPage.isEnabled;
}
fileChangeEvent(event) {
this.loading = true;
if (event.target.files && event.target.files[0]) {
this.file = event.target.files[0];
if (this.file.type !== 'image/png' && this.file.type !== 'image/jpeg') {
this.handleUpdateError('You must choose a file with type: image/png or image/jpeg!');
this.file = null;
} else if (this.file.size > this.maxsize) {
this.handleUpdateError('File exceeds size\'s limit! Maximum size 200KB.');
this.file = null;
} else {
const reader = new FileReader();
reader.readAsDataURL(this.file);
reader.onload = () => {
this.photo = reader.result;
this.photoChanged = true;
this.loading = false;
};
}
} else {
this.loading = false;
}
}
removePhoto() {
this.deletePhoto = true;
this.file = null;
this.fileInput.nativeElement.value = null;
this.photoChanged = !!this.curator.photo;
this.photo = 'assets/common-assets/curator-default.png';
}
handleUpdateError(message: string) {
UIkit.notification(message, {
status: 'danger',
timeout: 6000,
pos: 'bottom-right'
});
this.loading = false;
}
privacy() {
this.privacyStatement.cancelButton = false;
this.privacyStatement.okButtonText = 'Close';
this.privacyStatement.alertTitle = 'Privacy policy statement';
this.privacyStatement.open();
}
uploadPhoto(fileInput: HTMLInputElement) {
if (!this.curator.photo && !this.file) {
fileInput.click();
}
}
get hasChanged(): boolean {
return (this.curatorFb && this.curatorFb.dirty) || this.newCurator || this.photoChanged;
}
editAffiliationOpen(index = -1) {
this.index = index;
let affiliation: Affiliation = new Affiliation();
if(index === -1) {
this.affiliationModal.alertTitle = 'Add Affiliation';
this.affiliationModal.okButtonText = 'Add';
} else {
this.affiliationModal.alertTitle = 'Edit Affiliation';
this.affiliationModal.okButtonText = 'Update';
affiliation = this.affiliations.at(index).value;
}
this.affiliationFb = this.fb.group({
id: this.fb.control(affiliation.id),
name: this.fb.control(affiliation.name, Validators.required),
logo_url: this.fb.control(affiliation.logo_url, [Validators.required, StringUtils.urlValidator()]),
website_url: this.fb.control(affiliation.website_url, [Validators.required, StringUtils.urlValidator()])
});
this.affiliationModal.okButtonLeft = false;
this.affiliationModal.cancelButtonText = 'Cancel';
this.affiliationModal.open();
}
deleteAffiliationOpen(index: number) {
this.index = index;
let affiliation: Affiliation = this.affiliations.at(index).value;
this.removeAffiliationModal.alertTitle = 'Delete Affiliation';
this.removeAffiliationModal.message = 'Do you want to remove <b>' +
affiliation.name + '</b> from your Affiliations?';
this.removeAffiliationModal.okButtonText = 'Yes';
this.removeAffiliationModal.cancelButtonText = 'No';
this.removeAffiliationModal.open();
}
editAffiliation() {
if(this.index === -1) {
this.affiliations.push(this.affiliationFb);
} else {
this.affiliations.at(this.index).setValue(this.affiliationFb.value);
}
this.curatorFb.markAsDirty();
}
removeAffiliation() {
this.affiliations.removeAt(this.index);
this.curatorFb.markAsDirty();
}
private curatorsEnabledOpen() {
this.enableCuratorsModal.okButtonLeft = false;
this.enableCuratorsModal.alertTitle = 'Enable Curators Page';
this.enableCuratorsModal.okButtonText = 'Yes';
this.enableCuratorsModal.cancelButtonText = 'No';
this.enableCuratorsModal.open();
}
enableCurators() {
this.helpContentService.togglePages(this.communityId, [this.curatorsPage._id], true, this.properties.adminToolsAPIURL).subscribe(() => {
this.curatorsPage.isEnabled = true;
UIkit.notification('Curators Page has been <b>enabled successfully</b>', {
status: 'success',
timeout: 6000,
pos: 'bottom-right'
});
},error => {
this.handleUpdateError('An error has occurred. Try again later!');
});
}
}