[Explore & Library | explore-redesign]: Updated component for suggest SDGs or FoS.

1. alert.ts: Added previous button option.
2. composer.ts: Set feedback values in body | Make method "composeEmailForSdgsSuggestion()" generic to send feedback for either SDGs or FoS. - TODO: Rename method.
3. sdg-selection.component: Added some checks | Added method "getSelectedSubjects()".
4. sdg-fos-suggest.component: Updated component for suggestion | Send suggested subjects to composer | Add modal here | Make file generic for FoS or SDGs suggestion.
5. resultLanding.component: Updated how <sdg-fos-suggest> is called | Moved SDGs / FoS suggest modal inside <sdg-fos-suggest>.
This commit is contained in:
Konstantina Galouni 2023-02-18 10:52:24 +02:00
parent 79bcf402d6
commit 1ca647f56e
8 changed files with 196 additions and 83 deletions

View File

@ -1,5 +1,4 @@
import {HttpClient} from "@angular/common/http"; import {ChangeDetectorRef, Component, Input, ViewChild} from "@angular/core";
import {Component, Input, ViewChild} from "@angular/core";
import {FormBuilder, UntypedFormGroup, Validators} from "@angular/forms"; import {FormBuilder, UntypedFormGroup, Validators} from "@angular/forms";
import {SdgSelectionComponent} from "../../../sdg/sdg-selection/sdg-selection.component"; import {SdgSelectionComponent} from "../../../sdg/sdg-selection/sdg-selection.component";
import {properties} from "../../../../../environments/environment"; import {properties} from "../../../../../environments/environment";
@ -7,53 +6,67 @@ import {EnvProperties} from "../../../utils/properties/env-properties";
import {EmailService} from "../../../utils/email/email.service"; import {EmailService} from "../../../utils/email/email.service";
import {Subscription} from "rxjs"; import {Subscription} from "rxjs";
import {Composer} from "../../../utils/email/composer"; import {Composer} from "../../../utils/email/composer";
import {AlertModal} from "../../../utils/modal/alert";
import {StringUtils} from "../../../utils/string-utils.class";
@Component({ @Component({
selector: 'sdg-fos-suggest', selector: 'sdg-fos-suggest',
template: ` template: `
<sdg-selection #sdgSelection [class.uk-hidden]="!sdgSelectionStep1" [subjects]="subjects"></sdg-selection> <modal-alert #selectionModal [large]="true" (alertOutput)="modalOutput()" (cancelOutput)="modalCancel()"
<div [class.uk-hidden]="sdgSelectionStep1"> [okDisabled]="!sent && !selectionStep1 && (form.invalid || sending)">
<div class="uk-flex uk-flex-column uk-flex-middle"> <sdg-selection *ngIf="subjectType == 'sdg'" #selection [class.uk-hidden]="!selectionStep1"
<div>Thank you for your feedback.</div> [subjects]="subjects"></sdg-selection>
<div>Before sending us your options, would you like to leave us your e-mail to notify you about the reporting status?</div> <!-- <fos-selection *ngIf="subjectType == 'fos'" #selection [class.uk-hidden]="!selectionStep1"-->
<div input class="uk-width-1-2 uk-margin-medium-top uk-margin-medium-bottom" <!-- [subjects]="subjects"></fos-selection>-->
[formInput]="form.get('email')" placeholder="E-mail"> <div [class.uk-hidden]="selectionStep1">
</div> <div class="uk-flex uk-flex-column uk-flex-middle">
<div> <ng-container *ngIf="!sent && !error">
<re-captcha (resolved)="handleRecaptcha($event)" [siteKey]="properties.reCaptchaSiteKey"> <div>Thank you for your feedback.</div>
</re-captcha> <div>Before sending us your options, would you like to leave us your e-mail to notify you about the reporting status?</div>
</div> <div input class="uk-width-1-2 uk-margin-medium-top uk-margin-medium-bottom"
</div> [formInput]="form.get('email')" placeholder="E-mail">
</div> </div>
` <div>
<re-captcha (resolved)="handleRecaptcha($event)" [siteKey]="properties.reCaptchaSiteKey">
</re-captcha>
</div>
</ng-container>
<ng-container *ngIf="sent">
<p>Your feedback is successfully received and it will soon be reviewed by our graph experts!</p>
<icon customClass="uk-text-background" name="check" [ratio]="4"></icon>
</ng-container>
<div *ngIf="error" class="uk-alert uk-alert-danger uk-text-center uk-width-large ng-star-inserted"
role="alert">Email sent failed! Please try again.
</div>
</div>
</div>
</modal-alert>
`
}) })
export class SdgFosSuggestComponent { export class SdgFosSuggestComponent {
@Input("subjects") subjects; @Input() entityType: string;
@Input() title; @Input() title;
public subjectType: "fos" | "sdg" = "sdg";
public subjects;
public properties: EnvProperties = properties; public properties: EnvProperties = properties;
public sdgSelectionStep1: boolean = true; public selectionStep1: boolean = true;
@ViewChild("sdgSelection") sdgSelection: SdgSelectionComponent; @ViewChild("selectionModal") selectionModal: AlertModal;
@ViewChild("selection") selection: SdgSelectionComponent;// | FosSelectionComponent;
public form: UntypedFormGroup; public form: UntypedFormGroup;
public url: string = null;
public sending: boolean = false;
public sent: boolean = false;
public error: boolean = false;
subscriptions: Subscription[] = []; subscriptions: Subscription[] = [];
constructor( constructor(private emailService: EmailService, private fb: FormBuilder, private cdr: ChangeDetectorRef) {}
private emailService: EmailService,
private fb: FormBuilder
) {}
ngOnInit() { ngOnInit() {
let url;
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
url = window.location.href; this.url = window.location.href;
} }
this.form = this.fb.group({ this.init();
name: this.fb.control(this.title),
url: this.fb.control(url),
email: this.fb.control('', Validators.email),
sdgs: this.fb.array([], Validators.required),
recaptcha: this.fb.control('', Validators.required),
});
} }
ngOnDestroy() { ngOnDestroy() {
@ -64,16 +77,52 @@ export class SdgFosSuggestComponent {
}); });
} }
public sdgModalOutput() { init() {
if(this.sdgSelectionStep1) { this.form = this.fb.group({
this.sdgSelectionStep1 = false; name: this.fb.control(this.title),
} else { url: this.fb.control(this.url),
console.log(this.sdgSelection.sdgs.filter(element => element.checked == true)); email: this.fb.control('', Validators.email),
console.log(this.form.get('email').value); subjects: this.fb.control([]),
recaptcha: this.fb.control('', Validators.required),
});
}
public openSelectionModal() {
this.sent = false;
this.sending = false;
this.error = false;
this.selectionStep1 = true;
this.init();
this.selectionModal.cancelButton = false;
if(this.subjectType == "sdg") {
this.selectionModal.alertTitle = "Please select SDGs that are the most relevant for this "+this.getEntityName(this.entityType)+".";
} else {
this.selectionModal.alertTitle = "Please select Fields of Science that are the most relevant for this "+this.getEntityName(this.entityType)+".";
}
this.selectionModal.okButtonText = "Next";
this.selectionModal.stayOpen = true;
this.cdr.detectChanges();
this.selectionModal.open();
}
public modalOutput() {
this.selectionModal.previousButton = true;
this.selectionModal.okButtonText = "Send feedback";
if(this.selectionStep1) {
this.selectionStep1 = false;
if(this.subjectType == "sdg") {
this.selectionModal.alertTitle = "Please send your feedback on most relevant SDGs for this "+this.getEntityName(this.entityType)+".";
} else {
this.selectionModal.alertTitle = "Please send your feedback on most relevant Fields of Science for this "+this.getEntityName(this.entityType)+".";
}
} else {
// email functionality // email functionality
this.form.get("subjects").setValue(this.selection.getSelectedSubjects().map(subject => subject.id));
this.subscriptions.push(this.emailService.contact(this.properties, this.subscriptions.push(this.emailService.contact(this.properties,
Composer.composeEmailForSdgsSuggestion(this.form.value, [this.properties.feedbackmail]), this.form.get('recaptcha').value).subscribe(sent => { Composer.composeEmailForSdgsSuggestion(this.form.value, [this.properties.feedbackmail], this.subjectType), this.form.get('recaptcha').value).subscribe(sent => {
// this.error = !sent; this.error = !sent;
if (sent) { if (sent) {
if (this.form.get('email').value !== '') { if (this.form.get('email').value !== '') {
this.subscriptions.push(this.emailService.contact(this.properties, this.subscriptions.push(this.emailService.contact(this.properties,
@ -83,19 +132,41 @@ export class SdgFosSuggestComponent {
} }
})); }));
} }
// this.init(); this.init();
// this.sent = true; this.sent = true;
this.selectionModal.alertTitle = "Thank you for your feedback";
this.selectionModal.okButtonText = "OK";
this.selectionModal.previousButton = false;
this.selectionModal.stayOpen = false;
} }
// this.sending = false; // this.sending = false;
}, error => { }, error => {
console.log(error); console.log(error);
// this.error = true; this.error = true;
// this.sending = false; this.sending = false;
})); }));
} }
} }
public modalCancel() {
if(this.subjectType == "sdg") {
this.selectionModal.alertTitle = "Please select SDGs that are the most relevant for this "+this.getEntityName(this.entityType)+".";
} else {
this.selectionModal.alertTitle = "Please select Fields of Science that are the most relevant for this "+this.getEntityName(this.entityType)+".";
}
this.selectionStep1 = true;
this.selectionModal.previousButton = false;
this.selectionModal.okButtonText = "Next";
this.selectionModal.stayOpen = true;
this.error = false;
}
public handleRecaptcha(captchaResponse: string) { public handleRecaptcha(captchaResponse: string) {
this.form.get('recaptcha').setValue(captchaResponse); this.form.get('recaptcha').setValue(captchaResponse);
} }
private getEntityName (entityType:string) {
return StringUtils.getEntityName(entityType, false);
}
} }

