Adds a Support section to side menu that opens a dialog where the user will provide a "Subject" and a "Description" to be sent (Issue #128)

This commit is contained in:
apapachristou 2019-08-23 10:04:20 +03:00
parent 5cd5b96fb3
commit d635032224
17 changed files with 261 additions and 80 deletions

View File

@ -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
],
};
}

View File

@ -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;
}
}

View File

@ -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<ContactEmail> {
return this.http.post<ContactEmail>(this.actionUrl, contentEmail);
}
}

View File

@ -0,0 +1,33 @@
<div class="row d-flex flex-row">
<div mat-dialog-title class="col-auto">
{{'CONTACT.SUPPORT.SUBTITLE' | translate}}
</div>
<div class="col-auto ml-auto close-btn" (click)="close()">
<mat-icon>close</mat-icon>
</div>
</div>
<div mat-dialog-content class="row">
<form class="form" [formGroup]="data">
<mat-form-field class="full-width">
<!-- <input matInput placeholder="{{'CONTACT.SUPPORT.SUBJECT' | translate}}" [(ngModel)]="data.subject" name="contactSupportSubject" required> -->
<input matInput placeholder="{{'CONTACT.SUPPORT.SUBJECT' | translate}}" type="text" name="subject" formControlName="subject" required>
<mat-error *ngIf="data.get('subject').hasError('backendError')">
{{data.get('subject').getError('backendError').message}}</mat-error>
<mat-error *ngIf="data.get('subject').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<mat-form-field class="full-width">
<textarea matInput placeholder="{{'CONTACT.SUPPORT.DESCRIPTION' | translate}}" type="text" name="description" formControlName="description" required></textarea>
<mat-error *ngIf="data.get('description').hasError('backendError')">
{{data.get('description').getError('backendError').message}}</mat-error>
<mat-error *ngIf="data.get('description').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</form>
</div>
<div mat-dialog-actions class="row">
<div class="ml-auto col-auto"><button mat-button mat-dialog-close mat-raised-button color="primary" (click)="cancel()">{{'CONTACT.SUPPORT.CANCEL' | translate}}</button></div>
<div class="col-auto"><button mat-raised-button color="primary" type="button" [disabled]="!data.valid" (click)="send()"><i class="fa fa-paper-plane pr-2"></i>{{'CONTACT.SUPPORT.SEND' | translate}}</button></div>
</div>

View File

@ -0,0 +1,13 @@
.form {
min-width: 150px;
max-width: 500px;
width: 100%;
}
.full-width {
width: 100%;
}
.close-btn {
cursor: pointer;
}

View File

@ -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<ContactDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: FormGroup
) { }
cancel() {
this.dialogRef.close();
}
send() {
this.dialogRef.close(this.data);
}
close() {
this.dialogRef.close(false);
}
}

View File

@ -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() { }
}

View File

@ -34,7 +34,7 @@ import { DatasetProfileEditorDmpsAutoCompleteFieldComponent } from './editor/com
CommonFormsModule,
FormattingModule,
DatasetProfileRoutingModule,
ConfirmationDialogModule,
ConfirmationDialogModule
],
declarations: [
DatasetProfileListingComponent,

View File

@ -47,8 +47,6 @@ export class DatasetProfileCriteriaComponent extends BaseCriteriaComponent imple
}
}
openDialog(): void {
const dialogRef = this.dialog.open(DialodConfirmationUploadDatasetProfiles, {
data: {

View File

@ -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;
}

View File

@ -5,4 +5,5 @@
</a>
<a class="option vl" href="#"><i class="fa fa-book style-icon"></i>Guide</a>
<a class="option" href="#"><i class="fa fa-life-ring style-icon"></i>Help</a> -->
<p class="option" (click)="openContactDialog()"><i class="fa fa-question-circle pr-2"></i>{{'CONTACT.SUPPORT.TITLE' | translate}}</p>
</div>

View File

@ -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<SidebarFooterComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SidebarFooterComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SidebarFooterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -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 => {
(<any>this.contactEmailFormModel.validationErrorModel)[item] = (<any>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);
});
}
}
}

View File

@ -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 {

View File

@ -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<SidebarComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SidebarComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SidebarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -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: [

View File

@ -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",