Merge branch 'ui-redesign' of gitlab.eudat.eu:dmp/OpenAIRE-EUDAT-DMP-service-pilot into ui-redesign

# Conflicts:
#	dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts
This commit is contained in:
apapachristou 2020-07-07 13:19:38 +03:00
commit 6fa7b4a350
32 changed files with 779 additions and 278 deletions

View File

@ -28,6 +28,7 @@ import eu.eudat.models.data.helpers.common.DataTableData;
import eu.eudat.models.data.helpers.responses.ResponseItem;
import eu.eudat.models.data.listingmodels.DataManagementPlanListingModel;
import eu.eudat.models.data.listingmodels.DataManagementPlanOverviewModel;
import eu.eudat.models.data.listingmodels.UserInfoListingModel;
import eu.eudat.models.data.security.Principal;
import eu.eudat.query.DMPQuery;
import eu.eudat.types.ApiMessageCode;
@ -277,6 +278,19 @@ public class DMPs extends BaseController {
}
}
@Transactional
@RequestMapping(method = RequestMethod.POST, value = {"/updateusers/{id}"})
public ResponseEntity<ResponseItem<DMP>> updateUsers(@PathVariable String id, @RequestBody List<UserInfoListingModel> users, Principal principal) {
try {
this.dataManagementPlanManager.updateUsers(UUID.fromString(id), users, principal);
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<DMP>().status(ApiMessageCode.SUCCESS_MESSAGE).message("Successfully Updated Colaborators for Data Datamanagement Plan."));
} catch (Exception e) {
logger.error(e.getMessage(), e);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem<DMP>().status(ApiMessageCode.ERROR_MESSAGE).message("Failed to update the users of Data Management Plan."));
}
}
/*
* DOI Generation
* */

View File

@ -137,7 +137,7 @@ public class DashBoardManager {
List<Integer> roles = new LinkedList<>();
List<Dmp> finalDmps = dmps;
CompletableFuture dmpFuture = dataManagementPlanRepository.getAuthenticated(dmps != null ? dataManagementPlanRepository.asQueryable().where((builder, root) -> root.get("id").in(finalDmps.stream().map(Dmp::getId).collect(Collectors.toList()))) : dataManagementPlanRepository.getWithCriteria(dataManagementPlanCriteria), principal.getId(), roles).countAsync()
CompletableFuture dmpFuture = dataManagementPlanRepository.getAuthenticated(dmps != null ? dataManagementPlanRepository.asQueryable().where((builder, root) -> root.get("id").in(finalDmps.stream().map(Dmp::getId).collect(Collectors.toList()))) : dataManagementPlanRepository.getWithCriteria(dataManagementPlanCriteria), principal.getId(), roles).distinct().countAsync()
.whenComplete((dmpsStats, throwable) -> statistics.setTotalDataManagementPlanCount(dmpsStats));
List<eu.eudat.elastic.entities.Dataset> finalDatasets = datasets;
CompletableFuture datasetFuture = datasetRepository.getAuthenticated(datasets != null ? datasetRepository.asQueryable().where((builder, root) -> root.get("id").in(finalDatasets.stream().map(x -> UUID.fromString(x.getId())).collect(Collectors.toList()))) : datasetRepository.getWithCriteria(datasetCriteria), user, roles).countAsync()

View File

@ -189,7 +189,7 @@ public class DataManagementPlanManager {
.whenComplete((resultList, throwable) -> dataTable.setData(resultList));
}
CompletableFuture countFuture = authItems.countAsync().whenComplete((count, throwable) -> dataTable.setTotalCount(count));
CompletableFuture countFuture = authItems.distinct().countAsync().whenComplete((count, throwable) -> dataTable.setTotalCount(count));
CompletableFuture.allOf(itemsFuture, countFuture).join();
return dataTable;
}
@ -867,6 +867,18 @@ public class DataManagementPlanManager {
this.updateIndex(dmp);
}
public void updateUsers(UUID id, List<UserInfoListingModel> users, Principal principal) throws Exception {
DMP dmp = this.apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().find(id);
if (!isUserOwnerOfDmp(dmp, principal))
throw new Exception("User does not have the privilege to do this action.");
clearUsers(dmp);
for (UserInfoListingModel userListing : users) {
UserInfo tempUser = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(userListing.getId());
assignUser(dmp, tempUser, UserDMP.UserDMPRoles.fromInteger(userListing.getRole()));
}
}
/*
* Export Data
* */

View File