View File

@ -5,11 +5,13 @@ import {RecaptchaModule} from "ng-recaptcha";
import {SdgSelectionModule} from "src/app/openaireLibrary/sdg/sdg-selection/sdg-selection.module"; import {SdgSelectionModule} from "src/app/openaireLibrary/sdg/sdg-selection/sdg-selection.module";
import {InputModule} from "../../../sharedComponents/input/input.module"; import {InputModule} from "../../../sharedComponents/input/input.module";
import {SdgFosSuggestComponent} from "./sdg-fos-suggest.component"; import {SdgFosSuggestComponent} from "./sdg-fos-suggest.component";
import {AlertModalModule} from "../../../utils/modal/alertModal.module";
import {IconsModule} from "../../../utils/icons/icons.module";
@NgModule({ @NgModule({
imports: [ imports: [
CommonModule, FormsModule, InputModule, SdgSelectionModule, RecaptchaModule CommonModule, FormsModule, InputModule, SdgSelectionModule, RecaptchaModule, AlertModalModule, IconsModule
], ],
declarations: [ declarations: [
SdgFosSuggestComponent SdgFosSuggestComponent
], ],

View File

@ -794,7 +794,7 @@
<sdg [subjects]="resultLandingInfo.sdg" (viewAllClicked)="viewAll=$event" (suggestClicked)="suggestClicked($event)"></sdg> <sdg [subjects]="resultLandingInfo.sdg" (viewAllClicked)="viewAll=$event" (suggestClicked)="suggestClicked($event)"></sdg>
</div> </div>
<div *ngIf="resultLandingInfo.fos && resultLandingInfo.fos.length > 0 && (!viewAll || viewAll=='fos')"> <div *ngIf="resultLandingInfo.fos && resultLandingInfo.fos.length > 0 && (!viewAll || viewAll=='fos')">
<fos [subjects]="resultLandingInfo.fos" (viewAllClicked)="openSelectionModal($event)" (feedbackClicked)="feedbackClicked('Fields of Science and Technology (FOS)')"></fos> <fos [subjects]="resultLandingInfo.fos" (viewAllClicked)="viewAll=$event" (feedbackClicked)="suggestClicked('fos')"></fos>
</div> </div>
<!-- Funded By --> <!-- Funded By -->
<div *ngIf="resultLandingInfo.fundedByProjects && resultLandingInfo.fundedByProjects.length > 0 && (!viewAll || viewAll=='fundedBy')"> <div *ngIf="resultLandingInfo.fundedByProjects && resultLandingInfo.fundedByProjects.length > 0 && (!viewAll || viewAll=='fundedBy')">
@ -985,15 +985,17 @@
<div [innerHTML]="resultLandingInfo.description"></div> <div [innerHTML]="resultLandingInfo.description"></div>
</modal-alert> </modal-alert>
<modal-alert *ngIf="resultLandingInfo && resultLandingInfo.sdg?.length > 0" <!--<modal-alert *ngIf="resultLandingInfo && resultLandingInfo.sdg?.length > 0"-->
#sdgSelectionModal [large]="true" (alertOutput)="sdgModalOutput()"> <!-- #sdgSelectionModal [large]="true" (alertOutput)="sdgModalOutput()" (cancelOutput)="sdgModalCancel()">-->
<sdg-fos-suggest #sdgFosSuggest [subjects]="resultLandingInfo.sdg" [title]="resultLandingInfo.title"></sdg-fos-suggest> <sdg-fos-suggest *ngIf="resultLandingInfo && (resultLandingInfo.sdg?.length > 0 || resultLandingInfo.fos?.length > 0)"
</modal-alert> #sdgFosSuggest [title]="resultLandingInfo.title" [entityType]="resultLandingInfo.resultType">
</sdg-fos-suggest>
<!--</modal-alert>-->
<modal-alert *ngIf="resultLandingInfo && resultLandingInfo.fos?.length > 0" <modal-alert *ngIf="resultLandingInfo && resultLandingInfo.fos?.length > 0"
#fosSelectionModal [large]="true"> #fosSelectionModal [large]="true">
<div> <div>
<fos-selection [subjects]="resultLandingInfo.fos" [properties]="properties"></fos-selection> <!-- <fos-selection [subjects]="resultLandingInfo.fos" [properties]="properties"></fos-selection>-->
</div> </div>
</modal-alert> </modal-alert>

View File

@ -115,7 +115,7 @@ export class ResultLandingComponent {
'Publisher information', 'Funding Information', 'Publisher information', 'Funding Information',
'Persistent identifiers', 'Sustainable Development Goals (SDGs)', 'Persistent identifiers', 'Sustainable Development Goals (SDGs)',
'Fields of Science and Technology (FOS)', 'Other']; 'Fields of Science and Technology (FOS)', 'Other'];
public pidsArrayString: string = ""; public pidsArrayString: string = "";
public identifier: Identifier; public identifier: Identifier;
@ -143,8 +143,6 @@ export class ResultLandingComponent {
// public shouldSticky: boolean = true; // public shouldSticky: boolean = true;
public viewAll: string = ""; public viewAll: string = "";
@ViewChild("sdgSelectionModal") sdgSelectionModal;
@ViewChild("fosSelectionModal") fosSelectionModal;
@ViewChild("sdgFosSuggest") sdgFosSuggest: SdgFosSuggestComponent; @ViewChild("sdgFosSuggest") sdgFosSuggest: SdgFosSuggestComponent;
public noCommunities: boolean = false; public noCommunities: boolean = false;
@ -915,11 +913,15 @@ export class ResultLandingComponent {
public suggestClicked(value: string) { public suggestClicked(value: string) {
if(value == 'sdg') { if(value == 'sdg') {
this.openSdgSelectionModal(); this.sdgFosSuggest.subjects=this.resultLandingInfo.sdg;
this.sdgFosSuggest.subjectType="sdg";
} else if(value == 'fos') { } else if(value == 'fos') {
this.openFosSelectionModal(); console.log(this.resultLandingInfo.fos);
this.sdgFosSuggest.subjects=this.resultLandingInfo.fos;
this.sdgFosSuggest.subjectType="fos";
} }
} this.sdgFosSuggest.openSelectionModal();
}
public openDescriptionModal() { public openDescriptionModal() {
this.descriptionModal.alertFooter = false; this.descriptionModal.alertFooter = false;
@ -938,13 +940,13 @@ export class ResultLandingComponent {
return formatted.number + formatted.size; return formatted.number + formatted.size;
} }
private openSdgSelectionModal() { // private openSdgSelectionModal() {
this.sdgSelectionModal.cancelButton = false; // this.sdgSelectionModal.cancelButton = false;
this.sdgSelectionModal.alertTitle = "Please select SDGs that are the most relevant for this publication."; // this.sdgSelectionModal.alertTitle = "Please select SDGs that are the most relevant for this publication.";
this.sdgSelectionModal.okButtonText = "Send feedback"; // this.sdgSelectionModal.okButtonText = "Next";
this.sdgSelectionModal.stayOpen = true; // this.sdgSelectionModal.stayOpen = true;
this.sdgSelectionModal.open(); // this.sdgSelectionModal.open();
} // }
private openFosSelectionModal() { private openFosSelectionModal() {
this.fosSelectionModal.cancelButton = false; this.fosSelectionModal.cancelButton = false;

View File

@ -6,7 +6,7 @@
<div> <div>
<div *ngFor="let item of firstColumn; let i = index" <div *ngFor="let item of firstColumn; let i = index"
class="uk-margin-bottom"> class="uk-margin-bottom">
<label [class.uk-text-bolder]="subjects.includes(item.id)"> <label [class.uk-text-bolder]="subjects?.includes(item.id)">
<input [(ngModel)]="item.checked" <input [(ngModel)]="item.checked"
type="checkbox" class="uk-checkbox uk-margin-small-right"> type="checkbox" class="uk-checkbox uk-margin-small-right">
<span class="uk-text-uppercase uk-margin-xsmall-right">Goal</span> <span class="uk-text-uppercase uk-margin-xsmall-right">Goal</span>
@ -17,7 +17,7 @@
<div> <div>
<div *ngFor="let item of secondColumn; let i = index" <div *ngFor="let item of secondColumn; let i = index"
class="uk-margin-bottom"> class="uk-margin-bottom">
<label [class.uk-text-bolder]="subjects.includes(item.id)"> <label [class.uk-text-bolder]="subjects?.includes(item.id)">
<input [(ngModel)]="item.checked" <input [(ngModel)]="item.checked"
type="checkbox" class="uk-checkbox uk-margin-small-right"> type="checkbox" class="uk-checkbox uk-margin-small-right">
<span *ngIf="i !== secondColumn.length - 1" <span *ngIf="i !== secondColumn.length - 1"

View File

@ -23,7 +23,7 @@ export class SdgSelectionComponent {
this.loading = true; this.loading = true;
this.httpClient.get(this.properties.domain+'/assets/common-assets/vocabulary/sdg.json').subscribe(data => { this.httpClient.get(this.properties.domain+'/assets/common-assets/vocabulary/sdg.json').subscribe(data => {
data['sdg'].forEach(element => { data['sdg'].forEach(element => {
this.sdgs.push({code: element.code, id: element.id, label: element.label, html: element.html, checked: this.subjects.includes(element.id)}); this.sdgs.push({code: element.code, id: element.id, label: element.label, html: element.html, checked: this.subjects?.includes(element.id)});
}); });
this.sdgs.push({code: '18', id: 'No SDGs are relevant for this publication', label: 'Not relevant', html: 'Not relevant', checked: false}); this.sdgs.push({code: '18', id: 'No SDGs are relevant for this publication', label: 'Not relevant', html: 'Not relevant', checked: false});
}); });
@ -37,4 +37,8 @@ export class SdgSelectionComponent {
public get secondColumn() { public get secondColumn() {
return this.sdgs.slice(this.sdgs.length/2, this.sdgs.length); return this.sdgs.slice(this.sdgs.length/2, this.sdgs.length);
} }
public getSelectedSubjects() {
return this.sdgs.filter(sub => sub.checked == true);
}
} }

View File

@ -137,15 +137,26 @@ export class Composer {
return email; return email;
} }
public static composeEmailForSdgsSuggestion(info: {name: string, url: string, email: string, sdgs: any[]}, recipients: string[]): Email { public static composeEmailForSdgsSuggestion(info: {name: string, url: string, email: string, subjects: any[]}, recipients: string[], subjectType: "sdg" | "fos"): Email {
let typeName: string = "";
if(subjectType == "sdg") {
typeName = "Sustainable Development Goals (SDGs)";
} else {
typeName = "Fields of Science and Technology (FoS)";
}
let email: Email = new Email(); let email: Email = new Email();
email.subject = 'Feedback report for ' + info.name; email.subject = 'Feedback report for ' + info.name;
email.body = "<div style='font-size:" + this.noteBodySize + "'>" email.body = "<div style='font-size:" + this.noteBodySize + "'>"
+ "<p>A user" + ((info.email)?(" with email " + info.email):"") + " has reported the following SDG(s) for " + "<p>A user" + ((info.email)?(" with email " + info.email):"") + " has reported the following "+typeName+" for "
+ "<a href=\'" + info.url + "\'>" + info.name + "</a></p><ul>"; + "<a href=\'" + info.url + "\'>" + info.name + "</a></p><ul>";
info.sdgs.forEach((issue, index) => { if(info.subjects && info.subjects.length > 0) {
email.body += "<br><li><span><b><u>" + issue + "</u></b></span></li>"; info.subjects.forEach((subject, index) => {
}); email.body += "<br><li><span><b>" + subject + "</b></span></li>";
});
} else {
email.body += "<br><b>No "+typeName+" selected</b>";
}
email.body += "</ul></div>"; email.body += "</ul></div>";
email.recipients = recipients; email.recipients = recipients;
return email; return email;

View File

@ -27,15 +27,21 @@ declare var UIkit: any;
<span class="uk-margin-small-left">Don't show this message again</span> <span class="uk-margin-small-left">Don't show this message again</span>
</label> </label>
<div [ngClass]="(choice)?'uk-width-auto':'uk-width-1-1'"> <div [ngClass]="(choice)?'uk-width-auto':'uk-width-1-1'">
<div class="uk-flex-right uk-grid uk-grid-small" uk-grid> <div class="uk-width-1-1" [ngClass]="(previousButton && (cancelButton || okButton)) ? 'uk-flex uk-flex-between uk-grid' : ''">
<span *ngIf="okButton" [class.uk-flex-last]="!okButtonLeft"> <div *ngIf="previousButton" class="uk-flex-left">
<button class="uk-button uk-button-primary" [disabled]="okDisabled"
[class.uk-disabled]="okDisabled" (click)="ok()">{{okButtonText}}</button>
</span>
<span *ngIf="cancelButton">
<button class="uk-button uk-button-default uk-margin-small-left" <button class="uk-button uk-button-default uk-margin-small-left"
(click)="cancel()">{{cancelButtonText}}</button> (click)="previous()">{{previousButtonText}}</button>
</span> </div>
<div class="uk-flex-right uk-grid uk-grid-small" uk-grid>
<span *ngIf="okButton" [class.uk-flex-last]="!okButtonLeft">
<button class="uk-button uk-button-primary" [disabled]="okDisabled"
[class.uk-disabled]="okDisabled" (click)="ok()">{{okButtonText}}</button>
</span>
<span *ngIf="cancelButton">
<button class="uk-button uk-button-default uk-margin-small-left"
(click)="cancel()">{{cancelButtonText}}</button>
</span>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -85,6 +91,17 @@ export class AlertModal {
/** /**
* if the alertMessage is true it will show the contentString inside alert body. * if the alertMessage is true it will show the contentString inside alert body.
*/ */
/**
* Describes if the alert contains Previous Button.
* The default Previous button will emit the callback.
* Defaults to false.
*/
public previousButton: boolean = false;
/**
* Caption for the Previous button.
* Default: Previous
*/
public previousButtonText: string = 'Previous';
public alertMessage: boolean = true; public alertMessage: boolean = true;
/** /**
* Some message/content can be set in message which will be shown in alert body. * Some message/content can be set in message which will be shown in alert body.
@ -188,4 +205,8 @@ export class AlertModal {
UIkit.modal(this.element.nativeElement).hide(); UIkit.modal(this.element.nativeElement).hide();
this.cancelOutput.emit(true); this.cancelOutput.emit(true);
} }
previous() {
this.cancelOutput.emit();
}
} }