diff --git a/dmp-frontend/src/app/core/core-service.module.ts b/dmp-frontend/src/app/core/core-service.module.ts index 42148c2f3..15bde822f 100644 --- a/dmp-frontend/src/app/core/core-service.module.ts +++ b/dmp-frontend/src/app/core/core-service.module.ts @@ -36,6 +36,7 @@ import { QuickWizardService } from './services/quick-wizard/quick-wizard.service import { OrganisationService } from './services/organisation/organisation.service'; import { EmailConfirmationService } from './services/email-confirmation/email-confirmation.service'; import { FunderService } from './services/funder/funder.service'; +import { ContactSupportService } from './services/contact-support/contact-support.service'; // // // This is shared module that provides all the services. Its imported only once on the AppModule. @@ -91,7 +92,8 @@ export class CoreServiceModule { DatasetExternalAutocompleteService, QuickWizardService, OrganisationService, - EmailConfirmationService + EmailConfirmationService, + ContactSupportService ], }; } diff --git a/dmp-frontend/src/app/core/model/contact/contact-email-form-model.ts b/dmp-frontend/src/app/core/model/contact/contact-email-form-model.ts new file mode 100644 index 000000000..51cd07722 --- /dev/null +++ b/dmp-frontend/src/app/core/model/contact/contact-email-form-model.ts @@ -0,0 +1,27 @@ +import { ValidationErrorModel } from "../../../common/forms/validation/error-model/validation-error-model"; +import { FormGroup, FormBuilder, Validators } from "@angular/forms"; + +export interface ContactEmail { + subject: string; + description: string; +} + +export class ContactEmailFormModel { + subject: string; + description: string; + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel(); + + fromModel(item: ContactEmail): ContactEmailFormModel { + this.subject = item.subject; + this.description = item.description; + return this; + } + + buildForm(): FormGroup { + const formGroup = new FormBuilder().group({ + subject: [ this.subject, [Validators.required] ], + description: [ this.description, [Validators.required] ] + }); + return formGroup; + } +} diff --git a/dmp-frontend/src/app/core/services/contact-support/contact-support.service.ts b/dmp-frontend/src/app/core/services/contact-support/contact-support.service.ts new file mode 100644 index 000000000..1c1c3af08 --- /dev/null +++ b/dmp-frontend/src/app/core/services/contact-support/contact-support.service.ts @@ -0,0 +1,20 @@ +import { BaseHttpService } from "../../../core/services/http/base-http.service"; +import { HttpHeaders } from "@angular/common/http"; +import { environment } from "../../../../environments/environment"; +import { Injectable } from "@angular/core"; +import { ContactEmail } from "../../model/contact/contact-email-form-model"; +import { Observable } from "rxjs"; + +@Injectable() +export class ContactSupportService { + + private actionUrl: string; + + constructor(private http: BaseHttpService) { + this.actionUrl = environment.Server + 'contactEmail/'; + } + + postEmail(contentEmail: ContactEmail): Observable { + return this.http.post(this.actionUrl, contentEmail); + } +} diff --git a/dmp-frontend/src/app/library/contact-dialog/contact-dialog.component.html b/dmp-frontend/src/app/library/contact-dialog/contact-dialog.component.html new file mode 100644 index 000000000..5bd3b5757 --- /dev/null +++ b/dmp-frontend/src/app/library/contact-dialog/contact-dialog.component.html @@ -0,0 +1,33 @@ +
+
+ {{'CONTACT.SUPPORT.SUBTITLE' | translate}} +
+
+ close +
+
+ +
+
+ + + + + {{data.get('subject').getError('backendError').message}} + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + + + + + {{data.get('description').getError('backendError').message}} + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+
+
+
diff --git a/dmp-frontend/src/app/library/contact-dialog/contact-dialog.component.scss b/dmp-frontend/src/app/library/contact-dialog/contact-dialog.component.scss new file mode 100644 index 000000000..e2869c014 --- /dev/null +++ b/dmp-frontend/src/app/library/contact-dialog/contact-dialog.component.scss @@ -0,0 +1,13 @@ +.form { + min-width: 150px; + max-width: 500px; + width: 100%; +} + +.full-width { + width: 100%; +} + +.close-btn { + cursor: pointer; +} diff --git a/dmp-frontend/src/app/library/contact-dialog/contact-dialog.component.ts b/dmp-frontend/src/app/library/contact-dialog/contact-dialog.component.ts new file mode 100644 index 000000000..c886ad48f --- /dev/null +++ b/dmp-frontend/src/app/library/contact-dialog/contact-dialog.component.ts @@ -0,0 +1,28 @@ +import { Component, Inject } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { FormGroup } from '@angular/forms'; + +@Component({ + selector: 'app-contact-dialog', + templateUrl: './contact-dialog.component.html', + styleUrls: ['./contact-dialog.component.scss'] +}) +export class ContactDialogComponent { + + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: FormGroup + ) { } + + cancel() { + this.dialogRef.close(); + } + + send() { + this.dialogRef.close(this.data); + } + + close() { + this.dialogRef.close(false); + } +} diff --git a/dmp-frontend/src/app/library/contact-dialog/contact-dialog.module.ts b/dmp-frontend/src/app/library/contact-dialog/contact-dialog.module.ts new file mode 100644 index 000000000..a0fe4ea9f --- /dev/null +++ b/dmp-frontend/src/app/library/contact-dialog/contact-dialog.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { CommonUiModule } from '../../common/ui/common-ui.module'; +import { ContactDialogComponent } from './contact-dialog.component'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; + +@NgModule({ + imports: [CommonUiModule, FormsModule, ReactiveFormsModule], + declarations: [ContactDialogComponent], + exports: [ContactDialogComponent], + entryComponents: [ContactDialogComponent] +}) +export class ContactDialogModule { + constructor() { } +} diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/dataset-profile.module.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/dataset-profile.module.ts index 7c30e7dd9..c4fdba9cc 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/dataset-profile.module.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/dataset-profile.module.ts @@ -34,7 +34,7 @@ import { DatasetProfileEditorDmpsAutoCompleteFieldComponent } from './editor/com CommonFormsModule, FormattingModule, DatasetProfileRoutingModule, - ConfirmationDialogModule, + ConfirmationDialogModule ], declarations: [ DatasetProfileListingComponent, diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.ts index 0cece1565..645bbf351 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.ts @@ -47,8 +47,6 @@ export class DatasetProfileCriteriaComponent extends BaseCriteriaComponent imple } } - - openDialog(): void { const dialogRef = this.dialog.open(DialodConfirmationUploadDatasetProfiles, { data: { diff --git a/dmp-frontend/src/app/ui/sidebar/sidebar-footer/sidebar-footer.component.css b/dmp-frontend/src/app/ui/sidebar/sidebar-footer/sidebar-footer.component.css index dc4fe822d..7f8f7396d 100644 --- a/dmp-frontend/src/app/ui/sidebar/sidebar-footer/sidebar-footer.component.css +++ b/dmp-frontend/src/app/ui/sidebar/sidebar-footer/sidebar-footer.component.css @@ -1,28 +1,26 @@ .sidebar-footer { - padding: white; - color: rgb(117, 117, 117); - /* background-color: #ffffff; */ - /* box-shadow: 0 4px 20px 0px rgba(0, 0, 0, 0.14), 0 7px 10px -5px rgba(255, 255, 255, 0.4); */ + padding: white; + color: rgb(117, 117, 117); + /* background-color: #ffffff; */ + /* box-shadow: 0 4px 20px 0px rgba(0, 0, 0, 0.14), 0 7px 10px -5px rgba(255, 255, 255, 0.4); */ } .sidebar-footer .option { - margin: 0px; - padding-top: 2px; - padding-bottom: 2px; - padding-right: 10px; - border-radius: 0px; - color: white; + margin: 0px; + padding-top: 2px; + padding-bottom: 2px; + padding-right: 10px; + border-radius: 0px; + color: white; + cursor: pointer; +} + +.sidebar-footer :hover { + color: #4687e6; } .sidebar-footer .vl { - border-right: 1px solid #d4d4d4; - color: white; + border-right: 1px solid #d4d4d4; + color: white; } -.option .style-icon { - font-size: 15px; - margin: 0px; - width: 20px; - padding-top: 2px; - color: white; -} diff --git a/dmp-frontend/src/app/ui/sidebar/sidebar-footer/sidebar-footer.component.html b/dmp-frontend/src/app/ui/sidebar/sidebar-footer/sidebar-footer.component.html index 32b91c39c..5f3c086b2 100644 --- a/dmp-frontend/src/app/ui/sidebar/sidebar-footer/sidebar-footer.component.html +++ b/dmp-frontend/src/app/ui/sidebar/sidebar-footer/sidebar-footer.component.html @@ -5,4 +5,5 @@ Guide Help --> +

