Adds funder and project fields

This commit is contained in:
apapachristou 2019-08-20 18:32:42 +03:00
parent 9f1eafa4c6
commit 3ed25bca11
22 changed files with 453 additions and 76 deletions

View File

@ -0,0 +1,4 @@
export enum ProjectType {
External = 0,
Internal = 1
}

View File

@ -26,6 +26,7 @@ import { UiNotificationService } from './services/notification/ui-notification-s
import { ProgressIndicationService } from './services/progress-indication/progress-indication-service';
import { GrantFileUploadService } from './services/grant/grant-file-upload.service';
import { GrantService } from './services/grant/grant.service';
import { ProjectService } from './services/project/project.service';
import { SearchBarService } from './services/search-bar/search-bar.service';
import { TimezoneService } from './services/timezone/timezone-service';
import { UserService } from './services/user/user.service';
@ -34,6 +35,7 @@ import { TypeUtils } from './services/utilities/type-utils.service';
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';
//
//
// This is shared module that provides all the services. Its imported only once on the AppModule.
@ -69,6 +71,8 @@ export class CoreServiceModule {
DashboardService,
LanguageResolverService,
GrantService,
ProjectService,
FunderService,
GrantFileUploadService,
DmpService,
DmpProfileService,

View File

@ -7,6 +7,8 @@ import { UserModel } from "../user/user";
import { DmpDynamicField } from "./dmp-dynamic-field";
import { UserInfoListingModel } from "../user/user-info-listing";
import { DatasetModel } from "../dataset/dataset";
import { ProjectModel } from "../project/project";
import { FunderModel } from "../funder/funder";
export interface DmpModel {
id: string;
@ -18,6 +20,8 @@ export interface DmpModel {
lockable: boolean;
description: String;
grant: GrantListingModel;
project: ProjectModel;
funder: FunderModel;
datasets: DatasetModel[];
datasetsToBeFinalized: string[];
profiles: DmpProfile[];

View File

@ -0,0 +1,12 @@
import { Status } from "../../common/enum/Status";
export class FunderModel {
id: string;
label: string;
reference: string;
definition: string;
status: Status;
created: Date;
modified: Date;
type: number;
}

View File

@ -0,0 +1,26 @@
import { Status } from "../../common/enum/Status";
import { UrlListingItem } from "../../../library/url-listing/url-listing-item";
import { ProjectType } from "../../common/enum/project-type";
export class ProjectModel {
id?: string;
label?: string;
abbreviation?: string;
reference?: string;
type?: ProjectType;
uri?: String;
status?: Status;
startDate?: Date;
endDate?: Date;
description?: String;
contentUrl?: string;
files?: ContentFile[];
dmps?: UrlListingItem[];
}
export interface ContentFile {
filename?: string;
id?: string;
location?: string;
type?: string;
}

View File

@ -0,0 +1,3 @@
import { BaseCriteria } from "../base-criteria";
export class FunderCriteria extends BaseCriteria { }

View File

@ -0,0 +1,3 @@
import { BaseCriteria } from "../base-criteria";
export class ProjectCriteria extends BaseCriteria { }

View File

@ -0,0 +1,23 @@
import { Observable } from "rxjs";
import { Injectable } from "@angular/core";
import { HttpHeaders } from "@angular/common/http";
import { RequestItem } from "../../query/request-item";
import { BaseCriteria } from "../../query/base-criteria";
import { BaseHttpService } from "../http/base-http.service";
import { environment } from '../../../../environments/environment';
import { FunderCriteria } from "../../query/funder/funder-criteria";
@Injectable()
export class FunderService {
private actionUrl: string;
private headers: HttpHeaders;
constructor(private http: BaseHttpService) {
this.actionUrl = environment.Server + 'funders/';
}
getWithExternal(requestItem: RequestItem<FunderCriteria>): Observable<any[]> {
return this.http.post<any[]>(this.actionUrl + 'external', requestItem, { headers: this.headers });
// return this.http.post<ProjectListingModel[]>(this.actionUrl + 'external', requestItem, { headers: this.headers });
}
}

View File

@ -0,0 +1,23 @@
import { Observable } from "rxjs";
import { Injectable } from "@angular/core";
import { HttpHeaders } from "@angular/common/http";
import { RequestItem } from "../../query/request-item";
import { BaseCriteria } from "../../query/base-criteria";
import { BaseHttpService } from "../http/base-http.service";
import { environment } from '../../../../environments/environment';
import { ProjectCriteria } from "../../query/project/project-criteria";
@Injectable()
export class ProjectService {
private actionUrl: string;
private headers: HttpHeaders;
constructor(private http: BaseHttpService) {
this.actionUrl = environment.Server + 'projects/';
}
getWithExternal(requestItem: RequestItem<ProjectCriteria>): Observable<any[]> {
return this.http.post<any[]>(this.actionUrl + 'external', requestItem, { headers: this.headers });
// return this.http.post<ProjectListingModel[]>(this.actionUrl + 'external', requestItem, { headers: this.headers });
}
}

View File

@ -9,8 +9,7 @@
<h4 class="card-title">{{ 'DMP-EDITOR.TITLE.NEW' | translate }}</h4>
</div>
<div class="d-flex ml-auto p-2" *ngIf="!isNew">
<button *ngIf="!isFinalized" mat-icon-button [matMenuTriggerFor]="actionsMenu" class="ml-auto more-icon"
(click)="$event.stopImmediatePropagation();">
<button *ngIf="!isFinalized" mat-icon-button [matMenuTriggerFor]="actionsMenu" class="ml-auto more-icon" (click)="$event.stopImmediatePropagation();">
<mat-icon class="more-horiz">more_horiz</mat-icon>
</button>
<mat-menu #actionsMenu="matMenu">
@ -30,8 +29,7 @@
<mat-icon>save_alt</mat-icon>{{ 'DMP-LISTING.ACTIONS.ADV-EXP' | translate }}
</button>
</mat-menu>
<button mat-raised-button color="primary" (click)="downloadPDF(dmp.id)"
class="lightblue-btn ml-2 text-uppercase">
<button mat-raised-button color="primary" (click)="downloadPDF(dmp.id)" class="lightblue-btn ml-2 text-uppercase">
<mat-icon>save_alt</mat-icon> {{ 'DMP-LISTING.ACTIONS.EXPORT' | translate }}
</button>
</div>
@ -51,7 +49,7 @@
<mat-icon class="mr-2">work_outline</mat-icon>
{{ 'DMP-LISTING.COLUMNS.GRANT' | translate }}
</ng-template>
<app-grant-tab [formGroup]="formGroup.get('grant')" [isNew]="isNew" [isFinalized]="isFinalized"></app-grant-tab>
<app-grant-tab [formGroup]="formGroup.get('grant')" [projectFormGroup]="formGroup.get('project')" [funderFormGroup]="formGroup.get('funder')" [isNew]="isNew" [isFinalized]="isFinalized"></app-grant-tab>
</mat-tab>
<mat-tab *ngIf="!isNew">
<ng-template mat-tab-label>
@ -83,28 +81,25 @@
<div *ngIf="!isFinalized" class="d-flex justify-content-end pt-2 pb-4 pl-2 pr-2">
<div *ngIf="!isNew">
<button mat-raised-button color="primary" (click)="cancel(dmp.id)" type="button"
class="text-uppercase mr-2">
<button mat-raised-button color="primary" (click)="cancel(dmp.id)" type="button" class="text-uppercase mr-2">
{{'DMP-EDITOR.ACTIONS.CANCEL' | translate}}
</button>
</div>
<div *ngIf="isNew">
<button mat-raised-button color="primary" (click)="cancel()" type="button"
class="text-uppercase mr-2">
<button mat-raised-button color="primary" (click)="cancel()" type="button" class="text-uppercase mr-2">
{{'DMP-EDITOR.ACTIONS.CANCEL' | translate}}
</button>
</div>
<div *ngIf="formGroup.enabled">
<button *ngIf="!isNew" mat-raised-button type="submit" [disabled]="!formGroup.valid" class="text-uppercase dark-theme mr-2" color="primary">
<button *ngIf="!isNew" mat-raised-button type="submit" [disabled]="!formGroup.get('grant').valid" class="text-uppercase dark-theme mr-2" color="primary">
{{'DMP-EDITOR.ACTIONS.SAVE-CHANGES' | translate}}
</button>
<button *ngIf="isNew" mat-raised-button type="button" (click)="addDataset()" [disabled]="!formGroup.valid" class="text-uppercase dark-theme mr-2" color="primary">
<button *ngIf="isNew" mat-raised-button type="button" (click)="addDataset()" [disabled]="!formGroup.get('grant').valid" class="text-uppercase dark-theme mr-2" color="primary">
{{'DMP-EDITOR.ACTIONS.SAVE' | translate}}
</button>
</div>
<div *ngIf="formGroup.enabled && !isNew">
<button type="button" mat-raised-button color="primary" [disabled]="!formGroup.valid" class="text-uppercase mr-2"
(click)="saveAndFinalize()">{{'DMP-EDITOR.ACTIONS.FINALISE' | translate}}
<button type="button" mat-raised-button color="primary" [disabled]="!formGroup.get('grant').valid" class="text-uppercase mr-2" (click)="saveAndFinalize()">{{'DMP-EDITOR.ACTIONS.FINALISE' | translate}}
</button>
</div>
</div>

View File

@ -34,6 +34,8 @@ import { AuthService } from '../../../core/services/auth/auth.service';
import { ExportMethodDialogComponent } from '../../../library/export-method-dialog/export-method-dialog.component';
import { UserInfoListingModel } from '../../../core/model/user/user-info-listing';
import { GrantTabModel } from './grant-tab/grant-tab-model';
import { ProjectFormModel } from './grant-tab/project-form-model';
import { FunderFormModel } from './grant-tab/funder-form-model';
@Component({
selector: 'app-dmp-editor-component',
@ -64,6 +66,7 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC
private dmpProfileService: DmpProfileService,
private dmpService: DmpService,
private grantService: GrantService,
// private projectService: ProjectService,
private externalSourcesService: ExternalSourcesService,
private route: ActivatedRoute,
private snackBar: MatSnackBar,
@ -84,7 +87,6 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC
.pipe(takeUntil(this._destroyed))
.subscribe((params: Params) => {
const itemId = params['id'];
const grantId = params['grantId'];
const publicId = params['publicId'];
const queryParams = this.route.snapshot.queryParams;
const tabToNav = queryParams['tab'];
@ -108,6 +110,8 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC
.subscribe(async data => {
this.dmp = new DmpEditorModel();
this.dmp.grant = new GrantTabModel();
this.dmp.project = new ProjectFormModel();
this.dmp.funder = new FunderFormModel();
this.dmp.fromModel(data);
this.formGroup = this.dmp.buildForm();
//this.registerFormEventsForDmpProfile(this.dmp.definition);
@ -135,19 +139,6 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC
this.associatedUsers = data.associatedUsers;
this.people = data.users;
});
} else if (grantId != null) {
this.isNew = true;
this.grantService.getSingle(grantId).map(data => data as GrantTabModel)
.pipe(takeUntil(this._destroyed))
.subscribe(data => {
this.dmp = new DmpEditorModel();
this.dmp.grant = new GrantTabModel();
this.dmp.grant = data;
this.formGroup = this.dmp.buildForm();
//this.registerFormEventsForDmpProfile();
this.formGroup.get('grant').disable();
this.registerFormEventsForNewItem();
});
} else if (publicId != null) {
this.isNew = false;
this.isPublic = true;
@ -156,6 +147,8 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC
.subscribe(async data => {
this.dmp = new DmpEditorModel();
this.dmp.grant = new GrantTabModel();
this.dmp.project = new ProjectFormModel();
this.dmp.funder = new FunderFormModel();
this.dmp.fromModel(data);
this.formGroup = this.dmp.buildForm();
//this.registerFormEventsForDmpProfile(this.dmp.definition);
@ -177,8 +170,11 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC
// }
});
} else {
console.log('4');
this.dmp = new DmpEditorModel();
this.dmp.grant = new GrantTabModel();
this.dmp.project = new ProjectFormModel();
this.dmp.funder = new FunderFormModel();
this.formGroup = this.dmp.buildForm();
this.registerFormEventsForNewItem();
if (this.isAuthenticated) {
@ -250,13 +246,13 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC
this.dmpService.createDmp(this.formGroup.getRawValue())
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => {
if (showAddDatasetDialog) {
this.addDatasetOpenDialog(complete);
}
else { this.onCallbackSuccess() }
},
error => this.onCallbackError(error)
complete => {
if (showAddDatasetDialog) {
this.addDatasetOpenDialog(complete);
}
else { this.onCallbackSuccess() }
},
error => this.onCallbackError(error)
);
}
@ -304,8 +300,8 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC
this.dmpService.delete(this.dmp.id)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => { this.onCallbackSuccess() },
error => this.onDeleteCallbackError(error)
complete => { this.onCallbackSuccess() },
error => this.onDeleteCallbackError(error)
);
}
});

View File

@ -19,6 +19,8 @@ import { DmpProfileExternalAutoCompleteFieldDataEditorModel } from "../../admin/
import { DmpProfileType } from "../../../core/common/enum/dmp-profile-type";
import { DmpProfileFieldDataType } from "../../../core/common/enum/dmp-profile-field-type";
import { DmpProfileField } from "../../../core/model/dmp-profile/dmp-profile-field";
import { ProjectFormModel } from "./grant-tab/project-form-model";
import { FunderFormModel } from "./grant-tab/funder-form-model";
export class DmpEditorModel {
public id: string;
@ -31,6 +33,8 @@ export class DmpEditorModel {
public status: Status = Status.Active;
public description: String;
public grant: GrantTabModel;
public project: ProjectFormModel;
public funder: FunderFormModel;
public organisations: OrganizationModel[] = [];
public researchers: ResearcherModel[] = [];
public profiles: DmpProfile[] = [];
@ -52,6 +56,8 @@ export class DmpEditorModel {
this.lockable = item.lockable;
this.description = item.description;
this.grant.fromModel(item.grant);
this.project.fromModel(item.project);
this.funder.fromModel(item.funder);
this.organisations = item.organisations;
this.researchers = item.researchers;
this.profiles = item.profiles;
@ -77,6 +83,8 @@ export class DmpEditorModel {
status: [{ value: this.status, disabled: disabled }, context.getValidation('status').validators],
description: [{ value: this.description, disabled: disabled }],
grant: this.grant.buildForm(),
project: this.project.buildForm(),
funder: this.funder.buildForm(),
organisations: [{ value: this.organisations, disabled: disabled }, context.getValidation('organisations').validators],
researchers: [{ value: this.researchers, disabled: disabled }, context.getValidation('researchers').validators],
profiles: [{ value: this.profiles, disabled: disabled }, context.getValidation('profiles').validators],
@ -112,6 +120,8 @@ export class DmpEditorModel {
baseContext.validation.push({ key: 'status', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'status')] });
baseContext.validation.push({ key: 'description', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'description')] });
baseContext.validation.push({ key: 'grant', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'grant')] });
baseContext.validation.push({ key: 'project', validators: [BackendErrorValidator(this.validationErrorModel, 'project')] });
baseContext.validation.push({ key: 'funder', validators: [BackendErrorValidator(this.validationErrorModel, 'funder')] });
baseContext.validation.push({ key: 'organisations', validators: [BackendErrorValidator(this.validationErrorModel, 'organisations')] });
baseContext.validation.push({ key: 'researchers', validators: [BackendErrorValidator(this.validationErrorModel, 'researchers')] });
baseContext.validation.push({ key: 'profiles', validators: [Validators.required, ValidJsonValidator, BackendErrorValidator(this.validationErrorModel, 'profiles')] });

View File

@ -0,0 +1,46 @@
import { Status } from "../../../../core/common/enum/Status";
import { FunderModel } from "../../../../core/model/funder/funder";
import { ValidationErrorModel } from "../../../../common/forms/validation/error-model/validation-error-model";
import { ValidationContext } from "../../../../common/forms/validation/validation-context";
import { FormGroup, FormBuilder, Validators } from "@angular/forms";
import { BackendErrorValidator } from "../../../../common/forms/validation/custom-validator";
import { ValidJsonValidator } from "../../../../library/auto-complete/auto-complete-custom-validator";
export class FunderFormModel {
public id: string;
public label?: string;
public status: Status = Status.Active;
public definition: String;
public existFunder: FunderModel;
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();
fromModel(item: FunderModel): FunderFormModel {
this.existFunder = item;
return this;
}
buildForm(context: ValidationContext = null, disabled: boolean = false): FormGroup {
if (context == null) { context = this.createValidationContext(); }
const formGroup = new FormBuilder().group({
id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators],
label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators],
status: [{ value: this.status, disabled: disabled }, context.getValidation('status').validators],
definition: [{ value: this.definition, disabled: disabled }, context.getValidation('definition').validators],
existFunder: [{ value: this.existFunder, disabled: disabled }, context.getValidation('existFunder').validators],
});
return formGroup;
}
createValidationContext(): ValidationContext {
const baseContext: ValidationContext = new ValidationContext();
baseContext.validation.push({ key: 'id', validators: [] });
baseContext.validation.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] });
baseContext.validation.push({ key: 'status', validators: [] });
baseContext.validation.push({ key: 'definition', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'definition')] });
baseContext.validation.push({ key: 'existFunder', validators: [Validators.required, ValidJsonValidator, BackendErrorValidator(this.validationErrorModel, 'existFunder')] });
return baseContext;
}
}

