more frontend changes

This commit is contained in:
Diamantis Tziotzios 2023-12-29 18:36:02 +02:00
parent 7b28f499a8
commit de67f17603
16 changed files with 348 additions and 305 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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