more frontend changes
This commit is contained in:
parent
7b28f499a8
commit
de67f17603
|
@ -229,7 +229,7 @@ public class DmpController {
|
||||||
|
|
||||||
@GetMapping("{id}/token/{token}/invite-accept")
|
@GetMapping("{id}/token/{token}/invite-accept")
|
||||||
@Transactional
|
@Transactional
|
||||||
public ResponseEntity inviteUsers(@PathVariable("id") UUID id, @PathVariable("token") String token) throws InvalidApplicationException, JAXBException {
|
public ResponseEntity acceptInvitation(@PathVariable("id") UUID id, @PathVariable("token") String token) throws InvalidApplicationException, JAXBException {
|
||||||
logger.debug(new MapLogEntry("inviting users to dmp").And("id", id));
|
logger.debug(new MapLogEntry("inviting users to dmp").And("id", id));
|
||||||
|
|
||||||
this.dmpService.dmpInvitationAccept(token);
|
this.dmpService.dmpInvitationAccept(token);
|
||||||
|
|
|
@ -98,3 +98,14 @@ export interface DmpUserRemovePersist {
|
||||||
dmpId: Guid;
|
dmpId: Guid;
|
||||||
role: DmpUserRole;
|
role: DmpUserRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DmpUserInvitePersist {
|
||||||
|
users: DmpUserInviteTypePersist[];
|
||||||
|
role: DmpUserRole;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DmpUserInviteTypePersist {
|
||||||
|
userId: Guid;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { Injectable } from '@angular/core';
|
||||||
import { DmpStatus } from '@app/core/common/enum/dmp-status';
|
import { DmpStatus } from '@app/core/common/enum/dmp-status';
|
||||||
import { DmpUserRole } from '@app/core/common/enum/dmp-user-role';
|
import { DmpUserRole } from '@app/core/common/enum/dmp-user-role';
|
||||||
import { IsActive } from '@app/core/common/enum/is-active.enum';
|
import { IsActive } from '@app/core/common/enum/is-active.enum';
|
||||||
import { FileFormat } from '@app/core/model/file/file-format.model';
|
|
||||||
import { DmpDescriptionTemplateLookup } from '@app/core/query/dmp-description-template.lookup';
|
import { DmpDescriptionTemplateLookup } from '@app/core/query/dmp-description-template.lookup';
|
||||||
import { DmpLookup } from '@app/core/query/dmp.lookup';
|
import { DmpLookup } from '@app/core/query/dmp.lookup';
|
||||||
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
|
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
|
||||||
|
@ -16,7 +15,7 @@ import { catchError, map } from 'rxjs/operators';
|
||||||
import { nameof } from 'ts-simple-nameof';
|
import { nameof } from 'ts-simple-nameof';
|
||||||
import { BaseHttpParams } from '../../../../common/http/base-http-params';
|
import { BaseHttpParams } from '../../../../common/http/base-http-params';
|
||||||
import { InterceptorType } from '../../../../common/http/interceptors/interceptor-type';
|
import { InterceptorType } from '../../../../common/http/interceptors/interceptor-type';
|
||||||
import { CloneDmpPersist, Dmp, DmpPersist, DmpUser, DmpUserPersist, DmpUserRemovePersist, NewVersionDmpPersist } from '../../model/dmp/dmp';
|
import { CloneDmpPersist, Dmp, DmpPersist, DmpUser, DmpUserInvitePersist, DmpUserPersist, DmpUserRemovePersist, NewVersionDmpPersist } from '../../model/dmp/dmp';
|
||||||
import { AuthService } from '../auth/auth.service';
|
import { AuthService } from '../auth/auth.service';
|
||||||
import { ConfigurationService } from '../configuration/configuration.service';
|
import { ConfigurationService } from '../configuration/configuration.service';
|
||||||
import { BaseHttpV2Service } from '../http/base-http-v2.service';
|
import { BaseHttpV2Service } from '../http/base-http-v2.service';
|
||||||
|
@ -111,6 +110,20 @@ export class DmpService {
|
||||||
catchError((error: any) => throwError(error)));
|
catchError((error: any) => throwError(error)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inviteUsers(dmpId: Guid, item: DmpUserInvitePersist): Observable<any> {
|
||||||
|
const url = `${this.apiBase}/${dmpId}/invite-users`;
|
||||||
|
|
||||||
|
return this.http
|
||||||
|
.post<any>(url, item).pipe(
|
||||||
|
catchError((error: any) => throwError(error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptInvitation(dmpId: Guid, token: string): Observable<any> {
|
||||||
|
const url = `${this.apiBase}/${dmpId}/token/${token}/invite-accept`;
|
||||||
|
|
||||||
|
return this.http.get<any>(url).pipe(catchError((error: any) => throwError(error)));
|
||||||
|
}
|
||||||
|
|
||||||
public download(id: string, format: string): Observable<HttpResponse<Blob>> {
|
public download(id: string, format: string): Observable<HttpResponse<Blob>> {
|
||||||
//let headerDoc: HttpHeaders = this.headers.set('Content-Type', 'application/msword')
|
//let headerDoc: HttpHeaders = this.headers.set('Content-Type', 'application/msword')
|
||||||
return this.httpClient.get(`${this.apiBase}/${id}/export/${format}`, { responseType: 'blob', observe: 'response', headers: this.headers });
|
return this.httpClient.get(`${this.apiBase}/${id}/export/${format}`, { responseType: 'blob', observe: 'response', headers: this.headers });
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/serv
|
||||||
import { ReferenceService } from '@app/core/services/reference/reference.service';
|
import { ReferenceService } from '@app/core/services/reference/reference.service';
|
||||||
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
||||||
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
|
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
|
||||||
import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component';
|
import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dialog/dmp-invitation-dialog.component';
|
||||||
import { BaseComponent } from '@common/base/base.component';
|
import { BaseComponent } from '@common/base/base.component';
|
||||||
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
|
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
|
||||||
import { Guid } from '@common/types/guid';
|
import { Guid } from '@common/types/guid';
|
||||||
|
|
|
@ -36,7 +36,6 @@ import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||||
// DmpListingComponent,
|
// DmpListingComponent,
|
||||||
// DmpCriteriaComponent,
|
// DmpCriteriaComponent,
|
||||||
// DmpEditorComponent,
|
// DmpEditorComponent,
|
||||||
// DmpInvitationDialogComponent,
|
|
||||||
// InvitationAcceptedComponent,
|
// InvitationAcceptedComponent,
|
||||||
// DmpToDatasetDialogComponent,
|
// DmpToDatasetDialogComponent,
|
||||||
// DmpWizardComponent,
|
// DmpWizardComponent,
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<div class="dmp-invitation-dialog container-fluid" *ngIf="formGroup">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<h1 class="title">{{'DMP-USER-INVITATION-DIALOG.TITLE' | translate}}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto" (click)="closeDialog()">
|
||||||
|
<mat-icon class="close-icon">close</mat-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-content class="row content">
|
||||||
|
<mat-form-field class="col pt-0 pb-2 mb-4 search">
|
||||||
|
<app-multiple-auto-complete [formControl]="formGroup.get('users')" placeholder="{{'DMP-USER-INVITATION-DIALOG.FIELDS.USERS-PLACEHOLDER' | translate}}" [configuration]="usersAutoCompleteConfiguration" [showNoResultsLabel]="false" [separatorKeysCodes]="separatorKeysCodes" [minLength]="3">
|
||||||
|
</app-multiple-auto-complete>
|
||||||
|
</mat-form-field>
|
||||||
|
<p class="d-flex m-0 hint">
|
||||||
|
<mat-icon class="align-self-center mr-1">info_outlined</mat-icon>
|
||||||
|
{{'DMP-USER-INVITATION-DIALOG.FIELDS.USERS-HINT' | translate}}
|
||||||
|
</p>
|
||||||
|
<div class="col-12 d-flex justify-content-end align-items-center row m-0 pt-1 pb-1">
|
||||||
|
<div class="col">
|
||||||
|
<mat-form-field class="select-role">
|
||||||
|
<mat-select [formControl]="formGroup.get('role')">
|
||||||
|
<mat-option [value]="dmpUserRoleEnum.Owner">{{enumUtils.toDmpUserRoleString(dmpUserRoleEnum.Owner)}}</mat-option>
|
||||||
|
<mat-option [value]="dmpUserRoleEnum.Member">{{enumUtils.toDmpUserRoleString(dmpUserRoleEnum.Member)}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col mt-2">
|
||||||
|
<button mat-raised-button *ngIf="hasValue()" (click)="send()" type="button" class="invite-btn">{{'DMP-USER-INVITATION-DIALOG.ACTIONS.INVITE' | translate}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,150 @@
|
||||||
|
.dmp-invitation-dialog {
|
||||||
|
padding: 2.5rem;
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
width: 33em;
|
||||||
|
min-height: 14em;
|
||||||
|
padding: 0.28em 0.34em 0em 1.125em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
// margin-right: 20px;
|
||||||
|
padding: .4rem;
|
||||||
|
width: auto !important;
|
||||||
|
height: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-icon:hover {
|
||||||
|
background-color: #ECECED !important;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 2.375em;
|
||||||
|
font-weight: lighter;
|
||||||
|
color: #000000;
|
||||||
|
opacity: 0.8;
|
||||||
|
margin-bottom: 0.842em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: 31em;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .mat-form-field {
|
||||||
|
// background: #fafafa;
|
||||||
|
// border: 1px solid #d1d1d1;
|
||||||
|
// border-radius: 4px;
|
||||||
|
// }
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #212121;
|
||||||
|
opacity: 0.81;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-dialog-container {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
padding: 2px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .search {
|
||||||
|
.mat-form-field-infix {
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 0.6em 0 1em 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .mat-form-field-underline {
|
||||||
|
// display: none;
|
||||||
|
// }
|
||||||
|
// .mat-form-field-flex {
|
||||||
|
// padding: 0em;
|
||||||
|
// }
|
||||||
|
// .align-arrow-right {
|
||||||
|
// display: none;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-role {
|
||||||
|
width: 20% !important;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #848484;
|
||||||
|
height: min-content;
|
||||||
|
margin-right: 2rem;
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .select-role {
|
||||||
|
|
||||||
|
.mat-form-field-outline-start,
|
||||||
|
.mat-form-field-outline-gap,
|
||||||
|
.mat-form-field-outline-end {
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-select-arrow-wrapper {
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .select-role .mat-form-field-wrapper {
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invite-btn {
|
||||||
|
background: #ffffff 0% 0% no-repeat padding-box;
|
||||||
|
border: 1px solid var(--primary-color);
|
||||||
|
border-radius: 30px;
|
||||||
|
opacity: 1;
|
||||||
|
min-width: 101px;
|
||||||
|
height: 43px;
|
||||||
|
color: var(--primary-color);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invite-btn-disabled {
|
||||||
|
min-width: 6.64em;
|
||||||
|
height: 2.93em;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #b5b5b5;
|
||||||
|
border-radius: 30px;
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing: -0.35px;
|
||||||
|
color: #b5b5b5;
|
||||||
|
margin-bottom: 0.25em;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invite-btn:hover {
|
||||||
|
background: var(--primary-color);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blink {
|
||||||
|
0% {
|
||||||
|
border: 1px solid rgba(255, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
border: 1px solid rgba(255, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
border: 1px solid rgba(255, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep .invalid-email {
|
||||||
|
border: 1px solid red;
|
||||||
|
// animation: blink 1.4s infinite;
|
||||||
|
// animation-fill-mode: both;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,19 +2,26 @@
|
||||||
import { COMMA, ENTER } from '@angular/cdk/keycodes';
|
import { COMMA, ENTER } from '@angular/cdk/keycodes';
|
||||||
import { Component, Inject, OnInit } from '@angular/core';
|
import { Component, Inject, OnInit } from '@angular/core';
|
||||||
import { UntypedFormGroup } from '@angular/forms';
|
import { UntypedFormGroup } from '@angular/forms';
|
||||||
import { MatChipInputEvent } from '@angular/material/chips';
|
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { DmpUserRole } from '@app/core/common/enum/dmp-user-role';
|
import { DmpUserRole } from '@app/core/common/enum/dmp-user-role';
|
||||||
|
import { IsActive } from '@app/core/common/enum/is-active.enum';
|
||||||
|
import { DmpUserInvitePersist } from '@app/core/model/dmp/dmp';
|
||||||
import { User } from '@app/core/model/user/user';
|
import { User } from '@app/core/model/user/user';
|
||||||
import { RequestItem } from '@app/core/query/request-item';
|
import { UserLookup } from '@app/core/query/user.lookup';
|
||||||
|
import { DmpService } from '@app/core/services/dmp/dmp.service';
|
||||||
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
|
||||||
|
import { UserService } from '@app/core/services/user/user.service';
|
||||||
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
||||||
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
|
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
|
||||||
import { BaseComponent } from '@common/base/base.component';
|
import { BaseComponent } from '@common/base/base.component';
|
||||||
|
import { FilterService } from '@common/modules/text-filter/filter-service';
|
||||||
|
import { Guid } from '@common/types/guid';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { map, takeUntil } from 'rxjs/operators';
|
import { map, takeUntil } from 'rxjs/operators';
|
||||||
|
import { nameof } from 'ts-simple-nameof';
|
||||||
|
import { DmpInvitationDialogEditorModel } from './dmp-invitation-dialog.editor.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-invitation-dialog-component',
|
selector: 'app-invitation-dialog-component',
|
||||||
|
@ -23,37 +30,12 @@ import { map, takeUntil } from 'rxjs/operators';
|
||||||
})
|
})
|
||||||
export class DmpInvitationDialogComponent extends BaseComponent implements OnInit {
|
export class DmpInvitationDialogComponent extends BaseComponent implements OnInit {
|
||||||
|
|
||||||
public formGroup: UntypedFormGroup;
|
dmpId: Guid;
|
||||||
public filteredUsersAsync = false;
|
editorModel: DmpInvitationDialogEditorModel;
|
||||||
public filteredUsers: User[];
|
formGroup: UntypedFormGroup;
|
||||||
public emails: string[] = [];
|
dmpUserRoleEnum = DmpUserRole;
|
||||||
public roles = DmpUserRole;
|
|
||||||
visible = true;
|
|
||||||
selectable = true;
|
|
||||||
removable = true;
|
|
||||||
addOnBlur = true;
|
|
||||||
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
|
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
|
||||||
|
|
||||||
constructor(
|
|
||||||
public enumUtils: EnumUtils,
|
|
||||||
public route: ActivatedRoute,
|
|
||||||
public router: Router,
|
|
||||||
private language: TranslateService,
|
|
||||||
public dialogRef: MatDialogRef<DmpInvitationDialogComponent>,
|
|
||||||
private uiNotificationService: UiNotificationService,
|
|
||||||
@Inject(MAT_DIALOG_DATA) public data: any
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
//TODO refactor
|
|
||||||
// const invitation = new DmpInvitation();
|
|
||||||
// invitation.dataManagementPlan = this.data.dmpId;
|
|
||||||
// invitation.role = Role.Member;
|
|
||||||
// this.formGroup = invitation.buildForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
usersAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = {
|
usersAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = {
|
||||||
filterFn: this.filterUsers.bind(this),
|
filterFn: this.filterUsers.bind(this),
|
||||||
initialItems: (excludedItems: any[]) => this.filterUsers('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))),
|
initialItems: (excludedItems: any[]) => this.filterUsers('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))),
|
||||||
|
@ -74,61 +56,67 @@ export class DmpInvitationDialogComponent extends BaseComponent implements OnIni
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
add(event: MatChipInputEvent): void {
|
constructor(
|
||||||
const input = event.input;
|
public enumUtils: EnumUtils,
|
||||||
const value = event.value;
|
public route: ActivatedRoute,
|
||||||
if ((value || '').trim()) {
|
public router: Router,
|
||||||
this.emails.push(value.trim());
|
private language: TranslateService,
|
||||||
}
|
public dialogRef: MatDialogRef<DmpInvitationDialogComponent>,
|
||||||
if (input) {
|
private uiNotificationService: UiNotificationService,
|
||||||
input.value = '';
|
private dmpService: DmpService,
|
||||||
}
|
private userService: UserService,
|
||||||
|
private filterService: FilterService,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: any
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
this.dmpId = data.dmpId;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(email: string): void {
|
ngOnInit() {
|
||||||
const index = this.emails.indexOf(email);
|
this.editorModel = new DmpInvitationDialogEditorModel();
|
||||||
if (index >= 0) {
|
this.formGroup = this.editorModel.buildForm();
|
||||||
this.emails.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
send(value: any) {
|
send() {
|
||||||
let invitationObject: any = {};
|
if (!this.formGroup.valid) { return; }
|
||||||
invitationObject.dataManagementPlan = this.data.dmpId;
|
const value: DmpUserInvitePersist = this.formGroup.value;
|
||||||
invitationObject.role = this.formGroup.get('role').value;
|
// invitationObject.users.push(...(<any[]>this.formGroup.get('users').value).filter(user => typeof (user) === 'string').map(email => ({ email: email, name: email })));
|
||||||
invitationObject.users = [];
|
// invitationObject.users.push(...(<any[]>this.formGroup.get('users').value).filter(user => typeof (user) !== 'string'));
|
||||||
invitationObject.users.push(...(<any[]>this.formGroup.get('users').value).filter(user => typeof (user) === 'string').map(email => ({ email: email, name: email })));
|
// this.emails.forEach(email => {
|
||||||
invitationObject.users.push(...(<any[]>this.formGroup.get('users').value).filter(user => typeof (user) !== 'string'));
|
// invitationObject.users.push({ email: email, name: email });
|
||||||
//invitationObject.users.push(...this.formGroup.get('users').value);
|
// });
|
||||||
this.emails.forEach(email => {
|
|
||||||
invitationObject.users.push({ email: email, name: email });
|
|
||||||
});
|
|
||||||
|
|
||||||
//TODO refactor
|
this.dmpService.inviteUsers(this.dmpId, value)
|
||||||
// this.invitationService.inviteDmpInvitationUsers(invitationObject)
|
.pipe(takeUntil(this._destroyed))
|
||||||
// .pipe(takeUntil(this._destroyed))
|
.subscribe(
|
||||||
// .subscribe(
|
complete => {
|
||||||
// complete => {
|
this.dialogRef.close();
|
||||||
// this.dialogRef.close();
|
this.onCallbackSuccess();
|
||||||
// this.onCallbackSuccess();
|
},
|
||||||
// },
|
error => this.onCallbackError(error)
|
||||||
// error => this.onCallbackError(error)
|
);
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closeDialog(): void {
|
closeDialog(): void {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
filterUsers(value: string): Observable<User[]> {
|
filterUsers(like: string): Observable<User[]> {
|
||||||
this.filteredUsers = undefined;
|
//TODO: refactor. Change with a service that provides a list of the users assosiated with your account.
|
||||||
this.filteredUsersAsync = true;
|
const lookup: UserLookup = new UserLookup();
|
||||||
//TODO refactor
|
lookup.page = { size: 100, offset: 0 };
|
||||||
// const request = new RequestItem<DmpInvitationUserCriteria>();
|
// if (excludedIds && excludedIds.length > 0) { lookup.excludedIds = excludedIds; }
|
||||||
// request.criteria = { like: value };
|
// if (ids && ids.length > 0) { lookup.ids = ids; }
|
||||||
// return this.invitationService.getDmpInvitationUsers(request)
|
lookup.isActive = [IsActive.Active];
|
||||||
// .pipe(takeUntil(this._destroyed));
|
lookup.project = {
|
||||||
return null;
|
fields: [
|
||||||
|
nameof<User>(x => x.id),
|
||||||
|
nameof<User>(x => x.name)
|
||||||
|
]
|
||||||
|
};
|
||||||
|
lookup.order = { items: [nameof<User>(x => x.name)] };
|
||||||
|
if (like) { lookup.like = this.filterService.transformLike(like); }
|
||||||
|
return this.userService.query(lookup).pipe(takeUntil(this._destroyed), map(x => x.items));
|
||||||
}
|
}
|
||||||
|
|
||||||
hasValue(): boolean {
|
hasValue(): boolean {
|
||||||
|
@ -142,5 +130,4 @@ export class DmpInvitationDialogComponent extends BaseComponent implements OnIni
|
||||||
onCallbackError(errorResponse: any) {
|
onCallbackError(errorResponse: any) {
|
||||||
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.INVITATION-DIALOG.ERROR'), SnackBarNotificationLevel.Error);
|
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.INVITATION-DIALOG.ERROR'), SnackBarNotificationLevel.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
|
||||||
|
import { DmpUserRole } from "@app/core/common/enum/dmp-user-role";
|
||||||
|
import { DmpUserInvitePersist, DmpUserInviteTypePersist } from "@app/core/model/dmp/dmp";
|
||||||
|
import { BackendErrorValidator } from '@common/forms/validation/custom-validator';
|
||||||
|
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
|
||||||
|
import { Validation, ValidationContext } from '@common/forms/validation/validation-context';
|
||||||
|
|
||||||
|
export class DmpInvitationDialogEditorModel implements DmpUserInvitePersist {
|
||||||
|
users: DmpUserInviteTypePersist[];
|
||||||
|
role: DmpUserRole = DmpUserRole.User;
|
||||||
|
|
||||||
|
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();
|
||||||
|
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup {
|
||||||
|
if (context == null) { context = this.createValidationContext(); }
|
||||||
|
|
||||||
|
return this.formBuilder.group({
|
||||||
|
users: [{ value: this.users, disabled: disabled }, context.getValidation('users').validators],
|
||||||
|
role: [{ value: this.role, disabled: disabled }, context.getValidation('role').validators],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createValidationContext(): ValidationContext {
|
||||||
|
const baseContext: ValidationContext = new ValidationContext();
|
||||||
|
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||||
|
baseValidationArray.push({ key: 'users', validators: [BackendErrorValidator(this.validationErrorModel, 'users')] });
|
||||||
|
baseValidationArray.push({ key: 'role', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'role')] });
|
||||||
|
baseContext.validation = baseValidationArray;
|
||||||
|
return baseContext;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module';
|
||||||
|
import { RichTextEditorModule } from "@app/library/rich-text-editor/rich-text-editor.module";
|
||||||
|
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||||
|
import { DmpInvitationDialogComponent } from './dmp-invitation-dialog.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonUiModule, FormsModule, ReactiveFormsModule, AutoCompleteModule, RichTextEditorModule],
|
||||||
|
declarations: [DmpInvitationDialogComponent],
|
||||||
|
exports: [DmpInvitationDialogComponent]
|
||||||
|
})
|
||||||
|
export class DmpInvitationDialogModule {
|
||||||
|
constructor() { }
|
||||||
|
}
|
|
@ -1,75 +0,0 @@
|
||||||
<div class="form-container">
|
|
||||||
<form *ngIf="formGroup" [formGroup]="formGroup" class="m-0">
|
|
||||||
<div class="row d-flex justify-content-end m-0" (click)="closeDialog()">
|
|
||||||
<mat-icon class="close-icon">close</mat-icon>
|
|
||||||
</div>
|
|
||||||
<div class="row m-0">
|
|
||||||
<h1 mat-dialog-title class="title">{{'DMP-LISTING.ACTIONS.INVITE-AUTHORS' | translate}}</h1>
|
|
||||||
</div>
|
|
||||||
<div mat-dialog-content class="row content">
|
|
||||||
<mat-form-field class="col pt-0 pb-2 mb-4 search">
|
|
||||||
<!-- <mat-label>{{'INVITATION-EDITOR.AUTOCOMPLETE-USER-EMAIL' | translate}}</mat-label> -->
|
|
||||||
<app-multiple-auto-complete [formControl]="formGroup.get('users')" placeholder="{{'INVITATION-EDITOR.AUTOCOMPLETE-USER-EMAIL' | translate}}"
|
|
||||||
[configuration]="usersAutoCompleteConfiguration" [showNoResultsLabel]="false" [separatorKeysCodes]="separatorKeysCodes"
|
|
||||||
[minLength]="3">
|
|
||||||
</app-multiple-auto-complete>
|
|
||||||
</mat-form-field>
|
|
||||||
<p class="d-flex m-0 hint">
|
|
||||||
<mat-icon class="align-self-center mr-1">info_outlined</mat-icon>
|
|
||||||
{{'GENERAL.INVITATION-DIALOG.HINT' | translate}}
|
|
||||||
</p>
|
|
||||||
<div class="col-12 d-flex justify-content-end align-items-center row m-0 pt-1 pb-1">
|
|
||||||
<mat-form-field class="select-role">
|
|
||||||
<mat-select [formControl]="formGroup.get('role')">
|
|
||||||
<mat-option [value]="roles.Owner">{{enumUtils.toRoleString(roles.Owner)}}</mat-option>
|
|
||||||
<mat-option [value]="roles.Member">{{enumUtils.toRoleString(roles.Member)}}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
<button mat-raised-button *ngIf="hasValue()" (click)="send()" type="button" class="invite-btn mt-2">{{'DMP-LISTING.ACTIONS.INVITE-SHORT' | translate}}</button>
|
|
||||||
<button mat-raised-button *ngIf="!hasValue()" type="button" class="invite-btn-disabled mt-2">{{'DMP-LISTING.ACTIONS.INVITE-SHORT' | translate}}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- <form *ngIf="formGroup" [formGroup]="formGroup">
|
|
||||||
<h1 mat-dialog-title>{{'INVITATION-EDITOR.TITLE' | translate}} {{data.dmpName}}</h1>
|
|
||||||
<div mat-dialog-content> -->
|
|
||||||
<!-- User -->
|
|
||||||
<!-- <mat-form-field class="col">
|
|
||||||
<app-multiple-auto-complete [formControl]="formGroup.get('users')"
|
|
||||||
placeholder="{{'INVITATION-EDITOR.AUTOCOMPLETE-USER' | translate}}"
|
|
||||||
[configuration]="usersAutoCompleteConfiguration">
|
|
||||||
</app-multiple-auto-complete>
|
|
||||||
</mat-form-field> -->
|
|
||||||
|
|
||||||
<!-- Email -->
|
|
||||||
<!-- <mat-form-field class="example-chip-list col">
|
|
||||||
<mat-chip-list #chipList>
|
|
||||||
<mat-chip *ngFor="let email of emails" [selectable]="selectable" [removable]="removable"
|
|
||||||
(removed)="remove(email)">
|
|
||||||
{{email}}
|
|
||||||
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
|
|
||||||
</mat-chip>
|
|
||||||
<input placeholder="{{'INVITATION-EDITOR.AUTOCOMPLETE-EMAIL' | translate}}" [matChipInputFor]="chipList"
|
|
||||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="addOnBlur"
|
|
||||||
(matChipInputTokenEnd)="add($event)">
|
|
||||||
</mat-chip-list>
|
|
||||||
</mat-form-field> -->
|
|
||||||
|
|
||||||
<!-- <mat-form-field class="col">
|
|
||||||
<app-multiple-auto-complete required='false' [formControl]="formGroup.get('users').get('email')"
|
|
||||||
placeholder="{{'INVITATION-EDITOR.AUTOCOMPLETE-EMAIL' | translate}}"
|
|
||||||
[configuration]="emailAutoCompleteConfiguration" ()>
|
|
||||||
</app-multiple-auto-complete>
|
|
||||||
<mat-error *ngIf="formGroup.get('users').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field> -->
|
|
||||||
|
|
||||||
<!-- <div class="col-12 d-flex">
|
|
||||||
<button mat-raised-button color="primary" (click)="send()" type="button" class="ml-auto">{{'INVITATION-EDITOR.ACTIONS.SEND-INVITATION' | translate}}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form> -->
|
|
|
@ -1,135 +0,0 @@
|
||||||
.form-container {
|
|
||||||
width: 33em;
|
|
||||||
min-height: 14em;
|
|
||||||
padding: 0.28em 0.34em 0em 1.125em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
// margin-right: 20px;
|
|
||||||
padding: .4rem;
|
|
||||||
width: auto !important;
|
|
||||||
height: auto !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-icon:hover {
|
|
||||||
background-color: #ECECED !important;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 2.375em;
|
|
||||||
font-weight: lighter;
|
|
||||||
color: #000000;
|
|
||||||
opacity: 0.8;
|
|
||||||
margin-bottom: 0.842em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
width: 31em;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// .mat-form-field {
|
|
||||||
// background: #fafafa;
|
|
||||||
// border: 1px solid #d1d1d1;
|
|
||||||
// border-radius: 4px;
|
|
||||||
// }
|
|
||||||
|
|
||||||
.hint {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #212121;
|
|
||||||
opacity: 0.81;
|
|
||||||
}
|
|
||||||
|
|
||||||
::ng-deep .mat-dialog-container {
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search {
|
|
||||||
padding: 2px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
::ng-deep .search {
|
|
||||||
.mat-form-field-infix {
|
|
||||||
font-size: 1rem;
|
|
||||||
padding: 0.6em 0 1em 0 !important;
|
|
||||||
}
|
|
||||||
// .mat-form-field-underline {
|
|
||||||
// display: none;
|
|
||||||
// }
|
|
||||||
// .mat-form-field-flex {
|
|
||||||
// padding: 0em;
|
|
||||||
// }
|
|
||||||
// .align-arrow-right {
|
|
||||||
// display: none;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-role {
|
|
||||||
width: 20% !important;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #848484;
|
|
||||||
height: min-content;
|
|
||||||
margin-right: 2rem;
|
|
||||||
border: none;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
::ng-deep .select-role {
|
|
||||||
.mat-form-field-outline-start,
|
|
||||||
.mat-form-field-outline-gap,
|
|
||||||
.mat-form-field-outline-end {
|
|
||||||
border: none !important;
|
|
||||||
}
|
|
||||||
.mat-select-arrow-wrapper {
|
|
||||||
transform: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::ng-deep .select-role .mat-form-field-wrapper {
|
|
||||||
padding-bottom: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invite-btn {
|
|
||||||
background: #ffffff 0% 0% no-repeat padding-box;
|
|
||||||
border: 1px solid var(--primary-color);
|
|
||||||
border-radius: 30px;
|
|
||||||
opacity: 1;
|
|
||||||
min-width: 101px;
|
|
||||||
height: 43px;
|
|
||||||
color: var(--primary-color);
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invite-btn-disabled {
|
|
||||||
min-width: 6.64em;
|
|
||||||
height: 2.93em;
|
|
||||||
background: #ffffff;
|
|
||||||
border: 1px solid #b5b5b5;
|
|
||||||
border-radius: 30px;
|
|
||||||
font-weight: bold;
|
|
||||||
letter-spacing: -0.35px;
|
|
||||||
color: #b5b5b5;
|
|
||||||
margin-bottom: 0.25em;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invite-btn:hover {
|
|
||||||
background: var(--primary-color);
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes blink{
|
|
||||||
0% {border: 1px solid rgba(255, 0, 0, 0.2);}
|
|
||||||
50% {border: 1px solid rgba(255, 0, 0, 0.5);}
|
|
||||||
100% {border: 1px solid rgba(255, 0, 0, 0.2);}
|
|
||||||
}
|
|
||||||
|
|
||||||
:host ::ng-deep .invalid-email{
|
|
||||||
border: 1px solid red;
|
|
||||||
// animation: blink 1.4s infinite;
|
|
||||||
// animation-fill-mode: both;
|
|
||||||
}
|
|
|
@ -7,6 +7,7 @@ import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||||
import { CloneDmpDialogModule } from '../clone-dialog/dmp-clone-dialog.module';
|
import { CloneDmpDialogModule } from '../clone-dialog/dmp-clone-dialog.module';
|
||||||
import { NewVersionDmpDialogModule } from '../new-version-dialog/dmp-new-version-dialog.module';
|
import { NewVersionDmpDialogModule } from '../new-version-dialog/dmp-new-version-dialog.module';
|
||||||
import { DmpListingRoutingModule } from './dmp-listing.routing';
|
import { DmpListingRoutingModule } from './dmp-listing.routing';
|
||||||
|
import { DmpInvitationDialogModule } from '../invitation/dialog/dmp-invitation-dialog.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -15,6 +16,7 @@ import { DmpListingRoutingModule } from './dmp-listing.routing';
|
||||||
FormattingModule,
|
FormattingModule,
|
||||||
CloneDmpDialogModule,
|
CloneDmpDialogModule,
|
||||||
NewVersionDmpDialogModule,
|
NewVersionDmpDialogModule,
|
||||||
|
DmpInvitationDialogModule,
|
||||||
DmpListingRoutingModule
|
DmpListingRoutingModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
<div class="dmp-card-actions">
|
<div class="dmp-card-actions">
|
||||||
<a class="col-auto border-right pointer" [matMenuTriggerFor]="exportMenu"><span class="material-icons icon-align pr-2">open_in_new</span>{{'DMP-LISTING.ACTIONS.EXPORT' | translate}}</a>
|
<a class="col-auto border-right pointer" [matMenuTriggerFor]="exportMenu"><span class="material-icons icon-align pr-2">open_in_new</span>{{'DMP-LISTING.ACTIONS.EXPORT' | translate}}</a>
|
||||||
<a class="col-auto border-right pointer" *ngIf="isDraftDmp(dmp)" [routerLink]="['/plans/edit/' + dmp.id]" target="_blank"><span class="material-icons icon-align">add</span>{{'DMP-LISTING.ACTIONS.ADD-DESCRIPTION-SHORT' | translate}}</a>
|
<a class="col-auto border-right pointer" *ngIf="isDraftDmp(dmp)" [routerLink]="['/plans/edit/' + dmp.id]" target="_blank"><span class="material-icons icon-align">add</span>{{'DMP-LISTING.ACTIONS.ADD-DESCRIPTION-SHORT' | translate}}</a>
|
||||||
<a class="col-auto border-right pointer" *ngIf="isUserOwner(dmp)" (click)="openShareDialog(dmp.id, dmp.label)"><span class="material-icons icon-align pr-2">group_add</span>{{'DMP-LISTING.ACTIONS.INVITE-SHORT' | translate}}</a>
|
<a class="col-auto border-right pointer" *ngIf="isUserOwner(dmp)" (click)="inviteToDmp()"><span class="material-icons icon-align pr-2">group_add</span>{{'DMP-LISTING.ACTIONS.INVITE-SHORT' | translate}}</a>
|
||||||
<a class="col-auto border-right pointer" *ngIf="isAuthenticated()" (click)="cloneClicked()"><span class="material-icons icon-align pr-2">filter_none</span>{{'DMP-LISTING.ACTIONS.CLONE' | translate}}</a>
|
<a class="col-auto border-right pointer" *ngIf="isAuthenticated()" (click)="cloneClicked()"><span class="material-icons icon-align pr-2">filter_none</span>{{'DMP-LISTING.ACTIONS.CLONE' | translate}}</a>
|
||||||
<a class="col-auto border-right pointer" *ngIf="!isAuthenticated()" (click)="viewVersions(dmp)"><span class="material-icons icon-align pr-2">library_books</span>{{'DMP-LISTING.ACTIONS.VIEW-VERSION' | translate}}</a>
|
<a class="col-auto border-right pointer" *ngIf="!isAuthenticated()" (click)="viewVersions(dmp)"><span class="material-icons icon-align pr-2">library_books</span>{{'DMP-LISTING.ACTIONS.VIEW-VERSION' | translate}}</a>
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import { FileFormat } from '@app/core/model/file/file-format.model';
|
||||||
import { FileTransformerService } from '@app/core/services/file-transformer/file-transformer.service';
|
import { FileTransformerService } from '@app/core/services/file-transformer/file-transformer.service';
|
||||||
import { CloneDmpDialogComponent } from '../../clone-dialog/dmp-clone-dialog.component';
|
import { CloneDmpDialogComponent } from '../../clone-dialog/dmp-clone-dialog.component';
|
||||||
import { NewVersionDmpDialogComponent } from '../../new-version-dialog/dmp-new-version-dialog.component';
|
import { NewVersionDmpDialogComponent } from '../../new-version-dialog/dmp-new-version-dialog.component';
|
||||||
|
import { DmpInvitationDialogComponent } from '../../invitation/dialog/dmp-invitation-dialog.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-dmp-listing-item-component',
|
selector: 'app-dmp-listing-item-component',
|
||||||
|
@ -84,18 +85,16 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit {
|
||||||
return this.authentication.currentAccountIsAuthenticated();
|
return this.authentication.currentAccountIsAuthenticated();
|
||||||
}
|
}
|
||||||
|
|
||||||
openShareDialog(rowId: any, rowName: any) {
|
inviteToDmp() {
|
||||||
//TODO: add this.
|
const dialogRef = this.dialog.open(DmpInvitationDialogComponent, {
|
||||||
// const dialogRef = this.dialog.open(DmpInvitationDialogComponent, {
|
// height: '250px',
|
||||||
// // height: '250px',
|
// width: '700px',
|
||||||
// // width: '700px',
|
autoFocus: false,
|
||||||
// autoFocus: false,
|
restoreFocus: false,
|
||||||
// restoreFocus: false,
|
data: {
|
||||||
// data: {
|
dmpId: this.dmp.id
|
||||||
// dmpId: rowId,
|
}
|
||||||
// dmpName: rowName
|
});
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
editClicked(dmpId: String) {
|
editClicked(dmpId: String) {
|
||||||
|
|
|
@ -1780,6 +1780,17 @@
|
||||||
"NEW-VERSION": "Create New Version"
|
"NEW-VERSION": "Create New Version"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"DMP-USER-INVITATION-DIALOG": {
|
||||||
|
"TITLE": "Invite authors",
|
||||||
|
"FIELDS": {
|
||||||
|
"USERS": "Kat or kat@example.com",
|
||||||
|
"USERS-PLACEHOLDER": "Kat or kat@example.com",
|
||||||
|
"USERS-HINT": "Press comma or enter between authors"
|
||||||
|
},
|
||||||
|
"ACTIONS": {
|
||||||
|
"INVITE": "Invite"
|
||||||
|
}
|
||||||
|
},
|
||||||
"DMP-BLUEPRINT-LISTING": {
|
"DMP-BLUEPRINT-LISTING": {
|
||||||
"TITLE": "DMP Blueprints",
|
"TITLE": "DMP Blueprints",
|
||||||
"CREATE-DMP-BLUEPRINT": "Create DMP Blueprint",
|
"CREATE-DMP-BLUEPRINT": "Create DMP Blueprint",
|
||||||
|
|
Loading…
Reference in New Issue