This is an automated email from the ASF dual-hosted git repository. hshpak pushed a commit to branch feat/DATALAB-3009/share-with-user-on-another-project-pop-up-layout in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git
commit 155875e660e5243d81e4f72f00ef722d56e33d34 Author: Hennadii_Shpak <[email protected]> AuthorDate: Mon Sep 5 12:46:58 2022 +0300 initial commit --- .../src/main/resources/webapp/angular.json | 3 +- .../cluster-details/cluster-details.component.html | 30 +++--- .../cluster-details/cluster-details.component.ts | 9 +- .../image-action-dialog.component.html | 31 ++---- .../image-action-dialog.component.scss | 74 ------------- .../image-action-dialog.component.ts | 23 ++++- .../image-action-dialog.module.ts | 11 +- ...ion-dialog.module.ts => image-action.config.ts} | 24 ++--- ...tion-dialog.module.ts => image-action.model.ts} | 21 ++-- .../share-dialog/share-dialog.component.html | 111 ++++++++++++++++++++ .../share-dialog/share-dialog.component.scss | 114 +++++++++++++++++++++ .../share-dialog/share-dialog.component.ts | 110 ++++++++++++++++++++ .../share-user-data/share-user-data.component.html | 6 ++ .../share-user-data/share-user-data.component.scss | 43 ++++++++ .../share-user-data/share-user-data.component.ts | 22 ++++ .../terminate-dialog.component.html} | 27 +---- .../terminate-dialog.component.scss} | 66 +++++++++--- .../terminate-dialog.component.ts} | 21 ++-- .../src/app/resources/images/images.component.ts | 4 +- .../src/app/resources/images/images.model.ts | 19 +++- .../src/app/resources/images/images.service.ts | 11 ++ 21 files changed, 585 insertions(+), 195 deletions(-) diff --git a/services/self-service/src/main/resources/webapp/angular.json b/services/self-service/src/main/resources/webapp/angular.json index 5390f53c9..cc3beda92 100644 --- a/services/self-service/src/main/resources/webapp/angular.json +++ b/services/self-service/src/main/resources/webapp/angular.json @@ -14,7 +14,8 @@ "allowedCommonJsDependencies": [ "chart.js", "ng-daterangepicker", - "moment-timezone" + "moment-timezone", + "rxjs-compat" ], "aot": true, "outputPath": "dist", diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.html index b0d1eb66a..7870e7796 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.html +++ b/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.html @@ -130,7 +130,7 @@ <div class="m-top-10"> <p *ngFor="let item of resource.computational_url" class="ellipsis flex" (mouseleave)="hideCopyIcon()"> <span class="strong spark-url-desc">{{ item.description }}:</span> - <a + <a (click)="logAction(resource, environment, 'follow')" href="{{item.url}}" target="_blank" @@ -145,8 +145,8 @@ <span (click)="logAction(resource, environment, 'copy');$event.stopPropagation()" *ngIf="isCopyIconVissible[item.description]" - [matTooltip]="isCopied ? 'Copy ' + item.description + (item.description.indexOf('url') === -1 ? ' url' : ''): 'Copied'" - matTooltipPosition="above" + [matTooltip]="isCopied ? 'Copy ' + item.description + (item.description.indexOf('url') === -1 ? ' url' : ''): 'Copied'" + matTooltipPosition="above" class="copy-icon-wrapper" > <span class="link-icon" (click)="copyLink(item.url)" > @@ -156,8 +156,8 @@ </p> </div> </div> - <div - class="checkbox-group" + <div + class="checkbox-group" *ngIf="resource.image === 'docker.datalab-dataengine' && resource.status === 'running' && environment.image !== 'docker.datalab-zeppelin' @@ -170,13 +170,13 @@ <div class="checkbox-group"> <form [formGroup]="configurationForm" novalidate> <div class="config-details" [ngClass]="{ show: configuration?.nativeElement['checked'] || false }"> - <textarea - formControlName="configuration_parameters" + <textarea + formControlName="configuration_parameters" placeholder="Cluster configuration template, JSON" data-gramm_editor="false" ></textarea> <span class="danger_color" - *ngIf="!configurationForm.controls.configuration_parameters.valid + *ngIf="!configurationForm.controls.configuration_parameters.valid && configurationForm.controls['configuration_parameters'].dirty"> Configuration parameters is not in a valid format </span> @@ -185,23 +185,23 @@ </div> </div> <div *ngIf="environment.image === 'docker.datalab-zeppelin' && resource.status === 'running'"> - <small>Spark default configuration for Apache Zeppelin can not be changed from DataLab UI. + <small>Spark default configuration for Apache Zeppelin can not be changed from DataLab UI. Currently it can be done directly through Apache Zeppelin interpreter menu. - For more details please refer for Apache Zeppelin + For more details please refer for Apache Zeppelin <a href="https://zeppelin.apache.org/docs/0.9.0/usage/interpreter/overview.html" target="_blank">official documentation</a>. </small> </div> <div class="text-center m-top-30" *ngIf="configuration?.nativeElement['checked'] || false"> - <button - mat-raised-button type="button" - (click)="dialogRef.close()" + <button + mat-raised-button type="button" + (click)="dialogRef.close()" class="butt action" > Cancel </button> - <button - mat-raised-button type="submit" + <button + mat-raised-button type="submit" [disabled]="!configurationForm.valid" class="butt butt-success action" [ngClass]="{'not-allowed': !configurationForm.valid}" diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.ts index 1e6526b9e..1c6842c6d 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.ts +++ b/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.ts @@ -74,6 +74,7 @@ export class DetailComputationalResourcesComponent implements OnInit { if (this.resource.image === 'docker.datalab-dataengine') { this.getClusterConfiguration(); } + console.log(this.resource); } public isEllipsisActive($event): void { @@ -139,9 +140,9 @@ export class DetailComputationalResourcesComponent implements OnInit { }; this.auditService.sendDataToAudit( - { - resource_name: resource.computational_name, - info: JSON.stringify(clusterInfo), + { + resource_name: resource.computational_name, + info: JSON.stringify(clusterInfo), type: 'COMPUTE' } ).subscribe(); @@ -154,7 +155,7 @@ export class DetailComputationalResourcesComponent implements OnInit { public showCopyIcon(element) { this.isCopyIconVissible[element] = true; } - + public hideCopyIcon() { for (const key in this.isCopyIconVissible) { this.isCopyIconVissible[key] = false; diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.html index c09683c24..297a95bb7 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.html +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.html @@ -19,35 +19,22 @@ <div id="dialog-box"> <header class="dialog-header"> - <h4 class="modal-title">{{data.title}}</h4> + <h4 class="modal-title">{{data.title}}<span *ngIf="data.actionType === actionType.share">: {{data.imageName}}</span></h4> <button type="button" class="close" (click)="dialogRef.close()">×</button> </header> <section class="content"> - <p class="description" *ngIf="data.actionType === actionType.share"> - The image - <span class="shared-image-name">{{data.imageName}} </span> - <span>will be shared with all current user groups on the project with all the data and code.</span> - </p> - <div *ngIf="data.actionType === actionType.terminate" class="terminate-action__wrapper"> - <div class="list-header sans terminate-action__header--wrapper"> - <div class="image">Image</div> - <div class="status">Further status</div> - </div> - <div class="scrolling-content scrolling terminate-image-list__wrapper"> - <div class="image-name ellipsis">{{data.imageName}}</div> - <div class="status terminated">Terminated</div> - </div> - <p *ngIf="data.isShared" class="shared-warning">!The image is shared with other users.</p> - </div> - <p class="question center"> - Do you want proceed? - </p> - <div class="text-center m-top-30 m-bott-10"> + <ng-content select="[datalab-share-dialog]"> + </ng-content> + <ng-content select="[datalab-terminate-dialog]"> + </ng-content> + <div [hidden]="activeTabConfig && activeTabConfig.shareWith" class="text-center m-top-30 m-bott-10"> <button type="button" class="butt mat-raised-button" (click)="dialogRef.close()">No</button> <button + [disabled]="isApplyBtnDisabled" type="button" class="butt butt-success mat-raised-button" - (click)="dialogRef.close(true)">Yes + (click)="dialogRef.close(true)"> + {{confirmBtnName}} </button> </div> </section> diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.scss index 6facffe45..ec94d5903 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.scss +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.scss @@ -23,77 +23,3 @@ padding: 20px 30px; font-weight: 600; } - -.question { - color: #718ba6; -} - -.center { - text-align: center; -} - -.description { - margin-bottom: 20px; - color: #35afd5; - font-size: 14px; - text-align: center; - font-weight: 200; -} - -.image-name { - font-weight: 200; -} - -.shared-image-name { - font-weight: 600; -} - -.image-list { - display: flex; - justify-content: space-between; -} - -.terminate-action__wrapper { - width: 100%; -} - -.terminate-image-list__wrapper { - margin-bottom: 10px; - border-bottom: 1px solid rgba(0,0,0,.08); -} - -.terminate-action__header--wrapper, -.terminate-image-list__wrapper { - display: flex; - justify-content: space-between; - padding: 10px 20px; - line-height: 26px; - - & > .status { - text-align: right; - } -} - -.terminate-action__header--wrapper { - margin-top: 26px; -} - -.content { - & .terminate-image-list__wrapper { - & > .status { - font-weight: 300; - } - } -} - -.shared-warning { - margin-bottom: 10px; - color: red; - text-align: center; - font-weight: 400; -} - -.question, -.shared-warning { - line-height: 1; -} diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts index bcb2feaf0..2b7e46a1a 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts @@ -17,19 +17,38 @@ * under the License. */ -import { Component, Inject } from '@angular/core'; +import { Component, Inject, Input, OnInit } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { ImageActions, ImageActionModalData } from '../../images'; +import { DialogWindowTabConfig } from './image-action.model'; @Component({ selector: 'datalab-image-action-dialog', templateUrl: './image-action-dialog.component.html', styleUrls: ['./image-action-dialog.component.scss'] }) -export class ImageActionDialogComponent { +export class ImageActionDialogComponent implements OnInit { + @Input() activeTabConfig: DialogWindowTabConfig; + @Input() isApplyBtnDisabled: Boolean; + readonly actionType: typeof ImageActions = ImageActions; + + confirmBtnName!: string; + constructor( public dialogRef: MatDialogRef<ImageActionDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: ImageActionModalData, ) { } + + ngOnInit(): void { + this.createConfirmBtnName(); + } + + private createConfirmBtnName(): void { + const btnNameObj = { + share: 'Share', + terminate: 'Yes' + }; + this.confirmBtnName = btnNameObj[this.data.actionType]; + } } diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.module.ts index 9dff0d351..0892178e1 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.module.ts +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.module.ts @@ -20,15 +20,18 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ImageActionDialogComponent } from './image-action-dialog.component'; -import { NotificationDialogComponent } from '../../../shared/modal-dialog/notification-dialog'; import { MaterialModule } from '../../../shared/material.module'; +import { TerminateDialogComponent } from './terminate-dialog/terminate-dialog.component'; +import { ShareDialogComponent } from './share-dialog/share-dialog.component'; +import { FormsModule } from '@angular/forms'; +import { ShareUserDataComponent } from './share-user-data/share-user-data.component'; @NgModule({ - declarations: [ ImageActionDialogComponent ], - imports: [ CommonModule, MaterialModule ], - entryComponents: [ImageActionDialogComponent], + declarations: [ ImageActionDialogComponent, TerminateDialogComponent, ShareDialogComponent, ShareUserDataComponent ], + imports: [ CommonModule, MaterialModule, FormsModule ], + entryComponents: [TerminateDialogComponent , ShareDialogComponent], exports: [ ImageActionDialogComponent ] }) export class ImageActionDialogModule { } diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action.config.ts similarity index 58% copy from services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.module.ts copy to services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action.config.ts index 9dff0d351..4afac79bb 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.module.ts +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action.config.ts @@ -17,18 +17,16 @@ * under the License. */ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { ImageActionDialogComponent } from './image-action-dialog.component'; -import { NotificationDialogComponent } from '../../../shared/modal-dialog/notification-dialog'; -import { MaterialModule } from '../../../shared/material.module'; +export enum SharePlaceholder { + groupOrNameSearchInput = 'Enter user login or group name' +} +export enum TabName { + shareImage = 'shareImage', + shareWith = 'shareWith' +} - -@NgModule({ - declarations: [ ImageActionDialogComponent ], - imports: [ CommonModule, MaterialModule ], - entryComponents: [ImageActionDialogComponent], - exports: [ ImageActionDialogComponent ] -}) -export class ImageActionDialogModule { } +export enum UserDataTypeConfig { + group = 'GROUP', + user = 'USER' +} diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action.model.ts similarity index 58% copy from services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.module.ts copy to services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action.model.ts index 9dff0d351..8c16eecd5 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.module.ts +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action.model.ts @@ -17,18 +17,15 @@ * under the License. */ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { ImageActionDialogComponent } from './image-action-dialog.component'; -import { NotificationDialogComponent } from '../../../shared/modal-dialog/notification-dialog'; -import { MaterialModule } from '../../../shared/material.module'; +export interface DialogWindowTabConfig { + shareImage: boolean; + shareWith: boolean; +} +export interface UserData { + value: string; + type: UserDataType; +} +export type UserDataType = 'USER' | 'GROUP'; -@NgModule({ - declarations: [ ImageActionDialogComponent ], - imports: [ CommonModule, MaterialModule ], - entryComponents: [ImageActionDialogComponent], - exports: [ ImageActionDialogComponent ] -}) -export class ImageActionDialogModule { } diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.html new file mode 100644 index 000000000..8b7f10eb1 --- /dev/null +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.html @@ -0,0 +1,111 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. + --> + +<datalab-image-action-dialog [activeTabConfig]="activeTabConfig" [isApplyBtnDisabled]="isApplyBtnDisabled"> + <div datalab-share-dialog class="wrapper"> + <ul class="title__list"> + <li + class="title__item active-tab" + [ngClass]="{'active-tab': activeTabConfig.shareImage}" + (click)="onTabTitle(tabsName.shareImage)" + > + <button class="title__btn"> + <h4 class="title"> + SHARE IMAGE + </h4> + </button> + </li> + <li + class="title__item" + [ngClass]="{'active-tab': activeTabConfig.shareWith}" + (click)="onTabTitle(tabsName.shareWith)" + > + <button class="title__btn"> + <h4 class="title"> + SHARED WITH + </h4> + </button> + </li> + </ul> + + <div *ngIf="activeTabConfig.shareImage" class="search-input__wrapper"> + +<!-- TODO REMOVE IT WHEN REMOVING THE RADIO BUTTON FROM APP--> + + <div class="radio__wrapper"> + <div class="radio-input__wrapper"> + <label> + user + <input + name="user-radio" + type="radio" + [(ngModel)]="userNameOrGroup" + [value]="userDataTypeConfig.user"> + </label> + <label> + user group + <input + name="group-radio" + type="radio" + [(ngModel)]="userNameOrGroup" + [value]="userDataTypeConfig.group"> + </label> + </div> + <span [hidden]="!isSearchUserTouched" class="error">Select with whom to share the image</span> + </div> + <div class="input__wrapper"> + <input + class="search-input" + [placeholder]="placeholder.groupOrNameSearchInput" + [(ngModel)]="searchInput" + #searchUser="ngModel" + (keydown.enter)="onAddUser()" + required + > + <button + class="add-person__btn" + mat-icon-button + [disabled]="isAddUserDataBtnDisabled" + (click)="onAddUser()" + > + <mat-icon> person_add </mat-icon> + </button> + <span [hidden]="!isUserInputEmpty" class="error">This field is required</span> + <span [hidden]="!isLongInputMessage" class="error">Length can`t be more than 25 symbols</span> + </div> + <div class="user-list__wrapper scrolling"> + <ul class="user-list"> + <li *ngFor="let entity of temporaryUserList" class="user-list__item"> + <datalab-share-user-data (removeUserData)="onRemoveUserData($event, tabsName.shareImage)" [userData]="entity"></datalab-share-user-data> + </li> + </ul> + </div> + </div> + + <div *ngIf="activeTabConfig.shareWith" class="share-with__wrapper"> + <div class="user-list__wrapper scrolling"> + <ul class="user-list"> + <li *ngFor="let entity of userList" class="user-list__item"> + <datalab-share-user-data (removeUserData)="onRemoveUserData($event, tabsName.shareWith)" [userData]="entity"></datalab-share-user-data> + </li> + </ul> + </div> + </div> + </div> +</datalab-image-action-dialog> diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.scss new file mode 100644 index 000000000..ab614f5c6 --- /dev/null +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.scss @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +.title{ + color: #718ba6; + font-weight: 400; + + &__list { + display: flex; + justify-content: space-between; + margin-bottom: 30px; + } + + &__item { + width: 49%; + text-align: center; + border-bottom: 1px solid rgba(0,0,0,.08); + } + + &__btn { + width: 100%; + background-color: transparent; + border: none; + outline: none; + cursor: pointer; + } +} + +.search-input { + width: 80%; + color: #718ba6; + border: none; + border-bottom: 1px solid rgba(0,0,0,.08); + outline: none; + + &::placeholder { + color: #c6bcbc; + } + + &__wrapper { + } +} + +.active-tab { + border-bottom: 2px solid #35afd5; +} + +.error { + font-weight: 200; +} + +.add-person__btn { + position: absolute; + top: -12px; + right: 10px; +} + +.user-list { + display: flex; + flex-wrap: wrap; + max-height: 80px; + padding: 10px 0; + + &__wrapper{ + overflow-y: scroll; + } + + &__item { + position: relative; + margin-right: 20px; + margin-bottom: 20px; + } +} + +.input__wrapper { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + height: 35px; +} + +// TODO REMOVE IT WHEN REMOVING THE RADIO BUTTON FROM APP +//start +.radio__wrapper { + margin-bottom: 20px; + padding-left: 10px; + text-align: start; +} +input[name="user-radio"] { + margin-right: 20px; +} + +label { + font-weight: 300; +} +//end + diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.ts new file mode 100644 index 000000000..df605e04d --- /dev/null +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-dialog/share-dialog.component.ts @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { AfterViewInit, ChangeDetectionStrategy, Component, Inject, OnInit, ViewChild } from '@angular/core'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { ImageActionModalData } from '../../../images'; +import { SharePlaceholder, TabName, UserDataTypeConfig } from '../image-action.config'; +import { DialogWindowTabConfig, UserData, UserDataType } from '../image-action.model'; +import { NgModel } from '@angular/forms'; + +const mockedUserList: UserData[] = [ + { + value: 'Biba', + type: 'GROUP' + }, + { + value: 'Boba', + type: 'USER' + }, + { + value: '[email protected]', + type: 'USER' + }, + { + value: '[email protected]', + type: 'GROUP' + }, +]; + +@Component({ + selector: 'datalab-share-dialog', + templateUrl: './share-dialog.component.html', + styleUrls: ['./share-dialog.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ShareDialogComponent { + @ViewChild('searchUser') searchUser: NgModel; + + readonly placeholder: typeof SharePlaceholder = SharePlaceholder; + readonly tabsName: typeof TabName = TabName; + readonly userDataTypeConfig: typeof UserDataTypeConfig = UserDataTypeConfig; + + userList: UserData[] = mockedUserList; + temporaryUserList: UserData[] = []; + userNameOrGroup: UserDataType; + activeTabConfig: DialogWindowTabConfig = { + shareImage: true, + shareWith: false + }; + searchInput = ''; + + onAddUser(): void { + if (!this.searchInput) { + return; + } + const newUserEntity: UserData = { + value: this.searchInput, + type: this.userNameOrGroup + }; + this.temporaryUserList = [...this.temporaryUserList, newUserEntity]; + this.searchInput = ''; + } + + onTabTitle(tabName: keyof DialogWindowTabConfig): void { + Object.keys(this.activeTabConfig).forEach(item => this.activeTabConfig[item] = false); + this.activeTabConfig = {...this.activeTabConfig, [tabName]: true}; + } + + onRemoveUserData(userName: string, tabName: TabName): void { + this.temporaryUserList = this.temporaryUserList.filter(({value}) => value !== userName); + } + + get isApplyBtnDisabled(): boolean { + return this.searchInput.length > 25 || !Boolean(this.temporaryUserList.length); + } + + get isAddUserDataBtnDisabled(): boolean { + return !Boolean(this.userNameOrGroup) || this.searchInput.length > 25 || !Boolean(this.searchInput.length); + } + + get isSearchUserTouched(): boolean { + return this.searchUser?.control.touched && !Boolean(this.userNameOrGroup); + } + + get isUserInputEmpty(): boolean { + return this.searchUser?.control.touched + && !Boolean(this.searchInput.length) + && !Boolean(this.temporaryUserList.length); + } + + get isLongInputMessage() { + return this.searchInput.length > 25; + } +} diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.html new file mode 100644 index 000000000..81c60d5b1 --- /dev/null +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.html @@ -0,0 +1,6 @@ +<div class="user-data__wrapper"> + <span><mat-icon class="user-data__icon">{{userData.type === 'GROUP' ? 'people' : 'person'}}</mat-icon></span> + <span class="user-data__name">{{userData.value}} + <button type="button" (click)="removeUser(userData.value)" class="close-btn">×</button> +</span> +</div> diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.scss new file mode 100644 index 000000000..5bee7b44a --- /dev/null +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.scss @@ -0,0 +1,43 @@ +.user-data { + &__name { + position: relative; + padding: 2px 20px 2px 10px; + font-size: 13px; + font-weight: 400; + border-radius: 5px; + } + + &__icon { + position: absolute; + top: -5px; + left: 0; + display: flex; + justify-content: center; + align-items: center; + width: 30px; + height: 30px; + color: white; + font-size: 18px; + border-radius: 50%; + } + + &__icon, + &__name { + background-color: #D3F0F4; + } + + &__wrapper { + position: relative; + padding-left: 24px; + } +} + +.close-btn { + position: absolute; + top: 2px; + right: 4px; + background-color: transparent; + border: none; + outline: none; + cursor: pointer; +} diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.ts new file mode 100644 index 000000000..6550f2e58 --- /dev/null +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/share-user-data/share-user-data.component.ts @@ -0,0 +1,22 @@ +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { UserData } from '../image-action.model'; + +@Component({ + selector: 'datalab-share-user-data', + templateUrl: './share-user-data.component.html', + styleUrls: ['./share-user-data.component.scss'], +}) +export class ShareUserDataComponent implements OnInit { + @Input() userData: UserData; + + @Output() removeUserData: EventEmitter<string> = new EventEmitter<string>(); + + constructor() { } + + ngOnInit(): void { + } + + removeUser(userData: string): void { + this.removeUserData.emit(userData); + } +} diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component.html similarity index 60% copy from services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.html copy to services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component.html index c09683c24..532f4e2d0 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.html +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component.html @@ -17,18 +17,9 @@ ~ under the License. --> -<div id="dialog-box"> - <header class="dialog-header"> - <h4 class="modal-title">{{data.title}}</h4> - <button type="button" class="close" (click)="dialogRef.close()">×</button> - </header> - <section class="content"> - <p class="description" *ngIf="data.actionType === actionType.share"> - The image - <span class="shared-image-name">{{data.imageName}} </span> - <span>will be shared with all current user groups on the project with all the data and code.</span> - </p> - <div *ngIf="data.actionType === actionType.terminate" class="terminate-action__wrapper"> +<datalab-image-action-dialog> + <div datalab-terminate-dialog class="wrapper"> + <div class="terminate-action__wrapper"> <div class="list-header sans terminate-action__header--wrapper"> <div class="image">Image</div> <div class="status">Further status</div> @@ -42,13 +33,5 @@ <p class="question center"> Do you want proceed? </p> - <div class="text-center m-top-30 m-bott-10"> - <button type="button" class="butt mat-raised-button" (click)="dialogRef.close()">No</button> - <button - type="button" - class="butt butt-success mat-raised-button" - (click)="dialogRef.close(true)">Yes - </button> - </div> - </section> -</div> + </div> +</datalab-image-action-dialog> diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component.scss similarity index 52% copy from services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts copy to services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component.scss index bcb2feaf0..3b9c2a38f 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component.scss @@ -17,19 +17,55 @@ * under the License. */ -import { Component, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { ImageActions, ImageActionModalData } from '../../images'; - -@Component({ - selector: 'datalab-image-action-dialog', - templateUrl: './image-action-dialog.component.html', - styleUrls: ['./image-action-dialog.component.scss'] -}) -export class ImageActionDialogComponent { - readonly actionType: typeof ImageActions = ImageActions; - constructor( - public dialogRef: MatDialogRef<ImageActionDialogComponent>, - @Inject(MAT_DIALOG_DATA) public data: ImageActionModalData, - ) { } +.terminate-action__wrapper { + width: 100%; +} + +.terminate-image-list__wrapper { + margin-bottom: 10px; + border-bottom: 1px solid rgba(0,0,0,.08); +} + +.terminate-action__header--wrapper, +.terminate-image-list__wrapper { + display: flex; + justify-content: space-between; + padding: 10px 20px; + line-height: 26px; + + & > .status { + text-align: right; + } +} + +.terminate-action__header--wrapper { + margin-top: 26px; +} + +.wrapper { + & .terminate-image-list__wrapper { + & > .status { + font-weight: 300; + } + } +} + +.shared-warning { + margin-bottom: 10px; + color: red; + text-align: center; + font-weight: 400; +} + +.question, +.shared-warning { + line-height: 1; +} + +.question { + color: #718ba6; +} + +.center { + text-align: center; } diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component.ts similarity index 64% copy from services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts copy to services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component.ts index bcb2feaf0..0cba6292f 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/image-action-dialog.component.ts +++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component.ts @@ -17,19 +17,22 @@ * under the License. */ -import { Component, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { ImageActions, ImageActionModalData } from '../../images'; +import { Component, Inject, OnInit } from '@angular/core'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { ImageActionModalData } from '../../../images'; @Component({ - selector: 'datalab-image-action-dialog', - templateUrl: './image-action-dialog.component.html', - styleUrls: ['./image-action-dialog.component.scss'] + selector: 'datalab-terminate-dialog', + templateUrl: './terminate-dialog.component.html', + styleUrls: ['./terminate-dialog.component.scss'] }) -export class ImageActionDialogComponent { - readonly actionType: typeof ImageActions = ImageActions; +export class TerminateDialogComponent implements OnInit { + constructor( - public dialogRef: MatDialogRef<ImageActionDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: ImageActionModalData, ) { } + + ngOnInit(): void { + } + } diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts index a27af583a..dca4feafb 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts +++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts @@ -54,6 +54,7 @@ import { ImagesService } from './images.service'; import { ProgressBarService } from '../../core/services/progress-bar.service'; import { ImageDetailDialogComponent } from '../exploratory/image-detail-dialog/image-detail-dialog.component'; import { ActivatedRoute } from '@angular/router'; +import { TerminateDialogComponent } from '../exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component'; @Component({ selector: 'datalab-images', @@ -164,8 +165,9 @@ export class ImagesComponent implements OnInit, OnDestroy { const imageInfo = this.imagesService.createImageRequestInfo(image); const data = this.imagesService.createActionDialogConfig(image, actionType); const requestCallback = this.imagesService.getRequestByAction(actionType).bind(this.imagesService); + const component = this.imagesService.getComponentByAction(actionType); - this.dialog.open(ImageActionDialogComponent, { + this.dialog.open(component, { data, panelClass: 'modal-sm' }).afterClosed() diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts index 7957a0dda..09b32cce8 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts +++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts @@ -1,4 +1,21 @@ -import { ImageActions } from './images.config'; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ export interface ProjectImagesInfo { filterData: ImageFilterFormDropdownData; diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts index b58d610d4..a3dfee632 100644 --- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts +++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts @@ -16,6 +16,9 @@ import { } from './images.model'; import { ApplicationServiceFacade, UserImagesPageService } from '../../core/services'; import { ChangedColumnStartValue, FilterFormInitialValue, ModalTitle, SharedStatus } from './images.config'; +import { ShareDialogComponent } from '../exploratory/image-action-dialog/share-dialog/share-dialog.component'; +import { TerminateDialogComponent } from '../exploratory/image-action-dialog/terminate-dialog/terminate-dialog.component'; +import { ComponentType } from 'ngx-toastr'; @Injectable({ providedIn: 'root' @@ -203,6 +206,14 @@ export class ImagesService { return callbackList[actionType]; } + getComponentByAction(actionType): ComponentType<unknown> { + const componentList = { + share: ShareDialogComponent, + terminate: TerminateDialogComponent + }; + return componentList[actionType]; + } + initImagePageInfo(imagePageInfo: ProjectImagesInfo): void { this.getImagePageData(imagePageInfo.projectImagesInfos); this.getDropdownDataList(imagePageInfo.filterData); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