{{'CONTACT.SUPPORT.TITLE' | translate}}

diff --git a/dmp-frontend/src/app/ui/sidebar/sidebar-footer/sidebar-footer.component.spec.ts b/dmp-frontend/src/app/ui/sidebar/sidebar-footer/sidebar-footer.component.spec.ts deleted file mode 100644 index 3faf72650..000000000 --- a/dmp-frontend/src/app/ui/sidebar/sidebar-footer/sidebar-footer.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { SidebarFooterComponent } from './sidebar-footer.component'; - -describe('SidebarFooterComponent', () => { - let component: SidebarFooterComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ SidebarFooterComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(SidebarFooterComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/dmp-frontend/src/app/ui/sidebar/sidebar-footer/sidebar-footer.component.ts b/dmp-frontend/src/app/ui/sidebar/sidebar-footer/sidebar-footer.component.ts index 7b94c4269..5cd6689d9 100644 --- a/dmp-frontend/src/app/ui/sidebar/sidebar-footer/sidebar-footer.component.ts +++ b/dmp-frontend/src/app/ui/sidebar/sidebar-footer/sidebar-footer.component.ts @@ -1,15 +1,94 @@ import { Component, OnInit } from '@angular/core'; +import { MatDialog } from '@angular/material'; +import { takeUntil } from 'rxjs/operators'; +import { BaseComponent } from '../../../core/common/base/base.component'; +import { ContactDialogComponent } from '../../../library/contact-dialog/contact-dialog.component'; +import { ContactSupportService } from '../../../core/services/contact-support/contact-support.service'; +import { UiNotificationService, SnackBarNotificationLevel } from '../../../core/services/notification/ui-notification-service'; +import { TranslateService } from '@ngx-translate/core'; +import { ContactEmailFormModel } from '../../../core/model/contact/contact-email-form-model'; +import { FormGroup, AbstractControl, FormControl, FormArray } from '@angular/forms'; +import { ValidationErrorModel } from '../../../common/forms/validation/error-model/validation-error-model'; @Component({ - selector: 'app-sidebar-footer', - templateUrl: './sidebar-footer.component.html', - styleUrls: ['./sidebar-footer.component.css'] + selector: 'app-sidebar-footer', + templateUrl: './sidebar-footer.component.html', + styleUrls: ['./sidebar-footer.component.css'] }) -export class SidebarFooterComponent implements OnInit { +export class SidebarFooterComponent extends BaseComponent implements OnInit { - constructor() { } + private contactEmailFormModel: ContactEmailFormModel; + private formGroup: FormGroup; - ngOnInit() { - } + constructor( + private dialog: MatDialog, + private language: TranslateService, + private contactSupportService: ContactSupportService, + private uiNotificationService: UiNotificationService + ) { + super(); + } + + ngOnInit() { + this.contactEmailFormModel = new ContactEmailFormModel(); + this.formGroup = this.contactEmailFormModel.buildForm(); + } + + openContactDialog() { + if (this.dialog.openDialogs.length > 0) { + this.dialog.closeAll(); + } + else { + const dialogRef = this.dialog.open(ContactDialogComponent, { + width: '400px', + hasBackdrop: true, + autoFocus: false, + closeOnNavigation: true, + disableClose: false, + data: this.formGroup + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(data => { + if (data) { + this.contactSupportService.postEmail(data.value) + .pipe(takeUntil(this._destroyed)) + .subscribe( + complete => this.onCallbackSuccess(), + error => this.onCallbackError(error) + ); + this.formGroup.reset(); + } + }); + } + } + + onCallbackSuccess(): void { + this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-EMAIL-SEND'), SnackBarNotificationLevel.Success); + } + + onCallbackError(errorResponse: any) { + this.setErrorModel(errorResponse.error); + this.validateAllFormFields(this.formGroup); + } + + public setErrorModel(validationErrorModel: ValidationErrorModel) { + Object.keys(validationErrorModel).forEach(item => { + (this.contactEmailFormModel.validationErrorModel)[item] = (validationErrorModel)[item]; + }); + } + + public validateAllFormFields(formControl: AbstractControl) { + if (formControl instanceof FormControl) { + formControl.updateValueAndValidity({ emitEvent: false }); + } else if (formControl instanceof FormGroup) { + Object.keys(formControl.controls).forEach(item => { + const control = formControl.get(item); + this.validateAllFormFields(control); + }); + } else if (formControl instanceof FormArray) { + formControl.controls.forEach(item => { + this.validateAllFormFields(item); + }); + } + } } diff --git a/dmp-frontend/src/app/ui/sidebar/sidebar.component.css b/dmp-frontend/src/app/ui/sidebar/sidebar.component.css index 477b2ca5d..c6ad38929 100644 --- a/dmp-frontend/src/app/ui/sidebar/sidebar.component.css +++ b/dmp-frontend/src/app/ui/sidebar/sidebar.component.css @@ -20,6 +20,8 @@ width: 230px; border-top: 1px solid #fff; border-bottom: 2px solid #d4d4d4; + margin-top: 0.5rem; + margin-bottom: 0.8rem; } .sidebar-footer { diff --git a/dmp-frontend/src/app/ui/sidebar/sidebar.component.spec.ts b/dmp-frontend/src/app/ui/sidebar/sidebar.component.spec.ts deleted file mode 100644 index f29709fd1..000000000 --- a/dmp-frontend/src/app/ui/sidebar/sidebar.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { SidebarComponent } from './sidebar.component'; - -describe('SidebarComponent', () => { - let component: SidebarComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ SidebarComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(SidebarComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/dmp-frontend/src/app/ui/sidebar/sidebar.module.ts b/dmp-frontend/src/app/ui/sidebar/sidebar.module.ts index d7f6bbabe..bb0ac7da0 100644 --- a/dmp-frontend/src/app/ui/sidebar/sidebar.module.ts +++ b/dmp-frontend/src/app/ui/sidebar/sidebar.module.ts @@ -4,11 +4,13 @@ import { CommonFormsModule } from '../../common/forms/common-forms.module'; import { CommonUiModule } from '../../common/ui/common-ui.module'; import { SidebarComponent } from './sidebar.component'; import { SidebarFooterComponent } from './sidebar-footer/sidebar-footer.component'; +import { ContactDialogModule } from '../../library/contact-dialog/contact-dialog.module'; @NgModule({ imports: [ CommonUiModule, CommonFormsModule, + ContactDialogModule, RouterModule ], declarations: [ diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index 48a2aa62a..7f6d4a192 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -16,6 +16,7 @@ "SUCCESSFUL-UPDATE": "Updated Successfully", "SUCCESSFUL-LOGIN": "Successful Login", "SUCCESSFUL-LOGOUT": "Successful Logout", + "SUCCESSFUL-EMAIL-SEND": "Email sent successfully", "UNSUCCESSFUL-LOGOUT": "Unsuccessful Logout", "UNSUCCESSFUL-LOGIN": "Unsuccessful Login", "SUCCESSFUL-DATASET-PROFILE-DELETE": "Successful Delete", @@ -863,6 +864,19 @@ "WELCOME": "Welcome to OpenDMP", "WELCOME-MESSAGE": "Create, Link, Share Data Management Plans" }, + "CONTACT": { + "SUPPORT": { + "TITLE": "Contact Support", + "SUBTITLE": "How can we help you?", + "SUBJECT": "Subject", + "DESCRIPTION": "Description", + "CANCEL": "Cancel", + "SEND": "Send" + }, + "GUIDE": "Guide", + "HELP": "Help", + "GLOSSARY": "Glossary" + }, "DASHBOARD": { "MY-GRANTS": "My Grants", "GRANTS": "Grants",