View File

@ -1,53 +1,122 @@
<div class="container-fluid">
<form *ngIf="formGroup" [formGroup]="formGroup">
<!-- Toggle Between Add Grant or Use Existing -->
<div class="row" *ngIf="!isFinalized">
<div class="col-12 add-grant" *ngIf="isCreateNew" (click)="create()">
<mat-icon>keyboard_backspace</mat-icon>
<span>{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.ACTIONS.EXIST' | translate}}</span>
<!-- Create New DMP Mode -->
<div class="row">
<div class="col-6 pb-2 pl-4 pt-4" *ngIf="!isCreateNewFunder">
<mat-form-field>
<app-single-auto-complete [formControl]="funderFormGroup.get('existFunder')" placeholder="{{'DMP-EDITOR.FIELDS.FUNDER' | translate}}" [configuration]="funderAutoCompleteConfiguration">
</app-single-auto-complete>
</mat-form-field>
</div>
<div class="col-12 add-grant" *ngIf="!isCreateNew" (click)="create()">
<mat-icon>add</mat-icon>
<span>{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.ACTIONS.CREATE-NEW' | translate}}</span>
<!-- Create New Funder -->
<div class="col-6 pl-4 pt-4" *ngIf="isCreateNewFunder">
<mat-form-field>
<input matInput placeholder="{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.FIRST-STEP.FIELDS.FUNDER-LABEL' | translate}}" type="text" name="label" [formControl]="funderFormGroup.get('label')" required>
<mat-error *ngIf="funderFormGroup.get('label').hasError('backendError')">
{{funderFormGroup.get('label').getError('backendError').message}}</mat-error>
<mat-error *ngIf="funderFormGroup.get('label').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<!-- Toggle Between Add Funder or Use Existing -->
<div class="col d-flex">
<div class="row" *ngIf="!isFinalized">
<div class="col-12 add-entity" *ngIf="isCreateNewFunder" (click)="createFunder()">
<mat-icon>settings_backup_restore</mat-icon>
<span>{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.ACTIONS.EXIST-FUNDER' | translate}}</span>
</div>
<div class="col-12 add-entity" *ngIf="!isCreateNewFunder" (click)="createFunder()">
<mat-icon>add</mat-icon>
<span>{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.ACTIONS.CREATE-NEW-FUNDER' | translate}}</span>
</div>
</div>
</div>
</div>
<!-- Create New DMP Mode -->
<div class="row" *ngIf="!isCreateNew">
<div class="col-6 pb-4 pl-4 pt-2">
<div class="row">
<div class="col-6 pb-2 pl-4 pt-4" *ngIf="!isCreateNew">
<mat-form-field>
<app-single-auto-complete required='true' [formControl]="formGroup.get('existGrant')"
placeholder="{{this.languageResolverService.getBy('dmpEditor') | translate}}"
[configuration]="grantAutoCompleteConfiguration">
<app-single-auto-complete required='true' [formControl]="formGroup.get('existGrant')" placeholder="{{'DMP-EDITOR.FIELDS.GRANT' | translate}}" [configuration]="grantAutoCompleteConfiguration">
</app-single-auto-complete>
</mat-form-field>
</div>
</div>
<!-- <app-dynamic-fields-grant [formGroup]="formGroup"></app-dynamic-fields-grant> -->
<!-- Create New Grant -->
<div class="row" *ngIf="isCreateNew">
<div class="col-6 pb-4 pl-4">
<mat-form-field class="col-md-12">
<input matInput
placeholder="{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.FIRST-STEP.FIELDS.LABEL' | translate}}"
type="text" name="label" [formControl]="formGroup.get('label')" required>
<!-- Create New Grant -->
<div class="col-6 pl-4 pt-4" *ngIf="isCreateNew">
<mat-form-field>
<input matInput placeholder="{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.FIRST-STEP.FIELDS.GRANT-LABEL' | translate}}" type="text" name="label" [formControl]="formGroup.get('label')" required>
<mat-error *ngIf="formGroup.get('label').hasError('backendError')">
{{formGroup.get('label').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('label').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<mat-form-field class="col-md-12">
<textarea matInput class="description-area"
placeholder="{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.FIRST-STEP.FIELDS.DESCRIPTION' | translate}}"
[formControl]="formGroup.get('description')" required></textarea>
<mat-form-field>
<textarea matInput class="description-area" placeholder="{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.FIRST-STEP.FIELDS.DESCRIPTION' | translate}}" [formControl]="formGroup.get('description')" required></textarea>
<mat-error *ngIf="formGroup.get('description').hasError('backendError')">
{{formGroup.get('description').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('description').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<!-- Toggle Between Add Grant or Use Existing -->
<div class="col d-flex">
<div class="row" *ngIf="!isFinalized">
<div class="col-12 add-entity" *ngIf="isCreateNew" (click)="create()">
<mat-icon>settings_backup_restore</mat-icon>
<span>{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.ACTIONS.EXIST' | translate}}</span>
</div>
<div class="col-12 add-entity" *ngIf="!isCreateNew" (click)="create()">
<mat-icon>add</mat-icon>
<span>{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.ACTIONS.CREATE-NEW' | translate}}</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-6 pb-2 pl-4 pt-4" *ngIf="!isCreateNewProject">
<mat-form-field>
<app-single-auto-complete [formControl]="projectFormGroup.get('existProject')" placeholder="{{'DMP-EDITOR.FIELDS.PROJECT' | translate}}" [configuration]="projectAutoCompleteConfiguration">
</app-single-auto-complete>
</mat-form-field>
</div>
<!-- Create New Project -->
<div class="col-6 pb-4 pl-4 pt-4" *ngIf="isCreateNewProject">
<mat-form-field>
<input matInput placeholder="{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.FIRST-STEP.FIELDS.PROJECT-LABEL' | translate}}" type="text" name="label" [formControl]="projectFormGroup.get('label')">
<mat-error *ngIf="projectFormGroup.get('label').hasError('backendError')">
{{formGroup.get('label').getError('backendError').message}}</mat-error>
<mat-error *ngIf="projectFormGroup.get('label').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<mat-form-field>
<textarea matInput class="description-area" placeholder="{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.FIRST-STEP.FIELDS.DESCRIPTION' | translate}}" [formControl]="projectFormGroup.get('description')"></textarea>
<mat-error *ngIf="projectFormGroup.get('description').hasError('backendError')">
{{formGroup.get('description').getError('backendError').message}}</mat-error>
<mat-error *ngIf="projectFormGroup.get('description').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<!-- Toggle Between Add Project or Use Existing -->
<div class="col d-flex">
<div class="row" *ngIf="!isFinalized">
<div class="col-12 add-entity" *ngIf="isCreateNewProject" (click)="createProject()">
<mat-icon>settings_backup_restore</mat-icon>
<span>{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.ACTIONS.EXIST-PROJECT' | translate}}</span>
</div>
<div class="col-12 add-entity" *ngIf="!isCreateNewProject" (click)="createProject()">
<mat-icon>add</mat-icon>
<span>{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.ACTIONS.CREATE-NEW-PROJECT' | translate}}</span>
</div>
</div>
</div>
</div>
<!-- <app-dynamic-fields-grant [formGroup]="formGroup"></app-dynamic-fields-grant> -->
</form>
</div>

View File

@ -1,8 +1,27 @@
.add-grant {
.add-entity {
display: flex;
justify-content: flex-end;
margin-top: 1.5em;
padding-right: 2em;
// justify-content: flex-end;
// margin-top: 1.5em;
align-self: center;
padding-bottom: 1em;
cursor: pointer;
color: #0070c0;
}
.add-entity:after {
background: none repeat scroll 0 0 transparent;
bottom: 0;
content: "";
display: block;
height: 2px;
left: 50%;
position: absolute;
background: #0070c0;
transition: width 0.3s ease 0s, left 0.3s ease 0s;
width: 0;
}
.add-entity:hover:after {
width: 100%;
left: 0;
}

View File

@ -6,6 +6,10 @@ import { GrantCriteria } from '../../../../core/query/grant/grant-criteria';
import { GrantService } from '../../../../core/services/grant/grant.service';
import { LanguageResolverService } from '../../../../services/language-resolver/language-resolver.service';
import { GrantTabModel } from './grant-tab-model';
import { ProjectService } from '../../../../core/services/project/project.service';
import { FunderService } from '../../../../core/services/funder/funder.service';
import { FunderCriteria } from '../../../../core/query/funder/funder-criteria';
import { ProjectCriteria } from '../../../../core/query/project/project-criteria';
@Component({
selector: 'app-grant-tab',
@ -15,15 +19,23 @@ import { GrantTabModel } from './grant-tab-model';
export class GrantTabComponent implements OnInit {
@Input() formGroup: FormGroup;
@Input() projectFormGroup: FormGroup;
@Input() funderFormGroup: FormGroup;
@Input() isNew: boolean;
@Input() isFinalized: boolean;
isCreateNew = false;
isCreateNewProject = false;
isCreateNewFunder = false;
grant: GrantTabModel;
grantAutoCompleteConfiguration: SingleAutoCompleteConfiguration;
projectAutoCompleteConfiguration: SingleAutoCompleteConfiguration;
funderAutoCompleteConfiguration: SingleAutoCompleteConfiguration;
constructor(
private grantService: GrantService,
private projectService: ProjectService,
private funderService: FunderService,
public languageResolverService: LanguageResolverService
) { }
@ -39,8 +51,26 @@ export class GrantTabComponent implements OnInit {
titleFn: (item) => item['label']
};
this.projectAutoCompleteConfiguration = {
filterFn: this.searchProject.bind(this),
initialItems: (extraData) => this.searchProject(''),
displayFn: (item) => item['label'],
titleFn: (item) => item['label']
}
this.funderAutoCompleteConfiguration = {
filterFn: this.searchFunder.bind(this),
initialItems: (extraData) => this.searchFunder(''),
displayFn: (item) => item['label'],
titleFn: (item) => item['label']
}
this.isCreateNew = (this.formGroup.get('label').value != null && this.formGroup.get('label').value.length > 0);
this.isCreateNewProject = (this.projectFormGroup.get('label').value != null && this.projectFormGroup.get('label').value.length > 0);
this.isCreateNewFunder = (this.funderFormGroup.get('label').value != null && this.funderFormGroup.get('label').value.length > 0);
this.setValidators();
this.setProjectValidators();
this.setFunderValidators();
}
searchGrant(query: string) {
@ -50,11 +80,35 @@ export class GrantTabComponent implements OnInit {
return this.grantService.getWithExternal(grantRequestItem);
}
searchProject(query: string) {
const projectRequestItem: RequestItem<GrantCriteria> = new RequestItem();
projectRequestItem.criteria = new GrantCriteria();
projectRequestItem.criteria.like = query;
return this.projectService.getWithExternal(projectRequestItem);
}
searchFunder(query: string) {
const funderRequestItem: RequestItem<FunderCriteria> = new RequestItem();
funderRequestItem.criteria = new FunderCriteria();
funderRequestItem.criteria.like = query;
return this.funderService.getWithExternal(funderRequestItem);
}
create() {
this.isCreateNew = !this.isCreateNew;
this.setValidators();
}
createProject() {
this.isCreateNewProject = !this.isCreateNewProject;
this.setProjectValidators();
}
createFunder() {
this.isCreateNewFunder = !this.isCreateNewFunder;
this.setFunderValidators();
}
setValidators() {
if (this.isCreateNew) {
this.formGroup.get('existGrant').disable();
@ -74,4 +128,38 @@ export class GrantTabComponent implements OnInit {
}
}
setProjectValidators() {
if (this.isCreateNewProject) {
this.projectFormGroup.get('existProject').disable();
this.projectFormGroup.get('label').enable();
this.projectFormGroup.get('description').enable();
} else if (this.isFinalized) {
this.projectFormGroup.get('existProject').disable();
this.projectFormGroup.get('label').disable();
this.projectFormGroup.get('description').disable();
}
else {
this.projectFormGroup.get('existProject').enable();
this.projectFormGroup.get('label').disable();
this.projectFormGroup.get('label').reset();
this.projectFormGroup.get('description').disable();
this.projectFormGroup.get('description').reset();
}
}
setFunderValidators() {
if (this.isCreateNewFunder) {
this.funderFormGroup.get('existFunder').disable();
this.funderFormGroup.get('label').enable();
} else if (this.isFinalized) {
this.funderFormGroup.get('existFunder').disable();
this.funderFormGroup.get('label').disable();
}
else {
this.funderFormGroup.get('existFunder').enable();
this.funderFormGroup.get('label').disable();
this.funderFormGroup.get('label').reset();
}
}
}

View File

@ -0,0 +1,46 @@
import { Status } from "../../../../core/common/enum/Status";
import { ProjectModel } from "../../../../core/model/project/project";
import { ValidationErrorModel } from "../../../../common/forms/validation/error-model/validation-error-model";
import { ValidationContext } from "../../../../common/forms/validation/validation-context";
import { FormGroup, FormBuilder, Validators } from "@angular/forms";
import { BackendErrorValidator } from "../../../../common/forms/validation/custom-validator";
import { ValidJsonValidator } from "../../../../library/auto-complete/auto-complete-custom-validator";
export class ProjectFormModel {
public id: string;
public label?: string;
public status: Status = Status.Active;
public description: String;
public existProject: ProjectModel;
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();
fromModel(item: ProjectModel): ProjectFormModel {
this.existProject = item;
return this;
}
buildForm(context: ValidationContext = null, disabled: boolean = false): FormGroup {
if (context == null) { context = this.createValidationContext(); }
const formGroup = new FormBuilder().group({
id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators],
label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators],
status: [{ value: this.status, disabled: disabled }, context.getValidation('status').validators],
description: [{ value: this.description, disabled: disabled }, context.getValidation('description').validators],
existProject: [{ value: this.existProject, disabled: disabled }, context.getValidation('existProject').validators],
});
return formGroup;
}
createValidationContext(): ValidationContext {
const baseContext: ValidationContext = new ValidationContext();
baseContext.validation.push({ key: 'id', validators: [] });
baseContext.validation.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] });
baseContext.validation.push({ key: 'status', validators: [] });
baseContext.validation.push({ key: 'description', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'description')] });
baseContext.validation.push({ key: 'existProject', validators: [Validators.required, ValidJsonValidator, BackendErrorValidator(this.validationErrorModel, 'existProject')] });
return baseContext;
}
}

View File

@ -13,7 +13,7 @@
</mat-form-field>
<mat-form-field class="col-8">
<app-single-auto-complete [formControl]="formGroup.get('grant')"
placeholder="{{this.languageResolverService.getBy('dmpEditor') | translate}}"
placeholder="{{'DMP-EDITOR.FIELDS.GRANT' | translate}}"
[configuration]="grantAutoCompleteConfiguration">
</app-single-auto-complete>
<mat-error *ngIf="formGroup.get('grant').hasError('backendError')">

View File

@ -8,14 +8,14 @@
<mat-icon matSuffix class="style-icon">search</mat-icon>
</mat-form-field>
<mat-form-field class="col-10 filter-category">
<input matInput (focus)="periodStartPicker.open()" (click)="periodStartPicker.open()" placeholder=" {{this.languageResolver.getBy('criteriaStart')| translate}}" [matDatepicker]="periodStartPicker" name="grantCriteriaPeriodStart" [(ngModel)]="criteria.periodStart" (ngModelChange)="controlModified()" [errorStateMatcher]="this">
<input matInput (focus)="periodStartPicker.open()" (click)="periodStartPicker.open()" placeholder="{{'CRITERIA.GRANTS.PERIOD-FROM'| translate}}" [matDatepicker]="periodStartPicker" name="grantCriteriaPeriodStart" [(ngModel)]="criteria.periodStart" (ngModelChange)="controlModified()" [errorStateMatcher]="this">
<mat-datepicker-toggle matSuffix [for]="periodStartPicker"></mat-datepicker-toggle>
<mat-datepicker #periodStartPicker></mat-datepicker>
<mat-error>{{'GENERAL.VALIDATION.GRANT-START-AFTER-END' | translate}}</mat-error>
</mat-form-field>
<mat-form-field class="col-10 filter-category">
<input matInput (focus)="periodEndPicker.open()" (click)="periodEndPicker.open()" name="grantCriteriaPeriodEnd" placeholder=" {{this.languageResolver.getBy('criteriaEnd')| translate}}" [matDatepicker]="periodEndPicker" [(ngModel)]="criteria.periodEnd" (ngModelChange)="controlModified()" [errorStateMatcher]="this">
<input matInput (focus)="periodEndPicker.open()" (click)="periodEndPicker.open()" name="grantCriteriaPeriodEnd" placeholder="{{'CRITERIA.GRANTS.PERIOD-TO'| translate}}" [matDatepicker]="periodEndPicker" [(ngModel)]="criteria.periodEnd" (ngModelChange)="controlModified()" [errorStateMatcher]="this">
<mat-datepicker-toggle matSuffix [for]="periodEndPicker"></mat-datepicker-toggle>
<mat-datepicker #periodEndPicker></mat-datepicker>
<mat-error>{{'GENERAL.VALIDATION.GRANT-START-AFTER-END' | translate}}</mat-error>

View File

@ -3,7 +3,7 @@
<div class="card">
<div class="card-header card-header-plain d-flex">
<div class="card-desc d-flex flex-column justify-content-center">
<h4 class="card-title">{{languageResolverService.getBy('listingTitle') | translate}}</h4>
<h4 class="card-title">{{'GRANT-LISTING.TITLE' | translate}}</h4>
<!-- <p class="card-category">{{'GRANT-LISTING.SUBTITLE' | translate}}</p> -->
</div>
<div class="d-flex align-items-center ml-auto p-2">

View File

@ -6,8 +6,7 @@
<a mat-button class="buttonNav navbar-button" routerLink="/explore">{{'NAV-BAR.PUBLIC-DATASETS' | translate}}</a>
</div>
<div *ngIf="isAuthenticated()" class="col-auto">
<a mat-button class="buttonNav navbar-button" routerLink="/grants">{{this.languageResolver.getBy('navbar') |
translate}}</a>
<a mat-button class="buttonNav navbar-button" routerLink="/grants">{{'NAV-BAR.GRANTS' | translate}}</a>
<a mat-button class="buttonNav navbar-button" routerLink="/plans">{{'NAV-BAR.DMPS' | translate}}</a>
<a mat-button class="buttonNav navbar-button" routerLink="/datasets">{{'NAV-BAR.DATASETS' | translate}}</a>
<a *ngIf="isAdmin()" mat-button class="buttonNav navbar-button" routerLink="/users">{{'NAV-BAR.USERS' | translate}}</a>

View File

@ -552,7 +552,7 @@
},
"FIELDS": {
"NAME": "Title",
"GRANT": "Related Grant",
"RELATED-GRANT": "Related Grant",
"DESCRIPTION": "Description",
"ORGANISATIONS": "Organizations",
"ORGANISATIONS-HINT": "Add here the names of the organizations contributing to the creation and revision of the DMPs",
@ -561,6 +561,7 @@
"TEMPLATE": "DMP Template",
"DATASET-TEMPLATES": "Related Dataset Description Templates",
"PROFILE": "DMP Template",
"PROJECT": "Project",
"GRANT": "Grant",
"FUNDER": "Funder",
"STATUS": "DMP Status"
@ -972,8 +973,12 @@
"SAVE-AND-FINALIZE": "Save and Finalize",
"NEXT": "Next",
"BACK": "Back",
"CREATE-NEW": "Add grant",
"EXIST": "Existing Grant"
"CREATE-NEW": "Add Grant",
"EXIST": "Use Existing Grant",
"CREATE-NEW-PROJECT": "Add Project",
"EXIST-PROJECT": "Use Existing Project",
"CREATE-NEW-FUNDER": "Add Funder",
"EXIST-FUNDER": "Use Existing Funder"
},
"FIRST-STEP": {
"TITLE": "Grant",
@ -982,7 +987,9 @@
"OR": "or",
"FIELDS": {
"SELECT-GRANT": "Select the grant that the DMP is associated with",
"LABEL": "Grant Name",
"GRANT-LABEL": "Grant Name",
"FUNDER-LABEL": "Funder Name",
"PROJECT-LABEL": "Project Name",
"LABEL-HINT": "Add the name of the grant as it appears in the call for grant proposal",
"DESCRIPTION": "Description",
"DESCRIPTION-HINT": "Briefly explain the aims and objectives of the grant"