@ -3,6 +3,8 @@ package eu.eudat.models.data.dataset;
import eu.eudat.data.entities.Dataset;
import eu.eudat.models.DataModel;
import eu.eudat.models.data.datasetprofile.DatasetProfileOverviewModel;
import eu.eudat.models.data.grant.GrantOverviewModel;
import eu.eudat.models.data.listingmodels.DataManagementPlanOverviewModel;
import eu.eudat.models.data.listingmodels.UserInfoListingModel;
import java.util.Date;
@ -17,8 +19,8 @@ public class DatasetOverviewModel implements DataModel<Dataset, DatasetOverviewM
private short status;
private DatasetProfileOverviewModel datasetTemplate;
private List<UserInfoListingModel> users;
private String dmp;
private String grant;
private DataManagementPlanOverviewModel dmp;
private GrantOverviewModel grant;
private String description;
private Boolean isPublic;
private Date modified;
@ -59,19 +61,19 @@ public class DatasetOverviewModel implements DataModel<Dataset, DatasetOverviewM
this.users = users;
}
public String getDmp() {
public DataManagementPlanOverviewModel getDmp() {
return dmp;
}
public void setDmp(String dmp) {
public void setDmp(DataManagementPlanOverviewModel dmp) {
this.dmp = dmp;
}
public String getGrant() {
public GrantOverviewModel getGrant() {
return grant;
}
public void setGrant(String grant) {
public void setGrant(GrantOverviewModel grant) {
this.grant = grant;
}
@ -106,8 +108,8 @@ public class DatasetOverviewModel implements DataModel<Dataset, DatasetOverviewM
this.status = entity.getStatus();
this.datasetTemplate = new DatasetProfileOverviewModel().fromDataModel(entity.getProfile());
this.users = entity.getDmp().getUsers().stream().map(x -> new UserInfoListingModel().fromDataModel(x)).collect(Collectors.toList());
this.dmp = entity.getDmp().getLabel();
this.grant = entity.getDmp().getGrant().getLabel();
this.dmp = new DataManagementPlanOverviewModel().fromDataModel(entity.getDmp());
this.grant = new GrantOverviewModel().fromDataModel(entity.getDmp().getGrant());
this.description = entity.getDescription();
this.isPublic = entity.getDmp().isPublic();
this.modified = entity.getModified();

View File

@ -3,6 +3,7 @@ package eu.eudat.models.rda.mapper;
import eu.eudat.data.entities.*;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.logic.utilities.helpers.StreamDistinctBy;
import eu.eudat.models.rda.Cost;
import eu.eudat.models.rda.Dmp;
import eu.eudat.models.rda.DmpId;
import net.minidev.json.JSONObject;
@ -43,16 +44,34 @@ public class DmpRDAMapper {
Map<String, Object> extraProperties = new org.json.JSONObject(dmp.getExtraProperties()).toMap();
if (!extraProperties.isEmpty()) {
rda.setLanguage(LanguageRDAMapper.mapLanguageIsoToRDAIso(extraProperties.get("language").toString()));
if (extraProperties.get("language") != null) {
rda.setLanguage(LanguageRDAMapper.mapLanguageIsoToRDAIso(extraProperties.get("language").toString()));
}
if (extraProperties.get("costs") != null) {
rda.setCost(new ArrayList<>());
((List) extraProperties.get("costs")).forEach(costl -> {
Cost cost = new Cost();
Map<String, Object> code = new org.json.JSONObject((String) ((Map) costl).get("code")).toMap();
cost.setCurrencyCode(Cost.CurrencyCode.fromValue((String) code.get("value")));
cost.setDescription((String) ((Map) costl).get("description"));
cost.setTitle((String) ((Map) costl).get("title"));
cost.setValue(((Integer) ((Map) costl).get("value")).doubleValue());
rda.getCost().add(cost);
});
}
if (extraProperties.get("contact") != null) {
UserInfo contact = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(UUID.fromString((String)extraProperties.get("contact")));
rda.setContact(ContactRDAMapper.toRDA(contact));
}
}
UserInfo creator;
/*UserInfo creator;
if (dmp.getCreator() != null) {
creator = dmp.getCreator();
} else {
creator = dmp.getUsers().stream().filter(userDMP -> userDMP.getRole().equals(UserDMP.UserDMPRoles.OWNER.getValue())).map(UserDMP::getUser).findFirst().orElse(new UserInfo());
}
rda.setContact(ContactRDAMapper.toRDA(creator));
rda.setContact(ContactRDAMapper.toRDA(creator));*/
rda.setContributor(new ArrayList<>());
if (dmp.getResearchers() != null && !dmp.getResearchers().isEmpty()) {
rda.getContributor().addAll(dmp.getResearchers().stream().map(ContributorRDAMapper::toRDA).collect(Collectors.toList()));

View File

@ -1,5 +1,6 @@
import { DatasetProfileModel } from "./dataset-profile";
import { GrantOverviewModel } from '../grant/grant-overview';
import { DmpOverviewModel } from '../dmp/dmp-overview';
export interface DatasetOverviewModel {
id: string;
@ -8,7 +9,7 @@ export interface DatasetOverviewModel {
datasetTemplate: DatasetProfileModel;
users: any[];
dmp: String;
dmp: DmpOverviewModel;
grant: GrantOverviewModel;
description: String;
public: boolean;

View File

@ -0,0 +1,9 @@
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { ValidationContext } from '@common/forms/validation/validation-context';
export interface CostModel {
code: string;
description: string;
title: string;
value: number;
}

View File

@ -1,5 +1,5 @@
export interface ResearcherModel {
id: String;
id: string;
name: String;
reference: String;
lastName: String;

View File

@ -11,7 +11,7 @@ import { BaseHttpService } from '../http/base-http.service';
import { DatasetProfileCriteria } from '../../query/dataset-profile/dataset-profile-criteria';
import { ConfigurationService } from '../configuration/configuration.service';
import { DatasetOverviewModel } from '@app/core/model/dataset/dataset-overview';
import { HttpHeaders } from '@angular/common/http';
import { HttpHeaders, HttpResponse, HttpClient } from '@angular/common/http';
import { DatasetModel } from '@app/core/model/dataset/dataset';
@Injectable()
@ -22,7 +22,8 @@ export class DatasetService {
constructor(
private http: BaseHttpService,
private configurationSevice: ConfigurationService) {
private configurationSevice: ConfigurationService,
private httpClient: HttpClient) {
this.actionUrl = configurationSevice.server + 'datasets/';
}
@ -69,4 +70,27 @@ export class DatasetService {
delete(id: String): Observable<DatasetModel> {
return this.http.delete<DatasetModel>(this.actionUrl + 'delete/' + id, { headers: this.headers }); // + 'delete/'
}
publish(id: String): Observable<DatasetModel> {
return this.http.get<DatasetModel>(this.actionUrl + 'makepublic/' + id, { headers: this.headers });
}
public downloadXML(id: string): Observable<HttpResponse<Blob>> {
let headerXml: HttpHeaders = this.headers.set('Content-Type', 'application/xml')
return this.httpClient.get(this.actionUrl + id, { responseType: 'blob', observe: 'response', headers: headerXml }); //+ "/getXml/"
}
public downloadDocx(id: string): Observable<HttpResponse<Blob>> {
let headerDoc: HttpHeaders = this.headers.set('Content-Type', 'application/msword')
return this.httpClient.get(this.actionUrl + id, { responseType: 'blob', observe: 'response', headers: headerDoc });
}
public downloadPDF(id: string): Observable<HttpResponse<Blob>> {
let headerPdf: HttpHeaders = this.headers.set('Content-Type', 'application/pdf')
return this.httpClient.get(this.actionUrl + 'getPDF/' + id, { responseType: 'blob', observe: 'response', headers: headerPdf });
}
public downloadJson(id: string): Observable<HttpResponse<Blob>> {
return this.httpClient.get(this.actionUrl + 'rda/' + id, { responseType: 'blob', observe: 'response' });
}
}

View File

@ -19,6 +19,7 @@ import { ExploreDmpCriteriaModel } from '../../query/explore-dmp/explore-dmp-cri
import { RequestItem } from '../../query/request-item';
import { BaseHttpService } from '../http/base-http.service';
import { ConfigurationService } from '../configuration/configuration.service';
import { UserInfoListingModel } from '@app/core/model/user/user-info-listing';
@Injectable()
export class DmpService {
@ -96,6 +97,10 @@ export class DmpService {
return this.http.post<DmpModel>(this.actionUrl + 'unfinalize/' + id, { headers: this.headers });
}
updateUsers(id: string, users: UserInfoListingModel[]): Observable<DmpModel> {
return this.http.post<DmpModel>(`${this.actionUrl}updateusers/${id}`, users, {headers: this.headers});
}
getDoi(id: string): Observable<string> {
return this.http.post<string>(this.actionUrl + 'createZenodoDoi/' + id, { headers: this.headers });
}

View File

@ -8,8 +8,8 @@
<div class="row">
<div class="col-md-8 col-lg-8 pl-4">
<div class="row">
<span class="dataset-logo">{{ 'NAV-BAR.DATASET' | translate }}</span>
<p class="dataset-label ml-2 mb-0">{{ dataset.label }}</p>
<span class="col-auto dataset-logo">{{ 'NAV-BAR.DATASET' | translate }}</span>
<p class="col dataset-label p-0 ml-3 mb-0">{{ dataset.label }}</p>
</div>
<div class="row d-flex align-items-center mt-3 mb-4 label-txt">
<div *ngIf="isUserDatasetRelated()" class="d-flex">
@ -27,56 +27,40 @@
{{'DMP-OVERVIEW.PRIVATE' | translate}}
</div>
</div>
<!-- <div class="d-flex mr-4">
<div *ngIf="lockStatus" class="d-flex flex-row">
<mat-icon class="status-icon">lock_outline</mat-icon>
{{'DMP-OVERVIEW.LOCKED' | translate}}
</div>
<div *ngIf="!lockStatus" class="d-flex flex-row">
<mat-icon class="status-icon">lock_open</mat-icon>
{{'DMP-OVERVIEW.UNLOCKED' | translate}}
</div>
</div> -->
<!-- <button class="d-flex mr-4 version-btn label2-txt"
(click)="viewVersions(dmp.groupId, dmp.label)">
{{'DMP-LISTING.ACTIONS.VIEW-VERSION' | translate}}
</button> -->
<div class="d-flex mr-4">{{'GENERAL.STATUSES.EDIT' | translate}} :
<div *ngIf="lockStatus" class="d-flex flex-row mr-4">
<mat-icon class="status-icon">lock_outline</mat-icon>
{{'DMP-OVERVIEW.LOCKED' | translate}}
</div>
<div class="d-flex mr-3">{{'GENERAL.STATUSES.EDIT' | translate}} :
{{dataset.modified | date:"longDate"}}
</div>
<!-- <div class="d-flex mr-4">
<div *ngIf="dmp.status" class="d-flex flex-row uppercase">
<div class="d-flex mr-4">
<div *ngIf="dataset.status" class="d-flex flex-row uppercase">
<mat-icon class="status-icon">check</mat-icon>
{{'DATASET-LISTING.COLUMNS.FINALIZED' | translate}}
{{'TYPES.DATASET-STATUS.FINALISED' | translate}}
</div>
</div> -->
</div>
</div>
<div class="row mb-4 pb-3">
<button *ngIf="isAuthenticated()" (click)="cloneClicked(dataset)" mat-mini-fab
class="mr-3 d-flex justify-content-center align-items-center"
matTooltip="{{'DMP-LISTING.ACTIONS.CLONE' | translate}}" matTooltipPosition="above">
<button *ngIf="isAuthenticated()" (click)="openDmpSearchDialogue()" mat-mini-fab
class="mr-3 actions-btn" matTooltip="{{'DMP-LISTING.ACTIONS.CLONE' | translate}}"
matTooltipPosition="above">
<mat-icon class="mat-mini-fab-icon">content_copy</mat-icon>
</button>
<button *ngIf="isDraftDataset(dataset) && isUserOwner" (click)="editClicked(dataset)"
mat-mini-fab class="mr-3 d-flex justify-content-center align-items-center"
mat-mini-fab class="mr-3 actions-btn"
matTooltip="{{'DMP-LISTING.ACTIONS.EDIT' | translate}}" matTooltipPosition="above">
<mat-icon class="mat-mini-fab-icon">create</mat-icon>
</button>
<button *ngIf="isDraftDataset(dataset) && isUserOwner" (click)="deleteClicked()" mat-mini-fab
class="mr-3 d-flex justify-content-center align-items-center"
matTooltip="{{'DMP-LISTING.ACTIONS.DELETE' | translate}}" matTooltipPosition="above">
class="mr-3 actions-btn" matTooltip="{{'DMP-LISTING.ACTIONS.DELETE' | translate}}"
matTooltipPosition="above">
<mat-icon class="mat-mini-fab-icon">delete</mat-icon>
</button>
<!-- <button *ngIf="isDraftDmp(dmp) && isUserOwner && !lockStatus" mat-mini-fab (click)="createOrUpdate(dmp.id)"
class="mr-3 d-flex justify-content-center align-items-center">
<mat-icon class="mat-mini-fab-icon" matTooltip="{{'DMP-OVERVIEW.LOCK' | translate}}"
matTooltipPosition="above">lock_outline
</mat-icon>
</button> -->
</div>
<div class="row header">{{'DATASET-LISTING.TOOLTIP.PART-OF' | translate}}</div>
<div class="row ">
<button class="dmp-btn">
<button class="dmp-btn" (click)="dmpClicked(dataset.dmp.id)">
<div class="dmp-btn-label">
{{ 'DATASET-LISTING.TOOLTIP.DMP-FOR' | translate }}: {{ dataset.datasetTemplate.label }}
</div>
@ -85,17 +69,16 @@
</div>
<div class="row header">{{'DMP-OVERVIEW.GRANT' | translate}}</div>
<div class="row dmp-label">Grant label</div>
<!-- <div class="row dmp-label">{{ dataset.grant.label }}</div> -->
<div class="row dataset-label">{{ dataset.grant.label }}</div>
<div class="row header">{{'DMP-OVERVIEW.RESEARCHERS' | translate}}</div>
<div class="row">
<!-- <div *ngFor="let researcher of dataset.dmp.researchers; let last = last">
<a href="{{getOrcidPath() + researcher.id }}" target="blank" class="researcher">
<div *ngFor="let researcher of researchers; let last = last">
<a href="{{ getOrcidPath() + researcher.id }}" target="blank" class="researcher">
<div class="id-btn">&nbsp;</div>
<div *ngIf="!last">{{ researcher.name }}, </div>
<div *ngIf="last">{{ researcher.name }}</div>
</a>
</div> -->
</div>
</div>
<div class="row header">{{'DATASET-LISTING.COLUMNS.DESCRIPTION' | translate}}</div>
@ -104,31 +87,9 @@
</div>
</div>
<div class="col-md-4 col-lg-4 p-0">
<!-- <div *ngIf="!hasDoi(dmp)" class="row d-flex flex-column ml-0 mr-0 mb-3">
<p class="doi-label">{{'DMP-EDITOR.TITLE.SUBTITLE' | translate}}</p>
<div class="doi-panel">
<p *ngIf="!hasDoi(dmp)" class="mb-0 ml-3">
<textarea #doi class="doi-txt">{{ dmp.doi }}</textarea>
</p>
<div class="d-flex justify-content-end">
<button (click)="copyDoi(doi)" mat-mini-fab
class="mr-2 d-flex justify-content-center align-items-center"
matTooltip="{{'DMP-LISTING.ACTIONS.CLONE' | translate}}" matTooltipPosition="above">
<mat-icon class="mat-mini-fab-icon">content_copy</mat-icon>
</button>
<button mat-mini-fab class="mr-2 d-flex justify-content-center align-items-center"
matTooltip="{{'GRANT-EDITOR.ACTIONS.VISIT-WEBSITE' | translate}}"
matTooltipPosition="above">
<a [href]="createDoiLink(dmp.doi)" class="doi-link" target="_blank">
<mat-icon class="mat-mini-fab-icon">launch</mat-icon>
</a>
</button>
</div>
</div>
</div> -->
<div class="frame mb-3 pt-4 pl-3 pr-5 pb-1">
<!-- <div *ngIf="!dmp.status && isDraftDmp(dmp) && isUserOwner">
<div class="row ml-0 mr-0 pl-4 d-flex align-items-center" (click)="finalize(dmp)">
<!-- <div *ngIf="!dataset.status && isDraftDataset(dataset) && isUserOwner">
<div class="row ml-0 mr-0 pl-4 d-flex align-items-center" (click)="finalize(dataset)">
<button mat-mini-fab class="finalize-btn">
<mat-icon class="mat-mini-fab-icon">check</mat-icon>
</button>
@ -138,60 +99,38 @@
<hr class="hr-line">
</div>
</div> -->
<!-- <div *ngIf="hasDoi(dmp) && isFinalizedDmp(dmp) && !this.isPublicView && isUserOwner"
(click)="getDoi(dmp)" class="row ml-0 mr-0 pl-4 pb-3 d-flex align-items-center">
<button mat-mini-fab class="frame-btn">
<mat-icon class="mat-mini-fab-icon">archive</mat-icon>
</button>
<p class="mb-0 pl-2 frame-txt">{{ 'DMP-LISTING.ACTIONS.DEPOSIT' | translate }}</p>
</div> -->
<!-- <div class="row ml-0 mr-0 pl-4 pb-3 d-flex align-items-center">
<div class="row ml-0 mr-0 pl-4 pb-3 d-flex align-items-center">
<button mat-mini-fab class="frame-btn" [matMenuTriggerFor]="exportMenu">
<mat-icon class="mat-mini-fab-icon">open_in_new</mat-icon>
</button>
<p class="mb-0 mr-0 pl-2 frame-txt" [matMenuTriggerFor]="exportMenu">
{{ 'DMP-LISTING.ACTIONS.EXPORT' | translate }}</p>
</div> -->
<!-- <div class="row ml-0 mr-0 pl-4 pb-3 d-flex align-items-center" *ngIf="isUserOwner"
(click)="newVersion(dmp.id, dmp.label)">
<button mat-mini-fab class="frame-btn">
<mat-icon class="mat-mini-fab-icon">add_to_photos</mat-icon>
</button>
<p class="mb-0 pl-2 frame-txt">{{ 'DMP-LISTING.ACTIONS.START-NEW-VERSION' | translate }}
</p>
</div> -->
<!-- <div *ngIf="!dataset.public && showPublishButton(dmp) && isUserOwner"
class="row ml-0 mr-0 pl-4 pb-3 d-flex align-items-center" (click)="publish(dmp.id)">
<button mat-mini-fab class="frame-btn">
<mat-icon class="mat-mini-fab-icon">public</mat-icon>
</button>
<p class="mb-0 pl-2 frame-txt">{{ 'DMP-LISTING.ACTIONS.MAKE-PUBLIC' | translate }}</p>
</div>
<mat-menu #exportMenu="matMenu" xPosition="before">
<button mat-menu-item (click)="downloadPDF(dmp.id)">
<button mat-menu-item (click)="downloadPDF(dataset.id)">
<i class="fa fa-file-pdf-o pr-2"></i>
<span>{{'GENERAL.FILE-TYPES.PDF' | translate}}</span>
</button>
<button mat-menu-item (click)="downloadDocx(dmp.id)">
<button mat-menu-item (click)="downloadDocx(dataset.id)">
<i class="fa fa-file-word-o pr-2"></i>
<span>{{'GENERAL.FILE-TYPES.DOC' | translate}}</span>
</button>
<button mat-menu-item (click)="downloadXml(dmp.id)">
<button mat-menu-item (click)="downloadXml(dataset.id)">
<i class="fa fa-file-code-o pr-2"></i>
<span>{{'GENERAL.FILE-TYPES.XML' | translate}}</span>
</button>
<button mat-menu-item (click)="downloadJson(dmp.id)">
<button mat-menu-item (click)="downloadJson(dataset.id)">
<i class="fa fa-file-o pr-2"></i>
<span>{{'GENERAL.FILE-TYPES.JSON' | translate}}</span>
</button>
</mat-menu>
</div> -->
<!-- <div class="frame mb-3 pt-4 pl-3 pr-3 pb-1">
</div>
<div class="frame mb-3 pt-4 pl-3 pr-3 pb-1">
<div class="row ml-0 mr-0 pl-4 pb-3">
<p class="header">{{ 'DATASET-OVERVIEW.DATASET-AUTHORS' | translate }}</p>
</div>
<div class="row ml-0 mr-0 pl-4 ml-2 pb-3 d-flex align-items-center">
<div *ngFor="let user of dmp.users" class="row authors">
<div *ngFor="let user of dataset.users" class="row authors">
<div class="d-flex flex-row">
<button class="account_btn mr-3 pl-0">
<mat-icon class="account-icon">account_circle</mat-icon>
@ -204,19 +143,18 @@
<p class="authors-role">{{ roleDisplay(user) }}</p>
</div>
</div>
<button *ngIf="isUserOwner && !dmp.status && user.role"
(click)="removeCollaborator(user.id)"
<button *ngIf="isUserOwner && !dataset.status && user.role"
class="remove-btn">{{ 'GENERAL.CONFIRMATION-DIALOG.ACTIONS.REMOVE' | translate}}</button>
</div>
</div>
<div *ngIf="isUserOwner" (click)="openShareDialog(dmp.id,dmp.label)"
<div *ngIf="isUserOwner" (click)="openShareDialog(dataset.dmp.id, dataset.dmp.label)"
class="row mt-3 mb-3 d-flex align-items-center justify-content-center">
<button mat-raised-button class="invite-btn">
<mat-icon>group_add</mat-icon>
{{'DMP-LISTING.ACTIONS.INVITE-SHORT' | translate}}
</button>
</div>
</div> -->
</div>
</div>
</div>
</div>

View File

@ -20,7 +20,7 @@
font-size: 1.2em;
}
.mat-mini-fab:hover {
.actions-btn:hover {
background-color: #129D99;
color: #FFFFFF;
}
@ -139,6 +139,7 @@
.dataset-label {
font-weight: bold;
width: auto;
}
.uppercase {
@ -149,14 +150,13 @@
font-size: 0.875em;
color: #008887;
padding-right: 0.5em;
align-self: center;
align-self: center;;
}
.header {
opacity: 0.6;
margin-top: 1em;
margin-bottom: 0.25em;
margin-bottom: 0.5em;
}
.dataset-label, .header {
@ -262,7 +262,7 @@
// ********CENTER ELEMENTS********
.mat-mini-fab, .mat-mini-fab-icon,
.mat-mini-fab, .mat-mini-fab-icon, .actions-btn,
.status-icon, .dataset-logo, .frame-btn, .finalize-btn {
display: flex;
justify-content: center;

View File

@ -19,6 +19,15 @@ import { Location } from '@angular/common';
import { UserInfoListingModel } from '@app/core/model/user/user-info-listing';
import { DatasetStatus } from '@app/core/common/enum/dataset-status';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import * as FileSaver from 'file-saver';
import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation.component';
import { DatasetWizardEditorModel } from '../dataset-wizard/dataset-wizard-editor.model';
import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service';
import { FormControl } from '@angular/forms';
import { DatasetCopyDialogueComponent } from '../dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component';
import { DmpService } from '@app/core/services/dmp/dmp.service';
import { ResearcherModel } from '@app/core/model/researcher/researcher';
import { LockService } from '@app/core/services/lock/lock.service';
@Component({
@ -29,6 +38,7 @@ import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog
export class DatasetOverviewComponent extends BaseComponent implements OnInit {
dataset: DatasetOverviewModel;
datasetWizardModel: DatasetWizardEditorModel;
isNew = true;
isFinalized = false;
isPublicView = true;
@ -37,6 +47,8 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit {
isUserOwner: boolean;
expand = false;
hasDOIToken = false;
researchers: ResearcherModel[];
lockStatus: Boolean;
constructor(
private route: ActivatedRoute,
@ -50,7 +62,10 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit {
private configurationService: ConfigurationService,
private oauth2DialogService: Oauth2DialogService,
private userService: UserService,
private location: Location
private dmpService: DmpService,
private location: Location,
private datasetWizardService: DatasetWizardService,
private lockService: LockService
) {
super();
}
@ -69,7 +84,12 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit {
.pipe(takeUntil(this._destroyed))
.subscribe(data => {
this.dataset = data;
// this.checkLockStatus(this.dataset.id);
this.getDmpResearchers();
this.datasetWizardService.getSingle(this.dataset.id).pipe(takeUntil(this._destroyed))
.subscribe(data => {
this.datasetWizardModel = new DatasetWizardEditorModel().fromModel(data);
});
this.checkLockStatus(this.dataset.id);
this.setIsUserOwner();
const breadCrumbs = [];
breadCrumbs.push({ parentComponentName: null, label: this.language.instant('NAV-BAR.MY-DATASET-DESCRIPTIONS'), url: "/datasets" });
@ -92,7 +112,12 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit {
.pipe(takeUntil(this._destroyed))
.subscribe(data => {
this.dataset = data;
// this.checkLockStatus(this.dataset.id);
this.getDmpResearchers();
this.datasetWizardService.getSingle(this.dataset.id).pipe(takeUntil(this._destroyed))
.subscribe(data => {
this.datasetWizardModel = new DatasetWizardEditorModel().fromModel(data);
});
this.checkLockStatus(this.dataset.id);
this.setIsUserOwner();
const breadCrumbs = [];
breadCrumbs.push({ parentComponentName: null, label: this.language.instant('NAV-BAR.PUBLIC DATASETS'), url: "/explore" });
@ -110,112 +135,241 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit {
});
}
onFetchingDeletedCallbackError(redirectRoot: string) {
this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-OVERVIEW.ERROR.DELETED-DATASET'), SnackBarNotificationLevel.Error);
this.router.navigate([redirectRoot]);
checkLockStatus(id: string){
this.lockService.checkLockStatus(id).pipe(takeUntil(this._destroyed))
.subscribe(lockStatus => this.lockStatus = lockStatus);
}
onFetchingForbiddenCallbackError(redirectRoot: string) {
this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-OVERVIEW.ERROR.FORBIDEN-DATASET'), SnackBarNotificationLevel.Error);
this.router.navigate([redirectRoot]);
onFetchingDeletedCallbackError(redirectRoot: string) {
this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-OVERVIEW.ERROR.DELETED-DATASET'), SnackBarNotificationLevel.Error);
this.router.navigate([redirectRoot]);
}
onFetchingForbiddenCallbackError(redirectRoot: string) {
this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-OVERVIEW.ERROR.FORBIDEN-DATASET'), SnackBarNotificationLevel.Error);
this.router.navigate([redirectRoot]);
}
goBack(): void {
this.location.back();
}
this.location.back();
}
getDmpResearchers() {
this.dmpService.getSingle(this.dataset.dmp.id).pipe(takeUntil(this._destroyed))
.subscribe(data => {
this.researchers = data.researchers;
});
}
setIsUserOwner() {
if (this.dataset) {
const principal: Principal = this.authentication.current();
if (principal) this.isUserOwner = principal.id === this.dataset.users.find(x => x.role === Role.Owner).id;
}
if (this.dataset) {
const principal: Principal = this.authentication.current();
if (principal) this.isUserOwner = principal.id === this.dataset.users.find(x => x.role === Role.Owner).id;
}
}
isUserAuthor(userId: string): boolean {
const principal: Principal = this.authentication.current();
return userId === principal.id;
}
isUserDatasetRelated() {
const principal: Principal = this.authentication.current();
let isRelated: boolean = false;
if (this.dataset && principal) {
this.dataset.users.forEach(element => {
if (element.id === principal.id) {
isRelated = true;
}
})
}
return isRelated;
const principal: Principal = this.authentication.current();
let isRelated: boolean = false;
if (this.dataset && principal) {
this.dataset.users.forEach(element => {
if (element.id === principal.id) {
isRelated = true;
}
})
}
return isRelated;
}
roleDisplayFromList(value: UserInfoListingModel[]) {
const principal: Principal = this.authentication.current();
let role: number;
if (principal) {
value.forEach(element => {
if (principal.id === element.id) {
role = element.role;
}
});
}
if (role === Role.Owner) {
return this.translate.instant('DMP-LISTING.OWNER');
} else if (role === Role.Member) {
return this.translate.instant('DMP-LISTING.MEMBER');
} else {
return this.translate.instant('DMP-LISTING.OWNER');
}
}
public isAuthenticated(): boolean {
return !(!this.authentication.current());
}
cloneClicked(dataset: DatasetOverviewModel) {
this.router.navigate(['/datasets/clone/' + dataset.id]);
}
isDraftDataset(dataset: DatasetOverviewModel) {
return dataset.status == DatasetStatus.Draft;
}
editClicked(dataset: DatasetOverviewModel) {
this.router.navigate(['/datasets/edit/' + dataset.id]);
}
deleteClicked() {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
maxWidth: '300px',
data: {
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'),
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.DELETE'),
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'),
isDeleteConfirmation: true
}
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
this.datasetService.delete(this.dataset.id)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => { this.onCallbackSuccess() },
error => this.onDeleteCallbackError(error)
);
}
});
}
onCallbackSuccess(): void {
this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
this.router.navigate(['/datasets']);
}
onDeleteCallbackError(error) {
this.uiNotificationService.snackBarNotification(error.error.message ? error.error.message : this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DELETE'), SnackBarNotificationLevel.Error);
roleDisplay(value: UserInfoListingModel) {
if (value.role === Role.Owner) {
return this.translate.instant('DMP-LISTING.OWNER');
} else if (value.role === Role.Member) {
return this.translate.instant('DMP-LISTING.MEMBER');
} else {
return this.translate.instant('DMP-LISTING.OWNER');
}
}
roleDisplayFromList(value: UserInfoListingModel[]) {
const principal: Principal = this.authentication.current();
let role: number;
if (principal) {
value.forEach(element => {
if (principal.id === element.id) {
role = element.role;
}
});
}
if (role === Role.Owner) {
return this.translate.instant('DMP-LISTING.OWNER');
} else if (role === Role.Member) {
return this.translate.instant('DMP-LISTING.MEMBER');
} else {
return this.translate.instant('DMP-LISTING.OWNER');
}
}
openShareDialog(rowId: any, rowName: any) {
const dialogRef = this.dialog.open(DmpInvitationDialogComponent, {
restoreFocus: false,
data: {
dmpId: rowId,
dmpName: rowName
}
});
}
public isAuthenticated(): boolean {
return !(!this.authentication.current());
}
isDraftDataset(dataset: DatasetOverviewModel) {
return dataset.status == DatasetStatus.Draft;
}
isFinalizedDataset(dataset: DatasetOverviewModel) {
return dataset.status == DatasetStatus.Finalized;
}
editClicked(dataset: DatasetOverviewModel) {
if (dataset.public) {
this.router.navigate(['/datasets/publicEdit/' + dataset.id]);
} else {
this.router.navigate(['/datasets/edit/' + dataset.id]);
}
}
deleteClicked() {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
maxWidth: '300px',
data: {
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'),
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.DELETE'),
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'),
isDeleteConfirmation: true
}
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
this.datasetService.delete(this.dataset.id)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => { this.onCallbackSuccess() },
error => this.onDeleteCallbackError(error)
);
}
});
}
dmpClicked(dmpId: String) {
this.router.navigate(['/plans/overview/' + dmpId]);
}
onCallbackSuccess(): void {
this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
this.router.navigate(['/datasets']);
}
onDeleteCallbackError(error) {
this.uiNotificationService.snackBarNotification(error.error.message ? error.error.message : this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DELETE'), SnackBarNotificationLevel.Error);
}
public getOrcidPath(): string {
return this.configurationService.orcidPath;
return this.configurationService.orcidPath;
}
// showPublishButton(dataset: DatasetOverviewModel) {
// return this.isFinalizedDmp(dmp) && !dmp.isPublic && this.hasPublishButton;
// }
downloadPDF(id: string) {
this.datasetService.downloadPDF(id)
.pipe(takeUntil(this._destroyed))
.subscribe(response => {
const blob = new Blob([response.body], { type: 'application/pdf' });
const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition'));
FileSaver.saveAs(blob, filename);
});
}
downloadDocx(id: string) {
this.datasetService.downloadDocx(id)
.pipe(takeUntil(this._destroyed))
.subscribe(response => {
const blob = new Blob([response.body], { type: 'application/msword' });
const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition'));
FileSaver.saveAs(blob, filename);
});
}
downloadXml(id: string) {
this.datasetService.downloadXML(id)
.pipe(takeUntil(this._destroyed))
.subscribe(response => {
const blob = new Blob([response.body], { type: 'application/xml' });
const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition'));
FileSaver.saveAs(blob, filename);
});
}
downloadJson(id: string) {
this.datasetService.downloadJson(id)
.pipe(takeUntil(this._destroyed))
.subscribe(response => {
const blob = new Blob([response.body], { type: 'application/json' });
const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition'));
FileSaver.saveAs(blob, filename);
})
}
getFilenameFromContentDispositionHeader(header: string): string {
const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g);
const matches = header.match(regex);
let filename: string;
for (let i = 0; i < matches.length; i++) {
const match = matches[i];
if (match.includes('filename="')) {
filename = match.substring(10, match.length - 1);
break;
} else if (match.includes('filename=')) {
filename = match.substring(9);
break;
}
}
return filename;
}
openDmpSearchDialogue() {
const formControl = new FormControl();
const dialogRef = this.dialog.open(DatasetCopyDialogueComponent, {
width: '500px',
restoreFocus: false,
data: {
formControl: formControl,
datasetId: this.dataset.id,
datasetProfileId: this.datasetWizardModel.profile,
datasetProfileExist: false,
confirmButton: this.language.instant('DATASET-WIZARD.DIALOGUE.COPY'),
cancelButton: this.language.instant('DATASET-WIZARD.DIALOGUE.CANCEL')
}
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed))
.subscribe(result => {
if (result && result.datasetProfileExist) {
const newDmpId = result.formControl.value.id
this.router.navigate(['/datasets/copy/' + result.datasetId], { queryParams: { newDmpId: newDmpId } });
}
});
}
}

View File

@ -32,6 +32,8 @@ import { FormValidationErrorsDialogModule } from '@common/forms/form-validation-
import { CommonUiModule } from '@common/ui/common-ui.module';
import { MultipleChoiceDialogModule } from '@common/modules/multiple-choice-dialog/multiple-choice-dialog.module';
import { AddOrganizationComponent } from './editor/add-organization/add-organization.component';
import { AddCostComponent } from './editor/cost-editor/add-cost/add-cost.component';
import { CostListingComponent } from './editor/cost-editor/cost-listing/cost-listing.component';
import { DmpCriteriaDialogComponent } from './listing/criteria/dmp-criteria-dialog.component';
@NgModule({
@ -71,7 +73,10 @@ import { DmpCriteriaDialogComponent } from './listing/criteria/dmp-criteria-dial
DatasetsTabComponent,
DmpCloneComponent,
AddOrganizationComponent,
DmpCriteriaDialogComponent
DmpCriteriaDialogComponent,
AddOrganizationComponent,
AddCostComponent,
CostListingComponent
],
entryComponents: [
DmpInvitationDialogComponent,
@ -80,7 +85,9 @@ import { DmpCriteriaDialogComponent } from './listing/criteria/dmp-criteria-dial
DmpFinalizeDialogComponent,
DmpUploadDialogue,
AddOrganizationComponent,
DmpCriteriaDialogComponent
DmpCriteriaDialogComponent,
AddOrganizationComponent,
AddCostComponent
]
})
export class DmpModule { }

View File

@ -0,0 +1,28 @@
<form *ngIf="formGroup" [formGroup]="formGroup">
<h1 mat-dialog-title>{{'ADDEDITCOST-EDITOR.ADD-TITLE' | translate}}</h1>
<div mat-dialog-content class="row">
<mat-form-field class="col-12">
<app-single-auto-complete [formControl]="formGroup.get('code')" placeholder="{{'ADDEDITCOST-EDITOR.CODE' | translate}}" [configuration]="currencyAutoCompleteConfiguration">
</app-single-auto-complete>
<mat-error *ngIf="formGroup.get('description').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<mat-form-field class="col-12">
<input matInput formControlName="description" placeholder="{{'ADDEDITCOST-EDITOR.DESCRIPTION' | translate}}">
<mat-error *ngIf="formGroup.get('description').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<mat-form-field class="col-12">
<input matInput formControlName="title" placeholder="{{'ADDEDITCOST-EDITOR.TITLE' | translate}}" required>
<mat-error *ngIf="formGroup.get('title').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<mat-form-field class="col-12">
<input matInput formControlName="value" placeholder="{{'ADDEDITCOST-EDITOR.VALUE' | translate}}" type="number">
<mat-error *ngIf="formGroup.get('value').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<div class="col-12">
<div class="row">
<div class="ml-auto col-auto"><button mat-raised-button mat-dialog-close type="button">{{'ADDEDITCOST-EDITOR.ACTIONS.CANCEL' | translate}}</button></div>
<div class="col-auto"><button mat-raised-button [disabled]="!isFormValid()" color="primary" (click)="addCost()" type="button">{{'ADDEDITCOST-EDITOR.ACTIONS.SAVE' | translate}}</button></div>
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,60 @@
import { Component, Inject, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ExternalResearcherService } from '@app/core/services/external-sources/researcher/external-researcher.service';
import { BaseComponent } from '@common/base/base.component';
import { takeUntil } from 'rxjs/operators';
import { CostEditorModel } from './add-cost.model';
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
import { Observable } from 'rxjs';
import { LocalFetchModel } from '@app/core/model/local-fetch/local-fetch.model';
import { CurrencyService } from '@app/core/services/currency/currency.service';
@Component({
selector: 'app-add-cost-component',
templateUrl: 'add-cost.component.html',
})
export class AddCostComponent extends BaseComponent implements OnInit {
public formGroup: FormGroup;
currencyAutoCompleteConfiguration: SingleAutoCompleteConfiguration = {
filterFn: this.searchCurrency.bind(this),
initialItems: () => this.searchCurrency(''),
displayFn: (item) => typeof (item) == 'string' ? JSON.parse(item)['name'] : item.name,
titleFn: (item) => typeof (item) == 'string' ? JSON.parse(item)['name'] : item.name,
valueAssign: (item) => JSON.stringify(item)
};
constructor(
private externalResearcherService: ExternalResearcherService,
public dialogRef: MatDialogRef<AddCostComponent>,
private currencyService: CurrencyService,
@Inject(MAT_DIALOG_DATA) public data: any
) { super(); }
ngOnInit(): void {
const cost = new CostEditorModel();
this.formGroup = cost.buildForm();
}
send(value: any) {
this.externalResearcherService.createResearcher(this.formGroup.value)
.pipe(takeUntil(this._destroyed))
.subscribe(
null, null, () => this.dialogRef.close()
);
}
addCost() {
this.dialogRef.close(this.formGroup.value);
}
isFormValid() {
return this.formGroup.valid;
}
searchCurrency(like: string): Observable<LocalFetchModel[]> {
return this.currencyService.get(like);
}
}

View File

@ -0,0 +1,44 @@
import { FormBuilder, FormGroup } from '@angular/forms';
import { ResearcherModel } from '@app/core/model/researcher/researcher';
import { BackendErrorValidator } from '@common/forms/validation/custom-validator';
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
import { ValidationContext } from '@common/forms/validation/validation-context';
import { OrganizationModel } from '@app/core/model/organisation/organization';
import { CostModel } from '@app/core/model/dmp/cost';
export class CostEditorModel implements CostModel{
public code: string;
public description: string;
public title: string;
public value: number;
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();
fromModel(item: CostModel): CostEditorModel {
this.code = item.code;
this.description = item.description;
this.title = item.title;
this.value = item.value;
return this;
}
buildForm(context: ValidationContext = null, disabled: boolean = false): FormGroup {
if (context == null) { context = this.createValidationContext(); }
const formGroup = new FormBuilder().group({
code: [{ value: this.code, disabled: disabled }, context.getValidation('code').validators],
description: [{ value: this.description, disabled: disabled }, context.getValidation('description').validators],
title: [{ value: this.title, disabled: disabled }, context.getValidation('title').validators],
value: [{ value: this.value, disabled: disabled }, context.getValidation('value').validators]
});
return formGroup;
}
createValidationContext(): ValidationContext {
const baseContext: ValidationContext = new ValidationContext();
baseContext.validation.push({ key: 'code', validators: [] });
baseContext.validation.push({ key: 'description', validators: [] });
baseContext.validation.push({ key: 'title', validators: [BackendErrorValidator(this.validationErrorModel, 'title')] });
baseContext.validation.push({ key: 'value', validators: [] });
return baseContext;
}
}

View File

@ -0,0 +1,42 @@
<mat-card class="row listing-container">
<ng-template #costTemplate let-cost let-i="index">
<div class="row">
<div class="col-auto">
<mat-form-field>
<app-single-auto-complete [formControl]="cost.get('code')" placeholder="{{'ADDEDITCOST-EDITOR.CODE' | translate}}" [configuration]="currencyAutoCompleteConfiguration"></app-single-auto-complete>
</mat-form-field>
</div>
<div class="col-auto">
<mat-form-field>
<input matInput placeholder="{{'ADDEDITCOST-EDITOR.DESCRIPTION' | translate}}" type="text" [formControl]="cost.get('description')">
</mat-form-field>
</div>
<div class="col-auto">
<mat-form-field>
<input matInput placeholder="{{'ADDEDITCOST-EDITOR.TITLE' | translate}}" type="text" [formControl]="cost.get('title')">
</mat-form-field>
</div>
<div class="col-auto">
<mat-form-field>
<input matInput placeholder="{{'ADDEDITCOST-EDITOR.VALUE' | translate}}" type="text" [formControl]="cost.get('value')">
</mat-form-field>
</div>
</div>
<div class="col-12">
<div class="row"*ngIf="cost.disabled">
<div class=" ml-auto col-auto"><button type="button" mat-raised-button color="primary" (click)="switchEditMode(i)">Edit</button></div>
<div class="col-auto"><button type="button" mat-raised-button color="red" (click)="removeCost(i)">Delete</button></div>
</div>
<div class="row"*ngIf="!cost.disabled">
<div class=" ml-auto col-auto" *ngIf="!cost.disabled"><button type="button" mat-raised-button color="primary" (click)="switchEditMode(i)">Save</button></div>
<div class="col-auto" *ngIf="!cost.disabled"><button type="button" mat-raised-button (click)="revertEdits(i)">Cancel</button></div>
</div>
</div>
</ng-template>
<mat-card class="col-12 cost-element" *ngFor="let cost of form['controls']; let i = index">
<ng-container *ngTemplateOutlet="costTemplate; context: { $implicit: cost, index: i }">
</ng-container>
</mat-card>
</mat-card>

View File

@ -0,0 +1,10 @@
.listing-container {
height: fit-content;
width: fit-content;
background-color: whitesmoke;
}
.cost-element {
margin-bottom: 1em;
}

View File

@ -0,0 +1,63 @@
import { Component, OnInit, Input } from '@angular/core';
import { BaseComponent } from '@common/base/base.component';
import { FormArray, FormControl } from '@angular/forms';
import { takeUntil } from 'rxjs/operators';
import { CostModel } from '@app/core/model/dmp/cost';
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
import { CurrencyService } from '@app/core/services/currency/currency.service';
import { Observable } from 'rxjs';
import { LocalFetchModel } from '@app/core/model/local-fetch/local-fetch.model';
import { CostEditorModel } from '../add-cost/add-cost.model';
@Component({
selector: 'app-cost-listing',
templateUrl: './cost-listing.component.html',
styleUrls: ['./cost-listing.component.scss']
})
export class CostListingComponent extends BaseComponent implements OnInit {
@Input() form: FormArray;
private cost: CostEditorModel[] = [];
currencyAutoCompleteConfiguration: SingleAutoCompleteConfiguration = {
filterFn: this.searchCurrency.bind(this),
initialItems: () => this.searchCurrency(''),
displayFn: (item) => typeof (item) == 'string' ? JSON.parse(item)['name'] : item.name,
titleFn: (item) => typeof (item) == 'string' ? JSON.parse(item)['name'] : item.name,
valueAssign: (item) => JSON.stringify(item)
};
constructor(
private currencyService: CurrencyService,
) {
super();
}
ngOnInit() {
}
searchCurrency(like: string): Observable<LocalFetchModel[]> {
return this.currencyService.get(like);
}
switchEditMode(event: number) {
const control = this.form.at(event);
if (control.disabled) {
this.cost[event] = control.value;
control.enable();
} else {
control.disable();
}
}
removeCost(event: number) {
this.form.removeAt(event);
}
revertEdits(event: number) {
this.form.at(event).setValue(this.cost[event]);
this.form.at(event).disable();
}
}

View File

@ -1,30 +1,53 @@
import { ValidationContext } from '@common/forms/validation/validation-context';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { BackendErrorValidator } from '@common/forms/validation/custom-validator';
import { CostModel } from '@app/core/model/dmp/cost';
import { isNullOrUndefined } from 'util';
import { CostEditorModel } from '../cost-editor/add-cost/add-cost.model';
export class ExtraPropertiesFormModel {
public language: string;
public license: string;
public visible: boolean;
public publicDate: Date;
public contact: string;
public costs: CostEditorModel[] = [];
fromModel(item: any): ExtraPropertiesFormModel {
this.language = item.language;
this.license = item.license;
this.visible = item.visible;
this.publicDate = item.publicDate;
this.contact = item.contact;
if (!isNullOrUndefined(item.costs)) {
(<any[]>item.costs).forEach(element => {
this.costs.push(new CostEditorModel().fromModel(element));
});
}
return this;
}
buildForm(context: ValidationContext = null, disabled: boolean = false): FormGroup {
if (context == null) { context = this.createValidationContext(); }
const formBuilder = new FormBuilder();
const formGroup = new FormBuilder().group({
language: [{ value: this.language, disabled: disabled }, context.getValidation('language').validators],
license: [{ value: this.license, disabled: disabled }, context.getValidation('license').validators],
visible: [{ value: this.visible, disabled: disabled }, context.getValidation('visible').validators],
publicDate: [{ value: this.publicDate, disabled: disabled }, context.getValidation('publicDate').validators]
publicDate: [{ value: this.publicDate, disabled: disabled }, context.getValidation('publicDate').validators],
contact: [{ value: this.contact, disabled: disabled }, context.getValidation('contact').validators],
// costs: [{ value: this.costs, disabled: disabled }, context.getValidation('costs').validators]
});
const costArray = new Array<FormGroup>();
//if (this.externalDatasets && this.externalDatasets.length > 0) {
this.costs.forEach(item => {
costArray.push(item.buildForm(context.getValidation('costs').descendantValidations, true));
});
// } else {
// //externalDatasetsFormArray.push(new ExternalDatasetModel().buildForm(context.getValidation('externalDatasets').descendantValidations, disabled));
// }
formGroup.addControl('costs', formBuilder.array(costArray));
return formGroup;
}
@ -34,6 +57,8 @@ export class ExtraPropertiesFormModel {
baseContext.validation.push({ key: 'license', validators: [] });
baseContext.validation.push({ key: 'visible', validators: [] });
baseContext.validation.push({ key: 'publicDate', validators: [] });
baseContext.validation.push({ key: 'contact', validators: [] });
baseContext.validation.push({ key: 'costs', validators: [] });
return baseContext;
}

View File

@ -88,7 +88,7 @@
<app-single-auto-complete [formControl]="formGroup.get('extraProperties').get('license')" placeholder="{{'DMP-EDITOR.FIELDS.LICENSE' | translate}}" [configuration]="licenseAutoCompleteConfiguration">
</app-single-auto-complete>
<mat-error *ngIf="formGroup.get('extraProperties').get('license').hasError('backendError')">
{{formGroup.get('extraProperties').get('language').getError('backendError').message}}</mat-error>
{{formGroup.get('extraProperties').get('license').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('extraProperties').get('license').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
@ -117,13 +117,36 @@
<input matInput [matDatepicker]="picker" [formControl]="formGroup.get('extraProperties').get('publicDate')" placeholder="{{'DMP-EDITOR.FIELDS.PUBLICATION' | translate}}">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
<mat-error *ngIf="formGroup.get('extraProperties').get('visible').hasError('backendError')">
{{formGroup.get('extraProperties').get('visible').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('extraProperties').get('visible').hasError('required')">
<mat-error *ngIf="formGroup.get('extraProperties').get('publicDate').hasError('backendError')">
{{formGroup.get('extraProperties').get('publicDate').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('extraProperties').get('publicDate').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<!-- <h4 mat-subheader class="col-12">{{'DMP-EDITOR.FIELDS.PROFILE' | translate}}</h4> -->
</div>
<div class="row pt-3">
<mat-form-field class="col-sm-12 col-md-8">
<!-- <app-multiple-auto-complete [formControl]="formGroup.get('extraProperties').get('language')" placeholder="{{'DMP-EDITOR.FIELDS.RESEARCHERS' | translate}}" [configuration]="researchersAutoCompleteConfiguration">
</app-multiple-auto-complete> -->
<mat-select [formControl]="formGroup.get('extraProperties').get('contact')" placeholder="{{'DMP-EDITOR.FIELDS.CONTACT' | translate}}">
<mat-option *ngFor="let vis of getAssociates()" [value]="vis.id">
{{vis.name | translate}}
</mat-option>
</mat-select>
<mat-error *ngIf="formGroup.get('extraProperties').get('contact').hasError('backendError')">
{{formGroup.get('extraProperties').get('contact').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('extraProperties').get('contact').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<!-- <h4 mat-subheader class="col-12">{{'DMP-EDITOR.FIELDS.PROFILE' | translate}}</h4> -->
</div>
<div class="row pt-3">
<mat-label class="col-12 cost-placeholder">Costs</mat-label>
<app-cost-listing class="col-12" [form] = "formGroup.get('extraProperties').get('costs')"></app-cost-listing>
<button class="col-12 cost-add" matSuffix class="input-btn" type="button" (click)="addCost($event)">
<mat-icon class="icon-btn">add_circle</mat-icon>
</button>
</div>
<div class="row pt-2">
<mat-form-field class="col-sm-12 col-md-8">
<app-single-auto-complete [required]="false" [formControl]="formGroup.get('profile')" placeholder="{{'DMP-EDITOR.FIELDS.TEMPLATE' | translate}}" [configuration]="dmpProfileAutoCompleteConfiguration">

View File

@ -37,3 +37,11 @@
::ng-deep .mat-form-field-appearance-legacy .mat-form-field-wrapper {
padding-bottom: 1.25em;
}
.cost-placeholder {
text-decoration: underline;
}
.cost-add {
margin-top: 1em;
}

View File

@ -1,6 +1,6 @@
import { Component, Input, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { FormGroup, FormArray } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { DataTableRequest } from '@app/core/model/data-table/data-table-request';
import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile';
@ -26,6 +26,8 @@ import { ConfigurationService } from '@app/core/services/configuration/configura
import { LanguageInfoService } from '@app/core/services/culture/language-info-service';
import { LanguageInfo } from '@app/core/model/language-info';
import { LicenseCriteria } from '@app/core/query/license/license-criteria';
import { AddCostComponent } from '../cost-editor/add-cost/add-cost.component';
import { CostEditorModel } from '../cost-editor/add-cost/add-cost.model';
interface Visible {
value: boolean;
@ -253,4 +255,27 @@ export class GeneralTabComponent extends BaseComponent implements OnInit {
getLanguageInfos(): LanguageInfo[] {
return this.languageInfoService.getLanguageInfoValues();
}
getAssociates(): any[] {
let associates: any[] = [];
//associates = (this.formGroup.get('researchers').value as any[]);
associates = associates.concat(this.formGroup.get('associatedUsers').value);
return associates;
}
addCost(event: MouseEvent) {
event.stopPropagation();
const dialogRef = this.dialog.open(AddCostComponent, {
data: this.formGroup.get('extraProperties').get('costs')
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
const costsArray = this.formGroup.get('extraProperties').get('costs').value || [];
costsArray.push(result);
let costeditModel: CostEditorModel = new CostEditorModel();
costeditModel = costeditModel.fromModel(result);
(<FormArray>this.formGroup.get('extraProperties').get('costs')).push(costeditModel.buildForm(null, true));
}
});
}
}

View File

@ -26,15 +26,9 @@
{{'DMP-OVERVIEW.PRIVATE' | translate}}
</div>
</div>
<div class="d-flex mr-4">
<div *ngIf="lockStatus" class="d-flex flex-row">
<mat-icon class="status-icon">lock_outline</mat-icon>
{{'DMP-OVERVIEW.LOCKED' | translate}}
</div>
<div *ngIf="!lockStatus" class="d-flex flex-row">
<mat-icon class="status-icon">lock_open</mat-icon>
{{'DMP-OVERVIEW.UNLOCKED' | translate}}
</div>
<div *ngIf="lockStatus" class="d-flex flex-row mr-4">
<mat-icon class="status-icon">lock_outline</mat-icon>
{{'DMP-OVERVIEW.LOCKED' | translate}}
</div>
<button class="d-flex mr-4 version-btn label2-txt"
(click)="viewVersions(dmp.groupId, dmp.label)">
@ -46,7 +40,7 @@
<div class="d-flex mr-4">
<div *ngIf="dmp.status" class="d-flex flex-row uppercase">
<mat-icon class="status-icon">check</mat-icon>
{{'DATASET-LISTING.COLUMNS.FINALIZED' | translate}}
{{'TYPES.DMP.FINALISED' | translate}}
</div>
</div>
</div>
@ -56,22 +50,16 @@
matTooltip="{{'DMP-LISTING.ACTIONS.CLONE' | translate}}" matTooltipPosition="above">
<mat-icon class="mat-mini-fab-icon">content_copy</mat-icon>
</button>
<button *ngIf="isDraftDmp(dmp) && isUserOwner" (click)="editClicked(dmp)" mat-mini-fab
<button *ngIf="isDraftDmp(dmp) && isUserOwner && !lockStatus" (click)="editClicked(dmp)" mat-mini-fab
class="mr-3 d-flex justify-content-center align-items-center"
matTooltip="{{'DMP-LISTING.ACTIONS.EDIT' | translate}}" matTooltipPosition="above">
<mat-icon class="mat-mini-fab-icon">create</mat-icon>
</button>
<button *ngIf="isDraftDmp(dmp) && isUserOwner" (click)="deleteClicked()" mat-mini-fab
<button *ngIf="isDraftDmp(dmp) && isUserOwner && !lockStatus" (click)="deleteClicked()" mat-mini-fab
class="mr-3 d-flex justify-content-center align-items-center"
matTooltip="{{'DMP-LISTING.ACTIONS.DELETE' | translate}}" matTooltipPosition="above">
<mat-icon class="mat-mini-fab-icon">delete</mat-icon>
</button>
<button *ngIf="isDraftDmp(dmp) && isUserOwner && !lockStatus" mat-mini-fab (click)="createOrUpdate(dmp.id)"
class="mr-3 d-flex justify-content-center align-items-center">
<mat-icon class="mat-mini-fab-icon" matTooltip="{{'DMP-OVERVIEW.LOCK' | translate}}"
matTooltipPosition="above">lock_outline
</mat-icon>
</button>
</div>
<div class="row header">{{'DMP-OVERVIEW.GRANT' | translate}}</div>
<div class="row dmp-label">{{ dmp.grant.label }}</div>
@ -141,7 +129,7 @@
</div>
</div>
<div class="frame mb-3 pt-4 pl-3 pr-5 pb-1">
<div *ngIf="!dmp.status && isDraftDmp(dmp) && isUserOwner">
<div *ngIf="!dmp.status && isDraftDmp(dmp) && isUserOwner && !lockStatus">
<div class="row ml-0 mr-0 pl-4 d-flex align-items-center" (click)="finalize(dmp)">
<button mat-mini-fab class="finalize-btn">
<mat-icon class="mat-mini-fab-icon">check</mat-icon>

View File

@ -161,7 +161,7 @@
.header {
opacity: 0.6;
margin-top: 1em;
margin-bottom: 0.25em;
margin-bottom: 0.5em;
}
.dmp-label, .header {

View File

@ -52,7 +52,6 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
isUserOwner: boolean;
expand = false;
hasDOIToken = false;
lock: LockModel;
lockStatus: Boolean;
textMessage: any;
@ -183,11 +182,12 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
}
datasetClicked(datasetId: String) {
if (this.isPublicView) {
this.router.navigate(['/datasets/publicEdit/' + datasetId]);
} else {
this.router.navigate(['/datasets/edit/' + datasetId]);
}
// if (this.isPublicView) {
// this.router.navigate(['/datasets/publicEdit/' + datasetId]);
// } else {
// this.router.navigate(['/datasets/edit/' + datasetId]);
// }
this.router.navigate(['/datasets/overview/' + datasetId]);
}
datasetsClicked(dmpId: String) {
@ -609,24 +609,6 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
}
}
createOrUpdate(id: string): void {
if (!this.lockStatus) {
this.lock = new LockModel(id, this.getUserFromDMP());
this.lockService.createOrUpdate(this.lock).pipe(takeUntil(this._destroyed)).subscribe(async result => {
this.lock.id = Guid.parse(result);
this.checkLockStatus(id);
// interval(this.configurationService.lockInterval).pipe(takeUntil(this._destroyed)).subscribe(() => this.pumpLock());
});
}
}
// private pumpLock() {
// this.lock.touchedAt = new Date();
// this.lockStatus = true;
// this.lockService.createOrUpdate(this.lock).pipe(takeUntil(this._destroyed)).subscribe(async result => this.lock.id = Guid.parse(result));
// }
// advancedClicked() {
// const dialogRef = this.dialog.open(ExportMethodDialogComponent, {

View File

@ -106,6 +106,7 @@
"LOGIN": "Login",
"DMP-OVERVIEW": "DMP Overview",
"DMP-EDIT": "Edit DMP",
"DATASET-OVERVIEW": "Dataset Overview",
"DATASET-EDIT": "Dataset View/Edit",
"DMP-NEW-VERSION": "DMP New Version",
"DMP-CLONE": "Clone DMP",
@ -567,7 +568,7 @@
}
},
"DATASET-OVERVIEW": {
"DATASET-AUTHORS": "Dataset description Authors",
"DATASET-AUTHORS": "Dataset description authors",
"ERROR": {
"DELETED-DATASET": "The requested dataset is deleted",
"FORBIDEN-DATASET": "You are not allowed to access this dataset"
@ -755,7 +756,8 @@
"LANGUAGE": "Language",
"LICENSE": "License",
"VISIBILITY": "Visibility",
"PUBLICATION": "Publication Date"
"PUBLICATION": "Publication Date",
"CONTACT": "Contact"
},
"ACTIONS": {
"GO-TO-GRANT": "Go To DMP Grant",
@ -1030,6 +1032,18 @@
"CANCEL": "Cancel"
}
},
"ADDEDITCOST-EDITOR": {
"ADD-TITLE": "Add a Cost",
"EDIT-TITLE": "Edit the Cost",
"CODE": "Code",
"DESCRIPTION": "Description",
"TITLE": "Title",
"VALUE": "Value",
"ACTIONS": {
"SAVE": "Save",
"CANCEL": "Cancel"
}
},
"DMP-WIZARD": {
"FIRST-STEP": {
"DMP": "DMP Editor",

View File

@ -105,6 +105,7 @@
"LOGIN": "Iniciar sesión",
"DMP-OVERVIEW": "Resumen del PGD",
"DMP-EDIT": "Editar el PGD",
"DATASET-OVERVIEW": "Resumen del Dataset",
"DATASET-EDIT": "Ver/Editar el Dataset",
"DMP-NEW-VERSION": "Nueva versiónd del PGD",
"DMP-CLONE": "Clonar el PGD",
@ -565,7 +566,7 @@
}
},
"DATASET-OVERVIEW": {
"DATASET-AUTHORS": "Descripciones del Dataset Autores",
"DATASET-AUTHORS": "Descripciones del dataset autores",
"ERROR": {
"DELETED-DATASET": "El Dataset solicitado está borrado",
"FORBIDEN-DATASET": "No tiene permiso para acceder a esto Dataset"

View File

@ -105,6 +105,7 @@
"LOGIN": "Σύνδεση",
"DMP-OVERVIEW": "Επισκόπηση Σχεδίου Διαχείρισης Δεδομένων",
"DMP-EDIT": "Επεξεργασία Σχεδίου Διαχείρισης Δεδομένων",
"DATASET-OVERVIEW": "Επισκόπηση Συνόλου Δεδομένων",
"DATASET-EDIT": "Προβολή / Επεξεργασία Συνόλου Δεδομένων",
"DMP-NEW-VERSION": "Νέα έκδοση Σχεδίου Διαχείρισης Δεδομένων",
"DMP-CLONE": "Κλωνοποίηση Σχεδίου Διαχείρισης Δεδομένων",
@ -566,7 +567,7 @@
}
},
"DATASET-OVERVIEW": {
"DATASET-AUTHORS": "Συγγραφείς Περιγραφής Δεδομένων",
"DATASET-AUTHORS": "Συγγραφείς περιγραφής δεδομένων",
"ERROR": {
"DELETED-DATASET": "H επιλεγμένη Περιγραφή Δεδομένων θα διαγραφεί",
"FORBIDEN-DATASET": "Δεν επιτρέπεται η πρόσβαση σε αυτή την Περιγραφή Δεδομένων"

View File

@ -51,7 +51,9 @@
</div>
</li>
<li class="nav-item"><a class="nav-link" href="../contact.html">CONTACT</a></li>
<li class="nav-item"><li class="nav-item"><a class="nav-link" href="/login">SIGN IN</a></li></li>
<li class="nav-item">
<li class="nav-item"><a class="nav-link" href="/login">SIGN IN</a></li>
</li>
</ul>
</div>
</div>
@ -60,7 +62,7 @@
<section class="page-section how-it-works" id="how-it-works">
<div class="container-small">
<div class="col">
<div class="page-title">About</div>
<div class="page-title">FAQs</div>
</div>
<div class="col pt-5 pb-3">
<div class="row title-1">FAQs</div>
@ -108,7 +110,7 @@
</div>
<div class="collapse show" id="collapseFAQ-3">
<div class="faq-content">
<p>ARGOS is comprised of two main functionalities: DMP templates and Dataset Descriptions.
<div>ARGOS is comprised of two main functionalities: DMP templates and Dataset Descriptions.
Additional entities are Projects that link to funders and grants information.<br />ARGOS can
be used for:
<br /><br /><u style="padding:20px;"> A. viewing/ consulting publicly released DMPs and
@ -131,7 +133,7 @@
Examples may refer to embedding DMP and DMP tools in specific curricula or even utilization
of the tool for researchers and students familiarization with the concept and process, as
part of library instructions sessions.
</p>
</div>
</div>
</div>
</div>
@ -206,4 +208,4 @@
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
</body>
</html>
</html>

View File

@ -59,7 +59,7 @@
<section class="page-section how-it-works" id="how-it-works">
<div class="container-small">
<div class="col">
<div class="page-title">About</div>
<div class="page-title">How it works</div>
</div>
<div class="col pt-5 pb-2">
<div class="row title-1">How it works</div>