You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datalab.apache.org by yt...@apache.org on 2021/08/10 07:59:18 UTC
[incubator-datalab] 08/09: [DATALAB-2347] refactored resources
components
This is an automated email from the ASF dual-hosted git repository.
ytykhun pushed a commit to branch DATALAB-2347
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git
commit 65060c13a4d49b12f7711076a0e40b6e38221ad4
Author: Yurii Tykhun <ty...@gmail.com>
AuthorDate: Mon Aug 9 16:58:17 2021 +0300
[DATALAB-2347] refactored resources components
---
.../bucket-browser/bucket-browser.component.html | 276 ++++----
.../bucket-browser/bucket-browser.component.scss | 502 +++++++-------
.../bucket-browser/bucket-browser.component.ts | 165 ++---
.../bucket-browser/bucket-browser.module.ts | 46 +-
.../bucket-confirmation-dialog.component.html | 98 ++-
.../bucket-confirmation-dialog.component.scss | 93 +--
.../bucket-confirmation-dialog.component.ts | 13 +-
.../bucket-browser/bucket-data.service.ts | 83 ++-
.../buckets-tree/bucket-tree.component.html | 26 +-
.../buckets-tree/bucket-tree.component.scss | 44 +-
.../buckets-tree/bucket-tree.component.ts | 4 +-
.../folder-tree/folder-tree.component.html | 56 +-
.../folder-tree/folder-tree.component.scss | 45 +-
.../folder-tree/folder-tree.component.ts | 108 +--
.../bucket-browser/upload-window.component.scss | 94 +--
.../cluster-details/cluster-details.component.html | 123 ++--
.../cluster-details/cluster-details.component.scss | 6 +-
.../cluster-details/cluster-details.component.ts | 38 +-
.../computational/cluster-details/index.ts | 10 +-
...utational-resource-create-dialog.component.html | 378 ++++++-----
...utational-resource-create-dialog.component.scss | 43 +-
...mputational-resource-create-dialog.component.ts | 33 +-
.../computational-resources-list.component.html | 46 +-
.../computational-resources-list.component.scss | 208 +++---
.../computational-resources-list.component.ts | 2 +-
.../ami-create-dialog.component.html | 20 +-
.../ami-create-dialog.component.scss | 4 +-
.../ami-create-dialog.component.ts | 15 +-
.../cost-details-dialog.component.html | 41 +-
.../cost-details-dialog.component.scss | 18 +-
.../create-environment.component.html | 306 +++++----
.../create-environment.component.scss | 11 +-
.../create-environment.component.ts | 23 +-
.../detail-dialog/detail-dialog.component.html | 378 ++++++-----
.../detail-dialog/detail-dialog.component.scss | 41 +-
.../detail-dialog/detail-dialog.component.ts | 53 +-
.../install-libraries.component.html | 730 +++++++++++----------
.../install-libraries.component.scss | 118 ++--
.../install-libraries.component.ts | 66 +-
.../install-libraries/install-libraries.model.ts | 15 +-
.../libraries-info.component.scss | 43 +-
.../manage-ungit/manage-ungit.component.html | 78 ++-
.../manage-ungit/manage-ungit.component.scss | 18 +-
.../src/app/resources/resources-grid/index.ts | 38 +-
.../resources-grid/resources-grid.component.html | 227 ++++---
.../resources-grid/resources-grid.component.scss | 112 ++--
.../resources-grid/resources-grid.component.ts | 103 +--
.../src/app/resources/resources.component.html | 42 +-
.../src/app/resources/resources.component.ts | 24 +-
.../webapp/src/app/resources/resources.module.ts | 30 +-
.../resources/scheduler/scheduler.component.html | 317 +++++----
.../resources/scheduler/scheduler.component.scss | 18 +-
.../app/resources/scheduler/scheduler.component.ts | 21 +-
53 files changed, 3092 insertions(+), 2358 deletions(-)
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html
index 93ff8f4..1c38bc3 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.html
@@ -26,79 +26,99 @@
<div class="dialog-content tabs">
<div class="submit m-bott-10 m-top-10">
<div class="left-side-butts">
- <span [matTooltip]="isQueueFull ? 'Previous upload is still in progress, please wait.' : 'You have not permission to upload data'" matTooltipPosition="above" matTooltipDisabled="{{this.bucketStatus.upload && !isQueueFull}}">
- <button
- mat-raised-button
- type="button"
- class="butt first-btn"
- [disabled]="!this.bucketStatus.upload || allDisable || isSelectionOpened || !path || isQueueFull"
- (click)="handleFileInput($event)"
+ <span
+ [matTooltip]="isQueueFull ? 'Previous upload is still in progress, please wait.' : 'You have not permission to upload data'"
+ matTooltipPosition="above"
+ matTooltipDisabled="{{this.bucketStatus.upload && !isQueueFull}}"
>
- <input
- [ngClass]="{'not-allowed': !this.bucketStatus.upload || allDisable || isSelectionOpened || !path || isQueueFull}"
- type="file"
- (change)="handleFileInput($event)"
- title=""
- multiple
+ <button
+ mat-raised-button
+ type="button"
+ class="butt first-btn"
+ [disabled]="!this.bucketStatus.upload || allDisable || isSelectionOpened || !path || isQueueFull"
+ (click)="handleFileInput($event)"
>
- Upload files
- </button>
- </span>
- <span [matTooltip]="'You have not permission to create folder'" matTooltipPosition="above" matTooltipDisabled="{{this.bucketStatus.upload}}">
- <button
- mat-raised-button
- type="button"
- class="butt"
- (click)="createFolder(selectedFolder)"
- [disabled]="!this.bucketStatus.upload || allDisable || !path || isSelectionOpened"
- >
- Create folder
- </button>
- </span>
- <span [matTooltip]="'You have not permission to delete data'" matTooltipPosition="above" matTooltipDisabled="{{this.bucketStatus.delete}}">
- <button
- type="button"
- class="butt"
- mat-raised-button
- (click)="fileAction('delete')"
- [disabled]="(!selected?.length && !selectedFolderForAction?.length) || !this.bucketStatus.delete || allDisable || !path || isSelectionOpened"
+ <input
+ [ngClass]="{'not-allowed': !this.bucketStatus.upload || allDisable || isSelectionOpened || !path || isQueueFull}"
+ type="file"
+ (change)="handleFileInput($event)"
+ title=""
+ multiple
+ />
+ Upload files
+ </button>
+ </span>
+ <span
+ [matTooltip]="'You have not permission to create folder'"
+ matTooltipPosition="above"
+ matTooltipDisabled="{{this.bucketStatus.upload}}"
>
- Delete
- </button>
- </span>
- <div class="action-select-wrapper" >
- <span class="action-button-wrapper">
<button
- type="button" class="butt actions-btn"
mat-raised-button
- [disabled]=" selectedItems?.length !== 1 || allDisable || !path || isSelectionOpened"
- (click)="toogleActions();$event.stopPropagation()"
+ type="button"
+ class="butt"
+ (click)="createFolder(selectedFolder)"
+ [disabled]="!this.bucketStatus.upload || allDisable || !path || isSelectionOpened"
>
- Actions <i class="material-icons" >{{ !isActionsOpen ? 'expand_more' : 'expand_less' }}</i>
+ Create folder
</button>
- </span>
- <div class="action-menu" *ngIf="isActionsOpen">
- <span [matTooltip]="'You have not permission to download data'" matTooltipPosition="above" matTooltipDisabled="{{this.bucketStatus.download}}">
+ </span>
+ <span
+ [matTooltip]="'You have not permission to delete data'"
+ matTooltipPosition="above"
+ matTooltipDisabled="{{this.bucketStatus.delete}}"
+ >
<button
- type="button" class="butt action-menu-item"
- [ngClass]="{'disabled': !selected?.length || this.selectedItems?.length > 1 || !this.bucketStatus.download || allDisable || isSelectionOpened}"
+ type="button"
+ class="butt"
mat-raised-button
- [disabled]=" !selected?.length || this.selectedItems?.length > 1 || !this.bucketStatus.download || allDisable || isSelectionOpened"
- (click)="fileAction('download');$event.stopPropagation()"
+ (click)="fileAction('delete')"
+ [disabled]="(!selected?.length && !selectedFolderForAction?.length) ||
+ !this.bucketStatus.delete || allDisable || !path || isSelectionOpened"
>
- Download
+ Delete
</button>
+ </span>
+ <div class="action-select-wrapper" >
+ <span class="action-button-wrapper">
+ <button
+ type="button"
+ class="butt actions-btn"
+ mat-raised-button
+ [disabled]=" selectedItems?.length !== 1 || allDisable || !path || isSelectionOpened"
+ (click)="toogleActions();$event.stopPropagation()"
+ >
+ Actions <i class="material-icons" >{{ !isActionsOpen ? 'expand_more' : 'expand_less' }}</i>
+ </button>
+ </span>
+ <div class="action-menu" *ngIf="isActionsOpen">
+ <span
+ [matTooltip]="'You have not permission to download data'"
+ matTooltipPosition="above"
+ matTooltipDisabled="{{this.bucketStatus.download}}"
+ >
+ <button
+ type="button"
+ class="butt action-menu-item"
+ [ngClass]="{'disabled': !selected?.length || this.selectedItems?.length > 1 || !this.bucketStatus.download || allDisable || isSelectionOpened}"
+ mat-raised-button
+ [disabled]=" !selected?.length || this.selectedItems?.length > 1 || !this.bucketStatus.download || allDisable || isSelectionOpened"
+ (click)="fileAction('download');$event.stopPropagation()"
+ >
+ Download
+ </button>
</span>
- <button
- type="button" class="butt action-menu-item"
- mat-raised-button
- (click)="copyPath();$event.stopPropagation()"
- >
- Copy path
- </button>
+ <button
+ type="button"
+ class="butt action-menu-item"
+ mat-raised-button
+ (click)="copyPath();$event.stopPropagation()"
+ >
+ Copy path
+ </button>
+ </div>
</div>
</div>
- </div>
<button
mat-raised-button
type="button"
@@ -111,51 +131,70 @@
</div>
<p class="path"><span>Bucket path:</span>
<span class="url ellipsis" [ngClass]="{'cursor-not-allow': bucketDataService.emptyFolder}">
- <span class="path-folder" *ngFor="let folder of this.objectPath">
- <span class="url-icon" *ngIf="this.objectPath.indexOf(folder) !== 0">
- >
- </span>
- <span class="url-folder" (click)="folderTreeComponent.showItem(folder);" [ngClass]="{'not-allowed': bucketDataService.emptyFolder}">{{folder.item}}</span>
- </span>
+ <span class="path-folder" *ngFor="let folder of this.objectPath">
+ <span class="url-icon" *ngIf="this.objectPath.indexOf(folder) !== 0">
+ >
+ </span>
+ <span
+ class="url-folder"
+ (click)="folderTreeComponent.showItem(folder);"
+ [ngClass]="{'not-allowed': bucketDataService.emptyFolder}"
+ >
+ {{folder.item}}
+ </span>
+ </span>
</span>
</p>
<div class="bucket-wrapper" [ngClass]="{'added-upload': addedFiles.length}" id="scrolling">
<div class="bucket-selection" [ngClass]="{'opened': isSelectionOpened}">
<div class="button-wrapper" [ngClass]="{'cursor-not-allow': bucketDataService.emptyFolder}">
- <i (click)="toggleBucketSelection()" class="material-icons close" *ngIf="!isSelectionOpened" [ngClass]="{'not-allowed': bucketDataService.emptyFolder}">chevron_right</i>
+ <i
+ (click)="toggleBucketSelection()"
+ class="material-icons close"
+ *ngIf="!isSelectionOpened"
+ [ngClass]="{'not-allowed': bucketDataService.emptyFolder}"
+ >
+ chevron_right
+ </i>
</div>
<datalab-bucket-tree
- [hidden]="!isSelectionOpened"
- (emitActiveBucket)=openBucket($event)
- [buckets]='buckets'
- [openedBucket]=this.bucketName
- >
- </datalab-bucket-tree>
+ [hidden]="!isSelectionOpened"
+ (emitActiveBucket)=openBucket($event)
+ [buckets]='buckets'
+ [openedBucket]=this.bucketName
+ ></datalab-bucket-tree>
</div>
<div class="navigation scrolling" [ngClass]="{'selection-opened': isSelectionOpened}" [hidden]="!path">
- <datalab-folder-tree
- (showFolderContent)=onFolderClick($event)
- (disableAll)=dissableAll($event)
- [folders]=folders
- [endpoint]=endpoint
- [cloud]="cloud"
- ></datalab-folder-tree>
+ <datalab-folder-tree
+ (showFolderContent)=onFolderClick($event)
+ (disableAll)=dissableAll($event)
+ [folders]=folders
+ [endpoint]=endpoint
+ [cloud]="cloud"
+ ></datalab-folder-tree>
</div>
<div class="directory" [ngClass]="{'selection-opened': isSelectionOpened}" [hidden]="!path">
<div class="folder-item t_header">
<div class="folder-item-wrapper header-wrapper folder-tree header-item">
<div class="name" [ngClass]="{'cursor-not-allow': bucketDataService.emptyFolder}">
- <span class="th_name" (click)="isFilterVisible = true" *ngIf="!isFilterVisible" [ngClass]="{'not-allowed': bucketDataService.emptyFolder}">Name</span>
- <div class="filter-files" *ngIf="isFilterVisible">
- <input _ngcontent-yns-c13=""
- [(ngModel)]="searchValue"
- (keyup)=filterObjects()
- class="form-control filter-field filter-name"
- placeholder="Filter by name" type="text"
- >
+ <span
+ class="th_name"
+ (click)="isFilterVisible = true"
+ *ngIf="!isFilterVisible"
+ [ngClass]="{'not-allowed': bucketDataService.emptyFolder}"
+ >
+ Name
+ </span>
+ <div class="filter-files" *ngIf="isFilterVisible">
+ <input
+ _ngcontent-yns-c13=""
+ [(ngModel)]="searchValue"
+ (keyup)=filterObjects()
+ class="form-control filter-field filter-name"
+ placeholder="Filter by name" type="text"
+ />
<span><i (click)="closeFilterInput()" class="material-icons close">close</i></span>
</div>
-
</div>
<div class="size"><span class="th_size">Size</span></div>
<div class="date"><span class="th_date">Last modified</span></div>
@@ -168,30 +207,32 @@
<datalab-checkbox
[checked]="file.isFolderSelected"
(toggleSelection)="toggleSelectedFile(file, 'folder')"
+ ></datalab-checkbox>
+ <i class="material-icons folder-icon folder-name">folder</i>
+ <span
+ class="item-name name-wrap ellipsis"
+ matTooltip="{{file.item}}"
+ matTooltipPosition="above"
+ matTooltipShowDelay="1000"
+ [matTooltipClass]="'full-size-tooltip'"
>
- </datalab-checkbox>
-
- <i class="material-icons folder-icon folder-name">folder</i>
- <span class="item-name name-wrap ellipsis"
- matTooltip="{{file.item}}"
- matTooltipPosition="above"
- matTooltipShowDelay="1000"
- [matTooltipClass]="'full-size-tooltip'"
- >
- {{file.item}}
+ {{file.item}}
</span>
</div>
<div class="size size-folder">-</div>
<div class="date" *ngIf="!file.isDownloading">-</div>
</div>
- <div class="folder-item-wrapper" (click)="toggleSelectedFile(file, 'file')" *ngIf="!file.children && file.item !== 'ا'">
+ <div
+ class="folder-item-wrapper"
+ (click)="toggleSelectedFile(file, 'file')"
+ *ngIf="!file.children && file.item !== 'ا'"
+ >
<div class="name name-file">
<datalab-checkbox
[checked]="file.isSelected"
[disabled]="file.isDownloading"
(toggleSelection)="toggleSelectedFile(file, 'file')"
- >
- </datalab-checkbox>
+ ></datalab-checkbox>
<i class="material-icons folder-icon" >description</i>
<span
class="item-name name-wrap ellipsis"
@@ -208,8 +249,7 @@
<div class="progress-wrapper" *ngIf="file.isDownloading">
<div class="progres">
<span class="progress-bar-text">{{file.progress || 0}}% Downloading...</span>
- <div class="bar" [ngStyle]="{width: file.progress + '%'}">
- </div>
+ <div class="bar" [ngStyle]="{width: file.progress + '%'}"></div>
</div>
</div>
</div>
@@ -218,7 +258,7 @@
</div>
<div class="loading-block" *ngIf="!path">
<div class="uploading">
- <p>Please wait until DataLab loads bucket: <span class="strong">{{bucketName}}</span>...</p>
+ <p>Please wait until DataLab loads bucket: <span class="strong">{{bucketName}}</span>...</p>
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div>
</div>
@@ -226,8 +266,19 @@
<div class="upload-window" *ngIf="addedFiles.length">
<header class="upload-header">
<h4 class="modal-title">Upload files</h4>
- <span class="close" matTooltip="Upload is still in progress, please wait." matTooltipPosition="above" [ngClass]="{'not-allow': isFileUploading}" [matTooltipDisabled]="!isFileUploading">
- <button type="button" class="close" (click)="closeUploadWindow()" [disabled]="isFileUploading" [ngClass]="{'not-allow': isFileUploading}" >×</button>
+ <span
+ class="close"
+ matTooltip="Upload is still in progress, please wait."
+ matTooltipPosition="above"
+ [ngClass]="{'not-allow': isFileUploading}" [matTooltipDisabled]="!isFileUploading"
+ >
+ <button
+ type="button"
+ class="close"
+ (click)="closeUploadWindow()"
+ [disabled]="isFileUploading"
+ [ngClass]="{'not-allow': isFileUploading}"
+ >×</button>
</span>
</header>
<ul class="upload-files scrolling">
@@ -239,7 +290,6 @@
<div class="state"></div>
<div class="remove"></div>
</div>
-
</li>
<li *ngFor="let file of addedFiles" class="file">
<div class="name">
@@ -254,15 +304,21 @@
</div>
<div class="second-block">
<div class="upload-path">
- <span class="ellipsis" matTooltip="{{file.path}}" matTooltipPosition="above" [matTooltipClass]="'full-size-tooltip'">{{file.path}}</span>
+ <span
+ class="ellipsis"
+ matTooltip="{{file.path}}"
+ matTooltipPosition="above"
+ [matTooltipClass]="'full-size-tooltip'"
+ >
+ {{file.path}}
+ </span>
</div>
<div class="size">{{file.size | convertFileSize}} </div>
<div class="state">
<span *ngIf="file.status === 'uploaded'" class="running">Uploaded</span>
<div class="progres" *ngIf="file.status === 'uploading'">
<span class="progress-bar-text">{{file.progress || 0}}% Uploading...</span>
- <div class="bar" [ngStyle]="{width: file.progress + '%'}">
- </div>
+ <div class="bar" [ngStyle]="{width: file.progress + '%'}"></div>
</div>
<span *ngIf="file.status === 'failed'" class="error">Failed</span>
<span *ngIf="file.status === 'waiting'" class="stopped">Waiting for uploading...</span>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss
index f8e6e11..d5a4eee 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.scss
@@ -25,53 +25,55 @@
font-size: 14px;
font-weight: 400;
- .loading-block{
- width: 80%;
- margin: 0 auto;
+ .loading-block {
display: flex;
align-items: center;
justify-content: center;
+ width: 80%;
height: 100%;
- .uploading{
+ margin: 0 auto;
+
+ .uploading {
width: 100%;
- text-align: center;
padding-bottom: 50px;
- p{
+ text-align: center;
+
+ p {
margin-bottom: 20px;
}
}
}
- .path{
+ .path {
+ display: flex;
margin: 0 4px 30px 0;
padding: 4px 4px 4px 0;
- color: rgba(0,0,0,.87);
+ color: rgba(0, 0, 0, 0.87);
overflow: hidden;
white-space: nowrap;
- display: flex;
- &-folder{
+ &-folder {
position: relative;
}
- .url{
+ .url {
+ display: block;
font-weight: 600;
overflow: hidden;
- display: block;
direction: rtl;
- &-folder{
- padding-left: 5px;
- padding-right: 5px;
- color: #00bcd4;
- cursor: pointer;
+ &-folder {
+ padding-left: 5px;
+ padding-right: 5px;
+ color: #00bcd4;
+ cursor: pointer;
- &:first-of-type{
- padding-left: 5px;
- }
- }
+ &:first-of-type {
+ padding-left: 5px;
+ }
+ }
- &-icon{
+ &-icon {
color: lightgrey;
}
}
@@ -79,7 +81,7 @@
bottom: 0;
- .dialog-content{
+ .dialog-content {
padding: 0 35px;
}
@@ -87,48 +89,48 @@
height: 500px;
}
- .submit{
+ .submit {
display: flex;
justify-content: space-between;
- .left-side-butts{
+ .left-side-butts {
display: flex;
}
- .butt:not(.action-menu-item){
+ .butt:not(.action-menu-item) {
position: relative;
- overflow: hidden;
margin: 10px;
+ overflow: hidden;
- &.actions-btn{
+ &.actions-btn {
margin: 10px 10px 0 10px;
padding-right: 38px;
- .material-icons{
- transition: ease-in-out 1s;
- font-size: 25px;
- position: absolute;
- top: 7px;
- right: 30px;
- }
+ .material-icons {
+ position: absolute;
+ top: 7px;
+ right: 30px;
+ font-size: 25px;
+ transition: ease-in-out 1s;
+ }
}
- &.first-btn{
+ &.first-btn {
margin-left: 0;
}
- &.refresh{
+ &.refresh {
margin-right: 0;
}
- &.action-menu-item{
- &:hover{
- color: #00bcd4;
+ &.action-menu-item {
+ &:hover {
+ color: #00bcd4;
background-color: #fafafa;
}
- &.disabled{
- &:hover{
+ &.disabled {
+ &:hover {
color: #577289;
}
}
@@ -137,266 +139,264 @@
}
}
-.bucket-wrapper{
- height: 57vh;
- border: 2px solid rgba(0,0,0,.12);
- border-radius: 5px;
+.bucket-wrapper {
display: flex;
width: 100%;
+ height: 57vh;
+ border: 2px solid rgba(0, 0, 0, 0.12);
+ border-radius: 5px;
- &.added-upload{
+ &.added-upload {
height: 40vh;
}
- .bucket-selection{
- position: relative;
- width: 2%;
- border-right: 2px solid rgba(0,0,0,.12);
- padding-top: 6px;
- transition: .2s;
-
- &.opened{
- width: 33.3%;
-
- .button-wrapper {
- text-align: right;
- left: auto;
-
- i{
- padding-right: 3px;
- }
- }
- }
-
- .button-wrapper {
- position: absolute;
- left: 0;
- right: 0;
- top: 9px;
- text-align: center;
-
- i{
- cursor: pointer;
- font-size: 18px;
-
- &:hover{
- color: #00bcd4;
- }
- }
- }
- }
-
- }
-
- .navigation{
- transition: .2s;
- width: 31.3%;
- height: 100%;
- overflow: auto;
+
+ .bucket-selection {
+ position: relative;
+ width: 2%;
padding-top: 6px;
+ border-right: 2px solid rgba(0, 0, 0, 0.12);
+ transition: 0.2s;
- &.selection-opened{
- width: 66.7%;
- }
+ &.opened {
+ width: 33.3%;
+
+ .button-wrapper {
+ text-align: right;
+ left: auto;
- .folder-tree{
- .folder{
- line-height: 30px;
+ i {
+ padding-right: 3px;
+ }
}
}
- .mat-tree-node{
- min-height: auto;
+ .button-wrapper {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 9px;
+ text-align: center;
+
+ i {
+ cursor: pointer;
+ font-size: 18px;
+
+ &:hover {
+ color: #00bcd4;
+ }
+ }
}
}
+}
+
+.navigation {
+ width: 31.3%;
+ height: 100%;
+ padding-top: 6px;
+ overflow: auto;
+ transition: 0.2s;
- .directory{
+ &.selection-opened {
width: 66.7%;
- max-height: 100%;
- font-size: 14px;
- font-weight: 400;
- position: relative;
- border-left: 2px solid rgba(0,0,0,.12);
+ }
- &.selection-opened{
- display: none;
+ .folder-tree {
+ .folder {
+ line-height: 30px;
}
+ }
+
+ .mat-tree-node {
+ min-height: auto;
+ }
+}
+
+.directory {
+ position: relative;
+ width: 66.7%;
+ max-height: 100%;
+ font-size: 14px;
+ font-weight: 400;
+ border-left: 2px solid rgba(0, 0, 0, 0.12);
- .folder-tree{
- overflow-x: auto;
- overflow-y: overlay;
- max-height: 100%;
- padding: 6px 15px 15px 15px;
+ &.selection-opened {
+ display: none;
+ }
- .name{
- width: 60%;
- display: flex;
- align-items: center;
+ .folder-tree {
+ max-height: 100%;
+ padding: 6px 15px 15px 15px;
+ overflow-x: auto;
+ overflow-y: overlay;
+
+ .name {
+ display: flex;
+ align-items: center;
+ width: 60%;
+ overflow: hidden;
+
+ .name-wrap {
+ padding-right: 10px;
overflow: hidden;
+ white-space: nowrap;
+ }
- .name-wrap {
- overflow: hidden;
- white-space: nowrap;
- padding-right: 10px;
+ &-folder {
+ .folder-name {
+ padding-left: 8px;
}
+ }
- &-folder{
- .folder-name{
- padding-left: 8px;
- }
+ &-file {
+ i {
+ transform: translateX(6px);
}
+ span.item-name {
+ padding-left: 4px;
- &-file{
- i{
- transform: translateX(6px);
- }
- span.item-name{
+ &.removed-checkbox {
padding-left: 4px;
-
- &.removed-checkbox{
- padding-left: 4px;
- }
}
}
}
+ }
- .size{
- width: 15%;
+ .size {
+ width: 15%;
- &-folder{
- padding-left: 4px;
- }
+ &-folder {
+ padding-left: 4px;
}
+ }
- .date{
- width: 25%;
+ .date {
+ width: 25%;
- .th_date{
- font-size: 13px;
- }
+ .th_date {
+ font-size: 13px;
}
+ }
- .progress-wrapper{
- flex:1;
- }
+ .progress-wrapper {
+ flex: 1;
+ }
- .material-icons.close{
- font-size: 15px;
- margin: 0 10px;
- cursor: pointer;
- }
+ .material-icons.close {
+ font-size: 15px;
+ margin: 0 10px;
+ cursor: pointer;
}
}
+}
- .folder-item{
- display: flex;
- align-items: center;
+.folder-item {
+ display: flex;
+ align-items: center;
- &.t_header{
- top: -41px;
- position: absolute;
- left: 0;
- right: 0;
+ &.t_header {
+ position: absolute;
+ top: -41px;
+ left: 0;
+ right: 0;
+
+ .folder-item-wrapper {
+ height: 52px;
+ }
+
+ .th_name {
+ padding-left: 29px;
+ font-size: 14px;
+ cursor: pointer;
- .folder-item-wrapper{
- height: 52px;
+ &:hover {
+ color: #00bcd4;
}
+ }
+
+ .filter-files {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ padding: 3px 0 3px 3px;
- .th_name{
- padding-left: 29px;
+ .filter-name {
+ height: 20px;
+ width: 90%;
font-size: 14px;
- cursor: pointer;
+ }
- &:hover{
- color: #00bcd4;
+ span {
+ transform: translateY(2px);
+
+ &:hover {
+ i {
+ color: #00bcd4;
+ }
}
}
+ }
- .filter-files{
- width: 100%;
- padding: 3px 0;
- padding-left: 3px;
- display: flex;
- align-items: center;
-
- .filter-name{
- font-size: 14px;
- height: 20px;
- width: 90%;
- }
+ .th_size {
+ padding-left: 3px;
+ font-size: 14px;
+ cursor: auto;
+ }
+
+ .th_date {
+ font-size: 14px !important;
+ cursor: auto;
+ }
+ }
+ .folder-item-wrapper {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ cursor: pointer;
+ color: rgba(0, 0, 0, 0.87);
- span{
- transform: translateY(2px);
+ &.header-item {
+ cursor: auto;
- &:hover{
- i{
- color: #00bcd4;
- }
- }
- }
+ .name {
+ margin-left: -3px;
}
+ }
- .th_size{
- font-size: 14px;
- padding-left: 3px;
- cursor: auto;
+ i {
+ color: rgb(232, 232, 232);
+ }
+
+ &:hover:not(.header-wrapper) {
+ color: #00bcd4;
+ transition: 0.3s ease-in-out;
+
+ i {
+ color: #00bcd4;
+ transition: 0.3s ease-in-out;
}
- .th_date{
- font-size: 14px !important;
- cursor: auto;
+ .empty-checkbox {
+ border-color: #00bcd4;
+ }
+
+ .progress-wrapper {
+ .progres {
+ border-color: #00bcd4 !important;
+ }
}
}
- .folder-item-wrapper{
- width: 100%;
- display: flex;
- justify-content: space-between;
- align-items: center;
- cursor: pointer;
- color: rgba(0,0,0,.87);
-
- &.header-item{
- cursor: auto;
-
- .name{
- margin-left: -3px;
- }
-
- }
-
- i{
- color: rgb(232, 232, 232);
- }
-
- &:hover:not(.header-wrapper){
- color: #00bcd4;
- transition: .3s ease-in-out;
- i{
- color: #00bcd4;
- transition: .3s ease-in-out;
- }
-
- .empty-checkbox{
- border-color: #00bcd4
- }
-
- .progress-wrapper{
-
- .progres{
- border-color: #00bcd4 !important;
- }
- }
- }
- }
- }
+ }
+}
.empty-checkbox {
+ position: relative;
min-width: 16px;
width: 16px;
height: 16px;
+ margin-top: 2px;
border-radius: 2px;
border: 2px solid lightgrey;
- margin-top: 2px;
- position: relative;
&.checked {
border-color: #35afd5;
@@ -404,19 +404,19 @@
}
.checked-checkbox {
+ position: absolute;
top: 0;
left: 4px;
width: 5px;
height: 10px;
border-bottom: 2px solid white;
border-right: 2px solid white;
- position: absolute;
transform: rotate(45deg);
}
}
-.folder-item-name{
- span{
+.folder-item-name {
+ span {
color: #000;
}
@@ -426,19 +426,19 @@
}
.upload-window {
+ height: 15vh;
margin-top: 2vh;
- border: 2px solid rgba(0, 0, 0, .12);
+ border: 2px solid rgba(0, 0, 0, 0.12);
border-radius: 5px;
background-color: white;
- height: 15vh;
color: black;
.upload-header {
+ position: relative;
+ height: 30px;
padding-left: 8px;
background: #f6fafe;
- height: 30px;
line-height: 30px;
- position: relative;
.modal-title {
width: 90%;
@@ -452,7 +452,7 @@
background: #f6fafe;
&::after {
- content: '';
+ content: "";
display: block;
}
}
@@ -479,14 +479,6 @@
}
}
-.events-none{
+.events-none {
pointer-events: none;
}
-
-
-
-
-
-
-
-
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts
index 6a6aacd..507ac2a 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.component.ts
@@ -17,21 +17,21 @@
* under the License.
*/
-import {Component, OnInit, ViewChild, Inject, OnDestroy} from '@angular/core';
+import { Component, OnInit, ViewChild, Inject, OnDestroy } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ToastrService } from 'ngx-toastr';
-import {ApplicationSecurityService, ManageUngitService, StorageService} from '../../core/services';
-
-import {FolderTreeComponent} from './folder-tree/folder-tree.component';
-import {BucketBrowserService, TodoItemNode} from '../../core/services/bucket-browser.service';
-import {FileUtils, HelpUtils} from '../../core/util';
-import {BucketDataService} from './bucket-data.service';
-import {BucketConfirmationDialogComponent} from './bucket-confirmation-dialog/bucket-confirmation-dialog.component';
-import {HttpEventType} from '@angular/common/http';
-import {CopyPathUtils} from '../../core/util/copyPathUtils';
-import {Subject} from 'rxjs';
-import {takeUntil} from 'rxjs/operators';
+import { ApplicationSecurityService, ManageUngitService, StorageService } from '../../core/services';
+
+import { FolderTreeComponent } from './folder-tree/folder-tree.component';
+import { BucketBrowserService } from '../../core/services/bucket-browser.service';
+import { FileUtils, HelpUtils } from '../../core/util';
+import { BucketDataService } from './bucket-data.service';
+import { BucketConfirmationDialogComponent } from './bucket-confirmation-dialog/bucket-confirmation-dialog.component';
+import { HttpEventType } from '@angular/common/http';
+import { CopyPathUtils } from '../../core/util/copyPathUtils';
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'datalab-bucket-browser',
@@ -69,7 +69,7 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
public isFileUploading: boolean;
public cloud: string;
- @ViewChild(FolderTreeComponent, {static: true}) folderTreeComponent;
+ @ViewChild(FolderTreeComponent, { static: true }) folderTreeComponent;
constructor(
@Inject(MAT_DIALOG_DATA) public data: any,
@@ -82,9 +82,7 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
public bucketDataService: BucketDataService,
private auth: ApplicationSecurityService,
private storage: StorageService,
- ) {
-
- }
+ ) { }
ngOnInit() {
this.bucketName = this.data.bucket;
@@ -113,10 +111,10 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
takeUntil(this.unsubscribe$)
)
.subscribe(tokens => {
- this.storage.storeTokens(tokens);
- this.isTokenRefreshing = false;
- this.sendFile();
- });
+ this.storage.storeTokens(tokens);
+ this.isTokenRefreshing = false;
+ this.sendFile();
+ });
}
public showItem(item): void {
@@ -130,7 +128,9 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
public toggleSelectedFile(file, type): void {
console.log(file, type);
- type === 'file' ? file.isSelected = !file.isSelected : file.isFolderSelected = !file.isFolderSelected;
+ type === 'file'
+ ? file.isSelected = !file.isSelected
+ : file.isFolderSelected = !file.isFolderSelected;
this.selected = this.folderItems.filter(item => item.isSelected);
this.selectedFolderForAction = this.folderItems.filter(item => item.isFolderSelected);
this.selectedItems = [...this.selected, ...this.selectedFolderForAction];
@@ -156,15 +156,18 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
if (toMany) {
files.length = 50;
}
+
if (toBigFile || toMany) {
- this.dialog.open(BucketConfirmationDialogComponent, {data: {
- items: {toBig: toBigFile, toMany: toMany}, type: 'upload_limitation'
- } , width: '550px'})
+ this.dialog.open(BucketConfirmationDialogComponent, {
+ data: {
+ items: { toBig: toBigFile, toMany: toMany }, type: 'upload_limitation'
+ }, width: '550px'
+ })
.afterClosed().subscribe((res) => {
- if (res) {
- this.checkQueue(files);
- }
- });
+ if (res) {
+ this.checkQueue(files);
+ }
+ });
} else {
this.checkQueue(files);
}
@@ -235,7 +238,7 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
async openResolveDialog(existFile) {
const dialog = this.dialog.open(BucketConfirmationDialogComponent, {
- data: {items: existFile, type: 'resolve_conflicts'} , width: '550px'
+ data: { items: existFile, type: 'resolve_conflicts' }, width: '550px'
});
return dialog.afterClosed().toPromise().then(result => {
return Promise.resolve(result);
@@ -257,7 +260,9 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
this.objectPath = event.pathObject;
this.path = event.path;
this.originFolderItems = this.folderItems.map(v => v);
- this.pathInsideBucket = this.path.indexOf('/') !== -1 ? this.path.slice(this.path.indexOf('/') + 1) + '/' : '';
+ this.pathInsideBucket = this.path.indexOf('/') !== -1
+ ? this.path.slice(this.path.indexOf('/') + 1) + '/'
+ : '';
this.folderItems.forEach(item => item.isSelected = false);
}
}
@@ -275,17 +280,17 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
}
public deleteAddedFile(file): void {
- if ( file.subscr && file.request) {
- this.dialog.open(BucketConfirmationDialogComponent, {data: {items: file, type: 'cancel'} , width: '550px'})
+ if (file.subscr && file.request) {
+ this.dialog.open(BucketConfirmationDialogComponent, { data: { items: file, type: 'cancel' }, width: '550px' })
.afterClosed().subscribe((res) => {
res && file.subscr.unsubscribe();
res && this.addedFiles.splice(this.addedFiles.indexOf(file), 1);
this.isFileUploading = this.addedFiles.some(v => v.status === 'uploading');
this.sendFile();
- }, () => {
- this.isFileUploading = this.addedFiles.some(v => v.status === 'uploading');
- this.sendFile();
- });
+ }, () => {
+ this.isFileUploading = this.addedFiles.some(v => v.status === 'uploading');
+ this.sendFile();
+ });
} else {
this.addedFiles.splice(this.addedFiles.indexOf(file), 1);
this.isFileUploading = this.addedFiles.some(v => v.status === 'uploading');
@@ -294,7 +299,7 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
}
private uploadNewFile(file): void {
- const path = file.path.indexOf('/') !== -1 ? this.path.slice(this.path.indexOf('/') + 1) : '';
+ const path = file.path.indexOf('/') !== -1 ? this.path.slice(this.path.indexOf('/') + 1) : '';
const fullPath = path ? `${path}/${file.name}` : file.name;
const formData = new FormData();
formData.append('size', file.file.size);
@@ -317,39 +322,43 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
if ((this.refreshTokenLimit > this.getTokenValidTime()) && !this.isTokenRefreshing) {
this.refreshToken();
}
+
if (waitUploading.length && uploading.length < this.uploadingQueueLength) {
if (!file) {
file = waitUploading[0];
}
+
file.status = 'uploading';
this.isFileUploading = this.addedFiles.some(v => v.status === 'uploading');
this.isQueueFull = this.addedFiles.some(v => v.status === 'waiting');
- file.subscr = file.request.subscribe((event: any) => {
- if (event.type === HttpEventType.UploadProgress) {
- file.progress = Math.round(95 * event.loaded / event.total);
- if (file.progress === 95 && !file.interval) {
- file.interval = setInterval(() => {
- if (file.progress < 99) {
- return file.progress++;
- }
- }, file.size < 1094967296 ? 12000 : 20000);
+ file.subscr = file.request
+ .subscribe(
+ (event: any) => {
+ if (event.type === HttpEventType.UploadProgress) {
+ file.progress = Math.round(95 * event.loaded / event.total);
+ if (file.progress === 95 && !file.interval) {
+ file.interval = setInterval(() => {
+ if (file.progress < 99) {
+ return file.progress++;
+ }
+ }, file.size < 1094967296 ? 12000 : 20000);
+ }
+ } else if (event['type'] === HttpEventType.Response) {
+ window.clearInterval(file.interval);
+ file.status = 'uploaded';
+ delete file.request;
+ this.sendFile(this.addedFiles.find(v => v.status === 'waiting'));
+ this.bucketDataService.refreshBucketdata(this.bucketName, this.endpoint);
}
- } else if (event['type'] === HttpEventType.Response) {
+ },
+ error => {
window.clearInterval(file.interval);
- file.status = 'uploaded';
+ file.status = 'failed';
delete file.request;
this.sendFile(this.addedFiles.find(v => v.status === 'waiting'));
- this.bucketDataService.refreshBucketdata(this.bucketName, this.endpoint);
}
- }, error => {
- window.clearInterval(file.interval);
- file.status = 'failed';
- delete file.request;
- this.sendFile(this.addedFiles.find(v => v.status === 'waiting'));
- }
- );
+ );
}
-
}
public refreshBucket(): void {
@@ -391,7 +400,8 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
.pipe(
takeUntil(this.unsubscribe$)
)
- .subscribe(event => {
+ .subscribe(
+ event => {
if (event['type'] === HttpEventType.DownloadProgress) {
selected[0].progress = Math.round(100 * event['loaded'] / selected[0].object.size);
}
@@ -402,7 +412,8 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
selected[0].progress = 0;
}, 1000);
}
- }, error => {
+ },
+ error => {
this.toastr.error(error.message || 'File downloading error!', 'Oops!');
selected[0]['isDownloading'] = false;
}
@@ -417,24 +428,24 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
dataForServer.push(...this.bucketDataService.serverData.map(v => v.object).filter(v => v.indexOf(object) === 0));
});
dataForServer = [...dataForServer, ...objects].filter((v, i, arr) => i === arr.indexOf(v));
- this.dialog.open(BucketConfirmationDialogComponent, {data: {items: itemsForDeleting, type: 'delete'} , width: '550px'})
+ this.dialog.open(BucketConfirmationDialogComponent, { data: { items: itemsForDeleting, type: 'delete' }, width: '550px' })
.afterClosed().subscribe((res) => {
- !res && this.clearSelection();
- res && this.bucketBrowserService.deleteFile({
- bucket: this.bucketName, endpoint: this.endpoint, 'objects': dataForServer
- })
- .pipe(
- takeUntil(this.unsubscribe$)
- )
- .subscribe(() => {
- this.bucketDataService.refreshBucketdata(this.bucketName, this.endpoint);
- this.toastr.success('Objects successfully deleted!', 'Success!');
- this.clearSelection();
- }, error => {
- this.toastr.error(error.message || 'Objects deleting error!', 'Oops!');
- this.clearSelection();
+ !res && this.clearSelection();
+ res && this.bucketBrowserService.deleteFile({
+ bucket: this.bucketName, endpoint: this.endpoint, 'objects': dataForServer
+ })
+ .pipe(
+ takeUntil(this.unsubscribe$)
+ )
+ .subscribe(() => {
+ this.bucketDataService.refreshBucketdata(this.bucketName, this.endpoint);
+ this.toastr.success('Objects successfully deleted!', 'Success!');
+ this.clearSelection();
+ }, error => {
+ this.toastr.error(error.message || 'Objects deleting error!', 'Oops!');
+ this.clearSelection();
+ });
});
- });
}
}
@@ -473,8 +484,4 @@ export class BucketBrowserComponent implements OnInit, OnDestroy {
this.searchValue = '';
this.filterObjects();
}
-
}
-
-
-
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.module.ts
index 21deedf..7f9260f 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-browser.module.ts
@@ -25,31 +25,31 @@ import { MaterialModule } from '../../shared/material.module';
import { ResourcesGridModule } from '../resources-grid';
import { ExploratoryEnvironmentCreateModule } from '../exploratory/create-environment';
-import {BucketBrowserComponent} from './bucket-browser.component';
-import {FolderTreeComponent} from './folder-tree/folder-tree.component';
-import {MatTreeModule} from '@angular/material/tree';
-import {BucketDataService} from './bucket-data.service';
-import {BucketConfirmationDialogComponent} from './bucket-confirmation-dialog/bucket-confirmation-dialog.component';
-import {BucketTreeComponent} from './buckets-tree/bucket-tree.component';
-import {ConvertFileSizePipeModule} from '../../core/pipes/convert-file-size';
-import {LocalDatePipeModule} from '../../core/pipes/local-date-pipe';
-import {ConvertActionPipeModule} from '../../core/pipes/convert-action-pipe';
-import {CheckboxModule} from '../../shared/checkbox';
+import { BucketBrowserComponent } from './bucket-browser.component';
+import { FolderTreeComponent } from './folder-tree/folder-tree.component';
+import { MatTreeModule } from '@angular/material/tree';
+import { BucketDataService } from './bucket-data.service';
+import { BucketConfirmationDialogComponent } from './bucket-confirmation-dialog/bucket-confirmation-dialog.component';
+import { BucketTreeComponent } from './buckets-tree/bucket-tree.component';
+import { ConvertFileSizePipeModule } from '../../core/pipes/convert-file-size';
+import { LocalDatePipeModule } from '../../core/pipes/local-date-pipe';
+import { ConvertActionPipeModule } from '../../core/pipes/convert-action-pipe';
+import { CheckboxModule } from '../../shared/checkbox';
@NgModule({
- imports: [
- CommonModule,
- FormsModule,
- ReactiveFormsModule,
- ResourcesGridModule,
- ExploratoryEnvironmentCreateModule,
- MaterialModule,
- MatTreeModule,
- ConvertFileSizePipeModule,
- LocalDatePipeModule,
- ConvertActionPipeModule,
- CheckboxModule
- ],
+ imports: [
+ CommonModule,
+ FormsModule,
+ ReactiveFormsModule,
+ ResourcesGridModule,
+ ExploratoryEnvironmentCreateModule,
+ MaterialModule,
+ MatTreeModule,
+ ConvertFileSizePipeModule,
+ LocalDatePipeModule,
+ ConvertActionPipeModule,
+ CheckboxModule
+ ],
declarations: [
BucketBrowserComponent,
FolderTreeComponent,
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.html
index 3d31f7d..b9e5253 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.html
@@ -21,7 +21,7 @@
<header class="dialog-header">
<h4 class="modal-title">
- <span>{{ data.type.toUpperCase() | convertaction}} </span>
+ <span>{{ data.type.toUpperCase() | convertaction}} </span>
</h4>
<button type="button" class="close" (click)="dialogRef.close()">×</button>
</header>
@@ -42,11 +42,15 @@
<div class="object">
<span *ngIf="object['children']"><i class="material-icons folder-icon" >folder</i></span>
<span *ngIf="!object['children']"><i class="material-icons folder-icon file-icon" >description</i></span>
- <div class="ellipsis"
- matTooltip="{{object['item']}}"
- matTooltipPosition="above"
- matTooltipShowDelay="1000"
- [matTooltipClass]="'full-size-tooltip'">{{object['item']}}</div>
+ <div
+ class="ellipsis"
+ matTooltip="{{object['item']}}"
+ matTooltipPosition="above"
+ matTooltipShowDelay="1000"
+ [matTooltipClass]="'full-size-tooltip'"
+ >
+ {{object['item']}}
+ </div>
</div>
<div class="size">{{object['children'] ? '-' : object['object'].size | convertFileSize}}</div>
</mat-list-item>
@@ -67,30 +71,51 @@
<div *ngIf="data.type === 'resolve_conflicts'">
<p>
<span
- class="strong upload-item-name ellipsis"
- matTooltip="{{data.items}}"
- matTooltipPosition="above"
- matTooltipShowDelay="1000"
- [matTooltipClass]="'full-size-tooltip'"
- >{{data.items}}</span> already exists in selected folder. How would you like to resolve this conflict?</p>
+ class="strong upload-item-name ellipsis"
+ matTooltip="{{data.items}}"
+ matTooltipPosition="above"
+ matTooltipShowDelay="1000"
+ [matTooltipClass]="'full-size-tooltip'"
+ >
+ {{data.items}}
+ </span> already exists in selected folder. How would you like to resolve this conflict?
+ </p>
+
<mat-radio-group
aria-labelledby="upload-radio-group-label"
class="upload-radio-group"
- [(ngModel)]="fileAction">
- <mat-radio-button class="upload-radio-button" *ngFor="let action of uploadActions" [value]="action">
+ [(ngModel)]="fileAction"
+ >
+ <mat-radio-button
+ class="upload-radio-button"
+ *ngFor="let action of uploadActions"
+ [value]="action"
+ >
{{action}}
</mat-radio-button>
</mat-radio-group>
+
<div class="repeat-for-all" >
- <div class="empty-checkbox" [ngClass]="{'checked': actionForAll}" (click)="toggleActionForAll();$event.stopPropagation()" >
+ <div
+ class="empty-checkbox"
+ [ngClass]="{'checked': actionForAll}"
+ (click)="toggleActionForAll();$event.stopPropagation()"
+ >
<span class="checked-checkbox" *ngIf="actionForAll"></span>
</div>
- <span class="repeat-message" (click)="toggleActionForAll();$event.stopPropagation()">Repeat for all remaining conflicts</span>
+ <span
+ class="repeat-message"
+ (click)="toggleActionForAll();$event.stopPropagation()"
+ >
+ Repeat for all remaining conflicts
+ </span>
</div>
</div>
<div *ngIf="data.type === 'cancel'">
- <p class="upload-message"><span>Cancel uploading file </span> <span class="strong ellipsis upload-item-name">{{data.items.name}}.</span></p>
+ <p class="upload-message">
+ <span>Cancel uploading file </span> <span class="strong ellipsis upload-item-name">{{data.items.name}}.</span>
+ </p>
<div class="text-center m-top-20">
<span class="strong">Do you want to proceed?</span>
</div>
@@ -105,15 +130,42 @@
</div>
<div class="text-center m-top-20" *ngIf="data.type === 'delete' || data.type === 'cancel' || data.type === 'upload_limitation'">
- <button mat-raised-button type="button" class="butt action" (click)="dialogRef.close()">No</button>
- <button mat-raised-button type="button" class="butt butt-success action" (click)="dialogRef.close(true)">Yes</button>
+ <button
+ mat-raised-button
+ type="button"
+ class="butt action"
+ (click)="dialogRef.close()"
+ >
+ No
+ </button>
+ <button
+ mat-raised-button
+ type="button"
+ class="butt butt-success action"
+ (click)="dialogRef.close(true)"
+ >
+ Yes
+ </button>
</div>
<div class="text-center m-top-20" *ngIf="data.type === 'resolve_conflicts'">
- <button mat-raised-button type="button" class="butt action" (click)="dialogRef.close()">Cancel</button>
- <button mat-raised-button type="button" class="butt butt-success action" (click)="submitResolving()">Continue</button>
+ <button
+ mat-raised-button
+ type="button"
+ class="butt action"
+ (click)="dialogRef.close()"
+ >
+ Cancel
+ </button>
+ <button
+ mat-raised-button
+ type="button"
+ class="butt butt-success action"
+ (click)="submitResolving()"
+ >
+ Continue
+ </button>
</div>
-
</div>
</div>
-</div>
+</div>
\ No newline at end of file
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.scss
index ab9ade2..554bdc3 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.scss
@@ -18,94 +18,107 @@
*/
.confirmation-dialog {
+ color: #718ba6;
- .folder-icon{
- color: rgb(232, 232, 232);
+ .folder-icon {
margin-right: 3px;
+ color: rgb(232, 232, 232);
transform: translateX(-2px);
- &.file-icon{
+
+ &.file-icon {
transform: translateX(-4px);
margin-right: -2px;
}
}
- h3{
+
+ h3 {
margin-bottom: 20px;
}
- color: #718ba6;
+
p {
+ margin: 0 0 10px;
font-size: 14px;
font-weight: 400;
- margin: 0;
- margin-bottom: 10px;
- &.upload-limit-message{
+
+ &.upload-limit-message {
text-align: center;
color: red;
font-size: 14px;
}
+
&.info {
font-weight: 500;
}
}
+
.resources {
- .mat-list-base .mat-list-item.delete-item{
+ .mat-list-base .mat-list-item.delete-item {
height: 30px;
}
.object {
- width: 70%;
display: flex;
align-items: center;
+ width: 70%;
padding-right: 10px;
}
.size {
width: 30%;
}
+
.scrolling-content.delete-list {
max-height: 200px;
- overflow-y: auto;
padding-top: 11px;
+ overflow-y: auto;
}
}
.list-header {
+ width: 100%;
border-top: 1px solid #edf1f5;
border-bottom: 1px solid #edf1f5;
color: #577289;
- width: 100%;
}
- .bottom-message{
+ .bottom-message {
padding-top: 20px;
text-align: center;
- .confirm-message{
+
+ .confirm-message {
+ min-height: 18px;
+ padding-top: 20px;
color: #ef5c4b;
font-size: 13px;
- min-height: 18px;
text-align: center;
- padding-top: 20px}
+ }
}
-.upload-item-name{
- max-width: 300px;
- display: inline-flex;
-}
-.mat-radio-button{
- font-family: inherit;
- .mat-radio-ripple{
- display: none;
+ .upload-item-name {
+ display: inline-flex;
+ max-width: 300px;
}
-}
- .upload-radio-group{
+
+ .mat-radio-button {
+ font-family: inherit;
+ .mat-radio-ripple {
+ display: none;
+ }
+ }
+
+ .upload-radio-group {
display: flex;
flex-direction: column;
+
.upload-radio-button {
padding: 10px 0;
.mat-radio-container {
width: 15px;
height: 15px;
- .mat-radio-outer-circle, .mat-radio-inner-circle {
+
+ .mat-radio-outer-circle,
+ .mat-radio-inner-circle {
width: 15px;
height: 15px;
border-color: lightgrey;
@@ -123,47 +136,53 @@
}
}
}
- .repeat-for-all{
+
+ .repeat-for-all {
display: flex;
margin-top: 15px;
margin-bottom: 40px;
- .repeat-message{
+
+ .repeat-message {
padding-left: 5px;
cursor: pointer;
}
}
+
.empty-checkbox {
+ position: relative;
min-width: 16px;
width: 16px;
height: 16px;
+ margin-top: 2px;
border-radius: 2px;
border: 2px solid lightgrey;
- margin-top: 2px;
- position: relative;
cursor: pointer;
+
&.checked {
border-color: #35afd5;
background-color: #35afd5;
}
+
.checked-checkbox {
+ position: absolute;
top: 0;
left: 4px;
width: 5px;
height: 10px;
border-bottom: 2px solid white;
border-right: 2px solid white;
- position: absolute;
transform: rotate(45deg);
}
}
}
-.upload-message{
+
+.upload-message {
display: flex;
justify-content: center;
- .upload-item-name{
- max-width: 340px;
+
+ .upload-item-name {
display: block;
+ max-width: 340px;
padding-left: 4px;
}
-}
-
+}
\ No newline at end of file
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.ts
index 7c8e49a..33620d0 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-confirmation-dialog/bucket-confirmation-dialog.component.ts
@@ -33,13 +33,12 @@ export class BucketConfirmationDialogComponent implements OnInit {
uploadActions = ['Replace existing object', 'Skip uploading object'];
fileAction: string = this.uploadActions[1];
actionForAll: boolean = false;
+
constructor(
@Inject(MAT_DIALOG_DATA) public data: any,
public dialogRef: MatDialogRef<BucketConfirmationDialogComponent>,
public toastr: ToastrService
- ) {
-
- }
+ ) { }
ngOnInit() {
if (this.data.type === 'delete') {
@@ -52,7 +51,11 @@ export class BucketConfirmationDialogComponent implements OnInit {
}
submitResolving() {
- const submitObj = {replaceObject: !this.uploadActions.indexOf(this.fileAction), forAll: this.actionForAll};
+ const submitObj = {
+ replaceObject: !this.uploadActions.indexOf(this.fileAction),
+ forAll: this.actionForAll
+ };
+
this.dialogRef.close(submitObj);
}
-}
+}
\ No newline at end of file
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-data.service.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-data.service.ts
index 8a5bf48..3f47421 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-data.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/bucket-data.service.ts
@@ -88,8 +88,7 @@ export class BucketDataService {
constructor(
private bucketBrowserService: BucketBrowserService,
- ) {
- }
+ ) { }
public refreshBucketdata(bucket, endpoint) {
let backetData = [];
@@ -114,59 +113,59 @@ export class BucketDataService {
}
public buildFileTree(obj: {[key: string]: any}, level: number): TodoItemNode[] {
- return Object.keys(obj).reduce<TodoItemNode[]>((accumulator, key) => {
- if (key === '') {
- return accumulator;
- }
- const value = obj[key];
- const node = new TodoItemNode();
- node.item = key;
- if (value === '') {
- node.children = this.buildFileTree({}, level + 1);
- return accumulator.concat(node);
- }
- if (Object.keys(value).filter(v => v !== 'obj').length > 0) {
- if (typeof value === 'object') {
- node.object = value.obj || {'bucket': node.item, 'object': '', 'size': '', 'lastModifiedDate': ''};
- delete value.obj;
- node.children = this.buildFileTree(value, level + 1);
- } else {
- node.item = value;
- }
+ return Object.keys(obj).reduce<TodoItemNode[]>((accumulator, key) => {
+ if (key === '') {
+ return accumulator;
+ }
+ const value = obj[key];
+ const node = new TodoItemNode();
+ node.item = key;
+ if (value === '') {
+ node.children = this.buildFileTree({}, level + 1);
+ return accumulator.concat(node);
+ }
+ if (Object.keys(value).filter(v => v !== 'obj').length > 0) {
+ if (typeof value === 'object') {
+ node.object = value.obj || {'bucket': node.item, 'object': '', 'size': '', 'lastModifiedDate': ''};
+ delete value.obj;
+ node.children = this.buildFileTree(value, level + 1);
} else {
- node.object = value.obj;
+ node.item = value;
}
- return accumulator.concat(node);
- }, []);
- }
+ } else {
+ node.object = value.obj;
+ }
+ return accumulator.concat(node);
+ }, []);
+ }
public insertItem(parent: TodoItemNode, name, isFile, emptyFolderObj?) {
if (parent.children) {
- if (isFile) {
- parent.children.unshift(name as TodoItemNode);
+ if (isFile) {
+ parent.children.unshift(name as TodoItemNode);
+ } else {
+ if (name) {
+ console.log('parentObject', parent.object);
+ const child = {item: name, children: [], object: JSON.parse(JSON.stringify(parent.object))};
+ child.object.object = child.object.object.replace(/ا/g, '') + child.item + '/';
+ parent.children.unshift(child as TodoItemNode);
} else {
- if (name) {
- console.log('parentObject', parent.object);
- const child = {item: name, children: [], object: JSON.parse(JSON.stringify(parent.object))};
- child.object.object = child.object.object.replace(/ا/g, '') + child.item + '/';
- parent.children.unshift(child as TodoItemNode);
- } else {
- parent.children.unshift({item: '', children: [], object: {}} as TodoItemNode);
- this.emptyFolder = emptyFolderObj;
- this._bucketData.next(this.data);
- }
+ parent.children.unshift({item: '', children: [], object: {}} as TodoItemNode);
+ this.emptyFolder = emptyFolderObj;
+ this._bucketData.next(this.data);
}
}
}
+ }
public updateItem(node: TodoItemNode, file) {
- node.item = file;
- this._bucketData.next(this.data);
+ node.item = file;
+ this._bucketData.next(this.data);
}
public removeItem(parent, child) {
- parent.children.splice( parent.children.indexOf(child), 1);
- this._bucketData.next(this.data);
+ parent.children.splice( parent.children.indexOf(child), 1);
+ this._bucketData.next(this.data);
}
public processFiles = (files, target, object) => {
@@ -179,7 +178,6 @@ export class BucketDataService {
if (!pointer.obj) {
pointer.obj = object;
}
-
});
}
@@ -197,5 +195,4 @@ export class BucketDataService {
}
return finalData;
}
-
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.html
index 621370c..e8f7d56 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.html
@@ -20,9 +20,11 @@
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
<mat-tree-node
*matTreeNodeDef="let node"
- matTreeNodePadding matTreeNodePaddingIndent="17"
+ matTreeNodePadding
+ matTreeNodePaddingIndent="17"
(click)="openBucketData(node)"
- [ngClass]="{'active-item': activeBacket === node}">
+ [ngClass]="{'active-item': activeBacket === node}"
+ >
<button mat-icon-button disabled></button>
<div
class="ellipsis"
@@ -30,15 +32,25 @@
matTooltipPosition="above"
matTooltipShowDelay="1000"
[matTooltipClass]="'full-size-tooltip'"
- >{{node.name}}</div>
+ >
+ {{node.name}}
+ </div>
</mat-tree-node>
- <mat-tree-node *matTreeNodeDef="let node;when: hasChild" matTreeNodePadding matTreeNodePaddingIndent="17">
- <button mat-icon-button matTreeNodeToggle
- [attr.aria-label]="'toggle ' + node.name">
+
+ <mat-tree-node
+ *matTreeNodeDef="let node;when: hasChild"
+ matTreeNodePadding
+ matTreeNodePaddingIndent="17"
+ >
+ <button
+ mat-icon-button
+ matTreeNodeToggle
+ [attr.aria-label]="'toggle ' + node.name"
+ >
<mat-icon class="mat-icon-rtl-mirror">
{{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
</mat-icon>
</button>
<span (click)="toggleProject(node, treeControl.isExpanded(node))">{{node.name}}</span>
</mat-tree-node>
-</mat-tree>
+</mat-tree>
\ No newline at end of file
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.scss
index 06ea47e..6341141 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.scss
@@ -17,65 +17,65 @@
* under the License.
*/
-.folder{
- padding-left: 5px;
+.folder {
max-width: 350px;
+ padding-left: 5px;
white-space: nowrap;
overflow: hidden;
}
-.mat-tree{
- font-family: 'Open Sans', sans-serif;
+.mat-tree {
+ font-family: "Open Sans", sans-serif;
}
-.folder-icon{
+.folder-icon {
color: rgb(232, 232, 232);
}
-.folder-item-line{
+.folder-item-line {
display: flex;
align-items: center;
cursor: pointer;
-
}
.active-item {
color: #00bcd4;
- i{
+
+ i {
color: #00bcd4;
}
}
-.add-folder-form{
+.add-folder-form {
display: flex;
align-items: center;
}
-.mat-tree-node:not(.input-node){
- cursor: pointer;
- transition: .3s;
- overflow: unset;
+.mat-tree-node:not(.input-node) {
min-height: auto;
- button.mat-icon-button{
+ overflow: unset;
+ cursor: pointer;
+ transition: 0.3s;
+
+ button.mat-icon-button {
width: 25px;
height: 25px;
line-height: 28px;
}
- &:hover{
+ &:hover {
color: #00bcd4;
- i{
+ i {
color: #00bcd4;
}
}
}
.input-node {
- overflow: unset;
padding-top: 5px;
padding-bottom: 7px;
-
+ overflow: unset;
button.mat-icon-button {
&.action-btn {
@@ -108,10 +108,4 @@
.mat-error {
background-color: #ffffff;
}
-}
-
-
-
-
-
-
+}
\ No newline at end of file
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.ts
index e00375a..09218bb 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/buckets-tree/bucket-tree.component.ts
@@ -21,7 +21,6 @@ import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {FlatTreeControl} from '@angular/cdk/tree';
import {MatTreeFlatDataSource, MatTreeFlattener} from '@angular/material/tree';
-
interface BucketNode {
name: string;
endpoint?: string;
@@ -64,8 +63,7 @@ export class BucketTreeComponent implements OnInit {
private activeBucketName: string;
public activeBacket: any;
- constructor() {
- }
+ constructor() {}
ngOnInit() {
this.activeBucketName = this.openedBucket || '';
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.html
index ffa5900..f84495d 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.html
@@ -21,7 +21,8 @@
<mat-tree-node
*matTreeNodeDef="let node"
- matTreeNodeToggle matTreeNodePadding
+ matTreeNodeToggle
+ matTreeNodePadding
matTreeNodePaddingIndent="17"
[ngStyle]="{'display': 'none'}"
>
@@ -29,11 +30,21 @@
{{node.item}}
</mat-tree-node>
- <mat-tree-node *matTreeNodeDef="let node; when: hasNoContent" matTreeNodePadding matTreeNodePaddingIndent="17" class="input-node">
+ <mat-tree-node
+ *matTreeNodeDef="let node; when: hasNoContent"
+ matTreeNodePadding
+ matTreeNodePaddingIndent="17"
+ class="input-node"
+ >
<form class="add-folder-form" id="folder-form">
<mat-form-field>
<mat-label>New folder</mat-label>
- <input matInput #itemValue [formControl]="folderFormControl" [errorStateMatcher]="matcher">
+ <input
+ matInput
+ #itemValue
+ [formControl]="folderFormControl"
+ [errorStateMatcher]="matcher"
+ />
<mat-error *ngIf="!folderFormControl.hasError('required') && !folderFormControl.hasError('isDuplicate')">
The folder name can only contain Latin letters, numbers and special characters except for #, ?, /, \, %."
</mat-error>
@@ -45,22 +56,39 @@
</mat-error>
<mat-hint *ngIf="cloud === 'azure'">If you do not upload any object to the folder, this folder will be removed on MS Azure</mat-hint>
</mat-form-field>
- <button (click)="createFolder(node, itemValue.value)"
- [ngClass]="{'check': folderFormControl.valid && folderFormControl.dirty && !folderCreating}"
- mat-icon-button class="btn action-btn"
- [disabled]="!folderFormControl.valid || !folderFormControl.dirty"
- matTooltip="Please wait! Folder is creating."
- [matTooltipDisabled]="!folderCreating"
- matTooltipPosition="above"
+ <button
+ (click)="createFolder(node, itemValue.value)"
+ [ngClass]="{'check': folderFormControl.valid && folderFormControl.dirty && !folderCreating}"
+ mat-icon-button
+ class="btn action-btn"
+ [disabled]="!folderFormControl.valid || !folderFormControl.dirty"
+ matTooltip="Please wait! Folder is creating."
+ [matTooltipDisabled]="!folderCreating"
+ matTooltipPosition="above"
>
<span><i class="material-icons ">check</i></span></button>
- <button (click)="removeItem(node)" mat-icon-button class="btn close action-btn"><span ><i class="material-icons ">close</i></span></button>
+ <button
+ (click)="removeItem(node)"
+ mat-icon-button
+ class="btn close action-btn"
+ >
+ <span ><i class="material-icons ">close</i></span>
+ </button>
</form>
</mat-tree-node>
- <mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding matTreeNodePaddingIndent="17" [ngClass]="{'cursor-not-allow': bucketDataService.emptyFolder}">
- <button mat-icon-button matTreeNodeToggle
- [attr.aria-label]="'toggle ' + node.filename" [ngClass]="{'not-allowed': bucketDataService.emptyFolder}">
+ <mat-tree-node
+ *matTreeNodeDef="let node; when: hasChild"
+ matTreeNodePadding
+ matTreeNodePaddingIndent="17"
+ [ngClass]="{'cursor-not-allow': bucketDataService.emptyFolder}"
+ >
+ <button
+ mat-icon-button
+ matTreeNodeToggle
+ [attr.aria-label]="'toggle ' + node.filename"
+ [ngClass]="{'not-allowed': bucketDataService.emptyFolder}"
+ >
<mat-icon class="mat-icon-rtl-mirror" [ngClass]="{'active-item': (selectedFolder === node && !bucketDataService.emptyFolder)}">
{{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
</mat-icon>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.scss
index 2cd5cae..f048c57 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.scss
@@ -17,68 +17,67 @@
* under the License.
*/
-.folder{
- padding-left: 5px;
+.folder {
max-width: 350px;
+ padding-left: 5px;
white-space: nowrap;
overflow: hidden;
}
-.mat-tree{
- font-family: 'Open Sans', sans-serif;
+.mat-tree {
+ font-family: "Open Sans", sans-serif;
}
-.folder-icon{
+.folder-icon {
color: rgb(232, 232, 232);
}
-.folder-item-line{
+.folder-item-line {
display: flex;
align-items: center;
cursor: pointer;
-
}
.active-item {
color: #00bcd4;
- i{
+
+ i {
color: #00bcd4;
}
-
-
}
-.add-folder-form{
+.add-folder-form {
display: flex;
align-items: center;
}
-.mat-tree-node:not(.input-node){
+.mat-tree-node:not(.input-node) {
+ min-height: auto;
cursor: pointer;
- transition: .3s;
+ transition: 0.3s;
overflow: unset;
- min-height: auto;
- button.mat-icon-button{
+
+ button.mat-icon-button {
width: 25px;
height: 25px;
line-height: 28px;
}
}
-.mat-tree-node:not(.input-node):not(.cursor-not-allow){
- &:hover{
+.mat-tree-node:not(.input-node):not(.cursor-not-allow) {
+ &:hover {
color: #00bcd4;
- i{
+
+ i {
color: #00bcd4;
}
}
}
.input-node {
- overflow: unset;
padding-top: 5px;
padding-bottom: 7px;
-
+ overflow: unset;
button.mat-icon-button {
&.action-btn {
@@ -112,9 +111,3 @@
background-color: #ffffff;
}
}
-
-
-
-
-
-
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts
index 900723d..5a23058 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/folder-tree/folder-tree.component.ts
@@ -17,16 +17,16 @@
* under the License.
*/
-import {Component, Output, EventEmitter, OnDestroy, Input, OnInit} from '@angular/core';
-import {FlatTreeControl} from '@angular/cdk/tree';
-import {MatTreeFlatDataSource, MatTreeFlattener} from '@angular/material/tree';
-import {BucketBrowserService, TodoItemFlatNode, TodoItemNode} from '../../../core/services/bucket-browser.service';
-import {BucketDataService} from '../bucket-data.service';
-import {Subscription} from 'rxjs';
-import {FormControl, FormGroupDirective, NgForm, Validators} from '@angular/forms';
-import {ErrorStateMatcher} from '@angular/material/core';
-import {PATTERNS} from '../../../core/util';
-import {ToastrService} from 'ngx-toastr';
+import { Component, Output, EventEmitter, OnDestroy, Input } from '@angular/core';
+import { FlatTreeControl } from '@angular/cdk/tree';
+import { MatTreeFlatDataSource, MatTreeFlattener } from '@angular/material/tree';
+import { BucketBrowserService, TodoItemFlatNode, TodoItemNode } from '../../../core/services/bucket-browser.service';
+import { BucketDataService } from '../bucket-data.service';
+import { Subscription } from 'rxjs';
+import { FormControl, FormGroupDirective, NgForm, Validators } from '@angular/forms';
+import { ErrorStateMatcher } from '@angular/material/core';
+import { PATTERNS } from '../../../core/util';
+import { ToastrService } from 'ngx-toastr';
export class MyErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
@@ -35,8 +35,6 @@ export class MyErrorStateMatcher implements ErrorStateMatcher {
}
}
-
-
@Component({
selector: 'datalab-folder-tree',
templateUrl: './folder-tree.component.html',
@@ -76,32 +74,35 @@ export class FolderTreeComponent implements OnDestroy {
this.dataSource.data = data;
const subject = this.dataSource._flattenedData;
const subjectData = subject.getValue();
- if (this.selectedFolder) {
- if (this.cloud !== 'azure') {
- this.selectedFolder = subjectData.find(v => v.item === this.selectedFolder.item &&
- v.level === this.selectedFolder.level && v.obj === this.selectedFolder.obj);
- } else {
- const selectedFolderPath = this.selectedFolder.obj.slice(0, this.selectedFolder.obj.lastIndexOf('/') + 1);
- this.selectedFolder = subjectData.find(v => {
- const objectPath = v.obj.slice(0, v.obj.lastIndexOf('/') + 1);
- return v.item === this.selectedFolder.item &&
- v.level === this.selectedFolder.level && objectPath === selectedFolderPath;
- });
- }
- }
- this.expandAllParents(this.selectedFolder || subjectData[0]);
- this.showItem(this.selectedFolder || subjectData[0]);
- if (this.selectedFolder && !this.bucketDataService.emptyFolder) {
- setTimeout(() => {
- const element = document.querySelector('.folder-item-line.active-item');
- element && element.scrollIntoView({ block: 'start', behavior: 'smooth' });
- }, 0);
- } else if (this.selectedFolder && this.bucketDataService.emptyFolder) {
- setTimeout(() => {
- const element = document.querySelector('#folder-form');
- element && element.scrollIntoView({ block: 'end', behavior: 'smooth' });
- }, 0);
+
+ if (this.selectedFolder) {
+ if (this.cloud !== 'azure') {
+ this.selectedFolder = subjectData.find(v => v.item === this.selectedFolder.item &&
+ v.level === this.selectedFolder.level && v.obj === this.selectedFolder.obj);
+ } else {
+ const selectedFolderPath = this.selectedFolder.obj.slice(0, this.selectedFolder.obj.lastIndexOf('/') + 1);
+ this.selectedFolder = subjectData.find(v => {
+ const objectPath = v.obj.slice(0, v.obj.lastIndexOf('/') + 1);
+ return v.item === this.selectedFolder.item &&
+ v.level === this.selectedFolder.level && objectPath === selectedFolderPath;
+ });
}
+ }
+
+ this.expandAllParents(this.selectedFolder || subjectData[0]);
+ this.showItem(this.selectedFolder || subjectData[0]);
+
+ if (this.selectedFolder && !this.bucketDataService.emptyFolder) {
+ setTimeout(() => {
+ const element = document.querySelector('.folder-item-line.active-item');
+ element && element.scrollIntoView({ block: 'start', behavior: 'smooth' });
+ }, 0);
+ } else if (this.selectedFolder && this.bucketDataService.emptyFolder) {
+ setTimeout(() => {
+ const element = document.querySelector('#folder-form');
+ element && element.scrollIntoView({ block: 'end', behavior: 'smooth' });
+ }, 0);
+ }
}
}));
this.dataSource._flattenedData.subscribe();
@@ -213,21 +214,24 @@ export class FolderTreeComponent implements OnDestroy {
}
-private addNewItem(node: TodoItemFlatNode, file, isFile) {
- const currNode = this.flatNodeMap.get(node);
- if (!currNode.object) {
- currNode.object = {bucket: currNode.item, object: ''};
- }
- const emptyFolderObject = currNode.object;
- if (emptyFolderObject.object.lastIndexOf('ا') !== emptyFolderObject.object.length - 1 || emptyFolderObject.object === '') {
- emptyFolderObject.object += 'ا';
- }
- this.bucketDataService.insertItem(currNode!, file, isFile, emptyFolderObject);
- this.treeControl.expand(node);
- setTimeout(() => {
- const element = document.querySelector('#folder-form');
- element && element.scrollIntoView({ block: 'end', behavior: 'smooth' });
- }, 0);
+ private addNewItem(node: TodoItemFlatNode, file, isFile) {
+ const currNode = this.flatNodeMap.get(node);
+ if (!currNode.object) {
+ currNode.object = {
+ bucket: currNode.item,
+ object: ''
+ };
+ }
+ const emptyFolderObject = currNode.object;
+ if (emptyFolderObject.object.lastIndexOf('ا') !== emptyFolderObject.object.length - 1 || emptyFolderObject.object === '') {
+ emptyFolderObject.object += 'ا';
+ }
+ this.bucketDataService.insertItem(currNode!, file, isFile, emptyFolderObject);
+ this.treeControl.expand(node);
+ setTimeout(() => {
+ const element = document.querySelector('#folder-form');
+ element && element.scrollIntoView({ block: 'end', behavior: 'smooth' });
+ }, 0);
}
public removeItem(node: TodoItemFlatNode) {
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/upload-window.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/upload-window.component.scss
index e8e9a1e..03ace72 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/upload-window.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/bucket-browser/upload-window.component.scss
@@ -17,20 +17,20 @@
* under the License.
*/
-.upload-window{
+.upload-window {
+ height: 15vh;
margin-top: 2vh;
- border: 2px solid rgba(0,0,0,.12);
+ border: 2px solid rgba(0, 0, 0, 0.12);
border-radius: 5px;
background-color: white;
- height: 15vh;
color: black;
- .upload-header{
+ .upload-header {
+ position: relative;
+ height: 30px;
padding-left: 8px;
background: #f6fafe;
- height: 30px;
line-height: 30px;
- position: relative;
.modal-title {
font-weight: 500;
@@ -39,7 +39,7 @@
background: #f6fafe;
}
- .close{
+ .close {
position: absolute;
top: 0;
right: 0;
@@ -54,60 +54,63 @@
cursor: pointer;
transition: all 0.45s ease-in-out;
- &:hover{
+ &:hover {
color: #36afd5;
}
}
-
}
- .upload-files{
+ .upload-files {
+ width: 100%;
padding: 5px 0;
height: calc(100% - 30px);
overflow: auto;
overflow-y: overlay;
- width: 100%;
- .file{
- padding: 2px;
+
+ .file {
+ position: relative;
display: flex;
+ padding: 2px;
font-size: 12px;
- position: relative;
- &.upload-table-header{
+ &.upload-table-header {
font-size: 11px;
}
- .name{
- width: 33.3%;
- padding-left: 5px;
+ .name {
position: relative;
display: flex;
+ width: 33.3%;
+ padding-left: 5px;
- span{
+ span {
position: absolute;
max-width: calc(100% + 30px);
}
}
- .second-block{
- width: 66.7%;
+ .second-block {
display: flex;
+ width: 66.7%;
padding: 0 14px 0 17px;
- .upload-path{
+
+ .upload-path {
+ display: flex;
width: 60%;
padding-left: 24px;
padding-right: 1%;
- display: flex;
}
- .size{
+ .size {
width: 15%;
}
- .state{
- width: 22%;
+
+ .state {
display: flex;
align-items: center;
- .mat-raised-button{
+ width: 22%;
+
+ .mat-raised-button {
width: 60px;
padding: 5px;
border-radius: 0;
@@ -119,24 +122,26 @@
line-height: 8px;
}
}
- .remove{
+
+ .remove {
+ position: absolute;
+ right: 20px;
display: flex;
align-items: center;
width: 5%;
- position: absolute;
- right: 20px;
- .close{
- color: #577289;
- font-size: 12px;
- cursor: pointer;
+
+ .close {
position: absolute;
top: 3px;
right: 0;
height: 18px;
width: 14px;
+ color: #577289;
+ font-size: 12px;
+ cursor: pointer;
transition: all 0.45s ease-in-out;
- &:hover{
+ &:hover {
color: #f1696e;
}
}
@@ -146,14 +151,11 @@
}
}
-
-
-
-
@media only screen and (max-height: 920px) {
.bucket-wrapper {
height: 55vh;
- &.added-upload{
+
+ &.added-upload {
height: 38vh;
}
}
@@ -162,7 +164,8 @@
@media only screen and (max-height: 840px) {
.bucket-wrapper {
height: 53vh;
- &.added-upload{
+
+ &.added-upload {
height: 36vh;
}
}
@@ -171,7 +174,8 @@
@media only screen and (max-height: 760px) {
.bucket-wrapper {
height: 51vh;
- &.added-upload{
+
+ &.added-upload {
height: 34vh;
}
}
@@ -180,7 +184,8 @@
@media only screen and (max-height: 700px) {
.bucket-wrapper {
height: 49vh;
- &.added-upload{
+
+ &.added-upload {
height: 32vh;
}
}
@@ -189,7 +194,8 @@
@media only screen and (max-height: 650px) {
.bucket-wrapper {
height: 47vh;
- &.added-upload{
+
+ &.added-upload {
height: 30vh;
}
}
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 e353385..be41219 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
@@ -61,8 +61,7 @@
</div>
</div>
<div class="row-wrap">
- <div class="col"
- *ngIf="DICTIONARY[PROVIDER].cloud_provider === 'gcp'; else inst">
+ <div class="col" *ngIf="DICTIONARY[PROVIDER].cloud_provider === 'gcp'; else inst">
<p>Master instance number:</p>
</div>
<ng-template #inst>
@@ -71,13 +70,14 @@
</div>
</ng-template>
<div class="col">
- <span *ngIf="DICTIONARY[PROVIDER].cloud_provider === 'aws'">{{ resource.totalInstanceCount > 0 ? resource.totalInstanceCount : resource.dataengine_instance_count}}</span>
+ <span *ngIf="DICTIONARY[PROVIDER].cloud_provider === 'aws'">
+ {{ resource.totalInstanceCount > 0 ? resource.totalInstanceCount : resource.dataengine_instance_count}}
+ </span>
<span *ngIf="DICTIONARY[PROVIDER].cloud_provider === 'azure'">{{resource.dataengine_instance_count}}</span>
<span *ngIf="DICTIONARY[PROVIDER].cloud_provider === 'gcp'">1</span>
</div>
</div>
- <div class="row-wrap"
- *ngIf="DICTIONARY[PROVIDER].cloud_provider === 'gcp'">
+ <div class="row-wrap" *ngIf="DICTIONARY[PROVIDER].cloud_provider === 'gcp'">
<div class="col">
<p>Slave instance number:</p>
</div>
@@ -123,14 +123,15 @@
</ng-template>
<div *ngIf="resource.status === 'running'">
<div class="row-wrap">
- <p class="time_info">Up time {{upTimeInHours}} hour(s) since {{resource.up_time ? (resource.up_time | longDate) : "not specified."
- }}
+ <p class="time_info">
+ Up time {{upTimeInHours}} hour(s) since {{resource.up_time ? (resource.up_time | longDate) : "not specified."}}
</p>
</div>
<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 (click)="logAction(resource, environment, 'follow')"
+ <a
+ (click)="logAction(resource, environment, 'follow')"
href="{{item.url}}"
target="_blank"
matTooltip="{{item.url}}"
@@ -138,11 +139,15 @@
(mouseover)="showCopyIcon(item.description)"
class="spark-url ellipsis"
(contextmenu)="false"
- >{{ item.url }}</a>
+ >
+ {{ item.url }}
+ </a>
<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" class="copy-icon-wrapper"
+ [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)" >
<span _ngcontent-xpv-c19="" class="material-icons" (click)="this.isCopied = false">content_copy</span>
@@ -151,53 +156,59 @@
</p>
</div>
</div>
- <div class="checkbox-group"
- *ngIf="resource.image === 'docker.datalab-dataengine'
- && resource.status === 'running'
- && environment.image !== 'docker.datalab-zeppelin'
- && environment.image !== 'docker.datalab-superset'
- && environment.image !== 'docker.datalab-jupyterlab'"
- >
- <label>
- <input #configurationNode type="checkbox" (change)="selectConfiguration()"/> Cluster configurations
- </label>
- <div class="checkbox-group">
- <form [formGroup]="configurationForm" novalidate>
- <div class="config-details"
- [ngClass]="{ show: configuration?.nativeElement['checked'] || false }">
- <textarea
- formControlName="configuration_parameters"
- placeholder="Cluster configuration template, JSON"
- data-gramm_editor="false">
- </textarea>
- <span class="danger_color"
- *ngIf="!configurationForm.controls.configuration_parameters.valid
- && configurationForm.controls['configuration_parameters'].dirty">
- Configuration parameters is not in a valid format
- </span>
- </div>
- </form>
- </div>
+ <div
+ class="checkbox-group"
+ *ngIf="resource.image === 'docker.datalab-dataengine'
+ && resource.status === 'running'
+ && environment.image !== 'docker.datalab-zeppelin'
+ && environment.image !== 'docker.datalab-superset'
+ && environment.image !== 'docker.datalab-jupyterlab'"
+ >
+ <label>
+ <input #configurationNode type="checkbox" (change)="selectConfiguration()"/> Cluster configurations
+ </label>
+ <div class="checkbox-group">
+ <form [formGroup]="configurationForm" novalidate>
+ <div class="config-details" [ngClass]="{ show: configuration?.nativeElement['checked'] || false }">
+ <textarea
+ formControlName="configuration_parameters"
+ placeholder="Cluster configuration template, JSON"
+ data-gramm_editor="false"
+ ></textarea>
+ <span class="danger_color"
+ *ngIf="!configurationForm.controls.configuration_parameters.valid
+ && configurationForm.controls['configuration_parameters'].dirty">
+ Configuration parameters is not in a valid format
+ </span>
+ </div>
+ </form>
</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.
- Currently it can be done directly through Apache Zeppelin interpreter menu.
- 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()"
- class="butt action">Cancel
- </button>
- <button mat-raised-button type="submit"
- [disabled]="!configurationForm.valid"
- class="butt butt-success action"
- [ngClass]="{'not-allowed': !configurationForm.valid}"
- (click)="editClusterConfiguration(configurationForm.value)">Update
- </button>
+ </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.
+ Currently it can be done directly through Apache Zeppelin interpreter menu.
+ 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()"
+ class="butt action"
+ >
+ Cancel
+ </button>
+ <button
+ mat-raised-button type="submit"
+ [disabled]="!configurationForm.valid"
+ class="butt butt-success action"
+ [ngClass]="{'not-allowed': !configurationForm.valid}"
+ (click)="editClusterConfiguration(configurationForm.value)"
+ >
+ Update
+ </button>
</div>
</div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.scss
index be579fd..1c9d19b 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/cluster-details.component.scss
@@ -40,11 +40,11 @@
}
.config-details {
+ position: relative;
height: 0;
opacity: 0;
visibility: hidden;
text-align: left;
- position: relative;
&.show {
height: 200px;
@@ -69,12 +69,12 @@
}
}
-.spark-url{
+.spark-url {
max-width: 250px;
overflow: hidden;
}
-.copy-icon-wrapper{
+.copy-icon-wrapper {
margin-top: -2px;
margin-left: 5px;
}
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 f5e8f45..1e6526b 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
@@ -26,8 +26,8 @@ import { DateUtils, CheckUtils } from '../../../core/util';
import { DataengineConfigurationService } from '../../../core/services';
import { DICTIONARY } from '../../../../dictionary/global.dictionary';
import { CLUSTER_CONFIGURATION } from '../computational-resource-create-dialog/cluster-configuration-templates';
-import {AuditService} from '../../../core/services/audit.service';
-import {CopyPathUtils} from '../../../core/util/copyPathUtils';
+import { AuditService } from '../../../core/services/audit.service';
+import { CopyPathUtils } from '../../../core/util/copyPathUtils';
@Component({
selector: 'datalab-cluster-details',
@@ -68,21 +68,22 @@ export class DetailComputationalResourcesComponent implements OnInit {
this.resource = resource;
this.environment = environment;
-
this.upTimeInHours = (this.resource.up_time) ? DateUtils.diffBetweenDatesInHours(this.resource.up_time) : 0;
this.initFormModel();
- if (this.resource.image === 'docker.datalab-dataengine') this.getClusterConfiguration();
+ if (this.resource.image === 'docker.datalab-dataengine') {
+ this.getClusterConfiguration();
+ }
}
public isEllipsisActive($event): void {
- if ($event.target.offsetWidth < $event.target.scrollWidth)
+ if ($event.target.offsetWidth < $event.target.scrollWidth) {
this.tooltip = true;
+ }
}
public selectConfiguration() {
if (this.configuration.nativeElement.checked) {
-
this.configurationForm.controls['configuration_parameters']
.setValue(JSON.stringify(this.config.length ? this.config : CLUSTER_CONFIGURATION.SPARK, undefined, 2));
} else {
@@ -93,8 +94,10 @@ export class DetailComputationalResourcesComponent implements OnInit {
public getClusterConfiguration(): void {
this.dataengineConfigurationService
.getClusterConfiguration(this.environment.project, this.environment.name, this.resource.computational_name, this.PROVIDER)
- .subscribe((result: any) => this.config = result,
- error => this.toastr.error(error.message || 'Configuration loading failed!', 'Oops!'));
+ .subscribe(
+ (result: any) => this.config = result,
+ error => this.toastr.error(error.message || 'Configuration loading failed!', 'Oops!')
+ );
}
public editClusterConfiguration(data): void {
@@ -106,10 +109,12 @@ export class DetailComputationalResourcesComponent implements OnInit {
this.resource.computational_name,
this.PROVIDER
)
- .subscribe(result => {
- this.dialogRef.close();
- },
- error => this.toastr.error(error.message || 'Edit onfiguration failed!', 'Oops!'));
+ .subscribe(
+ result => {
+ this.dialogRef.close();
+ },
+ error => this.toastr.error(error.message || 'Edit onfiguration failed!', 'Oops!')
+ );
}
private initFormModel(): void {
@@ -134,8 +139,12 @@ export class DetailComputationalResourcesComponent implements OnInit {
};
this.auditService.sendDataToAudit(
- {resource_name: resource.computational_name, info: JSON.stringify(clusterInfo), type: 'COMPUTE'}
- ).subscribe();
+ {
+ resource_name: resource.computational_name,
+ info: JSON.stringify(clusterInfo),
+ type: 'COMPUTE'
+ }
+ ).subscribe();
}
public copyLink(url: string) {
@@ -145,6 +154,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/computational/cluster-details/index.ts b/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/index.ts
index 1cc87c8..234c5d2 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/index.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/computational/cluster-details/index.ts
@@ -23,12 +23,18 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MaterialModule } from '../../../shared/material.module';
import { DetailComputationalResourcesComponent } from './cluster-details.component';
-import {LongDatePipeModule} from '../../../core/pipes/long-date-pipe';
+import { LongDatePipeModule } from '../../../core/pipes/long-date-pipe';
export * from './cluster-details.component';
@NgModule({
- imports: [CommonModule, FormsModule, ReactiveFormsModule, MaterialModule, LongDatePipeModule],
+ imports: [
+ CommonModule,
+ FormsModule,
+ ReactiveFormsModule,
+ MaterialModule,
+ LongDatePipeModule
+ ],
declarations: [DetailComputationalResourcesComponent],
entryComponents: [DetailComputationalResourcesComponent],
exports: [DetailComputationalResourcesComponent],
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.html
index 7b339f7..5e9095c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.html
@@ -25,80 +25,102 @@
<div class="dialog-content selection">
<div class="content-box mat-reset">
<form [formGroup]="resourceForm" *ngIf="clusterTypes.length && resourceForm; else placeholder">
-
<div class="form-wrapper">
<div class="col">
- <div class="control-group" *ngIf="PROVIDER !== 'azure'" [hidden]="clusterTypes.length === 1">
- <label class="label">Select cluster type</label>
- <div class="control selector-wrapper">
- <mat-form-field>
- <mat-select formControlName="template_name"
- disableOptionCentering
- panelClass="scrolling"
- placeholder="Select cluster type">
- <mat-option *ngFor="let type of clusterTypes" [value]="type.template_name"
- (click)="selectImage(type)">{{ type.template_name }}
- </mat-option>
- <mat-option *ngIf="!clusterTypes.length" class="multiple-select ml-10" disabled>Clusters types list
- is empty</mat-option>
- </mat-select>
- <button class="caret">
- <i class="material-icons">keyboard_arrow_down</i>
- </button>
- </mat-form-field>
- </div>
+ <div class="control-group" *ngIf="PROVIDER !== 'azure'" [hidden]="clusterTypes.length === 1">
+ <label class="label">Select cluster type</label>
+ <div class="control selector-wrapper">
+ <mat-form-field>
+ <mat-select
+ formControlName="template_name"
+ disableOptionCentering
+ panelClass="scrolling"
+ placeholder="Select cluster type"
+ >
+ <mat-option
+ *ngFor="let type of clusterTypes"
+ [value]="type.template_name"
+ (click)="selectImage(type)"
+ >
+ {{ type.template_name }}
+ </mat-option>
+ <mat-option *ngIf="!clusterTypes.length" class="multiple-select ml-10" disabled>
+ Clusters types list is empty
+ </mat-option>
+ </mat-select>
+ <button class="caret">
+ <i class="material-icons">keyboard_arrow_down</i>
+ </button>
+ </mat-form-field>
</div>
+ </div>
- <div class="control-group alias-name" *ngIf="selectedImage?.image">
- <label class="label">Cluster alias</label>
- <div class="control">
- <input
- [class.danger_field]="!resourceForm?.controls['cluster_alias_name'].valid
- && resourceForm?.controls['cluster_alias_name'].dirty
- && resourceForm?.controls['cluster_alias_name'].hasError('duplication')"
- type="text" class="form-control" placeholder="Enter cluster alias"
- formControlName="cluster_alias_name" />
- <span class="error" *ngIf="resourceForm?.controls['cluster_alias_name'].hasError('user-duplication')">You have cluster with this name in current project.</span>
- <span class="error" *ngIf="resourceForm?.controls['cluster_alias_name'].hasError('other-user-duplication')">Other user has cluster with this name in current project.</span>
- <span class="error" *ngIf="resourceForm?.controls['cluster_alias_name'].hasError('maxlength') &&
- !resourceForm?.controls['cluster_alias_name'].hasError('pattern')"
- >
- Cluster name cannot be longer than {{maxClusterNameLength}} characters.
- </span>
- <span class="error" *ngIf="resourceForm?.controls['cluster_alias_name'].hasError('pattern')">
- Cluster name can only contain letters, numbers, hyphens and '_' but can not end with special
- characters.
- </span>
- </div>
+ <div class="control-group alias-name" *ngIf="selectedImage?.image">
+ <label class="label">Cluster alias</label>
+ <div class="control">
+ <input
+ [class.danger_field]="!resourceForm?.controls['cluster_alias_name'].valid
+ && resourceForm?.controls['cluster_alias_name'].dirty
+ && resourceForm?.controls['cluster_alias_name'].hasError('duplication')"
+ type="text"
+ class="form-control"
+ placeholder="Enter cluster alias"
+ formControlName="cluster_alias_name"
+ />
+ <span class="error" *ngIf="resourceForm?.controls['cluster_alias_name'].hasError('user-duplication')">
+ You have cluster with this name in current project.
+ </span>
+ <span class="error" *ngIf="resourceForm?.controls['cluster_alias_name'].hasError('other-user-duplication')">
+ Other user has cluster with this name in current project.
+ </span>
+ <span
+ class="error"
+ *ngIf="resourceForm?.controls['cluster_alias_name'].hasError('maxlength')
+ && !resourceForm?.controls['cluster_alias_name'].hasError('pattern')"
+ >
+ Cluster name cannot be longer than {{maxClusterNameLength}} characters.
+ </span>
+ <span class="error" *ngIf="resourceForm?.controls['cluster_alias_name'].hasError('pattern')">
+ Cluster name can only contain letters, numbers, hyphens and '_' but can not end with special
+ characters.
+ </span>
</div>
+ </div>
- <div class="control-group" *ngIf="selectedImage?.image">
- <label class="label">Master instance size</label>
- <div class="control selector-wrapper">
- <mat-form-field>
- <mat-label>Select instance size</mat-label>
- <mat-select panelClass="scrolling" formControlName="shape_master" disableOptionCentering>
- <mat-optgroup *ngFor="let item of (selectedImage.computation_resources_shapes | keys)"
- [label]="item.key | underscoreless">
- <mat-option *ngFor="let list_item of item.value" [value]="list_item.Type" (click)="clearGpuType('master')">
- <strong class="highlight icon-label">{{ list_item.Size }}</strong> {{ list_item.Type }}
- </mat-option>
- </mat-optgroup>
- </mat-select>
- <button class="caret">
- <i class="material-icons">keyboard_arrow_down</i>
- </button>
- </mat-form-field>
- </div>
+ <div class="control-group" *ngIf="selectedImage?.image">
+ <label class="label">Master instance size</label>
+ <div class="control selector-wrapper">
+ <mat-form-field>
+ <mat-label>Select instance size</mat-label>
+ <mat-select panelClass="scrolling" formControlName="shape_master" disableOptionCentering>
+ <mat-optgroup
+ *ngFor="let item of (selectedImage.computation_resources_shapes | keys)"
+ [label]="item.key | underscoreless"
+ >
+ <mat-option *ngFor="let list_item of item.value" [value]="list_item.Type" (click)="clearGpuType('master')">
+ <strong class="highlight icon-label">{{ list_item.Size }}</strong> {{ list_item.Type }}
+ </mat-option>
+ </mat-optgroup>
+ </mat-select>
+ <button class="caret">
+ <i class="material-icons">keyboard_arrow_down</i>
+ </button>
+ </mat-form-field>
</div>
+ </div>
</div>
<div class="col">
<div class="control-group" *ngIf="selectedImage?.image">
<label class="label">Total instance number</label>
<div class="control">
- <input type="number" class="form-control" min="{{minInstanceNumber}}" max="{{maxInstanceNumber}}"
- formControlName="instance_number" (keypress)="CheckUtils.isNumberKey($event)" />
+ <input
+ type="number"
+ class="form-control"
+ min="{{minInstanceNumber}}"
+ max="{{maxInstanceNumber}}"
+ formControlName="instance_number" (keypress)="CheckUtils.isNumberKey($event)"
+ />
<span class="error" *ngIf="!resourceForm?.controls.instance_number.valid">
<span>Only integer values greater than or equal to {{ minInstanceNumber }} and less than
{{ maxInstanceNumber }} are allowed.</span>
@@ -114,8 +136,9 @@
<mat-select panelClass="scrolling" formControlName="version" disableOptionCentering>
<mat-option *ngFor="let template of selectedImage.templates" [value]="template.version">
{{ template.version }}</mat-option>
- <mat-option *ngIf="!selectedImage.templates" class="multiple-select ml-10" disabled>Templates list
- is empty</mat-option>
+ <mat-option *ngIf="!selectedImage.templates" class="multiple-select ml-10" disabled>
+ Templates list is empty
+ </mat-option>
</mat-select>
<button class="caret">
<i class="material-icons">keyboard_arrow_down</i>
@@ -124,31 +147,35 @@
</div>
</div>
- <div class="control-group" *ngIf="selectedImage?.image">
- <label class="label">Slave instance size</label>
- <div class="control selector-wrapper">
- <mat-form-field>
- <mat-label>Select instance size</mat-label>
- <mat-select panelClass="scrolling" formControlName="shape_slave" disableOptionCentering>
- <mat-optgroup *ngFor="let item of (selectedImage.computation_resources_shapes | keys)"
- [label]="item.key | underscoreless">
- <mat-option *ngFor="let list_item of item.value" [value]="list_item.Type" (click)="clearGpuType('slave')">
- <strong class="highlight icon-label">{{ list_item.Size }}</strong> {{
- list_item.Type }}
- </mat-option>
- </mat-optgroup>
- </mat-select>
- <button class="caret">
- <i class="material-icons">keyboard_arrow_down</i>
- </button>
- </mat-form-field>
- </div>
+ <div class="control-group" *ngIf="selectedImage?.image">
+ <label class="label">Slave instance size</label>
+ <div class="control selector-wrapper">
+ <mat-form-field>
+ <mat-label>Select instance size</mat-label>
+ <mat-select panelClass="scrolling" formControlName="shape_slave" disableOptionCentering>
+ <mat-optgroup
+ *ngFor="let item of (selectedImage.computation_resources_shapes | keys)"
+ [label]="item.key | underscoreless"
+ >
+ <mat-option
+ *ngFor="let list_item of item.value"
+ [value]="list_item.Type"
+ (click)="clearGpuType('slave')"
+ >
+ <strong class="highlight icon-label">{{ list_item.Size }}</strong> {{
+ list_item.Type }}
+ </mat-option>
+ </mat-optgroup>
+ </mat-select>
+ <button class="caret">
+ <i class="material-icons">keyboard_arrow_down</i>
+ </button>
+ </mat-form-field>
</div>
-
+ </div>
</div>
</div>
- <div class="checkbox-group control-group"
- *ngIf="PROVIDER === 'gcp'">
+ <div class="checkbox-group control-group" *ngIf="PROVIDER === 'gcp'">
<div class="d-flex cursor-pointer label m-bott-20" (click)="addAdditionalParams('gpu')">
<div class="empty-checkbox ml-10" [ngClass]="{'checked': isSelected.gpu}">
<span class="checked-checkbox" *ngIf="isSelected.gpu"></span>
@@ -159,19 +186,21 @@
<div class="col">
<div class="control-group">
<label class="label">Master GPU type</label>
- <div class="control selector-wrapper"
- [ngClass]="{ 'not-active' : !resourceForm.controls['shape_master'].value}"
- [matTooltip]="'Please, select master instance size.'"
- [matTooltipPosition]="'above'"
- [matTooltipClass]="'full-size-tooltip'"
- [matTooltipDisabled]="!!resourceForm.controls['shape_master'].value.length"
+ <div
+ class="control selector-wrapper"
+ [ngClass]="{ 'not-active' : !resourceForm.controls['shape_master'].value}"
+ [matTooltip]="'Please, select master instance size.'"
+ [matTooltipPosition]="'above'"
+ [matTooltipClass]="'full-size-tooltip'"
+ [matTooltipDisabled]="!!resourceForm.controls['shape_master'].value.length"
>
<mat-form-field>
- <mat-select formControlName="master_GPU_type" disableOptionCentering
- placeholder="Select master GPU type"
- [disabled]="!resourceForm.controls['shape_master'].value">
- <mat-option *ngFor="let type of sortGpuTypes(selectedImage.computationGPU); index as i"
- [value]="type" >
+ <mat-select
+ formControlName="master_GPU_type" disableOptionCentering
+ placeholder="Select master GPU type"
+ [disabled]="!resourceForm.controls['shape_master'].value"
+ >
+ <mat-option *ngFor="let type of sortGpuTypes(selectedImage.computationGPU); index as i" [value]="type">
<strong class="highlight icon-label">{{ addSizeToGpuType(i) }}</strong> {{ type }}
</mat-option>
<mat-option *ngIf="!selectedImage.computationGPU?.length" class="multiple-select ml-10" disabled>
@@ -186,12 +215,13 @@
</div>
<div class="control-group">
<label class="label">Master GPU сount</label>
- <div class="control selector-wrapper"
- [ngClass]="{'not-active': !resourceForm.controls['master_GPU_type'].value}"
- [matTooltip]="'Please, select master GPU type.'"
- [matTooltipPosition]="'above'"
- [matTooltipClass]="'full-size-tooltip'"
- [matTooltipDisabled]="!!resourceForm.controls['master_GPU_type'].value"
+ <div
+ class="control selector-wrapper"
+ [ngClass]="{'not-active': !resourceForm.controls['master_GPU_type'].value}"
+ [matTooltip]="'Please, select master GPU type.'"
+ [matTooltipPosition]="'above'"
+ [matTooltipClass]="'full-size-tooltip'"
+ [matTooltipDisabled]="!!resourceForm.controls['master_GPU_type'].value"
>
<mat-form-field>
<mat-label>Select master GPU count</mat-label>
@@ -212,12 +242,13 @@
<div class="col">
<div class="control-group">
<label class="label">Slave GPU type</label>
- <div class="control selector-wrapper"
- [ngClass]="{ 'not-active': !resourceForm.controls['shape_slave'].value}"
- [matTooltip]="'Please, select slave instance size.'"
- [matTooltipPosition]="'above'"
- [matTooltipClass]="'full-size-tooltip'"
- [matTooltipDisabled]="!!resourceForm.controls['shape_slave'].value.length"
+ <div
+ class="control selector-wrapper"
+ [ngClass]="{ 'not-active': !resourceForm.controls['shape_slave'].value}"
+ [matTooltip]="'Please, select slave instance size.'"
+ [matTooltipPosition]="'above'"
+ [matTooltipClass]="'full-size-tooltip'"
+ [matTooltipDisabled]="!!resourceForm.controls['shape_slave'].value.length"
>
<!-- <span class="form-field-wrapper" >-->
<mat-form-field>
@@ -240,12 +271,13 @@
<div class="control-group">
<label class="label">Slave GPU сount</label>
- <div class="control selector-wrapper"
- [ngClass]="{'not-active': !resourceForm.controls['slave_GPU_type'].value}"
- [matTooltip]="'Please, select slave GPU type.'"
- [matTooltipPosition]="'above'"
- [matTooltipClass]="'full-size-tooltip'"
- [matTooltipDisabled]="!!resourceForm.controls['slave_GPU_type'].value"
+ <div
+ class="control selector-wrapper"
+ [ngClass]="{'not-active': !resourceForm.controls['slave_GPU_type'].value}"
+ [matTooltip]="'Please, select slave GPU type.'"
+ [matTooltipPosition]="'above'"
+ [matTooltipClass]="'full-size-tooltip'"
+ [matTooltipDisabled]="!!resourceForm.controls['slave_GPU_type'].value"
>
<mat-form-field>
<mat-label>Select slave GPU сount</mat-label>
@@ -266,20 +298,29 @@
</div>
</div>
</div>
- <div class="preemptible checkbox-group control-group"
- *ngIf="PROVIDER === 'gcp' && selectedImage?.image === 'docker.datalab-dataengine-service'">
+ <div
+ class="preemptible checkbox-group control-group"
+ *ngIf="PROVIDER === 'gcp' && selectedImage?.image === 'docker.datalab-dataengine-service'"
+ >
<div class="d-flex cursor-pointer label" (click)="addAdditionalParams('preemptible')">
<div class="empty-checkbox ml-10" [ngClass]="{'checked': isSelected.preemptible}" (click)="selectPreemptibleNodes(isSelected.preemptible)">
<span class="checked-checkbox" *ngIf="isSelected.preemptible"></span>
</div>
<span class="pl-5">Preemptible node</span>
</div>
- <div *ngIf="isSelected.preemptible" class="preemptible-details control"
- [ngClass]="{ show: isSelected.preemptible}">
- <input type="text" class="form-control" formControlName="preemptible_instance_number"
- (keypress)="CheckUtils.isNumberKey($event)"
- (keydown.arrowup)="preemptibleCounter($event, 'increment')"
- (keydown.arrowdown)="preemptibleCounter($event, 'decrement')"/>
+ <div
+ *ngIf="isSelected.preemptible"
+ class="preemptible-details control"
+ [ngClass]="{ show: isSelected.preemptible}"
+ >
+ <input
+ type="text"
+ class="form-control"
+ formControlName="preemptible_instance_number"
+ (keypress)="CheckUtils.isNumberKey($event)"
+ (keydown.arrowup)="preemptibleCounter($event, 'increment')"
+ (keydown.arrowdown)="preemptibleCounter($event, 'decrement')"
+ />
<span class="error error-bottom" *ngIf="!resourceForm?.controls.preemptible_instance_number.valid">
<span *ngIf="minPreemptibleInstanceNumber !== maxPreemptibleInstanceNumber; else equal">
Only integer values greater than or equal to {{ minPreemptibleInstanceNumber }} and less than
@@ -297,31 +338,45 @@
</div>
<span class="pl-5">Spot instance</span><span [hidden]="!isSelected.spotInstances"> bid, %</span>
</div>
- <div class="control spot-details" [ngClass]="{ show: isSelected.spotInstances }"
- *ngIf="isSelected.spotInstances">
- <input type="number" class="form-control" step="5" min="{{minSpotPrice}}" max="{{maxSpotPrice}}"
- formControlName="instance_price" (keypress)="CheckUtils.isNumberKey($event)">
+ <div
+ class="control spot-details"
+ [ngClass]="{ show: isSelected.spotInstances }"
+ *ngIf="isSelected.spotInstances"
+ >
+ <input
+ type="number"
+ class="form-control"
+ step="5"
+ min="{{minSpotPrice}}"
+ max="{{maxSpotPrice}}"
+ formControlName="instance_price"
+ (keypress)="CheckUtils.isNumberKey($event)"
+ />
<span class="error error-bottom" *ngIf="!resourceForm?.controls.instance_price.valid">
Only integer values greater than or equal to {{minSpotPrice}} and less than {{maxSpotPrice}} are allowed.
</span>
</div>
<span class="info ml-10" *ngIf="isSelected.spotInstances">When the current Spot price
rises above your bid price, the Spot instance is reclaimed by AWS so that it can be given to another
- customer. Make sure to backup your data on periodic basis.</span>
+ customer. Make sure to backup your data on periodic basis.
+ </span>
</div>
- <div class="checkbox-group control-group m-top-10"
- [hidden]="PROVIDER === 'gcp' && selectedImage?.image === 'docker.datalab-dataengine-service'"
- *ngIf="notebook_instance?.image !== 'docker.datalab-zeppelin'">
+ <div
+ class="checkbox-group control-group m-top-10"
+ [hidden]="PROVIDER === 'gcp' && selectedImage?.image === 'docker.datalab-dataengine-service'"
+ *ngIf="notebook_instance?.image !== 'docker.datalab-zeppelin'"
+ >
<div class="d-flex cursor-pointer label" (click)="addAdditionalParams('configuration')">
<div class="empty-checkbox ml-10" [ngClass]="{'checked': isSelected.configuration}">
<span class="checked-checkbox" *ngIf="isSelected.configuration"></span>
</div>
<span class="pl-5">Cluster configurations</span>
</div>
- <div class="config-link"
- *ngIf="(isSelected.configuration)
- && selectedImage?.image === 'docker.datalab-dataengine-service'
- && PROVIDER === 'aws'"
+ <div
+ class="config-link"
+ *ngIf="(isSelected.configuration)
+ && selectedImage?.image === 'docker.datalab-dataengine-service'
+ && PROVIDER === 'aws'"
>
To view example JSON of configurations refer for <a
href="https://docs.aws.amazon.com/emr/latest/ReleaseGuide/emr-configure-apps.html"
@@ -331,14 +386,17 @@
</div>
<div class="checkbox-group ml-10">
<div class="config-details" [ngClass]="{ show: isSelected.configuration }">
- <textarea formControlName="configuration_parameters"
- placeholder="Cluster configuration template, JSON"
- data-gramm_editor="false">
+ <textarea
+ formControlName="configuration_parameters"
+ placeholder="Cluster configuration template, JSON"
+ data-gramm_editor="false">
</textarea>
- <span class="error"
- *ngIf="!resourceForm?.controls.configuration_parameters.valid
- && resourceForm?.controls['configuration_parameters'].dirty">
- Configuration parameters is not in a valid format.
+ <span
+ class="error"
+ *ngIf="!resourceForm?.controls.configuration_parameters.valid
+ && resourceForm?.controls['configuration_parameters'].dirty"
+ >
+ Configuration parameters is not in a valid format.
</span>
</div>
</div>
@@ -352,29 +410,35 @@
</small>
</div>
<div class="text-center m-top-30">
- <button mat-raised-button
- type="button"
- (click)="dialogRef.close()"
- class="butt action">Cancel
+ <button
+ mat-raised-button
+ type="button"
+ (click)="dialogRef.close()"
+ class="butt action"
+ >
+ Cancel
</button>
- <button mat-raised-button
- type="button"
- [disabled]="!resourceForm?.valid
- || (!resourceForm.value.shape_slave)
- || (selectedImage?.image === 'docker.datalab-dataengine-service' && !resourceForm.value.version)"
- (click)="createComputationalResource(resourceForm.value)"
- class="butt butt-success action"
- [ngClass]="{'not-allowed': !resourceForm?.valid
- || (selectedImage?.image === 'docker.datalab-dataengine-service' && !resourceForm.value.shape_slave)
- || (selectedImage?.image === 'docker.datalab-dataengine-service' && !resourceForm.value.version) }">
- Create
+ <button
+ mat-raised-button
+ type="button"
+ [disabled]="!resourceForm?.valid
+ || (!resourceForm.value.shape_slave)
+ || (selectedImage?.image === 'docker.datalab-dataengine-service' && !resourceForm.value.version)"
+ (click)="createComputationalResource(resourceForm.value)"
+ class="butt butt-success action"
+ [ngClass]="{'not-allowed': !resourceForm?.valid
+ || (selectedImage?.image === 'docker.datalab-dataengine-service' && !resourceForm.value.shape_slave)
+ || (selectedImage?.image === 'docker.datalab-dataengine-service' && !resourceForm.value.version) }"
+ >
+ Create
</button>
</div>
</form>
</div>
<ng-template #placeholder>
<div *ngIf="!loading && !clusterTypes?.length" class="info message">
- Compute creations are not available. Please, check your permissions.</div>
+ Compute creations are not available. Please, check your permissions.
+ </div>
<div *ngIf="loading" class="info message">
Compute data is processing
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.scss
index c849fc8..e2b8405 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.scss
@@ -21,12 +21,15 @@
display: flex;
.col {
width: 50%;
+
.control-group {
position: relative;
+
.label {
width: 40%;
padding-left: 10px;
}
+
.control {
width: 60%;
}
@@ -34,19 +37,22 @@
span {
&.error {
position: absolute;
- padding-left: 10px;
right: 0;
top: 36px;
+ padding-left: 10px;
}
}
+
.alias-name {
.danger-color {
right: auto;
}
}
}
+
&.compress {
flex-direction: column;
+
.col {
width: 100%;
}
@@ -54,12 +60,14 @@
}
.checkbox-group {
text-align: left;
+
input[type="checkbox"] {
- vertical-align: middle;
margin-left: 10px;
margin-right: 5px;
margin-bottom: 4px;
+ vertical-align: middle;
}
+
.spot-details,
.preemptible-details,
.config-details {
@@ -74,21 +82,28 @@
opacity: 1;
}
}
+
&.preemptible {
height: 50px;
+
.label {
height: 36px;
padding-bottom: 14px;
}
+
.control {
position: relative;
}
}
+
.align {
margin-left: 25px;
}
- .spot-details, .preemptible-details {
+
+ .spot-details,
+ .preemptible-details {
position: relative;
+
.error {
bottom: 0;
}
@@ -98,15 +113,17 @@
text-align: right;
}
}
+
.config-details {
&.show {
+ position: relative;
height: 240px;
padding-top: 10px;
text-align: right;
- position: relative;
+
textarea {
- background: #f8f8f8 !important;
height: 100%;
+ background: #f8f8f8 !important;
resize: none;
font-size: 14px;
line-height: 1.5;
@@ -114,12 +131,14 @@
}
}
}
+
& > label {
+ padding-bottom: 10px;
font-size: 15px;
font-weight: 600;
- padding-bottom: 10px;
cursor: pointer;
}
+
span {
&.error {
position: absolute;
@@ -127,16 +146,19 @@
right: 0;
}
}
+
&.control-group {
padding: 0;
+
span.info {
- color: #35afd5;
- font-size: 13px;
- text-align: justify;
display: inline-block;
padding-top: 5px;
padding-bottom: 10px;
+ color: #35afd5;
+ font-size: 13px;
+ text-align: justify;
}
+
span {
&.error {
position: absolute;
@@ -144,13 +166,16 @@
right: 0;
}
}
+
.label {
width: 20%;
}
+
.control {
width: 80%;
}
}
+
.config-link {
font-size: 13px;
padding-top: 5px;
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.ts
index 714eb92..6fd9991 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.ts
@@ -24,7 +24,7 @@ import { ToastrService } from 'ngx-toastr';
import { ComputationalResourceModel } from './computational-resource-create.model';
import { UserResourceService } from '../../../core/services';
-import {HTTP_STATUS_CODES, PATTERNS, CheckUtils, SortUtils, HelpUtils} from '../../../core/util';
+import { HTTP_STATUS_CODES, PATTERNS, CheckUtils, SortUtils, HelpUtils } from '../../../core/util';
import { DICTIONARY } from '../../../../dictionary/global.dictionary';
import { CLUSTER_CONFIGURATION } from './cluster-configuration-templates';
@@ -91,8 +91,9 @@ export class ComputationalResourceCreateDialogComponent implements OnInit {
this.filterShapes();
this.getComputationalResourceLimits();
- if ($event.templates && $event.templates.length)
+ if ($event.templates && $event.templates.length) {
this.resourceForm.controls['version'].setValue($event.templates[0].version);
+ }
}
public selectSpotInstances(): void {
@@ -131,8 +132,9 @@ export class ComputationalResourceCreateDialogComponent implements OnInit {
}
public isAvailableSpots(): boolean {
- if (this.PROVIDER === 'aws' && this.selectedImage.image === 'docker.datalab-dataengine-service')
+ if (this.PROVIDER === 'aws' && this.selectedImage.image === 'docker.datalab-dataengine-service') {
return !!Object.keys(this.filterAvailableSpots()).length;
+ }
return false;
}
@@ -140,9 +142,12 @@ export class ComputationalResourceCreateDialogComponent implements OnInit {
public createComputationalResource(data) {
this.model.createComputationalResource(data, this.selectedImage, this.notebook_instance,
this.spotInstance, this.PROVIDER.toLowerCase(), this.isSelected.gpu)
- .subscribe((response: any) => {
- if (response.status === HTTP_STATUS_CODES.OK) this.dialogRef.close(true);
- }, error => this.toastr.error(error.message, 'Oops!'));
+ .subscribe(
+ (response: any) => {
+ if (response.status === HTTP_STATUS_CODES.OK) this.dialogRef.close(true);
+ },
+ error => this.toastr.error(error.message, 'Oops!')
+ );
}
private initFormModel(): void {
@@ -202,22 +207,24 @@ export class ComputationalResourceCreateDialogComponent implements OnInit {
// Validation
private validInstanceNumberRange(control) {
- if (control && control.value)
+ if (control && control.value) {
if (this.PROVIDER === 'gcp' && this.selectedImage.image === 'docker.datalab-dataengine-service') {
this.validPreemptibleNumberRange();
return control.value >= this.minInstanceNumber && control.value <= this.maxInstanceNumber ? null : { valid: false };
} else {
return control.value >= this.minInstanceNumber && control.value <= this.maxInstanceNumber ? null : { valid: false };
}
+ }
}
private validPreemptibleRange(control) {
- if (this.isSelected.preemptible)
+ if (this.isSelected.preemptible) {
return this.isSelected.preemptible
? (control.value !== null
&& control.value >= this.minPreemptibleInstanceNumber
&& control.value <= this.maxPreemptibleInstanceNumber ? null : { valid: false })
: control.value;
+ }
}
private validPreemptibleNumberRange() {
@@ -233,17 +240,19 @@ export class ComputationalResourceCreateDialogComponent implements OnInit {
}
private validInstanceSpotRange(control) {
- if (this.isSelected.spotInstances)
+ if (this.isSelected.spotInstances) {
return this.isSelected.spotInstances
? (control.value >= this.minSpotPrice && control.value <= this.maxSpotPrice ? null : { valid: false })
: control.value;
+ }
}
private validConfiguration(control) {
- if (this.isSelected.configuration)
- return this.isSelected.configuration ?
- (control.value && control.value !== null && CheckUtils.isJSON(control.value) ? null : { valid: false })
+ if (this.isSelected.configuration) {
+ return this.isSelected.configuration
+ ? (control.value && control.value !== null && CheckUtils.isJSON(control.value) ? null : { valid: false })
: null;
+ }
}
private checkDuplication(control) {
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.html
index dec0ebd..b1965fd 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.html
@@ -18,7 +18,6 @@
-->
<div class="source">
-
<div *ngIf="!resources?.length">
<span *ngIf="!resources.length" class="no-details">no details</span>
</div>
@@ -32,25 +31,44 @@
<span ngClass="{{resource.status.toLowerCase() || ''}}" class="resource-status">{{resource.status}}</span>
<div class="resource-actions">
- <a class="schedule" [ngClass]="{'active': resource.scheduler_data,
- 'not-allowed': environment.status !== 'running' && environment.status !== 'stopped'
- || resource.status !== 'running' && resource.status !== 'stopped' }">
+ <a
+ class="schedule"
+ [ngClass]="{
+ 'active': resource.scheduler_data,
+ 'not-allowed': environment.status !== 'running' && environment.status !== 'stopped'
+ || resource.status !== 'running' && resource.status !== 'stopped' }"
+ >
<i class="material-icons" (click)="openScheduleDialog(resource)">schedule</i>
</a>
- <a class="start-stop-action"
- *ngIf="resource.image === 'docker.datalab-dataengine' && environment.status === 'running'">
- <i class="material-icons" *ngIf="resource.status === 'running' || resource.status === 'stopping'"
- (click)="toggleResourceAction(resource, 'stop')"
- [ngClass]="{'not-allowed' : resource.status === 'stopping' }">pause_circle_outline</i>
- <i class="material-icons" *ngIf="resource.status === 'stopped' || resource.status === 'starting'"
- (click)="toggleResourceAction(resource, 'start')"
- [ngClass]="{'not-allowed' : resource.status === 'starting' }">play_circle_outline</i>
+ <a
+ class="start-stop-action"
+ *ngIf="resource.image === 'docker.datalab-dataengine' && environment.status === 'running'"
+ >
+ <i
+ class="material-icons"
+ *ngIf="resource.status === 'running' || resource.status === 'stopping'"
+ (click)="toggleResourceAction(resource, 'stop')"
+ [ngClass]="{'not-allowed' : resource.status === 'stopping' }"
+ >
+ pause_circle_outline
+ </i>
+ <i
+ class="material-icons"
+ *ngIf="resource.status === 'stopped' || resource.status === 'starting'"
+ (click)="toggleResourceAction(resource, 'start')"
+ [ngClass]="{'not-allowed' : resource.status === 'starting' }"
+ >
+ play_circle_outline
+ </i>
</a>
- <a class="remove_butt" [ngClass]="{'disabled' : environment.status !== 'running' || environment.status !== 'stopped'
+ <a
+ class="remove_butt"
+ [ngClass]="{'disabled' : environment.status !== 'running' || environment.status !== 'stopped'
&& resource.status != 'running' && resource.status != 'failed' && resource.status != 'stopped' }"
- (click)="toggleResourceAction(resource, 'terminate')">
+ (click)="toggleResourceAction(resource, 'terminate')"
+ >
<i class="material-icons">highlight_off</i>
</a>
</div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.scss
index 2d9d8a5..88afbcc 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.scss
@@ -1,4 +1,4 @@
- /*!
+/*!
* 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
@@ -17,116 +17,96 @@
* under the License.
*/
- :host{
- width:100%;
- }
-
- .source {
- .no-details {
- color: #bdc9d5;
- }
-
- .resource-wrap {
- display: flex;
- justify-content: space-between;
- height: 26px;
- align-items: center;
-
- .resource-name {
- width: 50%;
- white-space: nowrap;
- overflow: hidden;
-
- .detailed-link {
- color: #35afd5;
- cursor: pointer;
- }
- }
-
- .resource-status {
- width: 35%;
- text-transform: capitalize;
- }
-
- .resource-actions {
- width: 15%;
- display: flex;
- justify-content: flex-end;
- padding-right: 10px;
-
- a {
- text-decoration: none;
- color: #748ba3;
- font-size: 18px;
- text-align: center;
-
- i {
- font-size: 18px;
- padding: 0 2px;
- }
- }
-
- .schedule {
- cursor: pointer;
- display: flex;
- align-items: center;
- font-size: 18px;
-
- &.not-allowed {
- pointer-events: none;
- }
-
- &.active {
- color: #49af38;
- }
- }
-
- .start-stop-action {
- display: flex;
- align-items: center;
- font-size: 18px;
- color: #36afd5;
-
- i {
- cursor: pointer;
-
- &.not-allowed {
- pointer-events: none;
- }
- }
- }
-
- .remove_butt {
- cursor: pointer;
- color: #ef5c4b;
- display: flex;
- align-items: center;
-
- &.disabled {
- color: #f5d3d3;
- pointer-events: none;
- }
- }
- }
- }
- }
- //@media screen and (max-width: 1520px) {
- // .resources,
- // managment {
- // .source {
- // .resource-wrap {
- // .resource-name {
- // width: 42%;
- // }
- //
- // .resource-status {
- // width: 43%;
- // }
- //
- // .resource-actions {
- // width: 15%;
- // }
- // }
- // }
- // }
- //}
+:host {
+ width: 100%;
+}
+
+.source {
+ .no-details {
+ color: #bdc9d5;
+ }
+
+ .resource-wrap {
+ display: flex;
+ justify-content: space-between;
+ height: 26px;
+ align-items: center;
+
+ .resource-name {
+ width: 50%;
+ white-space: nowrap;
+ overflow: hidden;
+
+ .detailed-link {
+ color: #35afd5;
+ cursor: pointer;
+ }
+ }
+
+ .resource-status {
+ width: 35%;
+ text-transform: capitalize;
+ }
+
+ .resource-actions {
+ display: flex;
+ justify-content: flex-end;
+ width: 15%;
+ padding-right: 10px;
+
+ a {
+ text-decoration: none;
+ color: #748ba3;
+ font-size: 18px;
+ text-align: center;
+
+ i {
+ font-size: 18px;
+ padding: 0 2px;
+ }
+ }
+
+ .schedule {
+ display: flex;
+ align-items: center;
+ font-size: 18px;
+ cursor: pointer;
+
+ &.not-allowed {
+ pointer-events: none;
+ }
+
+ &.active {
+ color: #49af38;
+ }
+ }
+
+ .start-stop-action {
+ display: flex;
+ align-items: center;
+ font-size: 18px;
+ color: #36afd5;
+
+ i {
+ cursor: pointer;
+
+ &.not-allowed {
+ pointer-events: none;
+ }
+ }
+ }
+
+ .remove_butt {
+ display: flex;
+ align-items: center;
+ color: #ef5c4b;
+ cursor: pointer;
+
+ &.disabled {
+ color: #f5d3d3;
+ pointer-events: none;
+ }
+ }
+ }
+ }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.ts
index 7bc5126..b59b9e5 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resources-list/computational-resources-list.component.ts
@@ -88,7 +88,6 @@ export class ComputationalResourcesListComponent {
}
}
-
@Component({
selector: 'confirmation-dialog',
template: `
@@ -116,6 +115,7 @@ export class ComputationalResourcesListComponent {
</div>
`
})
+
export class ConfirmationDialogComponent {
constructor(
public dialogRef: MatDialogRef<ConfirmationDialogComponent>,
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html
index b628122..ae576f2 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.html
@@ -45,9 +45,23 @@
</div>
</form>
<div class="text-center m-top-30 m-bott-10">
- <button mat-raised-button type="button" class="butt action" (click)="dialogRef.close()">Cancel</button>
- <button mat-raised-button type="button" [disabled]="!createAMIForm.valid"
- (click)="assignChanges(createAMIForm.value)" class="butt butt-success action">Create</button>
+ <button
+ mat-raised-button
+ type="button"
+ class="butt action"
+ (click)="dialogRef.close()"
+ >
+ Cancel
+ </button>
+ <button
+ mat-raised-button
+ type="button"
+ [disabled]="!createAMIForm.valid"
+ (click)="assignChanges(createAMIForm.value)"
+ class="butt butt-success action"
+ >
+ Create
+ </button>
</div>
</div>
</div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.scss
index 367b1a4..9fca02a 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.scss
@@ -20,7 +20,6 @@
form {
textarea {
height: 70px;
- resize: none;
padding-top: 8px;
// font: 300 16px/26px 'Open Sans', sans-serif;
font-style: normal;
@@ -28,6 +27,7 @@ form {
font-size: 16px;
line-height: 26px;
font-family:'Open Sans', sans-serif;
+ resize: none;
}
}
.ami-dialog{
@@ -35,8 +35,8 @@ form {
&.name {
padding-bottom: 30px;
.error{
- top: 37px;
position: absolute;
+ top: 37px;
right: 0;
}
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts
index 56cb2a4..315f8f3 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/ami-create-dialog/ami-create-dialog.component.ts
@@ -23,7 +23,7 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ToastrService } from 'ngx-toastr';
import { UserResourceService } from '../../../core/services';
-import {HTTP_STATUS_CODES, PATTERNS} from '../../../core/util';
+import { HTTP_STATUS_CODES, PATTERNS } from '../../../core/util';
import { DICTIONARY } from '../../../../dictionary/global.dictionary';
@Component({
@@ -63,7 +63,12 @@ export class AmiCreateDialogComponent implements OnInit {
private initFormModel(): void {
this.createAMIForm = this._fb.group({
- name: ['', [Validators.required, Validators.pattern(PATTERNS.namePattern), Validators.maxLength(10), this.checkDuplication.bind(this)]],
+ name: ['', [
+ Validators.required,
+ Validators.pattern(PATTERNS.namePattern),
+ Validators.maxLength(10),
+ this.checkDuplication.bind(this)
+ ]],
description: [''],
exploratory_name: [this.notebook.name],
project_name: [this.notebook.project]
@@ -71,14 +76,16 @@ export class AmiCreateDialogComponent implements OnInit {
}
private checkDuplication(control) {
- if (control.value)
+ if (control.value) {
return this.isDuplicate(control.value) ? { duplication: true } : null;
+ }
}
private isDuplicate(value: string) {
for (let index = 0; index < this.imagesList.length; index++) {
- if (this.delimitersFiltering(value) === this.delimitersFiltering(this.imagesList[index].name))
+ if (this.delimitersFiltering(value) === this.delimitersFiltering(this.imagesList[index].name)) {
return true;
+ }
}
return false;
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/cost-details-dialog/cost-details-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/cost-details-dialog/cost-details-dialog.component.html
index a194a3d..3bf0518 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/cost-details-dialog/cost-details-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/cost-details-dialog/cost-details-dialog.component.html
@@ -33,29 +33,28 @@
</table>
<div class="billing-detail content-box">
<mat-list>
- <mat-list-item class="list-header" [ngClass]="{'pr-3': notebook.billing.report_lines.length > 6}">
- <div class="resource-name ellipsis" [ngClass]="{ 'wide-name-field' : provider === 'azure' }">Name</div>
- <div class="service">Product</div>
+ <mat-list-item class="list-header" [ngClass]="{'pr-3': notebook.billing.report_lines.length > 6}">
+ <div class="resource-name ellipsis" [ngClass]="{ 'wide-name-field' : provider === 'azure' }">Name</div>
+ <div class="service">Product</div>
<!-- <div class="resource-type" *ngIf="provider === 'aws'">Type</div>-->
- <div class="usage-date-start">Start</div>
- <div class="usage-date-end">End</div>
- <div class="cost-currency">Cost</div>
- </mat-list-item>
- <div class="scrolling-content" id="scrolling">
- <mat-list-item *ngFor="let item of notebook.billing.report_lines">
- <div class="resource-name ellipsis" [ngClass]="{ 'wide-name-field' : provider === 'azure' }"
- matTooltip="{{ item.resource_name }}"
- matTooltipPosition="above">
- {{ item.resource_name }}
- </div>
- <div class="service">{{ item.product }}</div>
+ <div class="usage-date-start">Start</div>
+ <div class="usage-date-end">End</div>
+ <div class="cost-currency">Cost</div>
+ </mat-list-item>
+ <div class="scrolling-content" id="scrolling">
+ <mat-list-item *ngFor="let item of notebook.billing.report_lines">
+ <div class="resource-name ellipsis" [ngClass]="{ 'wide-name-field' : provider === 'azure' }"
+ matTooltip="{{ item.resource_name }}"
+ matTooltipPosition="above">
+ {{ item.resource_name }}
+ </div>
+ <div class="service">{{ item.product }}</div>
<!-- <div class="resource-type" >{{ item.resourse_type }}</div>-->
- <div class="usage-date-start ellipsis">{{ item.from.join('/') | localDate : 'shortDate'}}</div>
- <div class="usage-date-end ellipsis">{{ item.to.join('/') | localDate : 'shortDate'}}</div>
- <div class="cost-currency">{{ item.cost | localcurrency }}</div>
- </mat-list-item>
- </div>
-
+ <div class="usage-date-start ellipsis">{{ item.from.join('/') | localDate : 'shortDate'}}</div>
+ <div class="usage-date-end ellipsis">{{ item.to.join('/') | localDate : 'shortDate'}}</div>
+ <div class="cost-currency">{{ item.cost | localcurrency }}</div>
+ </mat-list-item>
+ </div>
</mat-list>
<div class="total">
<strong>Total: </strong>{{ notebook.cost | localcurrency }}</div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/cost-details-dialog/cost-details-dialog.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/cost-details-dialog/cost-details-dialog.component.scss
index 897482c..615145c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/cost-details-dialog/cost-details-dialog.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/cost-details-dialog/cost-details-dialog.component.scss
@@ -21,17 +21,15 @@
table {
width: 100%;
- th,
- td {
- border: 1px solid #edf1f5;
+ th, td {
padding: 18px 15px;
+ border: 1px solid #edf1f5;
text-align: left;
}
}
}
.billing-detail {
-
div {
font-size: 13px;
}
@@ -48,17 +46,17 @@
.resource-name,
.usage-date-start,
.usage-date-end {
- overflow: hidden;
padding-right: 10px;
+ overflow: hidden;
white-space: nowrap;
}
- .resource-name{
+ .resource-name {
width: 20%;
}
.usage-date-start,
- .usage-date-end{
+ .usage-date-end {
width: 17%;
}
@@ -67,8 +65,8 @@
}
.resource-name {
- cursor: pointer;
width: 20%;
+ cursor: pointer;
}
.total {
@@ -86,10 +84,10 @@
}
.mat-list-item-content {
- font-size: 14px !important;
justify-content: space-between;
+ font-size: 14px !important;
}
-mat-tooltip-component>.mat-tooltip {
+mat-tooltip-component > .mat-tooltip {
white-space: pre;
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/create-environment/create-environment.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/create-environment/create-environment.component.html
index 90e2ba4..db3615d 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/create-environment/create-environment.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/create-environment/create-environment.component.html
@@ -29,10 +29,17 @@
<label class="label">Select project</label>
<div class="control selector-wrapper">
<mat-form-field>
- <mat-select formControlName="project" disableOptionCentering panelClass="create-resources-dialog scrolling" placeholder="Select project">
+ <mat-select
+ formControlName="project"
+ disableOptionCentering
+ panelClass="create-resources-dialog scrolling"
+ placeholder="Select project"
+ >
<mat-option *ngFor="let project of projects" [value]="project.name" (click)="setEndpoints(project)">
- {{ project.name }}</mat-option>
- <mat-option *ngIf="!projects.length" class="multiple-select ml-10" disabled>Projects list is empty
+ {{ project.name }}
+ </mat-option>
+ <mat-option *ngIf="!projects.length" class="multiple-select ml-10" disabled>
+ Projects list is empty
</mat-option>
</mat-select>
<button class="caret">
@@ -46,12 +53,22 @@
<label class="label">Select endpoint</label>
<div class="control selector-wrapper" [ngClass]="{ 'not-active' : !endpoints.length }">
<mat-form-field>
- <mat-select formControlName="endpoint" disableOptionCentering [disabled]="!endpoints.length"
- panelClass="create-resources-dialog scrolling" placeholder="Select endpoint">
- <mat-option *ngFor="let endpoint of endpoints" [value]="endpoint"
- (click)="getTemplates(createExploratoryForm?.controls['project'].value, endpoint)">{{ endpoint }}
+ <mat-select
+ formControlName="endpoint"
+ disableOptionCentering
+ [disabled]="!endpoints.length"
+ panelClass="create-resources-dialog scrolling"
+ placeholder="Select endpoint"
+ >
+ <mat-option
+ *ngFor="let endpoint of endpoints"
+ [value]="endpoint"
+ (click)="getTemplates(createExploratoryForm?.controls['project'].value, endpoint)"
+ >
+ {{ endpoint }}
</mat-option>
- <mat-option *ngIf="!endpoints.length" class="multiple-select ml-10" disabled>Endpoints list is empty
+ <mat-option *ngIf="!endpoints.length" class="multiple-select ml-10" disabled>
+ Endpoints list is empty
</mat-option>
</mat-select>
<button class="caret">
@@ -60,23 +77,34 @@
</mat-form-field>
</div>
</div>
+
<div class="control-group">
<label class="label">Select template</label>
- <div class="control selector-wrapper"
- [matTooltip]="'Notebook creations are not available. Please, check your permissions.'"
- matTooltipPosition="above"
- [matTooltipClass]="'full-size-tooltip'"
- [matTooltipDisabled]="!(templates.length === 0 && this.createExploratoryForm.controls.endpoint.value)"
+ <div
+ class="control selector-wrapper"
+ [matTooltip]="'Notebook creations are not available. Please, check your permissions.'"
+ matTooltipPosition="above"
+ [matTooltipClass]="'full-size-tooltip'"
+ [matTooltipDisabled]="!(templates.length === 0 && this.createExploratoryForm.controls.endpoint.value)"
>
<span class="form-field-wrapper" [ngClass]="{ 'not-active' : !templates.length || (templates.length === 0 && this.createExploratoryForm.controls.endpoint.value)}">
<mat-form-field>
- <mat-select formControlName="version" disableOptionCentering [disabled]="!templates.length || (templates.length === 0 && this.createExploratoryForm.controls.endpoint.value)"
- panelClass="create-resources-dialog scrolling" placeholder="Select template">
- <mat-option *ngFor="let template of templates"
- [value]="template.exploratory_environment_versions[0].version" (click)="getShapes(template)">
+ <mat-select
+ formControlName="version"
+ disableOptionCentering
+ [disabled]="!templates.length || (templates.length === 0 && this.createExploratoryForm.controls.endpoint.value)"
+ panelClass="create-resources-dialog scrolling"
+ placeholder="Select template"
+ >
+ <mat-option
+ *ngFor="let template of templates"
+ [value]="template.exploratory_environment_versions[0].version"
+ (click)="getShapes(template)"
+ >
{{ template.exploratory_environment_versions[0].template_name }}
</mat-option>
- <mat-option *ngIf="!templates.length" class="multiple-select ml-10" disabled>Templates list is empty
+ <mat-option *ngIf="!templates.length" class="multiple-select ml-10" disabled>
+ Templates list is empty
</mat-option>
</mat-select>
<button class="caret">
@@ -118,7 +146,8 @@
>
{{ image?.status ? image?.name + ' (custom image)' : image?.name }}
</mat-option>
- <mat-option *ngIf="!images?.length" class="multiple-select ml-10" disabled>Images list is empty
+ <mat-option *ngIf="!images?.length" class="multiple-select ml-10" disabled>
+ Images list is empty
</mat-option>
</mat-select>
<button class="caret">
@@ -142,13 +171,20 @@
<div class="control-group name-control">
<label class="label">Name</label>
<div class="control">
- <input [class.danger_field]=" !createExploratoryForm?.controls['name'].valid
- && createExploratoryForm?.controls['name'].dirty
- && createExploratoryForm?.controls['name'].hasError('duplication')" type="text"
- class="form-control" placeholder="Enter Name" formControlName="name">
+ <input
+ [class.danger_field]=" !createExploratoryForm?.controls['name'].valid
+ && createExploratoryForm?.controls['name'].dirty
+ && createExploratoryForm?.controls['name'].hasError('duplication')"
+ type="text"
+ class="form-control"
+ placeholder="Enter Name"
+ formControlName="name"
+ />
<span class="error" *ngIf="createExploratoryForm?.controls['name'].hasError('duplication')">This name already exists in current project.</span>
- <span class="error" *ngIf="createExploratoryForm?.controls['name'].hasError('maxlength') &&
- !createExploratoryForm?.controls['name'].hasError('pattern')"
+ <span
+ class="error"
+ *ngIf="createExploratoryForm?.controls['name'].hasError('maxlength')
+ && !createExploratoryForm?.controls['name'].hasError('pattern')"
>
Name cannot be longer than {{maxNotebookLength}} characters.
</span>
@@ -160,27 +196,33 @@
<div class="control-group">
<label class="label">Instance size</label>
- <div class="control selector-wrapper"
- [matTooltip]="'Instance size are not available. Please, check your permissions.'"
- matTooltipPosition="above"
- [matTooltipClass]="'full-size-tooltip'"
- [matTooltipDisabled]="!(!areShapes && currentTemplate)"
+ <div
+ class="control selector-wrapper"
+ [matTooltip]="'Instance size are not available. Please, check your permissions.'"
+ matTooltipPosition="above"
+ [matTooltipClass]="'full-size-tooltip'"
+ [matTooltipDisabled]="!(!areShapes && currentTemplate)"
>
<span class="form-field-wrapper" [ngClass]="{ 'not-active': !currentTemplate || !areShapes && currentTemplate}">
- <mat-form-field>
- <mat-label>Select instance size</mat-label>
- <mat-select formControlName="shape" disableOptionCentering [disabled]="!currentTemplate || !areShapes && currentTemplate"
- panelClass="create-resources-shapes scrolling" placeholder="Instance size">
- <mat-optgroup *ngFor="let item of (shapes | keys)" [label]="item.key | underscoreless">
- <mat-option *ngFor="let list_item of item.value" [value]="list_item.Type" (click)="setInstanceSize()">
- <strong class="highlight icon-label">{{ list_item.Size }}</strong> {{ list_item.Type }}
- </mat-option>
- </mat-optgroup>
- </mat-select>
- <button class="caret">
- <i class="material-icons">keyboard_arrow_down</i>
- </button>
- </mat-form-field>
+ <mat-form-field>
+ <mat-label>Select instance size</mat-label>
+ <mat-select
+ formControlName="shape"
+ disableOptionCentering
+ [disabled]="!currentTemplate || !areShapes && currentTemplate"
+ panelClass="create-resources-shapes scrolling"
+ placeholder="Instance size"
+ >
+ <mat-optgroup *ngFor="let item of (shapes | keys)" [label]="item.key | underscoreless">
+ <mat-option *ngFor="let list_item of item.value" [value]="list_item.Type" (click)="setInstanceSize()">
+ <strong class="highlight icon-label">{{ list_item.Size }}</strong> {{ list_item.Type }}
+ </mat-option>
+ </mat-optgroup>
+ </mat-select>
+ <button class="caret">
+ <i class="material-icons">keyboard_arrow_down</i>
+ </button>
+ </mat-form-field>
</span>
</div>
</div>
@@ -196,20 +238,23 @@
<span
class="error"
*ngIf="createExploratoryForm?.controls['custom_tag'].hasError('maxlength') &&
- !createExploratoryForm?.controls['custom_tag'].hasError('pattern')">
- Custom tag name cannot be longer than {{maxCustomTagLength}} characters.
- </span>
- <span class="error"
- *ngIf="createExploratoryForm?.controls['custom_tag'].hasError('pattern')">
+ !createExploratoryForm?.controls['custom_tag'].hasError('pattern')"
+ >
+ Custom tag name cannot be longer than {{maxCustomTagLength}} characters.
+ </span>
+ <span
+ class="error"
+ *ngIf="createExploratoryForm?.controls['custom_tag'].hasError('pattern')"
+ >
Custom tag can only contain letters, numbers, hyphens and '_' but can not end with special characters.</span>
</div>
<div *ngIf="currentTemplate">
<ng-template
- [ngIf]="selectedCloud === 'gcp' &&
- (currentTemplate?.image === 'docker.datalab-jupyter' ||
- currentTemplate?.image === 'docker.datalab-deeplearning' ||
- currentTemplate?.image === 'docker.datalab-tensor')"
+ [ngIf]="selectedCloud === 'gcp'
+ && (currentTemplate?.image === 'docker.datalab-jupyter'
+ || currentTemplate?.image === 'docker.datalab-deeplearning'
+ || currentTemplate?.image === 'docker.datalab-tensor')"
>
<div class="checkbox-group">
<div
@@ -229,60 +274,76 @@
>
<div class="control-group">
<label class="label">GPU type</label>
- <div class="control selector-wrapper"
- [matTooltip]="'Please, select instance size.'"
- matTooltipPosition="above"
- [matTooltipClass]="'full-size-tooltip'"
- [matTooltipDisabled]="!!createExploratoryForm.controls['shape'].value"
+ <div
+ class="control selector-wrapper"
+ [matTooltip]="'Please, select instance size.'"
+ matTooltipPosition="above"
+ [matTooltipClass]="'full-size-tooltip'"
+ [matTooltipDisabled]="!!createExploratoryForm.controls['shape'].value"
>
- <span class="form-field-wrapper" [ngClass]="{ 'not-active': !createExploratoryForm.controls['shape'].value}">
- <mat-form-field>
- <mat-label>Select GPU type</mat-label>
- <mat-select formControlName="gpu_type" disableOptionCentering [disabled]="!createExploratoryForm.controls['shape'].value"
- panelClass="create-resources-dialog" placeholder="GPU type">
- <mat-option *ngFor="let list_item of gpuTypes; index as i" [value]="list_item" (click)="setCount('', list_item)">
- <strong class="highlight icon-label">{{ addSizeToGpuType(i) }}</strong> {{ list_item }}
- </mat-option>
- <mat-option *ngIf="!gpuTypes.length" class="multiple-select ml-10" disabled>
- GPU list is empty
- </mat-option>
- </mat-select>
- <button class="caret">
- <i class="material-icons">keyboard_arrow_down</i>
- </button>
- </mat-form-field>
- </span>
+ <span class="form-field-wrapper" [ngClass]="{ 'not-active': !createExploratoryForm.controls['shape'].value}">
+ <mat-form-field>
+ <mat-label>Select GPU type</mat-label>
+ <mat-select
+ formControlName="gpu_type"
+ disableOptionCentering
+ [disabled]="!createExploratoryForm.controls['shape'].value"
+ panelClass="create-resources-dialog"
+ placeholder="GPU type"
+ >
+ <mat-option *ngFor="let list_item of gpuTypes; index as i" [value]="list_item" (click)="setCount('', list_item)">
+ <strong class="highlight icon-label">{{ addSizeToGpuType(i) }}</strong> {{ list_item }}
+ </mat-option>
+ <mat-option *ngIf="!gpuTypes.length" class="multiple-select ml-10" disabled>
+ GPU list is empty
+ </mat-option>
+ </mat-select>
+ <button class="caret">
+ <i class="material-icons">keyboard_arrow_down</i>
+ </button>
+ </mat-form-field>
+ </span>
</div>
</div>
<div class="control-group">
<label class="label">GPU count</label>
- <div class="control selector-wrapper"
- [matTooltip]="'Please, select GPU type.'"
- matTooltipPosition="above"
- [matTooltipClass]="'full-size-tooltip'"
- [matTooltipDisabled]="!!createExploratoryForm.controls['gpu_type'].value"
+ <div
+ class="control selector-wrapper"
+ [matTooltip]="'Please, select GPU type.'"
+ matTooltipPosition="above"
+ [matTooltipClass]="'full-size-tooltip'"
+ [matTooltipDisabled]="!!createExploratoryForm.controls['gpu_type'].value"
>
- <span class="form-field-wrapper" [ngClass]="{ 'not-active': !createExploratoryForm.controls['gpu_type'].value}">
- <mat-form-field>
- <mat-label>Select GPU count</mat-label>
- <mat-select formControlName="gpu_count" disableOptionCentering [disabled]="!createExploratoryForm.controls['gpu_type'].value"
- panelClass="create-resources-dialog" placeholder="GPU count">
- <mat-option *ngFor="let list_item of gpuCount" [value]="list_item">
- {{ list_item }}
- </mat-option>
- </mat-select>
- <button class="caret">
- <i class="material-icons">keyboard_arrow_down</i>
- </button>
- </mat-form-field>
- </span>
+ <span class="form-field-wrapper" [ngClass]="{ 'not-active': !createExploratoryForm.controls['gpu_type'].value}">
+ <mat-form-field>
+ <mat-label>Select GPU count</mat-label>
+ <mat-select
+ formControlName="gpu_count"
+ disableOptionCentering
+ [disabled]="!createExploratoryForm.controls['gpu_type'].value"
+ panelClass="create-resources-dialog"
+ placeholder="GPU count"
+ >
+ <mat-option *ngFor="let list_item of gpuCount" [value]="list_item">
+ {{ list_item }}
+ </mat-option>
+ </mat-select>
+ <button class="caret">
+ <i class="material-icons">keyboard_arrow_down</i>
+ </button>
+ </mat-form-field>
+ </span>
</div>
</div>
</ng-template>
- </div>
+ </div>
</ng-template>
- <div class="checkbox-group"
- *ngIf="currentTemplate?.image !== 'docker.datalab-zeppelin' && currentTemplate?.image !== 'docker.datalab-superset' && currentTemplate?.image !== 'docker.datalab-jupyterlab'">
+ <div
+ class="checkbox-group"
+ *ngIf="currentTemplate?.image !== 'docker.datalab-zeppelin'
+ && currentTemplate?.image !== 'docker.datalab-superset'
+ && currentTemplate?.image !== 'docker.datalab-jupyterlab'"
+ >
<div class="d-flex cursor-pointer label m-bott-20" (click)="selectConfiguration()">
<div class="empty-checkbox" [ngClass]="{'checked': this.additionalParams.configurationNode}">
<span class="checked-checkbox" *ngIf="this.additionalParams.configurationNode"></span>
@@ -290,27 +351,48 @@
<span class=" pl-5">Spark configurations</span>
</div>
<div class="config-details" [ngClass]="{ show: this.additionalParams.configurationNode}">
- <textarea formControlName="cluster_config" placeholder="Cluster configuration template, JSON"
- data-gramm_editor="false" id="config"></textarea>
- <span class="error spark-config"
- *ngIf="!createExploratoryForm?.controls.cluster_config.valid && createExploratoryForm?.controls['cluster_config'].dirty">Configuration
- parameters is not in a valid format.</span>
+ <textarea
+ formControlName="cluster_config"
+ placeholder="Cluster configuration template, JSON"
+ data-gramm_editor="false"
+ id="config"
+ ></textarea>
+ <span
+ class="error spark-config"
+ *ngIf="!createExploratoryForm?.controls.cluster_config.valid
+ && createExploratoryForm?.controls['cluster_config'].dirty"
+ >
+ Configuration parameters is not in a valid format.
+ </span>
</div>
</div>
- <small *ngIf="currentTemplate?.image === 'docker.datalab-zeppelin'">
- 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 <a
- href="https://zeppelin.apache.org/docs/0.9.0/usage/interpreter/overview.html" target="_blank">official
- documentation</a>.
- </small>
+ <small *ngIf="currentTemplate?.image === 'docker.datalab-zeppelin'">
+ 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 <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" id="buttons">
- <button mat-raised-button type="button" class="butt action" (click)="dialogRef.close()">Cancel</button>
- <button mat-raised-button type="button" class="butt butt-success action"
+ <button
+ mat-raised-button
+ type="button"
+ class="butt action"
+ (click)="dialogRef.close()"
+ >
+ Cancel
+ </button>
+ <button
+ mat-raised-button
+ type="button"
+ class="butt butt-success action"
[disabled]="!createExploratoryForm?.valid"
- (click)="createExploratoryEnvironment(createExploratoryForm.value)">Create</button>
+ (click)="createExploratoryEnvironment(createExploratoryForm.value)"
+ >
+ Create
+ </button>
</div>
</form>
</div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/create-environment/create-environment.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/create-environment/create-environment.component.scss
index 44aa50d..4bd5d07 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/create-environment/create-environment.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/create-environment/create-environment.component.scss
@@ -18,8 +18,7 @@
*/
.checkbox-group {
-
- .empty-checkbox{
+ .empty-checkbox {
margin: 0;
}
@@ -30,11 +29,11 @@
text-align: left;
&.show {
+ position: relative;
+ margin-top: 10px;
height: 200px;
visibility: visible;
opacity: 1;
- position: relative;
- margin-top: 10px;
}
textarea {
@@ -46,7 +45,7 @@
font-family: Consolas, monospace;
}
- .spark-config{
+ .spark-config {
position: absolute;
bottom: -15px;
right: 0;
@@ -88,7 +87,7 @@
overflow-y: auto;
}
-.form-field-wrapper{
+.form-field-wrapper {
width: 100%;
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/create-environment/create-environment.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/create-environment/create-environment.component.ts
index 0e6c5af..fa32e8e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/create-environment/create-environment.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/create-environment/create-environment.component.ts
@@ -24,11 +24,11 @@ import { ToastrService } from 'ngx-toastr';
import { Project } from '../../../administration/project/project.component';
import { UserResourceService, ProjectService } from '../../../core/services';
-import {CheckUtils, SortUtils, HTTP_STATUS_CODES, PATTERNS, HelpUtils} from '../../../core/util';
+import { CheckUtils, SortUtils, HTTP_STATUS_CODES, PATTERNS, HelpUtils } from '../../../core/util';
import { DICTIONARY } from '../../../../dictionary/global.dictionary';
import { CLUSTER_CONFIGURATION } from '../../computational/computational-resource-create-dialog/cluster-configuration-templates';
-import {tap} from 'rxjs/operators';
-import {timer} from 'rxjs';
+import { tap } from 'rxjs/operators';
+import { timer } from 'rxjs';
@Component({
selector: 'create-environment',
@@ -140,7 +140,6 @@ export class ExploratoryEnvironmentCreateComponent implements OnInit {
this.userResourceService.getExploratoryTemplates(project, endpoint)
.pipe(tap(results => {
-
results.sort((a, b) =>
(a.exploratory_environment_versions[0].template_name > b.exploratory_environment_versions[0].template_name) ?
1 : -1);
@@ -149,7 +148,6 @@ export class ExploratoryEnvironmentCreateComponent implements OnInit {
this.templates = templates;
}
);
-
}
public getShapes(template) {
@@ -217,7 +215,6 @@ export class ExploratoryEnvironmentCreateComponent implements OnInit {
template_name: this.currentTemplate.exploratory_environment_versions[0].template_name
};
-
if (!data.notebook_image_name
&& this.currentTemplate.image === 'docker.datalab-deeplearning'
&& this.selectedCloud === 'aws' || this.selectedCloud === 'azure') {
@@ -321,17 +318,19 @@ export class ExploratoryEnvironmentCreateComponent implements OnInit {
private getImagesList() {
this.userResourceService.getUserImages(this.currentTemplate.image, this.createExploratoryForm.controls['project'].value,
this.createExploratoryForm.controls['endpoint'].value)
- .subscribe((res: any) => {
- this.images = res.filter(el => el.status === 'CREATED');
-
- if(this.selectedCloud === 'gcp' && this.currentTemplate.image === 'docker.datalab-deeplearning') {
+ .subscribe(
+ (res: any) => {
+ this.images = res.filter(el => el.status === 'CREATED');
+
+ if(this.selectedCloud === 'gcp' && this.currentTemplate.image === 'docker.datalab-deeplearning') {
this.currentTemplate.exploratory_environment_images = this.currentTemplate.exploratory_environment_images.map(image => {
return {name: image['Image family'] ?? image.name, description: image['Description'] ?? image.description}
});
this.images.push(...this.currentTemplate.exploratory_environment_images);
}
- },
- error => this.toastr.error(error.message || 'Images list loading failed!', 'Oops!'));
+ },
+ error => this.toastr.error(error.message || 'Images list loading failed!', 'Oops!')
+ );
}
private checkDuplication(control) {
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html
index fbd5ff1..bcced52 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html
@@ -60,34 +60,44 @@
<div *ngIf="data.type === 'environment'" class="detail-info">
<p>Open following URL(s) in your browser to access this box:</p>
<div class="links_block">
-<!-- <p *ngFor="let item of notebook.exploratory_urls">-->
-<!-- <span class="description">{{item.description}}: </span>-->
-<!-- <a class="ellipsis" matTooltip="{{item.url}}" matTooltipPosition="above" href="{{item.url}}"-->
-<!-- target="_blank" (click)="logAction(notebook.name, item.description)">-->
-<!-- {{item.url}}{{notebook.name}}-->
-<!-- </a>-->
-<!-- </p>-->
+ <!-- <p *ngFor="let item of notebook.exploratory_urls">-->
+ <!-- <span class="description">{{item.description}}: </span>-->
+ <!-- <a class="ellipsis" matTooltip="{{item.url}}" matTooltipPosition="above" href="{{item.url}}"-->
+ <!-- target="_blank" (click)="logAction(notebook.name, item.description)">-->
+ <!-- {{item.url}}{{notebook.name}}-->
+ <!-- </a>-->
+ <!-- </p> -->
<ng-container *ngFor="let item of notebook.exploratory_urls">
- <span class="d-none" *ngIf="item.description.toLowerCase() === 'ungit' && notebook.exploratory_urls[0].description.toLowerCase().indexOf('zeppelin') !== -1; else ungit">
- </span>
+ <span
+ class="d-none"
+ *ngIf="item.description.toLowerCase() === 'ungit'
+ && notebook.exploratory_urls[0].description.toLowerCase().indexOf('zeppelin') !== -1; else ungit"
+ ></span>
<ng-template #ungit>
<p (mouseleave)="hideCopyIcon()">
<span class="description">{{item.description}}: </span>
- <a (mouseover)="showCopyIcon(item.description)"
- (click)="logAction(notebook.name, item.description)"
- class="ellipsis none-select resources-url" matTooltip="{{item.url}}"
- matTooltipPosition="above"
- href="{{item.url}}"
- target="_blank"
- (contextmenu)="false"
+ <a
+ (mouseover)="showCopyIcon(item.description)"
+ (click)="logAction(notebook.name, item.description)"
+ class="ellipsis none-select resources-url" matTooltip="{{item.url}}"
+ matTooltipPosition="above"
+ href="{{item.url}}"
+ target="_blank"
+ (contextmenu)="false"
>
{{item.url}}
</a>
- <span (click)="logAction(notebook.name, item.description, 'Copy');$event.stopPropagation()" *ngIf="isCopyIconVissible[item.description]" [matTooltip]="isCopied ? 'Copy ' + item.description + ' url': 'Copied'" matTooltipPosition="above" class="copy-icon-wrapper">
- <span class="link-icon" (click)="copyLink(item.url)" >
- <span _ngcontent-xpv-c19="" class="material-icons" (click)="this.isCopied = false">content_copy</span>
- </span>
+ <span
+ (click)="logAction(notebook.name, item.description, 'Copy');$event.stopPropagation()"
+ *ngIf="isCopyIconVissible[item.description]"
+ [matTooltip]="isCopied ? 'Copy ' + item.description + ' url': 'Copied'"
+ matTooltipPosition="above"
+ class="copy-icon-wrapper"
+ >
+ <span class="link-icon" (click)="copyLink(item.url)" >
+ <span _ngcontent-xpv-c19="" class="material-icons" (click)="this.isCopied = false">content_copy</span>
</span>
+ </span>
</p>
</ng-template>
</ng-container>
@@ -97,200 +107,236 @@
<div class="detail-info" *ngIf="!notebook.error_message">
<p>Edge Node IP Address {{notebook.node_ip}}</p>
<p *ngIf="notebook.status === 'running'">Up time {{upTimeInHours}} hour(s) since
- {{notebook.time ? (notebook.time | longDate ) : "not specified."}}</p>
+ {{notebook.time ? (notebook.time | longDate ) : "not specified."}}
+ </p>
<p *ngIf="notebook.url?.length">Open following URL(s) in your browser to access this box:</p>
<div class="links_block">
<ng-container *ngFor="let item of notebook.url">
- <span class="d-none" *ngIf="item.description.toLowerCase() === 'ungit' && notebook.template_name.toLowerCase().indexOf('zeppelin ') !== -1; else ungit">
- </span>
+ <span
+ class="d-none"
+ *ngIf="item.description.toLowerCase() === 'ungit'
+ && notebook.template_name.toLowerCase().indexOf('zeppelin ') !== -1; else ungit"
+ ></span>
<ng-template #ungit>
<p (mouseleave)="hideCopyIcon()">
<span class="description">{{item.description}}: </span>
- <a (mouseover)="showCopyIcon(item.description)"
- (click)="logAction(notebook.name, item.description)"
- class="ellipsis none-select resources-url" matTooltip="{{item.url}}"
- matTooltipPosition="above"
- href="{{item.url}}"
+ <a
+ (mouseover)="showCopyIcon(item.description)"
+ (click)="logAction(notebook.name, item.description)"
+ class="ellipsis none-select resources-url" matTooltip="{{item.url}}"
+ matTooltipPosition="above"
+ href="{{item.url}}"
target="_blank"
- (contextmenu)="false"
+ (contextmenu)="false"
>
{{item.url}}
</a>
- <span (click)="logAction(notebook.name, item.description, 'Copy');$event.stopPropagation()" *ngIf="isCopyIconVissible[item.description]" [matTooltip]="isCopied ? 'Copy ' + item.description + ' url': 'Copied'" matTooltipPosition="above" class="copy-icon-wrapper">
+ <span
+ (click)="logAction(notebook.name, item.description, 'Copy');$event.stopPropagation()"
+ *ngIf="isCopyIconVissible[item.description]"
+ [matTooltip]="isCopied ? 'Copy ' + item.description + ' url': 'Copied'"
+ matTooltipPosition="above"
+ class="copy-icon-wrapper"
+ >
<span class="link-icon" (click)="copyLink(item.url)" >
<span _ngcontent-xpv-c19="" class="material-icons" (click)="this.isCopied = false">content_copy</span>
- </span>
+ </span>
</span>
</p>
</ng-template>
</ng-container>
</div>
- <p class="flex" *ngIf="notebook.username">Node User: <span
- class="strong">{{ notebook.username }}</span></p>
- <p class="flex" *ngIf="notebook.password">Password: <span
- class="strong">{{ notebook.password }}</span></p>
+ <p class="flex" *ngIf="notebook.username">
+ Node User: <span class="strong">{{ notebook.username }}</span>
+ </p>
+ <p class="flex" *ngIf="notebook.password">
+ Password: <span class="strong">{{ notebook.password }}</span>
+ </p>
<p class="m-top-30">{{ 'Project bucket' }}: </p>
<div class="links_block" (mouseleave)="hideCopyIcon()">
- <p *ngIf="PROVIDER === 'azure' && notebook.account_name" class="bucket-info-wrapper">
- <span
- class="bucket-info"
- (mouseover)="showCopyIcon('bucket')"
- [matTooltip]="notebook.bucket_name + '@' + notebook.account_name + '.blob.core.windows.net'"
- matTooltipPosition="above"
- [matTooltipClass]="'full-size-tooltip'"
- >
- {{notebook.bucket_name + '@' + notebook.account_name + '.blob.core.windows.net'}}
- </span>
+ <p *ngIf="PROVIDER === 'azure' && notebook.account_name" class="bucket-info-wrapper">
+ <span
+ class="bucket-info"
+ (mouseover)="showCopyIcon('bucket')"
+ [matTooltip]="notebook.bucket_name + '@' + notebook.account_name + '.blob.core.windows.net'"
+ matTooltipPosition="above"
+ [matTooltipClass]="'full-size-tooltip'"
+ >
+ {{notebook.bucket_name + '@' + notebook.account_name + '.blob.core.windows.net'}}
+ </span>
- <span *ngIf="isCopyIconVissible.bucket" [matTooltip]="isCopied ? 'Copy bucket name' : 'Copied'" matTooltipPosition="above">
- <span class="link-icon" (click)="copyLink(notebook.bucket_name + '@' + notebook.account_name + '.blob.core.windows.net', true);$event.stopPropagation()" >
+ <span *ngIf="isCopyIconVissible.bucket" [matTooltip]="isCopied ? 'Copy bucket name' : 'Copied'" matTooltipPosition="above">
+ <span class="link-icon" (click)="copyLink(notebook.bucket_name + '@' + notebook.account_name + '.blob.core.windows.net', true);$event.stopPropagation()" >
<span _ngcontent-xpv-c19="" class="material-icons" (click)="this.isCopied = false">content_copy</span>
</span>
- </span>
- </p>
- <p *ngIf="notebook.bucket_name && PROVIDER !== 'azure'">{{ DICTIONARY[PROVIDER].container }}
- <span
- class="bucket-info"
- (mouseover)="showCopyIcon('bucket')"
- >
- {{ notebook.bucket_name }}
</span>
- <span *ngIf="isCopyIconVissible.bucket" [matTooltip]="isCopied ? 'Copy bucket name' : 'Copied'" matTooltipPosition="above">
- <span class="link-icon" (click)="copyLink(notebook.bucket_name, true);$event.stopPropagation()" >
+ </p>
+ <p *ngIf="notebook.bucket_name && PROVIDER !== 'azure'">{{ DICTIONARY[PROVIDER].container }}
+ <span
+ class="bucket-info"
+ (mouseover)="showCopyIcon('bucket')"
+ >
+ {{ notebook.bucket_name }}
+ </span>
+ <span *ngIf="isCopyIconVissible.bucket" [matTooltip]="isCopied ? 'Copy bucket name' : 'Copied'" matTooltipPosition="above">
+ <span class="link-icon" (click)="copyLink(notebook.bucket_name, true);$event.stopPropagation()" >
<span _ngcontent-xpv-c19="" class="material-icons" (click)="this.isCopied = false">content_copy</span>
</span>
- </span>
- </p>
+ </span>
+ </p>
</div>
<div class="bucket-info bucket-link">
<span></span>
-<!-- <button-->
-<!-- type="button"-->
-<!-- class="butt"-->
-<!-- mat-raised-button-->
-<!-- >-->
-<!-- Open bucket browser-->
-<!-- </button>-->
-<!-- <span class="description open-bucket"-->
-<!-- [ngClass]="{'not-allow': !this.bucketStatus['view'] || !thisdata.buckets.length}"-->
-<!-- (click)="bucketBrowser(notebook.bucket_name, notebook.endpoint, this.bucketStatus['view'] && thisdata.buckets.length)"-->
-<!-- >-->
- <span class="description open-bucket"
- [matTooltip]="!this.bucketStatus['view']
- ? 'You have not permission to open bucket'
- : 'You have not any bucket'"
- matTooltipDisabled="{{this.bucketStatus['view'] && this.data.buckets.length}}"
- matTooltipPosition="above"
- [matTooltipClass]="'full-size-tooltip'"
- [ngClass]="{'not-allow': !this.bucketStatus['view'] || !this.data.buckets.length}"
- (click)="bucketBrowser(notebook.cloud_provider !== 'azure' ? notebook.bucket_name : notebook.account_name + '.' + notebook.bucket_name, notebook.endpoint, this.bucketStatus['view'] && this.data.buckets.length)"
- >
+ <!-- <button-->
+ <!-- type="button"-->
+ <!-- class="butt"-->
+ <!-- mat-raised-button-->
+ <!-- >-->
+ <!-- Open bucket browser-->
+ <!-- </button>-->
+ <!-- <span class="description open-bucket"-->
+ <!-- [ngClass]="{'not-allow': !this.bucketStatus['view'] || !thisdata.buckets.length}"-->
+ <!-- (click)="bucketBrowser(notebook.bucket_name, notebook.endpoint, this.bucketStatus['view'] && thisdata.buckets.length)"-->
+ <!-- >-->
+ <span
+ class="description open-bucket"
+ [matTooltip]="!this.bucketStatus['view']
+ ? 'You have not permission to open bucket'
+ : 'You have not any bucket'"
+ matTooltipDisabled="{{this.bucketStatus['view'] && this.data.buckets.length}}"
+ matTooltipPosition="above"
+ [matTooltipClass]="'full-size-tooltip'"
+ [ngClass]="{'not-allow': !this.bucketStatus['view'] || !this.data.buckets.length}"
+ (click)="bucketBrowser(
+ notebook.cloud_provider !== 'azure' ? notebook.bucket_name : notebook.account_name + '.' + notebook.bucket_name,
+ notebook.endpoint,
+ this.bucketStatus['view'] && this.data.buckets.length
+ )"
+ >
Open bucket browser
</span>
</div>
-<!-- <p>Shared endpoint bucket: </p>-->
-<!-- <div class="links_block" (click)="bucketBrowser(notebook.shared_bucket_name, notebook.endpoint, this.bucketStatus['view'])"-->
-<!-- [matTooltip]="'You have not permission to open bucket'"-->
-<!-- matTooltipDisabled="{{this.bucketStatus['view']}}"-->
-<!-- matTooltipPosition="above"-->
-<!-- [matTooltipClass]="'full-size-tooltip'"-->
-<!-- >-->
-<!-- <p *ngIf="DICTIONARY[PROVIDER === 'azure' && notebook.shared_account_name">{{ DICTIONARY[PROVIDER].account }}-->
-<!-- <span class="bucket-info bucket-link" [ngClass]="{'not-allow': !this.bucketStatus['view']}" (mouseover)="showCopyIcon('shared')">{{ notebook.shared_account_name}}</span>-->
-<!-- <span *ngIf="isCopyIconVissible.shared" class="link-icon" (click)="copyBucketName(notebook.shared_account_name)">-->
-<!-- <span _ngcontent-xpv-c19="" class="material-icons" matTooltip="Copy bucket name" matTooltipPosition="above">content_copy</span>-->
-<!-- </span>-->
-<!-- </p>-->
-<!-- <p *ngIf="notebook.shared_bucket_name">{{ DICTIONARY[PROVIDER].container }}-->
-<!-- <span-->
-<!-- class="bucket-info bucket-link"-->
-<!-- [ngClass]="{'not-allow': !this.bucketStatus['view']}"-->
-<!-- (mouseover)="showCopyIcon('shared')"-->
-<!-- (click)="bucketBrowser(notebook.shared_bucket_name, notebook.endpoint, this.bucketStatus['view'])"-->
-<!-- >-->
-<!-- {{ notebook.shared_bucket_name }}-->
-<!-- </span>-->
-<!-- <span *ngIf="isCopyIconVissible.shared" class="link-icon" (click)="copyBucketName(notebook.shared_bucket_name)">-->
-<!-- <span _ngcontent-xpv-c19="" class="material-icons" matTooltip="Copy bucket name" matTooltipPosition="above">content_copy</span>-->
-<!-- </span>-->
-<!-- </p>-->
-<!-- </div>-->
-<!-- <br />-->
+ <!-- <p>Shared endpoint bucket: </p>-->
+ <!-- <div class="links_block" (click)="bucketBrowser(notebook.shared_bucket_name, notebook.endpoint, this.bucketStatus['view'])"-->
+ <!-- [matTooltip]="'You have not permission to open bucket'"-->
+ <!-- matTooltipDisabled="{{this.bucketStatus['view']}}"-->
+ <!-- matTooltipPosition="above"-->
+ <!-- [matTooltipClass]="'full-size-tooltip'"-->
+ <!-- >-->
+ <!-- <p *ngIf="DICTIONARY[PROVIDER === 'azure' && notebook.shared_account_name">{{ DICTIONARY[PROVIDER].account }}-->
+ <!-- <span class="bucket-info bucket-link" [ngClass]="{'not-allow': !this.bucketStatus['view']}" (mouseover)="showCopyIcon('shared')">{{ notebook.shared_account_name}}</span>-->
+ <!-- <span *ngIf="isCopyIconVissible.shared" class="link-icon" (click)="copyBucketName(notebook.shared_account_name)">-->
+ <!-- <span _ngcontent-xpv-c19="" class="material-icons" matTooltip="Copy bucket name" matTooltipPosition="above">content_copy</span>-->
+ <!-- </span>-->
+ <!-- </p>-->
+ <!-- <p *ngIf="notebook.shared_bucket_name">{{ DICTIONARY[PROVIDER].container }}-->
+ <!-- <span-->
+ <!-- class="bucket-info bucket-link"-->
+ <!-- [ngClass]="{'not-allow': !this.bucketStatus['view']}"-->
+ <!-- (mouseover)="showCopyIcon('shared')"-->
+ <!-- (click)="bucketBrowser(notebook.shared_bucket_name, notebook.endpoint, this.bucketStatus['view'])"-->
+ <!-- >-->
+ <!-- {{ notebook.shared_bucket_name }}-->
+ <!-- </span>-->
+ <!-- <span *ngIf="isCopyIconVissible.shared" class="link-icon" (click)="copyBucketName(notebook.shared_bucket_name)">-->
+ <!-- <span _ngcontent-xpv-c19="" class="material-icons" matTooltip="Copy bucket name" matTooltipPosition="above">content_copy</span>-->
+ <!-- </span>-->
+ <!-- </p>-->
+ <!-- </div>-->
+ <!-- <br />-->
-<!-- <div *ngIf="DICTIONARY[PROVIDER === 'azure' && notebook.datalake_name">-->
-<!-- <p>Data Lake Store: </p>-->
-<!-- <div class="links_block">-->
-<!-- <p>Data Lake Store Account: <span class="bucket-info">{{ notebook.datalake_name }}</span></p>-->
-<!-- <p>Personal folder: <span class="bucket-info">{{ notebook.datalake_directory }}</span></p>-->
-<!-- <p>Collaboration folder: <span class="bucket-info">{{ notebook.datalake_shared_directory }}</span>-->
-<!-- </p>-->
-<!-- </div>-->
-<!-- </div>-->
+ <!-- <div *ngIf="DICTIONARY[PROVIDER === 'azure' && notebook.datalake_name">-->
+ <!-- <p>Data Lake Store: </p>-->
+ <!-- <div class="links_block">-->
+ <!-- <p>Data Lake Store Account: <span class="bucket-info">{{ notebook.datalake_name }}</span></p>-->
+ <!-- <p>Personal folder: <span class="bucket-info">{{ notebook.datalake_directory }}</span></p>-->
+ <!-- <p>Collaboration folder: <span class="bucket-info">{{ notebook.datalake_shared_directory }}</span>-->
+ <!-- </p>-->
+ <!-- </div>-->
+ <!-- </div>-->
- <!-- <p>
- <a href="#/help/accessnotebookguide" target="_blank">
- <small class="helper_instruction">
- <i class="material-icons">help_outline</i>
- Read instruction how to create the tunnel</small>
- </a>
- </p> -->
+ <!-- <p>
+ <a href="#/help/accessnotebookguide" target="_blank">
+ <small class="helper_instruction">
+ <i class="material-icons">help_outline</i>
+ Read instruction how to create the tunnel</small>
+ </a>
+ </p> -->
</div>
- <div class="checkbox-group" *ngIf="notebook.image !== 'docker.datalab-zeppelin'; else not_support"
- [hidden]="notebook.status !== 'running' || notebook.image === 'docker.datalab-superset' || notebook.image === 'docker.datalab-jupyterlab'">
- <label>
- <input #configurationNode type="checkbox" (change)="selectConfiguration()"/> Cluster configurations
- </label>
- <div class="checkbox-group">
- <form [formGroup]="configurationForm" novalidate>
- <div class="config-details" *ngIf="configuration?.nativeElement['checked'] || false">
- <textarea formControlName="configuration_parameters" id="config"
- placeholder="Cluster configuration template, JSON" data-gramm_editor="false"></textarea>
- <span class="danger_color"
- *ngIf="!configurationForm.controls.configuration_parameters.valid && configurationForm.controls['configuration_parameters'].dirty">Configuration
- parameters is not in a valid format</span>
+ <div
+ class="checkbox-group"
+ *ngIf="notebook.image !== 'docker.datalab-zeppelin'; else not_support"
+ [hidden]="notebook.status !== 'running' || notebook.image === 'docker.datalab-superset'
+ || notebook.image === 'docker.datalab-jupyterlab'"
+ >
+ <label>
+ <input #configurationNode type="checkbox" (change)="selectConfiguration()"/> Cluster configurations
+ </label>
+ <div class="checkbox-group">
+ <form [formGroup]="configurationForm" novalidate>
+ <div class="config-details" *ngIf="configuration?.nativeElement['checked'] || false">
+ <textarea
+ formControlName="configuration_parameters"
+ id="config"
+ placeholder="Cluster configuration template, JSON"
+ data-gramm_editor="false"
+ ></textarea>
+ <span
+ class="danger_color"
+ *ngIf="!configurationForm.controls.configuration_parameters.valid
+ && configurationForm.controls['configuration_parameters'].dirty"
+ >
+ Configuration parameters is not in a valid format
+ </span>
</div>
- </form>
- </div>
- </div>
- <ng-template #not_support>
- <small [hidden]="notebook.status !== 'running'">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 <a
- href="https://zeppelin.apache.org/docs/0.9.0/usage/interpreter/overview.html" target="_blank">official
- documentation</a>.
- </small>
- </ng-template>
- <div [scrollTo]="configuration?.nativeElement['checked'] || false" class="text-center m-top-20 m-bott-10"
- *ngIf="(configuration?.nativeElement['checked'] || false) && notebook.status === 'running'">
- <button mat-raised-button type="button" (click)="dialogRef.close()" class="butt action">Cancel</button>
- <button mat-raised-button type="submit" [disabled]="!configurationForm.valid"
- class="butt butt-success action" [ngClass]="{'not-allowed': !configurationForm.valid}"
- (click)="editClusterConfiguration(configurationForm.value)">Update</button>
+ </form>
</div>
</div>
+ <ng-template #not_support>
+ <small [hidden]="notebook.status !== 'running'">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 <a
+ href="https://zeppelin.apache.org/docs/0.9.0/usage/interpreter/overview.html" target="_blank">official
+ documentation</a>.
+ </small>
+ </ng-template>
+ <div [scrollTo]="configuration?.nativeElement['checked'] || false" class="text-center m-top-20 m-bott-10"
+ *ngIf="(configuration?.nativeElement['checked'] || false) && notebook.status === 'running'">
+ <button mat-raised-button type="button" (click)="dialogRef.close()" class="butt action">Cancel</button>
+ <button mat-raised-button type="submit" [disabled]="!configurationForm.valid"
+ class="butt butt-success action" [ngClass]="{'not-allowed': !configurationForm.valid}"
+ (click)="editClusterConfiguration(configurationForm.value)">Update</button>
+ </div>
+ </div>
</div>
</div>
<div class="legion-info" *ngIf="data.odahu">
<div class="content-box">
<div class="detail-info" *ngIf="!odahu.error_message">
- <div class="links_block">
- <div *ngFor="let url of odahu.url" class="odahu-links">
- <div class="odahu-link">
- <span class="description">{{url.description }}: </span>
- <a class="ellipsis" matTooltip="{{ url.url}}" matTooltipPosition="above" href="{{ url.url}}"
- target="_blank">{{ url.url}}
- </a>
- </div>
- <div class="grafana" *ngIf="url.description === 'Grafana'">
- <div><span>Gafana user: </span><span class="creds">{{odahu.bucket_name}}</span></div>
- <div><span>Gafana password: </span><span class="creds">{{odahu.shared_bucket_name}}</span></div>
+ <div class="links_block">
+ <div *ngFor="let url of odahu.url" class="odahu-links">
+ <div class="odahu-link">
+ <span class="description">{{url.description }}: </span>
+ <a
+ class="ellipsis"
+ matTooltip="{{ url.url}}"
+ matTooltipPosition="above"
+ href="{{ url.url}}"
+ target="_blank"
+ >
+ {{ url.url}}
+ </a>
+ </div>
+ <div class="grafana" *ngIf="url.description === 'Grafana'">
+ <div><span>Gafana user: </span><span class="creds">{{odahu.bucket_name}}</span></div>
+ <div><span>Gafana password: </span><span class="creds">{{odahu.shared_bucket_name}}</span></div>
+ </div>
</div>
</div>
</div>
- </div>
</div>
</div>
</div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.scss
index edb7aac..9549179 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.scss
@@ -17,7 +17,7 @@
* under the License.
*/
-@import '_variables.scss';
+@import "_variables.scss";
.scroll-box {
max-height: 65vh;
@@ -26,36 +26,36 @@
}
.links_block {
-
- .odahu-links:not(:last-child){
+ .odahu-links:not(:last-child) {
margin-bottom: 20px;
}
- .grafana{
- white-space: nowrap;
+ .grafana {
padding-left: 30px;
+ white-space: nowrap;
color: $blue-grey-color;
- div{
+ div {
display: flex;
- span{
+ span {
font-size: 13px;
font-weight: 500;
- &.creds{
+ &.creds {
font-weight: 600;
}
}
}
}
- >p, .odahu-link {
- display: flex;
+ > p,
+ .odahu-link {
+ display: flex;
.description {
- white-space: nowrap;
padding-left: 7px;
+ white-space: nowrap;
font-weight: 600;
color: $blue-grey-color;
}
@@ -73,7 +73,6 @@
}
span {
-
&.danger_color {
position: absolute;
bottom: -20px;
@@ -83,9 +82,9 @@
}
.config-details {
- text-align: left;
position: relative;
height: 280px;
+ text-align: left;
textarea {
height: 100%;
@@ -111,35 +110,33 @@
color: $blue-grey-color;
}
-.bucket-link{
+.bucket-link {
padding: 15px;
padding-left: 0;
color: #35afd5;
- .open-bucket{
+ .open-bucket {
cursor: pointer;
font-size: 14px;
}
- &.not-allow{
+ &.not-allow {
cursor: not-allowed;
color: $blue-grey-color !important;
}
}
-.copy-icon-wrapper{
+.copy-icon-wrapper {
width: 20px;
margin-left: 5px;
margin-right: 5px;
}
-.bucket-info-wrapper{
+.bucket-info-wrapper {
width: 100%;
- .bucket-info{
+ .bucket-info {
max-width: 90%;
}
}
-.resources-url{
+.resources-url {
max-width: 70%;
}
-
-
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts
index ad41322..2ca7cba 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts
@@ -20,15 +20,15 @@
import { Component, ViewChild, OnInit, Inject } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
import { ToastrService } from 'ngx-toastr';
-import {MatDialogRef, MAT_DIALOG_DATA, MatDialog} from '@angular/material/dialog';
+import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog';
-import {DateUtils, CheckUtils, HelpUtils} from '../../../core/util';
+import { DateUtils, CheckUtils, HelpUtils } from '../../../core/util';
import { DICTIONARY } from '../../../../dictionary/global.dictionary';
import { DataengineConfigurationService } from '../../../core/services';
import { CLUSTER_CONFIGURATION } from '../../computational/computational-resource-create-dialog/cluster-configuration-templates';
-import {CopyPathUtils} from '../../../core/util/copyPathUtils';
-import {AuditService} from '../../../core/services/audit.service';
-import {BucketBrowserComponent} from '../../bucket-browser/bucket-browser.component';
+import { CopyPathUtils } from '../../../core/util/copyPathUtils';
+import { AuditService } from '../../../core/services/audit.service';
+import { BucketBrowserComponent } from '../../bucket-browser/bucket-browser.component';
@Component({
selector: 'detail-dialog',
@@ -51,7 +51,6 @@ export class DetailDialogComponent implements OnInit {
public configurationForm: FormGroup;
@ViewChild('configurationNode') configuration;
-
constructor(
@Inject(MAT_DIALOG_DATA) public data: any,
private dataengineConfigurationService: DataengineConfigurationService,
@@ -80,11 +79,12 @@ export class DetailDialogComponent implements OnInit {
this.upTimeInHours = (this.notebook.time) ? DateUtils.diffBetweenDatesInHours(this.notebook.time) : 0;
this.initFormModel();
this.getClusterConfiguration();
- if (this.notebook.edgeNodeStatus === 'terminated' ||
- this.notebook.edgeNodeStatus === 'terminating' ||
- this.notebook.edgeNodeStatus === 'failed') {
- this.isBucketAllowed = false;
- }
+
+ if (this.notebook.edgeNodeStatus === 'terminated' ||
+ this.notebook.edgeNodeStatus === 'terminating' ||
+ this.notebook.edgeNodeStatus === 'failed') {
+ this.isBucketAllowed = false;
+ }
}
}
@@ -98,12 +98,12 @@ export class DetailDialogComponent implements OnInit {
.getExploratorySparkConfiguration(this.notebook.project, this.notebook.name)
.subscribe(
(result: any) => this.config = result,
- error => this.toastr.error(error.message || 'Configuration loading failed!', 'Oops!'));
+ error => this.toastr.error(error.message || 'Configuration loading failed!', 'Oops!')
+ );
}
public selectConfiguration() {
if (this.configuration.nativeElement.checked) {
-
this.configurationForm.controls['configuration_parameters']
.setValue(JSON.stringify(this.config.length ? this.config : CLUSTER_CONFIGURATION.SPARK, undefined, 2));
document.querySelector('#config').scrollIntoView({ block: 'start', behavior: 'smooth' });
@@ -115,16 +115,19 @@ export class DetailDialogComponent implements OnInit {
public editClusterConfiguration(data): void {
this.dataengineConfigurationService
.editExploratorySparkConfiguration(data.configuration_parameters, this.notebook.project, this.notebook.name)
- .subscribe(() => {
- this.dialogRef.close();
- },
- error => this.toastr.error(error.message || 'Edit onfiguration failed!', 'Oops!'));
+ .subscribe(
+ () => {
+ this.dialogRef.close();
+ },
+ error => this.toastr.error(error.message || 'Edit onfiguration failed!', 'Oops!')
+ );
}
public resetDialog() {
this.initFormModel();
-
- if (this.configuration) this.configuration.nativeElement['checked'] = false;
+ if (this.configuration) {
+ this.configuration.nativeElement['checked'] = false;
+ }
}
private initFormModel(): void {
@@ -147,14 +150,20 @@ export class DetailDialogComponent implements OnInit {
bucketName = this.isBucketAllowed ? bucketName : this.data.buckets[0].children[0].name;
// bucketName = 'ofuks-1304-pr2-local-bucket';
this.dialog.open(BucketBrowserComponent, { data:
- {bucket: bucketName, endpoint: endpoint, bucketStatus: this.bucketStatus, buckets: this.data.buckets},
- panelClass: 'modal-fullscreen' })
- .afterClosed().subscribe();
+ {
+ bucket: bucketName,
+ endpoint: endpoint,
+ bucketStatus: this.bucketStatus,
+ buckets: this.data.buckets
+ },
+ panelClass: 'modal-fullscreen' }
+ ).afterClosed().subscribe();
}
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/install-libraries/install-libraries.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.html
index 9954a91..8ef3f8a 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.html
@@ -24,368 +24,418 @@
</header>
<mat-progress-bar *ngIf="autoComplete === 'UPDATING'" mode="indeterminate"></mat-progress-bar>
<div class="dialog-content">
- <div class="aligner">
- <div class="info" *ngIf="notebook?.status !== 'running'">
- <p class=" message">Cannot install libraries: Exploratory
- <strong>{{ notebook?.name }}</strong> is not running</p>
- </div>
- <div *ngIf="notebook?.status === 'running'" class="top-wrapper">
- <div class="loading-block" *ngIf="!libs_uploaded && uploading && data.status === 'running'">
- <div class="uploading">
- <p>Please wait until DataLab loads list of available groups for your resource...</p>
- <img src="assets/img/gif-spinner.gif" alt="loading">
- </div>
+ <div class="aligner">
+ <div class="info" *ngIf="notebook?.status !== 'running'">
+ <p class=" message">Cannot install libraries: Exploratory
+ <strong>{{ notebook?.name }}</strong> is not running</p>
</div>
- <div *ngIf="notebook?.status === 'running' && !uploading" class="lib-view-wrap">
- <div class="search-box">
- <div class="search-form">
- <div>
- <div class="control-group constol-select">
- <label class="label">Select resource</label>
- <div class="control">
- <dropdown-list #resourceSelect (selectedItem)="onUpdate($event)" (emitClick)="emitClick()"></dropdown-list>
- </div>
- </div>
- <div class="control-group control-select">
- <label class="label">Select group</label>
- <div class="control">
- <dropdown-list #groupSelect (selectedItem)="onUpdate($event)" (emitClick)="emitClick()"></dropdown-list>
- <span class="error-message" *ngIf="!group && libSearch?.value">Group field is required. Please choose appropriate group.</span>
- </div>
- </div>
+ <div *ngIf="notebook?.status === 'running'" class="top-wrapper">
+ <div class="loading-block" *ngIf="!libs_uploaded && uploading && data.status === 'running'">
+ <div class="uploading">
+ <p>Please wait until DataLab loads list of available groups for your resource...</p>
+ <img src="assets/img/gif-spinner.gif" alt="loading">
</div>
- <div class="m-top-20" *ngIf="group !== 'java'; else javaGroup">
- <div class="control-group constol-select">
- <label class="label">Library name</label>
- <div class="control control-relative">
- <span class="other-message" *ngIf="group === 'others'">
- Other group can include libs from Python 2 and Python 3 groups.
- Some libs cannot be installed due to Python 2 is no longer supported
- </span>
- <input
- type="text" [placeholder]="'Enter library name'"
- [disabled]="!group"
- [matAutocomplete]="auto"
- [formControl]="libSearch"
- #trigger="matAutocompleteTrigger"
- (keydown.enter)="addLibrary(lib)"
- (keyup)="clearLibSelection($event)"
- class="library-input"
- >
+ </div>
+ <div *ngIf="notebook?.status === 'running' && !uploading" class="lib-view-wrap">
+ <div class="search-box">
+ <div class="search-form">
+ <div>
+ <div class="control-group constol-select">
+ <label class="label">Select resource</label>
+ <div class="control">
+ <dropdown-list #resourceSelect (selectedItem)="onUpdate($event)" (emitClick)="emitClick()"></dropdown-list>
+ </div>
+ </div>
+ <div class="control-group control-select">
+ <label class="label">Select group</label>
+ <div class="control">
+ <dropdown-list #groupSelect (selectedItem)="onUpdate($event)" (emitClick)="emitClick()"></dropdown-list>
+ <span class="error-message" *ngIf="!group && libSearch?.value">Group field is required. Please choose appropriate group.</span>
+ </div>
+ </div>
</div>
- <mat-autocomplete #auto="matAutocomplete" class="suggestions scrolling">
- <ng-template ngFor let-item [ngForOf]="filteredList" let-i="index">
- <mat-option>
- <div class="option" (click)="selectLibrary(item);$event.stopPropagation()" [ngClass]="{'not-allow': item.isInSelectedList}">
- <a *ngIf="!item.isInSelectedList">
- <span [innerHTML]="item.name | highlight:lib.name"></span>
- </a>
- <span *ngIf="item.isInSelectedList">{{ item.name }}</span>
- <strong *ngIf="item.isInSelectedList">selected
- <i class="material-icons">done</i>
- </strong>
- <strong *ngIf="item.isInstalled && !item.isInSelectedList">installed
- <i class="material-icons">done</i>
- </strong>
- </div>
- </mat-option>
- </ng-template>
- <mat-option *ngIf="!filteredList?.length && !validity_format && autoComplete === 'ENABLED' && lib.name?.length > 0" disabled>
- <span class="configuring">No matches found</span>
- </mat-option>
- <mat-option *ngIf="validity_format?.length > 0 && autoComplete === 'ENABLED'">
- <span class="configuring" >{{ validity_format }}</span >
- </mat-option>
- <mat-option *ngIf="autoComplete === 'NONE' && lib.name?.length > 1" disabled>
- <span class="configuring" >Autocomplete is currently unavailable for {{groupsListMap[group]}} group</span >
- </mat-option>
- <mat-option *ngIf="autoComplete === 'UPDATING' && lib.name?.length > 1" disabled>
- <span class="configuring" >Library list is being loaded at the moment. Please wait...</span>
- </mat-option>
- </mat-autocomplete>
- </div>
- <div class="control-group control-select">
- <label class="label">Library version</label>
- <div class="control control-relative">
- <input
- type="text"
- class="library-input"
- [placeholder]="'Enter library version (optional)'"
- [(ngModel)]="lib.version"
- [disabled]="!lib.name?.trim() || lib.name?.trim().length < 2"
- (keyup)="validateVersion(lib.version)"
- (keydown.enter)="addLibrary(lib)"
- >
- <span
- class="error-message version-error"
- *ngIf="isVersionInvalid">
- Library version can only contain Latin letters, numbers and special characters -, _, :, /, ~, ., +.
- </span>
- <span class="plus-icon"
+ <div class="m-top-20" *ngIf="group !== 'java'; else javaGroup">
+ <div class="control-group constol-select">
+ <label class="label">Library name</label>
+ <div class="control control-relative">
+ <span class="other-message" *ngIf="group === 'others'">
+ Other group can include libs from Python 2 and Python 3 groups.
+ Some libs cannot be installed due to Python 2 is no longer supported
+ </span>
+ <input
+ type="text" [placeholder]="'Enter library name'"
+ [disabled]="!group"
+ [matAutocomplete]="auto"
+ [formControl]="libSearch"
+ #trigger="matAutocompleteTrigger"
+ (keydown.enter)="addLibrary(lib)"
+ (keyup)="clearLibSelection($event)"
+ class="library-input"
+ />
+ </div>
+ <mat-autocomplete #auto="matAutocomplete" class="suggestions scrolling">
+ <ng-template ngFor let-item [ngForOf]="filteredList" let-i="index">
+ <mat-option>
+ <div class="option" (click)="selectLibrary(item);$event.stopPropagation()" [ngClass]="{'not-allow': item.isInSelectedList}">
+ <a *ngIf="!item.isInSelectedList">
+ <span [innerHTML]="item.name | highlight:lib.name"></span>
+ </a>
+ <span *ngIf="item.isInSelectedList">{{ item.name }}</span>
+ <strong *ngIf="item.isInSelectedList">selected
+ <i class="material-icons">done</i>
+ </strong>
+ <strong *ngIf="item.isInstalled && !item.isInSelectedList">installed
+ <i class="material-icons">done</i>
+ </strong>
+ </div>
+ </mat-option>
+ </ng-template>
+ <mat-option *ngIf="!filteredList?.length && !validity_format && autoComplete === 'ENABLED' && lib.name?.length > 0" disabled>
+ <span class="configuring">No matches found</span>
+ </mat-option>
+ <mat-option *ngIf="validity_format?.length > 0 && autoComplete === 'ENABLED'">
+ <span class="configuring" >{{ validity_format }}</span >
+ </mat-option>
+ <mat-option *ngIf="autoComplete === 'NONE' && lib.name?.length > 1" disabled>
+ <span class="configuring" >Autocomplete is currently unavailable for {{groupsListMap[group]}} group</span >
+ </mat-option>
+ <mat-option *ngIf="autoComplete === 'UPDATING' && lib.name?.length > 1" disabled>
+ <span class="configuring" >Library list is being loaded at the moment. Please wait...</span>
+ </mat-option>
+ </mat-autocomplete>
+ </div>
+ <div class="control-group control-select">
+ <label class="label">Library version</label>
+ <div class="control control-relative">
+ <input
+ type="text"
+ class="library-input"
+ [placeholder]="'Enter library version (optional)'"
+ [(ngModel)]="lib.version"
+ [disabled]="!lib.name?.trim() || lib.name?.trim().length < 2"
+ (keyup)="validateVersion(lib.version)"
+ (keydown.enter)="addLibrary(lib)"
+ />
+ <span
+ class="error-message version-error"
+ *ngIf="isVersionInvalid"
+ >
+ Library version can only contain Latin letters, numbers and special characters -, _, :, /, ~, ., +.
+ </span>
+ <span
+ class="plus-icon"
[ngClass]="{'not-allow': lib.name?.trim().length < 2
- || (autoComplete === 'ENABLED' && !isLibSelected && filteredList?.length)
- || this.selectedLib?.isInSelectedList || isVersionInvalid || autoComplete === 'UPDATING'}"
+ || (autoComplete === 'ENABLED' && !isLibSelected && filteredList?.length)
+ || this.selectedLib?.isInSelectedList || isVersionInvalid || autoComplete === 'UPDATING'}"
[matTooltip]="this.selectedLib?.isInSelectedList ? 'Library is in selected list' : 'Please select library from autocomplete'"
- matTooltipPosition="above" [matTooltipDisabled]="(!this.selectedLib?.isInSelectedList && isLibSelected) || lib.name?.length < 2 || !this.selectedLib?.isInSelectedList && autoComplete === 'NONE'"
- >
- <mat-icon
- (click)="addLibrary(lib)"
- [ngClass]="{'not-allowed': lib.name?.trim().length < 2 || (autoComplete === 'ENABLED' && !isLibSelected && filteredList?.length)
- || this.selectedLib?.isInSelectedList || isVersionInvalid || autoComplete === 'UPDATING'}">add</mat-icon>
- </span>
+ matTooltipPosition="above"
+ [matTooltipDisabled]="(!this.selectedLib?.isInSelectedList && isLibSelected)
+ || lib.name?.length < 2 || !this.selectedLib?.isInSelectedList && autoComplete === 'NONE'"
+ >
+ <mat-icon
+ (click)="addLibrary(lib)"
+ [ngClass]="{'not-allowed': lib.name?.trim().length < 2
+ || (autoComplete === 'ENABLED' && !isLibSelected && filteredList?.length)
+ || this.selectedLib?.isInSelectedList || isVersionInvalid || autoComplete === 'UPDATING'}"
+ >
+ add
+ </mat-icon>
+ </span>
+ </div>
+ </div>
</div>
- </div>
- </div>
- <ng-template #javaGroup>
- <div class="m-top-20">
- <div class="control-group constol-select java-library-search">
- <label class="label">Library</label>
- <div class="control control-relative">
- <input
- type="text" [placeholder]="'Enter library name in <groupId>:<artifactId>:<versionId> format'"
- class="library-input"
- [disabled]="!group"
- [matAutocomplete]="auto"
- [formControl]="libSearch"
- #trigger="matAutocompleteTrigger"
- (keydown.enter)="addLibrary(lib)"
- (keyup)="clearLibSelection($event)"
- >
- <span
- class="plus-icon"
- [ngClass]="{'not-allow': !this.selectedLib || this.selectedLib?.isInSelectedList || !isLibSelected}"
- matTooltip="Library is in selected list" matTooltipPosition="above" [matTooltipDisabled]="!this.selectedLib?.isInSelectedList || !isLibSelected"
- >
- <mat-icon (click)="addLibrary(lib)" (keyup.enter)="addLibrary(lib)" [ngClass]="{'not-allowed': !this.selectedLib || this.selectedLib?.isInSelectedList || !isLibSelected}">add</mat-icon>
- </span>
- </div>
- <mat-autocomplete #auto="matAutocomplete" class="suggestions">
- <ng-template ngFor let-item [ngForOf]="filteredList" let-i="index">
- <mat-option >
- <div class="option" (click)="selectLibrary(item);$event.stopPropagation()" [ngClass]="{'not-allow': item.isInSelectedList}">
- <a *ngIf="!item.isInSelectedList">
- <span [innerHTML]="item.name + ':' + item.version | highlight:item.name">
- <span>{{ item.version }}</span>
- </span>
- </a>
- <span *ngIf="item.isInSelectedList">{{ item.name }}
- <span *ngIf="item.version && item.version !== 'N/A'">{{ item.version }}</span>
+ <ng-template #javaGroup>
+ <div class="m-top-20">
+ <div class="control-group constol-select java-library-search">
+ <label class="label">Library</label>
+ <div class="control control-relative">
+ <input
+ type="text"
+ [placeholder]="'Enter library name in <groupId>:<artifactId>:<versionId> format'"
+ class="library-input"
+ [disabled]="!group"
+ [matAutocomplete]="auto"
+ [formControl]="libSearch"
+ #trigger="matAutocompleteTrigger"
+ (keydown.enter)="addLibrary(lib)"
+ (keyup)="clearLibSelection($event)"
+ />
+ <span
+ class="plus-icon"
+ [ngClass]="{'not-allow': !this.selectedLib || this.selectedLib?.isInSelectedList || !isLibSelected}"
+ matTooltip="Library is in selected list"
+ matTooltipPosition="above"
+ [matTooltipDisabled]="!this.selectedLib?.isInSelectedList || !isLibSelected"
+ >
+ <mat-icon
+ (click)="addLibrary(lib)"
+ (keyup.enter)="addLibrary(lib)"
+ [ngClass]="{'not-allowed': !this.selectedLib || this.selectedLib?.isInSelectedList || !isLibSelected}"
+ >
+ add
+ </mat-icon>
</span>
- <strong *ngIf="item.isInSelectedList">selected
- <i class="material-icons">done</i>
- </strong>
- <strong *ngIf="item.isInstalled && !item.isInSelectedList">installed
- <i class="material-icons">done</i>
- </strong>
- </div>
- </mat-option>
- </ng-template>
- <mat-option *ngIf="model.isEmpty(filteredList) && !validity_format">
- <span class="configuring">No matches found</span>
- </mat-option>
- <mat-option *ngIf="validity_format?.length > 0">
- <span class="configuring" >{{ validity_format }}</span >
- </mat-option>
- </mat-autocomplete>
- </div>
- </div>
- </ng-template>
+ </div>
+ <mat-autocomplete #auto="matAutocomplete" class="suggestions">
+ <ng-template ngFor let-item [ngForOf]="filteredList" let-i="index">
+ <mat-option>
+ <div class="option" (click)="selectLibrary(item);$event.stopPropagation()" [ngClass]="{'not-allow': item.isInSelectedList}">
+ <a *ngIf="!item.isInSelectedList">
+ <span [innerHTML]="item.name + ':' + item.version | highlight:item.name">
+ <span>{{ item.version }}</span>
+ </span>
+ </a>
+ <span *ngIf="item.isInSelectedList">{{ item.name }}
+ <span *ngIf="item.version && item.version !== 'N/A'">{{ item.version }}</span>
+ </span>
+ <strong *ngIf="item.isInSelectedList">selected
+ <i class="material-icons">done</i>
+ </strong>
+ <strong *ngIf="item.isInstalled && !item.isInSelectedList">installed
+ <i class="material-icons">done</i>
+ </strong>
+ </div>
+ </mat-option>
+ </ng-template>
+ <mat-option *ngIf="model.isEmpty(filteredList) && !validity_format">
+ <span class="configuring">No matches found</span>
+ </mat-option>
+ <mat-option *ngIf="validity_format?.length > 0">
+ <span class="configuring" >{{ validity_format }}</span >
+ </mat-option>
+ </mat-autocomplete>
+ </div>
+ </div>
+ </ng-template>
- <div class="search">
- <div class="list-selected list-container scrolling">
- <mat-chip-list *ngIf="model.selectedLibs.length && libs_uploaded" [disabled]="true">
- <mat-chip *ngFor="let item of model.selectedLibs">
- {{ item.name }}
- <span *ngIf="item.version && item.version !== 'N/A'"> {{ item.version }}</span>
- <strong> ({{ groupsListMap[item.group] }}) </strong>
- <a class="material-icons" (click)="removeSelectedLibrary(item)">clear</a>
- </mat-chip>
- </mat-chip-list>
+ <div class="search">
+ <div class="list-selected list-container scrolling">
+ <mat-chip-list *ngIf="model.selectedLibs.length && libs_uploaded" [disabled]="true">
+ <mat-chip *ngFor="let item of model.selectedLibs">
+ {{ item.name }}
+ <span *ngIf="item.version && item.version !== 'N/A'"> {{ item.version }}</span>
+ <strong> ({{ groupsListMap[item.group] }}) </strong>
+ <a class="material-icons" (click)="removeSelectedLibrary(item)">clear</a>
+ </mat-chip>
+ </mat-chip-list>
+ </div>
+ </div>
</div>
</div>
</div>
</div>
- </div>
- </div>
- <div class="libs-info">
- <mat-list class="scrolling" [ngStyle]="notebook?.status !== 'running' && {'max-height': '60vh', 'height': 'unset'}">
- <mat-list-item class="list-header">
- <div class="lib-name">Name
- <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
- <i class="material-icons">
- <span>more_vert</span>
- </i>
- </button></div>
- <div class="lib-group">Group
- <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
- <i class="material-icons">
- <span>more_vert</span>
- </i>
- </button>
- </div>
- <div class="lib-destination"><span class="">Destination</span>
- <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
- <i class="material-icons">
- <span>more_vert</span>
- </i>
- </button>
- </div>
- <div class="lib-resource-type">Resource type
- <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
- <i class="material-icons">
- <span>more_vert</span>
- </i>
- </button>
- </div>
- <div class="lib-status"><span>Status</span>
- <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
- <i class="material-icons">
- <span>more_vert</span>
- </i>
- </button>
- </div>
- </mat-list-item>
+ <div class="libs-info">
+ <mat-list class="scrolling" [ngStyle]="notebook?.status !== 'running' && {'max-height': '60vh', 'height': 'unset'}">
+ <mat-list-item class="list-header">
+ <div class="lib-name">Name
+ <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
+ <i class="material-icons">
+ <span>more_vert</span>
+ </i>
+ </button>
+ </div>
+ <div class="lib-group">Group
+ <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
+ <i class="material-icons">
+ <span>more_vert</span>
+ </i>
+ </button>
+ </div>
+ <div class="lib-destination"><span class="">Destination</span>
+ <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
+ <i class="material-icons">
+ <span>more_vert</span>
+ </i>
+ </button>
+ </div>
+ <div class="lib-resource-type">Resource type
+ <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
+ <i class="material-icons">
+ <span>more_vert</span>
+ </i>
+ </button>
+ </div>
+ <div class="lib-status"><span>Status</span>
+ <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()">
+ <i class="material-icons">
+ <span>more_vert</span>
+ </i>
+ </button>
+ </div>
+ </mat-list-item>
- <ng-container *ngIf="filtered" >
- <mat-list-item class="filter-row">
- <th class="lib-name filter-col">
- <input
- placeholder="Filter by library name"
- [value]="filterModel.name"
- (input)="onFilterNameUpdate($event.target['value'])"
- type="text"
- class="form-control filter-field"
- />
- </th>
- <th class="lib-group filter-col">
- <multi-select-dropdown
- (selectionChange)="onFilterUpdate($event)"
- [items]="this.filterConfiguration.group"
- [type]="'group'"
- [model]="this.filterModel.group"
- >
- </multi-select-dropdown>
- </th>
- <th class="lib-destination filter-col">
- <multi-select-dropdown
- (selectionChange)="onFilterUpdate($event)"
- [items]="this.filterConfiguration.resource"
- [type]="'resource'"
- [model]="this.filterModel.resource"
- >
- </multi-select-dropdown>
- </th>
- <th class="lib-resource-type filter-col">
- <multi-select-dropdown
- (selectionChange)="onFilterUpdate($event)"
- [items]="this.filterConfiguration.resource_type"
- [type]="'resource_type'"
- [model]="this.filterModel.resource_type"
- >
- </multi-select-dropdown>
- </th>
- <th class="lib-status filter-col">
- <multi-select-dropdown
- (selectionChange)="onFilterUpdate($event)"
- [items]="this.filterConfiguration.status"
- [type]="'status'"
- [model]="this.filterModel.status"
- >
- </multi-select-dropdown>
- </th>
- <ng-container matColumnDef="action-filter" stickyEnd>
- <th>
- <div class="filter-actions actions">
- <button mat-icon-button class="btn reset" (click)="resetFilterConfigurations()" [disabled]="!isFilterSelected">
- <i class="material-icons">close</i>
- </button>
+ <ng-container *ngIf="filtered" >
+ <mat-list-item class="filter-row">
+ <th class="lib-name filter-col">
+ <input
+ placeholder="Filter by library name"
+ [value]="filterModel.name"
+ (input)="onFilterNameUpdate($event.target['value'])"
+ type="text"
+ class="form-control filter-field"
+ />
+ </th>
+ <th class="lib-group filter-col">
+ <multi-select-dropdown
+ (selectionChange)="onFilterUpdate($event)"
+ [items]="this.filterConfiguration.group"
+ [type]="'group'"
+ [model]="this.filterModel.group"
+ ></multi-select-dropdown>
+ </th>
+ <th class="lib-destination filter-col">
+ <multi-select-dropdown
+ (selectionChange)="onFilterUpdate($event)"
+ [items]="this.filterConfiguration.resource"
+ [type]="'resource'"
+ [model]="this.filterModel.resource"
+ ></multi-select-dropdown>
+ </th>
+ <th class="lib-resource-type filter-col">
+ <multi-select-dropdown
+ (selectionChange)="onFilterUpdate($event)"
+ [items]="this.filterConfiguration.resource_type"
+ [type]="'resource_type'"
+ [model]="this.filterModel.resource_type"
+ ></multi-select-dropdown>
+ </th>
+ <th class="lib-status filter-col">
+ <multi-select-dropdown
+ (selectionChange)="onFilterUpdate($event)"
+ [items]="this.filterConfiguration.status"
+ [type]="'status'"
+ [model]="this.filterModel.status"
+ ></multi-select-dropdown>
+ </th>
+ <ng-container matColumnDef="action-filter" stickyEnd>
+ <th>
+ <div class="filter-actions actions">
+ <button
+ mat-icon-button
+ class="btn reset"
+ (click)="resetFilterConfigurations()"
+ [disabled]="!isFilterSelected"
+ >
+ <i class="material-icons">close</i>
+ </button>
- <button mat-icon-button class="btn apply" (click)="filterLibs(true)" [disabled]="isFilterChanged">
- <i class="material-icons" [ngClass]="{'not-allowed': filterModel['length'] === 0}">done</i>
- </button>
- </div>
- </th>
- </ng-container>
- </mat-list-item>
- </ng-container>
+ <button
+ mat-icon-button
+ class="btn apply"
+ (click)="filterLibs(true)"
+ [disabled]="isFilterChanged"
+ >
+ <i class="material-icons" [ngClass]="{'not-allowed': filterModel['length'] === 0}">done</i>
+ </button>
+ </div>
+ </th>
+ </ng-container>
+ </mat-list-item>
+ </ng-container>
- <div class="scrollingList" id="scrolling" *ngIf="notebookLibs?.length">
-<!-- <div *ngIf="notebook?.status !== 'running' && notebookFailedLibs.length > 0" class="info message">-->
-<!-- <p>Cannot retry to reinstall failed libraries: Exploratory {{ notebook?.name }} is not running</p>-->
-<!-- </div>-->
- <ng-container *ngFor="let lib of filtredNotebookLibs">
- <mat-list-item class="table-item">
- <div class="lib-name lib-name-col ellipsis" >
- <span [matTooltip]="lib.name + ' ' + lib.version" matTooltipPosition="above" class="lib-name-wrapper ellipsis">
- <span class="stong" >{{ lib.name }}</span>
- <span *ngIf="lib.version && lib.version !== 'N/A'">{{ lib.version }}</span>
- </span>
- </div>
- <div class="lib-group-col">{{ groupsListMap[lib.group] }}</div>
- <div class="st-group">
- <ng-template let-item ngFor [ngForOf]="lib.filteredStatus">
- <div class="wrap-col">
- <div class="lib-destination-col">{{ item.resource }}</div>
- <div class="lib-resource-type-col uppercase">{{ item.resourceType }}</div>
- <div class="lib-status-col uppercase" ngClass="{{ item.status.toLowerCase() || 'installation_error' }}">{{ item.status.replace('_', ' ') }}
- <div class="warn-action" *ngIf="(item.status === 'installation_error' || item.status.toLowerCase() === 'invalid_version' || item.add_pkgs?.length) && notebook?.status === 'running'">
- <div *ngIf="!item.available_versions">
- <span *ngIf="!installingInProgress && item.status === 'installation_error'" (click)="reinstallLibrary(item, lib)" matTooltip="Retry installation" matTooltipPosition="above">
- <i class="material-icons">replay</i>
- </span>
- <span class="not-allow" *ngIf="installingInProgress && item.status === 'installation_error'" matTooltip="Please wait until lib installation completes"
- matTooltipPosition="above">
- <i class="material-icons not-allowed">replay</i>
- </span>
- </div>
- <div *ngIf="item.status === 'installation_error' && item.error" class="lib-error" (click)="showErrorMessage(item)" matTooltip="Show error message" matTooltipPosition="above">
- <i class="material-icons terminated">error_outline</i>
- </div>
- <div class="lib-error"
- *ngIf="item.status.toLowerCase() === 'invalid_version' && item.available_versions?.length"
- (click)="openLibInfo(item, 'available');$event.stopPropagation()"
- matTooltip="Show available version" matTooltipPosition="above"
- >
- <i class="material-icons">error_outline</i>
- </div>
- <div matTooltip="Show installed dependency" matTooltipPosition="above" *ngIf="item.add_pkgs?.length">
- <span class="info-icon" (click)="openLibInfo(item, 'added');$event.stopPropagation() ">
- <i class="material-icons">info</i>
- </span>
+ <div class="scrollingList" id="scrolling" *ngIf="notebookLibs?.length">
+ <!-- <div *ngIf="notebook?.status !== 'running' && notebookFailedLibs.length > 0" class="info message">-->
+ <!-- <p>Cannot retry to reinstall failed libraries: Exploratory {{ notebook?.name }} is not running</p>-->
+ <!-- </div>-->
+ <ng-container *ngFor="let lib of filtredNotebookLibs">
+ <mat-list-item class="table-item">
+ <div class="lib-name lib-name-col ellipsis" >
+ <span [matTooltip]="lib.name + ' ' + lib.version" matTooltipPosition="above" class="lib-name-wrapper ellipsis">
+ <span class="stong" >{{ lib.name }}</span>
+ <span *ngIf="lib.version && lib.version !== 'N/A'">{{ lib.version }}</span>
+ </span>
+ </div>
+ <div class="lib-group-col">{{ groupsListMap[lib.group] }}</div>
+ <div class="st-group">
+ <ng-template let-item ngFor [ngForOf]="lib.filteredStatus">
+ <div class="wrap-col">
+ <div class="lib-destination-col">{{ item.resource }}</div>
+ <div class="lib-resource-type-col uppercase">{{ item.resourceType }}</div>
+ <div class="lib-status-col uppercase" ngClass="{{ item.status.toLowerCase() || 'installation_error' }}">{{ item.status.replace('_', ' ') }}
+ <div class="warn-action" *ngIf="(item.status === 'installation_error' || item.status.toLowerCase() === 'invalid_version' || item.add_pkgs?.length) && notebook?.status === 'running'">
+ <div *ngIf="!item.available_versions">
+ <span
+ *ngIf="!installingInProgress && item.status === 'installation_error'"
+ (click)="reinstallLibrary(item, lib)"
+ matTooltip="Retry installation"
+ matTooltipPosition="above"
+ >
+ <i class="material-icons">replay</i>
+ </span>
+ <span
+ class="not-allow"
+ *ngIf="installingInProgress && item.status === 'installation_error'"
+ matTooltip="Please wait until lib installation completes"
+ matTooltipPosition="above"
+ >
+ <i class="material-icons not-allowed">replay</i>
+ </span>
+ </div>
+ <div
+ *ngIf="item.status === 'installation_error' && item.error"
+ class="lib-error"
+ (click)="showErrorMessage(item)"
+ matTooltip="Show error message"
+ matTooltipPosition="above"
+ >
+ <i class="material-icons terminated">error_outline</i>
+ </div>
+ <div
+ class="lib-error"
+ *ngIf="item.status.toLowerCase() === 'invalid_version' && item.available_versions?.length"
+ (click)="openLibInfo(item, 'available');$event.stopPropagation()"
+ matTooltip="Show available version"
+ matTooltipPosition="above"
+ >
+ <i class="material-icons">error_outline</i>
+ </div>
+ <div matTooltip="Show installed dependency" matTooltipPosition="above" *ngIf="item.add_pkgs?.length">
+ <span class="info-icon" (click)="openLibInfo(item, 'added');$event.stopPropagation() ">
+ <i class="material-icons">info</i>
+ </span>
+ </div>
+ </div>
</div>
</div>
-
- </div>
+ </ng-template>
</div>
-
- </ng-template>
- </div>
- </mat-list-item>
- </ng-container>
-
- </div>
- <div *ngIf="!notebookLibs?.length" class="scrollingList info message">
- <p>You have no libraries installed</p>
- </div>
- <div *ngIf="!filtredNotebookLibs.length && notebookLibs?.length" class="scrollingList info message">
- <p>No matches found</p>
- </div>
- </mat-list>
- </div>
- <div class="m-top-15 actions" *ngIf="!uploading && notebook?.status === 'running'">
- <button mat-raised-button type="button" class="butt action close-btn" (click)="dialogRef.close()">Close</button>
- <span matTooltip="Please wait until lib installation completes" [matTooltipDisabled]="!installingInProgress" matTooltipPosition="above">
- <button mat-raised-button type="submit"
- class="butt butt-success action install-btn"
- (click)="model.confirmAction()"
- [disabled]="!model.selectedLibs.length || installingInProgress || !destination"
+ </mat-list-item>
+ </ng-container>
+ </div>
+ <div *ngIf="!notebookLibs?.length" class="scrollingList info message">
+ <p>You have no libraries installed</p>
+ </div>
+ <div *ngIf="!filtredNotebookLibs.length && notebookLibs?.length" class="scrollingList info message">
+ <p>No matches found</p>
+ </div>
+ </mat-list>
+ </div>
+ <div class="m-top-15 actions" *ngIf="!uploading && notebook?.status === 'running'">
+ <button
+ mat-raised-button
+ type="button"
+ class="butt action close-btn"
+ (click)="dialogRef.close()"
>
- Install
+ Close
</button>
- </span>
+ <span
+ matTooltip="Please wait until lib installation completes"
+ [matTooltipDisabled]="!installingInProgress"
+ matTooltipPosition="above"
+ >
+ <button
+ mat-raised-button type="submit"
+ class="butt butt-success action install-btn"
+ (click)="model.confirmAction()"
+ [disabled]="!model.selectedLibs.length || installingInProgress || !destination"
+ >
+ Install
+ </button>
+ </span>
+ </div>
</div>
- </div>
</div>
</div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.scss
index 1333fd5..94d9c3f 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.scss
@@ -17,26 +17,27 @@
* under the License.
*/
-@import '_variables.scss';
+@import "_variables.scss";
.aligner {
- height: 100%;
display: flex;
flex-direction: column;
+ height: 100%;
- >div {
+ > div {
&.actions {
+ position: absolute;
+ bottom: 30px;
+ left: 0;
+ right: 0;
display: flex;
flex: initial;
justify-content: center;
flex-direction: initial;
margin-bottom: 10px;
- bottom: 30px;
- left: 0;
- right: 0;
- position: absolute;
}
- strong{
+
+ strong {
font-weight: 600;
}
}
@@ -46,20 +47,20 @@
height: 30%;
}
-.top-wrapper{
+.top-wrapper {
height: 285px;
}
-.list-item{
+.list-item {
display: flex;
width: 100%;
padding-left: 20px;
}
.object {
- width: 70%;
display: flex;
align-items: center;
+ width: 70%;
padding-right: 10px;
}
@@ -67,7 +68,7 @@
width: 30%;
}
-ul.resources{
+ul.resources {
padding-left: 10px;
}
@@ -80,7 +81,7 @@ ul.resources{
flex-direction: column;
width: 100%;
- &>div {
+ & > div {
display: flex;
flex-direction: row;
}
@@ -89,22 +90,22 @@ ul.resources{
width: 50%;
padding-bottom: 0;
- &.java-library-search{
+ &.java-library-search {
width: 100%;
- .label{
+ .label {
width: 10%;
}
- .control{
+ .control {
width: 85%;
}
}
- .control{
+ .control {
width: 70%;
- input{
+ input {
font-size: 15px;
padding-left: 15px;
}
@@ -124,8 +125,8 @@ ul.resources{
flex-grow: 1;
padding-bottom: 0;
flex-direction: column;
- box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .2), 0 2px 2px 0 rgba(0, 0, 0, .14), 0 1px 5px 0 rgba(0, 0, 0, .12);
margin-top: 40px;
+ box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
.mat-form-field-flex {
padding-left: 15px;
@@ -135,11 +136,10 @@ ul.resources{
width: 100%;
overflow-y: auto;
-
&.mat-form-field {
width: 100%;
- .mat-form-field-infix{
+ .mat-form-field-infix {
display: flex;
}
}
@@ -152,17 +152,16 @@ ul.resources{
}
}
-
.suggestions {
text-decoration: none;
list-style: none;
mat-option {
- font-size: 13px;
- font-weight: 300;
- font-family: 'Open Sans', sans-serif;
position: relative;
padding-top: 4px;
+ font-size: 13px;
+ font-weight: 300;
+ font-family: "Open Sans", sans-serif;
a {
display: block;
@@ -189,7 +188,7 @@ ul.resources{
position: absolute;
top: 9px;
right: -21px;
- color: #35afd5
+ color: #35afd5;
}
}
}
@@ -209,7 +208,7 @@ ul.resources{
mat-chip.mat-chip:not(.mat-basic-chip) {
margin: 2px !important;
- background-color: rgba(47, 174, 215, .2) !important;
+ background-color: rgba(47, 174, 215, 0.2) !important;
color: #687e96;
}
@@ -221,29 +220,28 @@ mat-chip.mat-chip:not(.mat-basic-chip) {
.libs-info {
.mat-list {
- width: 100%;
- overflow-y: auto;
position: relative;
+ width: 100%;
height: 350px;
padding-top: 0;
margin-top: 8px;
-
- .list-header{
- line-height: 40px;
+ overflow-y: auto;
+
+ .list-header {
position: sticky;
top: 0;
z-index: 99;
+ line-height: 40px;
background-color: #fff;
}
-
- .filter-row{
+
+ .filter-row {
position: sticky;
top: 56px;
z-index: 99;
background-color: #fff;
}
-
-
+
.scrollingList {
.info {
p {
@@ -260,11 +258,11 @@ mat-chip.mat-chip:not(.mat-basic-chip) {
padding-right: 10px;
padding-left: 20px;
- &-col{
+ &-col {
display: flex;
}
- .lib-name-wrapper{
+ .lib-name-wrapper {
overflow: hidden;
display: block;
}
@@ -315,8 +313,8 @@ mat-chip.mat-chip:not(.mat-basic-chip) {
.lib-resource-type-col {
width: 33.3%;
- color: #36afd5;
padding-left: 15px;
+ color: #36afd5;
}
.lib-status-col {
@@ -331,8 +329,8 @@ mat-chip.mat-chip:not(.mat-basic-chip) {
div {
display: inline-block;
- cursor: pointer;
width: 25px;
+ cursor: pointer;
pointer-events: auto;
span {
@@ -349,11 +347,11 @@ mat-chip.mat-chip:not(.mat-basic-chip) {
}
.mat-dialog-container.mat-dialog-container .install-libraries#dialog-box {
- .mat-header-cell{
- padding: 0;
- border: none;
+ .mat-header-cell {
position: absolute;
right: 10px;
+ padding: 0;
+ border: none;
}
.filter-actions {
@@ -361,10 +359,10 @@ mat-chip.mat-chip:not(.mat-basic-chip) {
margin-left: 6px;
.btn {
- padding: 6px;
height: auto;
width: auto;
min-width: 0;
+ padding: 6px;
.mat-button-wrapper {
display: flex;
@@ -390,24 +388,25 @@ mat-chip.mat-chip:not(.mat-basic-chip) {
}
}
-.error-message{
+.error-message {
position: absolute;
left: 20%;
top: 40px;
font-size: 11px;
color: red;
- &.version-error{
+
+ &.version-error {
right: 0;
left: unset;
}
}
-.info-icon{
+.info-icon {
color: lightgray;
cursor: pointer;
}
-.lib-info{
+.lib-info {
width: 100%;
.delete-item {
@@ -417,21 +416,21 @@ mat-chip.mat-chip:not(.mat-basic-chip) {
width: 100%;
padding: 5px 10px;
- .butt.action{
+ .butt.action {
line-height: 26px;
}
}
}
-.btn{
+.btn {
line-height: 26px;
- &.install-btn{
+ &.install-btn {
margin-left: 0;
}
}
-.control-relative{
+.control-relative {
position: relative;
.plus-icon {
@@ -447,12 +446,12 @@ mat-chip.mat-chip:not(.mat-basic-chip) {
}
}
- .other-message{
+ .other-message {
position: absolute;
- font-size: 12px;
- color: $link-color;
top: 40px;
left: 12px;
+ font-size: 12px;
+ color: $link-color;
}
}
@@ -460,7 +459,7 @@ mat-chip.mat-chip:not(.mat-basic-chip) {
.libs-info {
.mat-list {
height: 250px;
- .dropdown-multiselect .list-menu{
+ .dropdown-multiselect .list-menu {
max-height: 143px;
}
}
@@ -471,7 +470,7 @@ mat-chip.mat-chip:not(.mat-basic-chip) {
.libs-info {
.mat-list {
height: 200px;
- .dropdown-multiselect .list-menu{
+ .dropdown-multiselect .list-menu {
max-height: 94px;
}
}
@@ -479,8 +478,7 @@ mat-chip.mat-chip:not(.mat-basic-chip) {
}
@media screen and (max-height: 640px) {
-
- .aligner>div.actions {
+ .aligner > div.actions {
margin-bottom: 0;
}
@@ -491,7 +489,7 @@ mat-chip.mat-chip:not(.mat-basic-chip) {
.libs-info {
.mat-list {
height: 160px;
- .dropdown-multiselect .list-menu{
+ .dropdown-multiselect .list-menu {
max-height: 54px;
}
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.ts
index f434e29..8a867e9 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.component.ts
@@ -26,10 +26,10 @@ import {debounceTime, filter, take, takeUntil} from 'rxjs/operators';
import { InstallLibrariesModel } from './install-libraries.model';
import { LibrariesInstallationService } from '../../../core/services';
-import {SortUtils, HTTP_STATUS_CODES, PATTERNS} from '../../../core/util';
-import {FilterLibsModel} from './filter-libs.model';
-import {Subject, timer} from 'rxjs';
-import {CompareUtils} from '../../../core/util/compareUtils';
+import { SortUtils, HTTP_STATUS_CODES, PATTERNS } from '../../../core/util';
+import { FilterLibsModel } from './filter-libs.model';
+import { Subject, timer } from 'rxjs';
+import { CompareUtils } from '../../../core/util/compareUtils';
interface Library {
name: string;
@@ -170,12 +170,15 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy {
public filterList(): void {
this.validity_format = '';
- (this.lib.name && this.lib.name.length >= 2 && this.group ) ? this.getFilteredList() : this.filteredList = null;
+ (this.lib.name && this.lib.name.length >= 2 && this.group )
+ ? this.getFilteredList()
+ : this.filteredList = null;
}
public filterGroups(groupsList): Array<string> {
const CURRENT_TEMPLATE = this.notebook.template_name.toLowerCase();
- if (CURRENT_TEMPLATE.indexOf('jupyter with tensorflow') !== -1 || CURRENT_TEMPLATE.indexOf('deep learning') !== -1) {
+ if (CURRENT_TEMPLATE.indexOf('jupyter with tensorflow') !== -1
+ || CURRENT_TEMPLATE.indexOf('deep learning') !== -1) {
const filtered = groupsList.filter(group => group !== 'r_pkg');
return SortUtils.libGroupsSort(filtered);
}
@@ -311,14 +314,14 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy {
public isInstallingInProgress(): void {
this.installingInProgress = this.notebookLibs.some(lib => lib.filteredStatus.some(status => status.status === 'installing'));
- if (this.installingInProgress) {
- timer(this.INSTALLATION_IN_PROGRESS_CHECK)
- .pipe(
- take(1),
- takeUntil(this.unsubscribe$)
- )
- .subscribe(v => this.getInstalledLibrariesList());
- }
+ if (this.installingInProgress) {
+ timer(this.INSTALLATION_IN_PROGRESS_CHECK)
+ .pipe(
+ take(1),
+ takeUntil(this.unsubscribe$)
+ )
+ .subscribe(v => this.getInstalledLibrariesList());
+ }
}
public reinstallLibrary(item, lib): void {
@@ -376,7 +379,6 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy {
private libsUploadingStatus(groupsList): void {
if (groupsList.length) {
-
this.groupsList = this.filterGroups(groupsList);
this.libs_uploaded = true;
this.uploading = false;
@@ -497,15 +499,23 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy {
Object.setPrototypeOf(this.cashedFilterForm, Object.getPrototypeOf(this.filterModel));
}
this.filtredNotebookLibs = this.notebookLibs.filter((lib) => {
- const isName = this.cashedFilterForm.name ?
- lib.name.toLowerCase().indexOf(this.cashedFilterForm.name.toLowerCase().trim()) !== -1
- || lib.version.indexOf(this.cashedFilterForm.name.toLowerCase().trim()) !== -1 : true;
- const isGroup = this.cashedFilterForm.group.length ? this.cashedFilterForm.group.includes(this.groupsListMap[lib.group]) : true;
+ const isName = this.cashedFilterForm.name
+ ? lib.name.toLowerCase().indexOf(this.cashedFilterForm.name.toLowerCase().trim()) !== -1
+ || lib.version.indexOf(this.cashedFilterForm.name.toLowerCase().trim()) !== -1
+ : true;
+ const isGroup = this.cashedFilterForm.group.length
+ ? this.cashedFilterForm.group.includes(this.groupsListMap[lib.group])
+ : true;
lib.filteredStatus = lib.status.filter(status => {
- const isResource = this.cashedFilterForm.resource.length ? this.cashedFilterForm.resource.includes(status.resource) : true;
- const isResourceType = this.cashedFilterForm.resource_type.length ?
- this.cashedFilterForm.resource_type.includes(status.resourceType) : true;
- const isStatus = this.cashedFilterForm.status.length ? this.cashedFilterForm.status.includes(status.status) : true;
+ const isResource = this.cashedFilterForm.resource.length
+ ? this.cashedFilterForm.resource.includes(status.resource)
+ : true;
+ const isResourceType = this.cashedFilterForm.resource_type.length
+ ? this.cashedFilterForm.resource_type.includes(status.resourceType)
+ : true;
+ const isStatus = this.cashedFilterForm.status.length
+ ? this.cashedFilterForm.status.includes(status.status)
+ : true;
return isResource && isResourceType && isStatus;
});
this.checkFilters();
@@ -521,7 +531,7 @@ export class InstallLibrariesComponent implements OnInit, OnDestroy {
public openLibInfo(lib, type) {
this.dialog.open(
- LibInfoDialogComponent, { data: {lib, type}, width: '550px', panelClass: 'error-modalbox' });
+ LibInfoDialogComponent, { data: { lib, type }, width: '550px', panelClass: 'error-modalbox' });
}
public emitClick() {
@@ -566,9 +576,7 @@ export class ErrorLibMessageDialogComponent {
constructor(
public dialogRef: MatDialogRef<ErrorLibMessageDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any
- ) {
-
- }
+ ) { }
}
@Component({
@@ -598,7 +606,5 @@ export class LibInfoDialogComponent {
constructor(
public dialogRef: MatDialogRef<ErrorLibMessageDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any
- ) {
-
- }
+ ) { }
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.model.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.model.ts
index b201904..d902c74 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.model.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/install-libraries.model.ts
@@ -62,12 +62,11 @@ export class InstallLibrariesModel {
group: group,
start_with: query
};
- if (this.computational_name)
+ if (this.computational_name) {
lib_query.computational_name = this.computational_name;
+ }
- return this.librariesInstallationService.getAvailableLibrariesList(
- lib_query
- );
+ return this.librariesInstallationService.getAvailableLibrariesList(lib_query);
}
public getDependencies(query: string): Observable<{}> {
@@ -86,9 +85,13 @@ export class InstallLibrariesModel {
exploratory_name: this.notebook.name,
libs: retry ? retry : this.selectedLibs
};
- if (this.computational_name)
+ if (this.computational_name) {
lib_list.computational_name = this.computational_name;
- if (item) lib_list.computational_name = item;
+ }
+
+ if (item) {
+ lib_list.computational_name = item;
+ }
return this.librariesInstallationService.installLibraries(lib_list);
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/libraries-info.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/libraries-info.component.scss
index 2789a66..17f5f86 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/libraries-info.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/install-libraries/libraries-info.component.scss
@@ -46,9 +46,9 @@
.mat-list-item-content {
height: auto !important;
min-height: 54px !important;
- border-bottom: 1px solid #edf1f5;
padding-right: 10px;
padding-left: 0;
+ border-bottom: 1px solid #edf1f5;
}
}
@@ -66,9 +66,9 @@
}
.list-selected {
- padding: 10px;
- font-family: 'Open Sans', sans-serif;
height: 130px;
+ padding: 10px;
+ font-family: "Open Sans", sans-serif;
overflow-y: auto;
h4 {
@@ -86,17 +86,17 @@
}
.loading-block {
- height: 40%;
display: flex;
justify-content: center;
+ height: 40%;
.uploading {
- padding-top: 200px;
+ display: flex;
flex-direction: column;
align-items: center;
align-self: center;
+ padding-top: 200px;
list-style: none;
- display: flex;
p {
font-size: 16px;
@@ -104,50 +104,51 @@
}
}
- .list-header{
+ .list-header {
line-height: 40px;
}
}
.libs-info {
- padding: 0 25px;
display: flex;
flex: 1 1 auto;
min-height: 0;
+ padding: 0 25px;
.filter-row {
- .filter-col{
+
+ .filter-col {
padding-left: 0;
- &.lib-name{
+
+ &.lib-name {
padding-left: 5px;
- .filter-field{
+
+ .filter-field {
padding-left: 15px;
}
}
- .status{
+ .status {
text-transform: none;
}
}
}
.mat-list {
-
- .list-header{
- line-height: 40px;
+ .list-header {
position: sticky;
top: 0;
z-index: 99;
+ line-height: 40px;
background-color: #fff;
}
- .filter-row{
+ .filter-row {
position: sticky;
top: 56px;
z-index: 99;
background-color: #fff;
}
-
.scrollingList {
.info {
p {
@@ -163,13 +164,13 @@
padding-right: 10px;
padding-left: 20px;
- &-col{
+ &-col {
display: flex;
}
- .lib-name-wrapper{
- overflow: hidden;
+ .lib-name-wrapper {
display: block;
+ overflow: hidden;
text-overflow: ellipsis;
}
@@ -235,8 +236,8 @@
div {
display: inline-block;
- cursor: pointer;
width: 25px;
+ cursor: pointer;
pointer-events: auto;
span {
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/manage-ungit/manage-ungit.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/manage-ungit/manage-ungit.component.html
index bb59fbc..02f517d 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/manage-ungit/manage-ungit.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/manage-ungit/manage-ungit.component.html
@@ -62,9 +62,22 @@
</div>
<div class="text-center m-top-30 m-bott-10" *ngIf="editableForm && gitCredentials?.length">
- <button mat-raised-button type="button" class="butt action" (click)="cancelAllModifyings()">Cancel</button>
- <button mat-raised-button type="button" class="butt butt-success action"
- (click)="editAccounts_btnClick()">Apply changes</button>
+ <button
+ mat-raised-button
+ type="button"
+ class="butt action"
+ (click)="cancelAllModifyings()"
+ >
+ Cancel
+ </button>
+ <button
+ mat-raised-button
+ type="button"
+ class="butt butt-success action"
+ (click)="editAccounts_btnClick()"
+ >
+ Apply changes
+ </button>
</div>
</mat-tab>
<mat-tab label="MANAGE ACCOUNT">
@@ -77,13 +90,19 @@
<input type="text" formControlName="hostname"
placeholder="Enter host name ( without http:// or https:// )">
</div>
- <span class="danger_color"
+ <span
+ class="danger_color"
*ngIf="!updateAccountCredentialsForm.controls['hostname'].valid
- && updateAccountCredentialsForm.controls['hostname'].touched && !updateAccountCredentialsForm.controls['hostname'].hasError('duplicate')">
+ && updateAccountCredentialsForm.controls['hostname'].touched
+ && !updateAccountCredentialsForm.controls['hostname'].hasError('duplicate')"
+ >
Hostname field is required. Please provide a qualified domain name (e.g. gitlab.com or github.com).
</span>
- <span class="danger_color"
- *ngIf="updateAccountCredentialsForm.controls['hostname'].hasError('duplicate') && updateAccountCredentialsForm.controls['hostname'].dirty">
+ <span
+ class="danger_color"
+ *ngIf="updateAccountCredentialsForm.controls['hostname'].hasError('duplicate')
+ && updateAccountCredentialsForm.controls['hostname'].dirty"
+ >
The Host name is already in use.
</span>
</div>
@@ -102,8 +121,11 @@
<div class="control">
<input type="email" formControlName="email" placeholder="Enter email">
</div>
- <span class="danger_color"
- *ngIf="!updateAccountCredentialsForm.controls['email'].valid && updateAccountCredentialsForm.controls['email'].touched">
+ <span
+ class="danger_color"
+ *ngIf="!updateAccountCredentialsForm.controls['email'].valid
+ && updateAccountCredentialsForm.controls['email'].touched"
+ >
Email field is required. Please provide a valid email.
</span>
</div>
@@ -114,8 +136,11 @@
<div class="control">
<input type="text" formControlName="login" placeholder="Enter login">
</div>
- <span class="danger_color"
- *ngIf="!updateAccountCredentialsForm.controls['login'].valid && updateAccountCredentialsForm.controls['login'].touched">
+ <span
+ class="danger_color"
+ *ngIf="!updateAccountCredentialsForm.controls['login'].valid
+ && updateAccountCredentialsForm.controls['login'].touched"
+ >
Login field is required. Please provide a valid login.
</span>
</div>
@@ -125,7 +150,9 @@
<input type="password" formControlName="password" placeholder="Enter Password">
</div>
<span class="danger_color"
- *ngIf="!updateAccountCredentialsForm.controls['password'].valid && updateAccountCredentialsForm.controls['password'].touched">
+ *ngIf="!updateAccountCredentialsForm.controls['password'].valid
+ && updateAccountCredentialsForm.controls['password'].touched"
+ >
Field is required. Password must be at least 6 characters.
</span>
</div>
@@ -134,17 +161,36 @@
<div class="control">
<input type="password" formControlName="confirmPassword" placeholder="Enter Password">
</div>
- <span class="danger_color" *ngIf="updateAccountCredentialsForm.value.password !== updateAccountCredentialsForm.value.confirmPassword && updateAccountCredentialsForm.controls['confirmPassword'].touched && !!updateAccountCredentialsForm.value.password">
+ <span
+ class="danger_color"
+ *ngIf="updateAccountCredentialsForm.value.password !== updateAccountCredentialsForm.value.confirmPassword
+ && updateAccountCredentialsForm.controls['confirmPassword'].touched
+ && !!updateAccountCredentialsForm.value.password"
+ >
Passwords don't match.
</span>
</div>
</div>
</div>
<div class="text-center submit m-bott-10">
- <button mat-raised-button type="button" class="butt action" (click)="resetForm()">Clear</button>
- <button mat-raised-button type="button" [disabled]="!updateAccountCredentialsForm.valid || updateAccountCredentialsForm.value.password !== updateAccountCredentialsForm.value.confirmPassword"
+ <button
+ mat-raised-button
+ type="button"
+ class="butt action"
+ (click)="resetForm()"
+ >
+ Clear
+ </button>
+ <button
+ mat-raised-button
+ type="button"
+ [disabled]="!updateAccountCredentialsForm.valid
+ || updateAccountCredentialsForm.value.password !== updateAccountCredentialsForm.value.confirmPassword"
(click)="assignChanges(updateAccountCredentialsForm.value)"
- class="butt butt-success action">Assign</button>
+ class="butt butt-success action"
+ >
+ Assign
+ </button>
</div>
</form>
</mat-tab>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/manage-ungit/manage-ungit.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/manage-ungit/manage-ungit.component.scss
index 2362d15..50a9da3 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/manage-ungit/manage-ungit.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/manage-ungit/manage-ungit.component.scss
@@ -32,9 +32,9 @@
}
.scrollingList {
- overflow-x: hidden;
- padding: 5px 0;
height: 250px;
+ padding: 5px 0;
+ overflow-x: hidden;
&.info {
display: flex;
@@ -51,8 +51,8 @@
.user-field,
.login-field {
width: 25%;
- overflow: hidden;
padding: 5px;
+ overflow: hidden;
}
.hostname-field {
@@ -63,8 +63,8 @@
.actions {
position: relative;
- overflow: visible;
width: 50px;
+ overflow: visible;
span {
position: absolute;
@@ -86,14 +86,14 @@
display: flex;
.control-group {
+ position: relative;
width: 100%;
- font-family: 'Open Sans', sans-serif;
padding: 10px 0 25px;
- position: relative;
+ font-family: "Open Sans", sans-serif;
- input[type='text'],
- input[type='password'],
- input[type='email'] {
+ input[type="text"],
+ input[type="password"],
+ input[type="email"] {
font-size: 14px;
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/index.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/index.ts
index 6bbc5a0..be43d80 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/index.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/index.ts
@@ -32,27 +32,27 @@ import { InstallLibrariesModule } from '../exploratory/install-libraries';
import { AmiCreateDialogModule } from '../exploratory/ami-create-dialog';
import { SchedulerModule } from '../scheduler';
import { UnderscorelessPipeModule } from '../../core/pipes/underscoreless-pipe';
-import {LocalCurrencyModule} from '../../core/pipes/local-currency-pipe';
+import { LocalCurrencyModule } from '../../core/pipes/local-currency-pipe';
@NgModule({
- imports: [
- CommonModule,
- RouterModule,
- ComputationalResourcesModule,
- ConfirmationDialogModule,
- BubbleModule,
- DetailDialogModule,
- ComputationalResourceCreateDialogModule,
- FormControlsModule,
- CostDetailsDialogModule,
- InstallLibrariesModule,
- SchedulerModule,
- AmiCreateDialogModule,
- UnderscorelessPipeModule,
- MaterialModule,
- LocalCurrencyModule
- ],
+ imports: [
+ CommonModule,
+ RouterModule,
+ ComputationalResourcesModule,
+ ConfirmationDialogModule,
+ BubbleModule,
+ DetailDialogModule,
+ ComputationalResourceCreateDialogModule,
+ FormControlsModule,
+ CostDetailsDialogModule,
+ InstallLibrariesModule,
+ SchedulerModule,
+ AmiCreateDialogModule,
+ UnderscorelessPipeModule,
+ MaterialModule,
+ LocalCurrencyModule
+ ],
declarations: [ResourcesGridComponent],
exports: [ResourcesGridComponent]
})
-export class ResourcesGridModule {}
+export class ResourcesGridModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.html
index bb20c4e..738827b 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.html
@@ -19,8 +19,13 @@
<section class="table-wrapper resources-wrapper">
<div class="table-wrapper scrolling">
- <table mat-table [dataSource]="filteredEnvironments" multiTemplateDataRows
- class="data-grid resources mat-elevation-z6" [trackBy]="trackBy">
+ <table
+ mat-table
+ [dataSource]="filteredEnvironments"
+ multiTemplateDataRows
+ class="data-grid resources mat-elevation-z6"
+ [trackBy]="trackBy"
+ >
<ng-container matColumnDef="project">
<td mat-cell *matCellDef="let element" [attr.colspan]="8" class="project-name"> {{ element.project }} </td>
@@ -50,6 +55,7 @@
</button>
</th>
</ng-container>
+
<ng-container matColumnDef="statuses">
<th mat-header-cell *matHeaderCellDef class="status-col label-header">
<span class="label"> Status </span>
@@ -61,6 +67,7 @@
</button>
</th>
</ng-container>
+
<ng-container matColumnDef="shapes">
<th mat-header-cell *matHeaderCellDef class="shape-col label-header">
<span class="label"> Shape </span>
@@ -72,6 +79,7 @@
</button>
</th>
</ng-container>
+
<ng-container matColumnDef="tag">
<th mat-header-cell *matHeaderCellDef class="tag-col label-header">
<span class="label"> Tags </span>
@@ -82,6 +90,7 @@
</button>
</th>
</ng-container>
+
<ng-container matColumnDef="resources">
<th mat-header-cell *matHeaderCellDef class="resources-col label-header">
<span class="label"> Compute </span>
@@ -93,6 +102,7 @@
</button>
</th>
</ng-container>
+
<ng-container matColumnDef="cost">
<th mat-header-cell *matHeaderCellDef class="cost-col label-header">
<span class="label"> Cost </span>
@@ -103,6 +113,7 @@
</button>
</th>
</ng-container>
+
<ng-container matColumnDef="actions" stickyEnd>
<th mat-header-cell *matHeaderCellDef class="settings label-header">
<span class="label"> Actions </span>
@@ -126,9 +137,10 @@
{{ element.name }}
</span>
<ng-template #odahu>
- <span [matTooltip]=" element.name "
- matTooltipPosition="above"
- (click)="printDetailOdahuModal(element)"
+ <span
+ [matTooltip]=" element.name "
+ matTooltipPosition="above"
+ (click)="printDetailOdahuModal(element)"
>
{{ element.name }}
</span>
@@ -165,57 +177,86 @@
</td>
<td class="resources-col">
- <computational-resources-list [resources]="element.resources" [environment]="element"
- (buildGrid)="buildGrid()">
- </computational-resources-list>
+ <computational-resources-list
+ [resources]="element.resources"
+ [environment]="element"
+ (buildGrid)="buildGrid()"
+ ></computational-resources-list>
</td>
<td *ngIf="healthStatus?.billingEnabled" class="cost-col">
<span class="total_cost">{{ element.billing?.report_lines?.length ? (element.cost | localcurrency) : 'N/A' }}</span>
- <span (click)="element.billing && printCostDetails(element)" class="currency_details"
- [ngClass]="{ 'not-allowed' : !element.billing?.report_lines?.length }">
- <i class="material-icons">help_outline</i>
- </span>
+ <span
+ (click)="element.billing && printCostDetails(element)"
+ class="currency_details"
+ [ngClass]="{ 'not-allowed' : !element.billing?.report_lines?.length }"
+ >
+ <i class="material-icons">help_outline</i>
+ </span>
</td>
<td class="settings">
- <span #settings (click)="actions.toggle($event, settings)" class="actions"
- [ngClass]="{ 'disabled': element.status.toLowerCase() === 'creating'
- || (element.image === 'docker.datalab-superset' && element.status !== 'running' && element.status !== 'stopped')
- || (element.image === 'docker.datalab-jupyterlab' && element.status !== 'running' && element.status !== 'stopped') }">
- </span>
+ <span
+ #settings
+ (click)="actions.toggle($event, settings)"
+ class="actions"
+ [ngClass]="{ 'disabled': element.status.toLowerCase() === 'creating'
+ || (element.image === 'docker.datalab-superset' && element.status !== 'running' && element.status !== 'stopped')
+ || (element.image === 'docker.datalab-jupyterlab' && element.status !== 'running' && element.status !== 'stopped') }"
+ ></span>
<bubble-up #actions class="list-menu scrolling" position="bottom-left" alternative="top-left">
<ul class="list-unstyled" *ngIf="element.shape !== 'odahu cluster'">
- <div class="active-items" *ngIf="element.status.toLowerCase() !== 'failed'
- && element.status !== 'terminating'
- && element.status !== 'terminated'
- && element.status !== 'creating image'">
+ <div
+ class="active-items"
+ *ngIf="element.status.toLowerCase() !== 'failed'
+ && element.status !== 'terminating'
+ && element.status !== 'terminated'
+ && element.status !== 'creating image'"
+ >
<li
- *ngIf="element.status !== 'stopped' && element.status !== 'stopping' && element.status !== 'starting' && element.status !== 'creating image' && element.status.toLowerCase() !== 'reconfiguring'"
+ *ngIf="element.status !== 'stopped' && element.status !== 'stopping'
+ && element.status !== 'starting' && element.status !== 'creating image'
+ && element.status.toLowerCase() !== 'reconfiguring'"
matTooltip="Unable to stop notebook because at least one compute is in progress"
- matTooltipPosition="above" [matTooltipDisabled]="!isResourcesInProgress(element)">
- <div (click)="exploratoryAction(element, 'stop')"
- [ngClass]="{'not-allowed': isResourcesInProgress(element) }">
+ matTooltipPosition="above"
+ [matTooltipDisabled]="!isResourcesInProgress(element)"
+ >
+ <div
+ (click)="exploratoryAction(element, 'stop')"
+ [ngClass]="{'not-allowed': isResourcesInProgress(element) }"
+ >
<i class="material-icons">pause_circle_outline</i>
<span>Stop</span>
</div>
</li>
- <li *ngIf="element.status.toLowerCase() === 'stopped' || element.status.toLowerCase() === 'stopping'"
- matTooltip="{{element.edgeNodeStatus !== 'running' ? 'Unable to run notebook if edge node is not running.' : 'Unable to run notebook until it will be stopped.'}}" matTooltipPosition="above"
- [matTooltipDisabled]="!isResourcesInProgress(element) && element.status.toLowerCase() !== 'stopping' && element.edgeNodeStatus === 'running'"
- [ngClass]="{'not-allow': isResourcesInProgress(element) || element.status.toLowerCase() === 'stopping' || element.edgeNodeStatus !== 'running' }"
+ <li
+ *ngIf="element.status.toLowerCase() === 'stopped' || element.status.toLowerCase() === 'stopping'"
+ matTooltip="{{element.edgeNodeStatus !== 'running'
+ ? 'Unable to run notebook if edge node is not running.'
+ : 'Unable to run notebook until it will be stopped.'}}"
+ matTooltipPosition="above"
+ [matTooltipDisabled]="!isResourcesInProgress(element) && element.status.toLowerCase() !== 'stopping'
+ && element.edgeNodeStatus === 'running'"
+ [ngClass]="{'not-allow': isResourcesInProgress(element) || element.status.toLowerCase() === 'stopping'
+ || element.edgeNodeStatus !== 'running' }"
>
- <div (click)="exploratoryAction(element, 'run')"
- [ngClass]="{'not-allowed': isResourcesInProgress(element) || element.status.toLowerCase() === 'stopping' || element.edgeNodeStatus !== 'running' }">
+ <div
+ (click)="exploratoryAction(element, 'run')"
+ [ngClass]="{'not-allowed': isResourcesInProgress(element) || element.status.toLowerCase() === 'stopping'
+ || element.edgeNodeStatus !== 'running' }">
<i class="material-icons">play_circle_outline</i>
<span>Run</span>
</div>
</li>
- <li *ngIf="element.status.toLowerCase() === 'running' || element.status.toLowerCase() === 'stopped'"
- matTooltip="Unable to terminate notebook because at least one compute is in progress"
- matTooltipPosition="above" [matTooltipDisabled]="!isResourcesInProgress(element)">
- <div (click)="exploratoryAction(element, 'terminate')"
- [ngClass]="{'not-allowed': isResourcesInProgress(element) }">
+ <li
+ *ngIf="element.status.toLowerCase() === 'running' || element.status.toLowerCase() === 'stopped'"
+ matTooltip="Unable to terminate notebook because at least one compute is in progress"
+ matTooltipPosition="above" [matTooltipDisabled]="!isResourcesInProgress(element)"
+ >
+ <div
+ (click)="exploratoryAction(element, 'terminate')"
+ [ngClass]="{'not-allowed': isResourcesInProgress(element) }"
+ >
<i class="material-icons">phonelink_off</i>
<span>Terminate</span>
</div>
@@ -223,7 +264,8 @@
<li
*ngIf="element.status === 'running' && element.image !== 'docker.datalab-superset' && element.image !== 'docker.datalab-jupyterlab'"
matTooltip="Only one compute can be associated with analytical tool at a time"
- matTooltipPosition="above" [matTooltipDisabled]="!element.activeCompute"
+ matTooltipPosition="above"
+ [matTooltipDisabled]="!element.activeCompute"
[matTooltipClass]="'full-size-tooltip'"
[ngClass]="{'not-allow': element.activeCompute }"
>
@@ -235,8 +277,11 @@
<span>Add compute</span>
</div>
</li>
- <li (click)="exploratoryAction(element, 'schedule')" *ngIf="element.status.toLowerCase() === 'running'
- || element.status.toLowerCase() === 'stopped'">
+ <li
+ (click)="exploratoryAction(element, 'schedule')"
+ *ngIf="element.status.toLowerCase() === 'running'
+ || element.status.toLowerCase() === 'stopped'"
+ >
<div>
<i class="material-icons">schedule</i>
<span>Scheduler</span>
@@ -244,20 +289,26 @@
</li>
</div>
<li
- matTooltip="You can create an image after the library installation is complete"
- matTooltipPosition="above" [matTooltipDisabled]="!checkLibStatus(element)"
- [matTooltipClass]="'full-size-tooltip'"
- [ngClass]="{'not-allow': checkLibStatus(element) }"
- *ngIf="element.status === 'running' && element.image !== 'docker.datalab-superset' && element.image !== 'docker.datalab-jupyterlab'">
+ matTooltip="You can create an image after the library installation is complete"
+ matTooltipPosition="above"
+ [matTooltipDisabled]="!checkLibStatus(element)"
+ [matTooltipClass]="'full-size-tooltip'"
+ [ngClass]="{'not-allow': checkLibStatus(element) }"
+ *ngIf="element.status === 'running' && element.image !== 'docker.datalab-superset'
+ && element.image !== 'docker.datalab-jupyterlab'"
+ >
<div
(click)="exploratoryAction(element, 'ami')"
- [ngClass]="{'not-allowed': checkLibStatus(element) }">
+ [ngClass]="{'not-allowed': checkLibStatus(element) }"
+ >
<i class="material-icons">view_module</i>
<span>Create AMI</span>
</div>
</li>
- <li (click)="exploratoryAction(element, 'install')"
- *ngIf="element.image !== 'docker.datalab-superset' && element.image !== 'docker.datalab-jupyterlab'">
+ <li
+ (click)="exploratoryAction(element, 'install')"
+ *ngIf="element.image !== 'docker.datalab-superset' && element.image !== 'docker.datalab-jupyterlab'"
+ >
<div>
<i class="material-icons">developer_board</i>
<span>Manage libraries</span>
@@ -294,9 +345,12 @@
</a>
</div>
</li>
- <li class="project-seting-item"
- [ngClass]="{'disabled': element.status === 'stopped' || element.status.toLowerCase() === 'stopping' || element.status.toLowerCase() === 'starting'}"
- *ngIf="element.status === element.status !== 'terminated' && element.status !== 'terminating'" (click)="odahuAction(element, 'terminate')"
+ <li
+ class="project-seting-item"
+ [ngClass]="{'disabled': element.status === 'stopped' || element.status.toLowerCase() === 'stopping'
+ || element.status.toLowerCase() === 'starting'}"
+ *ngIf="element.status === element.status !== 'terminated' && element.status !== 'terminating'"
+ (click)="odahuAction(element, 'terminate')"
>
<div>
<i class="material-icons">phonelink_off</i>
@@ -316,40 +370,51 @@
<ng-container matColumnDef="name-filter" sticky>
<th mat-header-cell *matHeaderCellDef class="name-col filter-row-item ">
<div class="input-wrapper">
- <input placeholder="Filter by environment name" type="text" class="form-control filter-field"
- [value]="filterForm.name" (input)="onFilterNameUpdate($event.target['value'])"/>
+ <input
+ placeholder="Filter by environment name"
+ type="text"
+ class="form-control filter-field"
+ [value]="filterForm.name"
+ (input)="onFilterNameUpdate($event.target['value'])"
+ />
</div>
</th>
</ng-container>
<ng-container matColumnDef="status-filter">
<th mat-header-cell *matHeaderCellDef class="status-col filter-row-item">
- <multi-select-dropdown (selectionChange)="onUpdate($event)" [type]="'statuses'"
- [items]="filterConfiguration.statuses" [model]="filterForm.statuses"></multi-select-dropdown>
+ <multi-select-dropdown
+ (selectionChange)="onUpdate($event)"
+ [type]="'statuses'"
+ [items]="filterConfiguration.statuses"
+ [model]="filterForm.statuses"
+ ></multi-select-dropdown>
</th>
</ng-container>
<ng-container matColumnDef="shape-filter">
<th mat-header-cell *matHeaderCellDef class="shape-col filter-row-item">
- <multi-select-dropdown (selectionChange)="onUpdate($event)"
- [type]="'shapes'" [items]="filterConfiguration.shapes"
- [model]="filterForm.shapes"></multi-select-dropdown>
+ <multi-select-dropdown
+ (selectionChange)="onUpdate($event)"
+ [type]="'shapes'"
+ [items]="filterConfiguration.shapes"
+ [model]="filterForm.shapes"
+ ></multi-select-dropdown>
</th>
</ng-container>
<ng-container matColumnDef="tag-filter">
- <th mat-header-cell *matHeaderCellDef class="tag-col filter-row-item">
-
-
- </th>
+ <th mat-header-cell *matHeaderCellDef class="tag-col filter-row-item"></th>
</ng-container>
<ng-container matColumnDef="resource-filter">
<th mat-header-cell *matHeaderCellDef class="resources-col filter-row-item">
- <multi-select-dropdown (selectionChange)="onUpdate($event)" [type]="'resources'"
- [items]="filterConfiguration.resources" [model]="filterForm.resources"></multi-select-dropdown>
+ <multi-select-dropdown
+ (selectionChange)="onUpdate($event)"
+ [type]="'resources'"
+ [items]="filterConfiguration.resources"
+ [model]="filterForm.resources"
+ ></multi-select-dropdown>
</th>
</ng-container>
<ng-container matColumnDef="cost-filter">
- <th mat-header-cell *matHeaderCellDef class="cost-col filter-row-item">
-
- </th>
+ <th mat-header-cell *matHeaderCellDef class="cost-col filter-row-item"></th>
</ng-container>
<ng-container matColumnDef="action-filter" stickyEnd>
@@ -359,19 +424,27 @@
<i class="material-icons">close</i>
</button>
- <button mat-icon-button class="btn apply" (click)="applyFilter_btnClick(filterForm)"
- [disabled]="!isFilterChanged">
+ <button
+ mat-icon-button
+ class="btn apply"
+ (click)="applyFilter_btnClick(filterForm)"
+ [disabled]="!isFilterChanged"
+ >
<i class="material-icons">done</i>
</button>
</div>
</th>
</ng-container>
<ng-container matColumnDef="placeholder">
- <td mat-footer-cell *matFooterCellDef
- [colSpan]="!healthStatus?.billingEnabled ? displayedFilterColumns.length -1 : displayedFilterColumns.length"
- class="info">
- <span *ngIf="(!filteredEnvironments) && !filtering || (filteredEnvironments.length == 0) && !filtering">
- To start working, please, create new environment</span>
+ <td
+ mat-footer-cell
+ *matFooterCellDef
+ [colSpan]="!healthStatus?.billingEnabled ? displayedFilterColumns.length -1 : displayedFilterColumns.length"
+ class="info"
+ >
+ <span *ngIf="(!filteredEnvironments) && !filtering || (filteredEnvironments.length == 0) && !filtering">
+ To start working, please, create new environment
+ </span>
<span *ngIf="(filteredEnvironments.length == 0) && filtering">No matches found</span>
</td>
</ng-container>
@@ -380,8 +453,12 @@
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true" class="header-row"></tr>
- <tr [hidden]="!collapseFilterRow" mat-header-row *matHeaderRowDef="displayedFilterColumns; sticky: true"
- class="filter-row"></tr>
+ <tr
+ [hidden]="!collapseFilterRow"
+ mat-header-row
+ *matHeaderRowDef="displayedFilterColumns; sticky: true"
+ class="filter-row"
+ ></tr>
<tr mat-row *matRowDef="let element; columns: ['project']" class="element-row">
<!-- [class.expanded-row]="expandedElement === element"-->
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.scss
index 5c7e81b..6728ed7 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.scss
@@ -23,16 +23,16 @@
width: 100%;
}
-.resources-wrapper{
+.resources-wrapper {
height: calc(100vh - 105px);
- overflow: hidden;
- margin: -15px;
+ margin: -15px;
padding: 15px;
- .table-wrapper{
- overflow: auto;
+ overflow: hidden;
+ .table-wrapper {
height: calc(100vh - 115px);
- margin: 0 -15px -15px -15px;
+ margin: 0 -15px -15px -15px;
padding: 0 15px 15px 15px;
+ overflow: auto;
}
}
@@ -42,13 +42,13 @@ table.resources {
.header-row {
position: sticky;
- background-clip:padding-box;
z-index: 1;
+ background-clip: padding-box;
th {
padding: 5px;
padding-left: 20px;
- .mat-icon-button{
+ .mat-icon-button {
line-height: 45px;
}
}
@@ -61,7 +61,7 @@ table.resources {
}
}
- .filter-field{
+ .filter-field {
font-size: 13px;
padding-left: 10px;
}
@@ -75,20 +75,19 @@ table.resources {
height: initial;
td {
- padding: 5px;
- padding-left: 20px;
display: flex;
align-items: center;
+ padding: 5px 5px 5px 20px;
&.name-col {
padding-left: 21px;
overflow: hidden;
- span{
+ span {
cursor: pointer;
}
}
- &.shape-col{
+ &.shape-col {
align-items: start;
flex-direction: column;
justify-content: center;
@@ -112,13 +111,12 @@ table.resources {
}
.filter-row {
- background-clip:padding-box;
+ background-clip: padding-box;
th {
padding: 5px;
background-clip: padding-box;
-
&:last-child {
padding-right: 6px;
}
@@ -132,21 +130,20 @@ table.resources {
background-color: inherit;
}
- .project-name{
+ .project-name {
font-weight: 600;
color: #577289;
padding-left: 21px;
}
-
.status-col {
width: 11%;
}
- .shape-col{
+ .shape-col {
width: 13%;
color: #577289;
- .label{
+ .label {
color: $table-header-color;
}
}
@@ -155,14 +152,14 @@ table.resources {
width: 13%;
mat-chip {
+ display: inline-block;
min-height: 20px;
+ max-width: 110px !important;
+ margin: 2px;
padding: 5px 10px;
font-size: 13px;
- max-width: 110px !important;
white-space: nowrap;
- display: inline-block;
line-height: 10px;
- margin: 2px;
}
}
@@ -175,12 +172,12 @@ table.resources {
justify-content: space-between;
}
- .label-header{
- &.cost-col{
+ .label-header {
+ &.cost-col {
z-index: 6 !important;
}
- &.status-col{
+ &.status-col {
z-index: 10 !important;
}
@@ -203,24 +200,26 @@ table.resources {
.actions-col {
padding-right: 24px;
+ z-index: 5 !important;
text-align: right;
background-color: inherit;
- z-index: 5 !important;
- .label{
+
+ .label {
padding-right: 5px;
}
}
+
tr.detail-row {
height: 0;
}
-
+
.element-diagram {
+ height: 104px;
min-width: 80px;
- border: 2px solid black;
padding: 8px;
- font-weight: lighter;
margin: 8px 0;
- height: 104px;
+ border: 2px solid black;
+ font-weight: lighter;
}
.element-symbol {
@@ -237,23 +236,19 @@ table.resources {
opacity: 0.5;
}
-
-
.dashboard_table {
width: 100%;
table-layout: fixed;
}
-
-
.dashboard_table tr {
vertical-align: top;
}
.dashboard_table tr th span {
width: 50px;
- text-align: center;
height: 100%;
+ text-align: center;
line-height: 40px;
}
@@ -280,8 +275,8 @@ table.resources {
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
- &::after{
- content: '';
+ &::after {
+ content: "";
display: block;
}
}
@@ -307,15 +302,15 @@ table.resources {
}
.dashboard_table th {
+ padding-left: 12px;
background: #a1b7d1;
color: #fff;
font-weight: 600;
line-height: 40px;
text-transform: uppercase;
- padding-left: 12px;
font-size: 13px;
}
-
+
&.data-grid .status-col .status {
text-transform: capitalize;
}
@@ -332,10 +327,10 @@ table.resources {
.currency_details {
display: flex;
+ padding-left: 10px;
color: #35afd5;
cursor: pointer;
transition: all 0.45s ease-in-out;
- padding-left: 10px;
}
.currency_details:hover {
@@ -357,39 +352,38 @@ table.resources {
text-align: center;
}
- .content-row{
+ .content-row {
background-clip: padding-box;
}
- .not-allow{
+ .not-allow {
cursor: not-allowed !important;
}
}
.data-grid .list-menu {
left: auto;
- margin-left: 0;
right: 15px;
- padding: 10px 15px;
- box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
- 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);
- border: none;
min-width: 190px;
max-height: calc(100vh / 2 - 70px);
+ margin-left: 0;
+ padding: 10px 15px;
+ box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);
+ border: none;
}
.settings:not(.label-header) {
position: relative;
- text-align: right;
width: 6%;
padding-right: 15px !important;
justify-content: flex-end;
+ text-align: right;
.actions {
- background-image: url(../../../assets/svg/settings_icon.svg);
+ display: inline-block;
width: 16px;
height: 16px;
- display: inline-block;
+ background-image: url(../../../assets/svg/settings_icon.svg);
text-align: center;
cursor: pointer;
}
@@ -401,16 +395,16 @@ table.resources {
}
}
-
.data-grid .list-menu li {
+ margin: 5px -5px;
+ padding-bottom: 5px;
font-size: 13px;
border-bottom: 1px solid #edf1f5;
- padding-bottom: 5px;
cursor: pointer;
- margin: 5px -5px;
color: #577289;
transition: all 0.45s ease-in-out;
- div{
+
+ div {
padding: 8px 15px;
}
}
@@ -422,8 +416,8 @@ table.resources {
}
.data-grid .list-menu li i {
- font-size: 18px;
padding-right: 5px;
+ font-size: 18px;
vertical-align: bottom;
}
@@ -457,18 +451,16 @@ table.resources {
}
}
-
-
.info {
padding: 40px;
text-align: center;
}
-.odahu-icon{
+.odahu-icon {
vertical-align: middle;
margin-left: 10px;
}
-.content-row{
+.content-row {
background-clip: padding-box;
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
index f9347a8..997b07b 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import {Project} from '../../administration/project/project.component';
+import { Project } from '../../administration/project/project.component';
import {
Component,
EventEmitter,
@@ -28,7 +28,7 @@ import {
import { animate, state, style, transition, trigger } from '@angular/animations';
import { ToastrService } from 'ngx-toastr';
import { MatDialog } from '@angular/material/dialog';
-import {ProjectService, UserResourceService, OdahuDeploymentService} from '../../core/services';
+import { ProjectService, UserResourceService, OdahuDeploymentService } from '../../core/services';
import { ExploratoryModel } from './resources-grid.model';
import { FilterConfigurationModel } from './filter-configuration.model';
import { GeneralEnvironmentStatus } from '../../administration/management/management.model';
@@ -42,13 +42,12 @@ import { CostDetailsDialogComponent } from '../exploratory/cost-details-dialog';
import { ConfirmationDialogComponent } from '../../shared/modal-dialog/confirmation-dialog';
import { SchedulerComponent } from '../scheduler';
import { DICTIONARY } from '../../../dictionary/global.dictionary';
-import {ProgressBarService} from '../../core/services/progress-bar.service';
-import {ComputationModel} from '../computational/computational-resource.model';
-import {NotebookModel} from '../exploratory/notebook.model';
-import {AuditService} from '../../core/services/audit.service';
-import {CompareUtils} from '../../core/util/compareUtils';
-import {timer} from 'rxjs';
-import {OdahuActionDialogComponent} from '../../shared/modal-dialog/odahu-action-dialog';
+import { ProgressBarService } from '../../core/services/progress-bar.service';
+import { ComputationModel } from '../computational/computational-resource.model';
+import { NotebookModel } from '../exploratory/notebook.model';
+import { AuditService } from '../../core/services/audit.service';
+import { CompareUtils } from '../../core/util/compareUtils';
+import { OdahuActionDialogComponent } from '../../shared/modal-dialog/odahu-action-dialog';
export interface SharedEndpoint {
edge_node_ip: string;
@@ -112,7 +111,6 @@ export class ResourcesGridComponent implements OnInit {
public filterConfiguration: FilterConfigurationModel = new FilterConfigurationModel('', [], [], [], '', '');
public filterForm: FilterConfigurationModel = new FilterConfigurationModel('', [], [], [], '', '');
-
public filteringColumns: Array<any> = [
{ title: 'Environment name', name: 'name', class: 'name-col', filter_class: 'name-filter', filtering: true },
{ title: 'Status', name: 'statuses', class: 'status-col', filter_class: 'status-filter', filtering: true },
@@ -155,15 +153,18 @@ export class ResourcesGridComponent implements OnInit {
public buildGrid(): void {
this.progressBarService.startProgressBar();
this.userResourceService.getUserProvisionedResources()
- .subscribe((result: any) => {
- this.environments = ExploratoryModel.loadEnvironments(result);
- this.getEnvironments.emit(this.environments);
- this.getBuckets();
- this.getDefaultFilterConfiguration();
- (this.environments.length) ? this.getUserPreferences() : this.filteredEnvironments = [];
- this.healthStatus && !this.healthStatus.billingEnabled && this.modifyGrid();
- this.progressBarService.stopProgressBar();
- }, () => this.progressBarService.stopProgressBar());
+ .subscribe(
+ (result: any) => {
+ this.environments = ExploratoryModel.loadEnvironments(result);
+ this.getEnvironments.emit(this.environments);
+ this.getBuckets();
+ this.getDefaultFilterConfiguration();
+ (this.environments.length) ? this.getUserPreferences() : this.filteredEnvironments = [];
+ this.healthStatus && !this.healthStatus.billingEnabled && this.modifyGrid();
+ this.progressBarService.stopProgressBar();
+ },
+ () => this.progressBarService.stopProgressBar()
+ );
}
public toggleFilterRow(): void {
@@ -193,12 +194,11 @@ export class ResourcesGridComponent implements OnInit {
public containsNotebook(notebook_name: string, envoirmentNames: Array<string>): boolean {
if (notebook_name && envoirmentNames.length ) {
- return envoirmentNames
- .some(item => CheckUtils.delimitersFiltering(notebook_name) === CheckUtils.delimitersFiltering(item));
- }
+ return envoirmentNames
+ .some(item => CheckUtils.delimitersFiltering(notebook_name) === CheckUtils.delimitersFiltering(item));
+ }
return false;
- }
-
+ }
public isResourcesInProgress(notebook): boolean {
const env = this.getResourceByName(notebook.name, notebook.project);
@@ -227,14 +227,19 @@ export class ResourcesGridComponent implements OnInit {
public printDetailEnvironmentModal(data): void {
this.dialog.open(DetailDialogComponent, { data:
- {notebook: data, bucketStatus: this.healthStatus.bucketBrowser, buckets: this.bucketsList, type: 'resource'},
+ {
+ notebook: data,
+ bucketStatus: this.healthStatus.bucketBrowser,
+ buckets: this.bucketsList,
+ type: 'resource'
+ },
panelClass: 'modal-lg'
})
.afterClosed().subscribe(() => this.buildGrid());
}
public printDetailOdahuModal(data): void {
- this.dialog.open(DetailDialogComponent, { data: {odahu: data}, panelClass: 'modal-lg' })
+ this.dialog.open(DetailDialogComponent, { data: { odahu: data }, panelClass: 'modal-lg' })
.afterClosed().subscribe(() => this.buildGrid());
}
@@ -246,7 +251,13 @@ export class ResourcesGridComponent implements OnInit {
public exploratoryAction(data, action: string): void {
const resource = this.getResourceByName(data.name, data.project);
if (action === 'deploy') {
- this.dialog.open(ComputationalResourceCreateDialogComponent, { data: { notebook: resource, full_list: this.environments }, panelClass: 'modal-xxl' })
+ this.dialog.open(ComputationalResourceCreateDialogComponent, { data:
+ {
+ notebook: resource,
+ full_list: this.environments
+ },
+ panelClass: 'modal-xxl'
+ })
.afterClosed().subscribe((res) => {
res && this.buildGrid();
});
@@ -266,7 +277,13 @@ export class ResourcesGridComponent implements OnInit {
} else if (action === 'terminate') {
const compute = data.resources.filter(cluster => cluster.status === 'running' || cluster.status === 'stopped');
this.dialog.open(ConfirmationDialogComponent, { data:
- { notebook: data, compute, type: ConfirmationDialogType.TerminateExploratory }, panelClass: 'modal-sm' })
+ {
+ notebook: data,
+ compute,
+ type: ConfirmationDialogType.TerminateExploratory
+ },
+ panelClass: 'modal-sm'
+ })
.afterClosed().subscribe((res) => res && this.buildGrid());
} else if (action === 'install') {
this.dialog.open(InstallLibrariesComponent, { data: data, panelClass: 'modal-fullscreen' })
@@ -287,7 +304,11 @@ export class ResourcesGridComponent implements OnInit {
project.endpoints.forEach((endpoint: ProjectEndpoint) => {
if (endpoint.status === 'ACTIVE') {
const currEndpoint: SharedEndpoint = project.projectEndpoints[endpoint.name];
- const edgeItem: BucketList = {name: `${project.project} (${endpoint.name})`, children: [], cloud: endpoint.cloudProvider};
+ const edgeItem: BucketList = {
+ name: `${project.project} (${endpoint.name})`,
+ children: [],
+ cloud: endpoint.cloudProvider
+ };
let projectBucket: string = currEndpoint.user_own_bicket_name
|| currEndpoint.user_own_bucket_name;
if (currEndpoint.user_container_name) {
@@ -313,10 +334,10 @@ export class ResourcesGridComponent implements OnInit {
this.bucketsList = SortUtils.flatDeep(bucketsList, 1).filter(v => v.children.length);
}
-
// PRIVATE
private getResourceByName(notebook_name: string, project_name: string) {
- return this.getEnvironmentsListCopy().filter(environments => environments.project === project_name)
+ return this.getEnvironmentsListCopy()
+ .filter(environments => environments.project === project_name)
.map(env => env.exploratory.find(({ name }) => name === notebook_name))
.filter(name => !!name)[0];
}
@@ -359,6 +380,7 @@ export class ResourcesGridComponent implements OnInit {
if (filteredData.some((v) => v.exploratory.length)) {
this.filtering = true;
}
+
if (config) {
this.activeProject = config.project;
filteredData = filteredData
@@ -443,14 +465,17 @@ export class ResourcesGridComponent implements OnInit {
private getUserPreferences(): void {
this.userResourceService.getUserPreferences()
- .subscribe((result: FilterConfigurationModel) => {
- if (result) {
- this.isActiveFilter(result);
- this.filterForm = this.loadUserPreferences(result.type ? this.filterActiveInstances() : this.aliveStatuses(result));
- }
- this.applyFilter_btnClick(result || this.filterForm);
- this.checkFilters();
- }, () => this.applyFilter_btnClick(null));
+ .subscribe(
+ (result: FilterConfigurationModel) => {
+ if (result) {
+ this.isActiveFilter(result);
+ this.filterForm = this.loadUserPreferences(result.type ? this.filterActiveInstances() : this.aliveStatuses(result));
+ }
+ this.applyFilter_btnClick(result || this.filterForm);
+ this.checkFilters();
+ },
+ () => this.applyFilter_btnClick(null)
+ );
}
private loadUserPreferences(config): FilterConfigurationModel {
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.html
index 94366f6..43aaf5a 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.html
@@ -26,10 +26,14 @@
[matTooltipClass]="'full-size-tooltip'"
[matTooltipDisabled]="healthStatus?.projectAssigned && resourcesGrid.activeProjectsList?.length !== 0"
>
- <button mat-raised-button class="butt butt-create" (click)="createEnvironment()"
- [disabled]="!healthStatus?.projectAssigned || !resourcesGrid.activeProjectsList?.length">
- <i class="material-icons">add</i>Create new
- </button>
+ <button
+ mat-raised-button
+ class="butt butt-create"
+ (click)="createEnvironment()"
+ [disabled]="!healthStatus?.projectAssigned || !resourcesGrid.activeProjectsList?.length"
+ >
+ <i class="material-icons">add</i>Create new
+ </button>
</span>
<div class="mat-reset">
<div class="control selector-wrapper" *ngIf="projects?.length">
@@ -39,19 +43,22 @@
<mat-select
disableOptionCentering
[(value)]="resourcesGrid.activeProject"
- panelClass="top-select scrolling">
+ panelClass="top-select scrolling"
+ >
<mat-option
*ngIf="projects?.length > 1"
- (click)="setActiveProject('')">Show all
+ (click)="setActiveProject('')"
+ >
+ Show all
</mat-option>
<mat-option
*ngFor="let project of projects"
[value]="project"
- (click)="setActiveProject(project)">
+ (click)="setActiveProject(project)"
+ >
{{ project }}
</mat-option>
- <mat-option *ngIf="!projects?.length" class="multiple-select ml-10" disabled>Projects list is empty
- </mat-option>
+ <mat-option *ngIf="!projects?.length" class="multiple-select ml-10" disabled>Projects list is empty</mat-option>
</mat-select>
<button class="caret">
<i class="material-icons">keyboard_arrow_down</i>
@@ -62,13 +69,18 @@
</div>
<div>
- <span matTooltip="{{!this.bucketStatus?.view ? 'You have not permission to open bucket browser' : 'You have not any bucket'}}"
- matTooltipPosition="above"
- matTooltipDisabled="{{resourcesGrid.bucketsList?.length > 0 && this.bucketStatus?.view}}"
- [matTooltipClass]="'full-size-tooltip'"
+ <span
+ matTooltip="{{!this.bucketStatus?.view ? 'You have not permission to open bucket browser' : 'You have not any bucket'}}"
+ matTooltipPosition="above"
+ matTooltipDisabled="{{resourcesGrid.bucketsList?.length > 0 && this.bucketStatus?.view}}"
+ [matTooltipClass]="'full-size-tooltip'"
>
- <button mat-raised-button class="butt butt-tool" (click)="bucketBrowser(this.bucketStatus?.view)"
- [disabled]="!this.bucketStatus?.view || resourcesGrid.bucketsList?.length === 0">
+ <button
+ mat-raised-button
+ class="butt butt-tool"
+ (click)="bucketBrowser(this.bucketStatus?.view)"
+ [disabled]="!this.bucketStatus?.view || resourcesGrid.bucketsList?.length === 0"
+ >
<i class="material-icons"></i>Bucket browser
</button>
</span>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.ts
index a5185b0..9f53c46 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources.component.ts
@@ -17,16 +17,15 @@
* under the License.
*/
-import {AfterViewInit, Component, OnInit, ViewChild} from '@angular/core';
+import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
import { ToastrService } from 'ngx-toastr';
import { MatDialog } from '@angular/material/dialog';
import { ResourcesGridComponent } from './resources-grid/resources-grid.component';
import { ExploratoryEnvironmentCreateComponent } from './exploratory/create-environment';
-import {ApplicationSecurityService, HealthStatusService} from '../core/services';
+import { ApplicationSecurityService, HealthStatusService } from '../core/services';
import { ManageUngitComponent } from './manage-ungit/manage-ungit.component';
-import {BucketBrowserComponent} from './bucket-browser/bucket-browser.component';
-
+import { BucketBrowserComponent } from './bucket-browser/bucket-browser.component';
@Component({
selector: 'datalab-resources',
@@ -69,8 +68,6 @@ export class ResourcesComponent implements OnInit, AfterViewInit {
this.exploratoryEnvironments = this.resourcesGrid.environments;
}
-
-
public toggleFiltering(): void {
if (this.resourcesGrid.activeFiltering) {
this.resourcesGrid.resetFilterConfigurations();
@@ -93,7 +90,8 @@ export class ResourcesComponent implements OnInit, AfterViewInit {
bucketStatus: this.bucketStatus,
buckets: this.resourcesGrid.bucketsList
},
- panelClass: 'modal-fullscreen' })
+ panelClass: 'modal-fullscreen'
+ })
.afterClosed().subscribe();
}
@@ -108,19 +106,16 @@ export class ResourcesComponent implements OnInit, AfterViewInit {
}
private checkAutorize() {
- this.applicationSecurityService.isLoggedIn().subscribe( () => {
- this.getEnvironmentHealthStatus();
- }
- );
+ this.applicationSecurityService.isLoggedIn().subscribe(() => {
+ this.getEnvironmentHealthStatus();
+ });
}
-
public getEnvironments(environment) {
this.exploratoryEnvironments = environment;
this.projects = environment.map(env => env.project);
}
-
private getEnvironmentHealthStatus() {
this.healthStatusService.getEnvironmentHealthStatus().subscribe(
(result: any) => {
@@ -128,6 +123,7 @@ export class ResourcesComponent implements OnInit, AfterViewInit {
this.resourcesGrid.healthStatus = this.healthStatus;
this.bucketStatus = this.healthStatus.bucketBrowser;
},
- error => this.toastr.error(error.message, 'Oops!'));
+ error => this.toastr.error(error.message, 'Oops!')
+ );
}
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
index cd98dd5..47aea4c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
@@ -27,23 +27,23 @@ import { ResourcesGridModule } from './resources-grid';
import { ExploratoryEnvironmentCreateModule } from './exploratory/create-environment';
import { ManageUngitComponent } from './manage-ungit/manage-ungit.component';
import { ConfirmDeleteAccountDialogComponent } from './manage-ungit/manage-ungit.component';
-import {MatTreeModule} from '@angular/material/tree';
-import {BucketDataService} from './bucket-browser/bucket-data.service';
-import {ConvertFileSizePipeModule} from '../core/pipes/convert-file-size';
-import {BucketBrowserModule} from './bucket-browser/bucket-browser.module';
+import { MatTreeModule } from '@angular/material/tree';
+import { BucketDataService } from './bucket-browser/bucket-data.service';
+import { ConvertFileSizePipeModule } from '../core/pipes/convert-file-size';
+import { BucketBrowserModule } from './bucket-browser/bucket-browser.module';
@NgModule({
- imports: [
- CommonModule,
- FormsModule,
- ReactiveFormsModule,
- ResourcesGridModule,
- ExploratoryEnvironmentCreateModule,
- MaterialModule,
- MatTreeModule,
- ConvertFileSizePipeModule,
- BucketBrowserModule
- ],
+ imports: [
+ CommonModule,
+ FormsModule,
+ ReactiveFormsModule,
+ ResourcesGridModule,
+ ExploratoryEnvironmentCreateModule,
+ MaterialModule,
+ MatTreeModule,
+ ConvertFileSizePipeModule,
+ BucketBrowserModule
+ ],
declarations: [
ResourcesComponent,
ManageUngitComponent,
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/scheduler/scheduler.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/scheduler/scheduler.component.html
index eb81592..8db2137 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/scheduler/scheduler.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/scheduler/scheduler.component.html
@@ -35,55 +35,81 @@
<span *ngIf="toggleSchedule" class="hold-label">Scheduler by time</span>
</mat-slide-toggle>
<div class="idle" *ngIf="destination.image !== 'docker.datalab-dataengine-service'">
- <mat-slide-toggle labelPosition="before" [checked]="enableIdleTime" (change)="toggleIdleTimes($event)">
- <span *ngIf="toggleSchedule" class="hold-label">Scheduler by inactivity</span>
- </mat-slide-toggle>
+ <mat-slide-toggle labelPosition="before" [checked]="enableIdleTime" (change)="toggleIdleTimes($event)">
+ <span *ngIf="toggleSchedule" class="hold-label">Scheduler by inactivity</span>
+ </mat-slide-toggle>
</div>
</div>
- <div class="note m-bott-10" *ngIf="destination.image !== 'docker.datalab-dataengine-service'">
- NOTE: In case of turning on inactivity time-check, your schedule
- configuration will be decommissioned.
- </div>
+ <div class="note m-bott-10" *ngIf="destination.image !== 'docker.datalab-dataengine-service'">
+ NOTE: In case of turning on inactivity time-check, your schedule
+ configuration will be decommissioned.
+ </div>
<div class="control-group idle-control" [ngClass]="{ show: enableIdleTimeView }">
<label class="label">Scheduler by inactivity, min</label>
<div class="control">
- <input type="text" class="form-control" placeholder="Enter time in min" formControlName="inactivityTime"
- (keypress)="CheckUtils.isNumberKey($event)" (keydown.arrowup)="inactivityCounter($event, 'increment')"
- (keydown.arrowdown)="inactivityCounter($event, 'decrement')" />
+ <input
+ type="text"
+ class="form-control"
+ placeholder="Enter time in min"
+ formControlName="inactivityTime"
+ (keypress)="CheckUtils.isNumberKey($event)"
+ (keydown.arrowup)="inactivityCounter($event, 'increment')"
+ (keydown.arrowdown)="inactivityCounter($event, 'decrement')"
+ />
<span class="error" *ngIf="!schedulerForm.controls.inactivityTime.valid">
<span>The value should be an integer greater than or equal to {{ inactivityLimits.min }} and
cannot exceed 1 week ({{ inactivityLimits.max }}) in min.</span>
</span>
</div>
</div>
- <div class="schedule-by-time" *ngIf="!enableIdleTimeView" [ngClass]="{ hide: enableIdleTimeView, resource: destination.type === 'СOMPUTATIONAL',
- des: destination.image === 'docker.datalab-dataengine-service' }">
- <div class="row-wrap" *ngIf="destination.image !== 'docker.datalab-dataengine-service'">
- <div class="col-3">
- <mat-form-field>
- <input matInput [matDatepicker]="startDate" placeholder="Choose start date"
- formControlName="startDate">
- <mat-datepicker-toggle
-
- matSuffix [for]="startDate"
- [ngClass]="{'not-allowed' : destination.type === 'СOMPUTATIONAL' && inherit || !enableSchedule }"></mat-datepicker-toggle>
- <mat-datepicker #startDate></mat-datepicker>
- </mat-form-field>
- </div>
- <div class="col-3">
+ <div
+ class="schedule-by-time"
+ *ngIf="!enableIdleTimeView"
+ [ngClass]="{ hide: enableIdleTimeView, resource: destination.type === 'СOMPUTATIONAL',
+ des: destination.image === 'docker.datalab-dataengine-service' }"
+ >
+ <div class="row-wrap" *ngIf="destination.image !== 'docker.datalab-dataengine-service'">
+ <div class="col-3">
+ <mat-form-field>
+ <input
+ matInput
+ [matDatepicker]="startDate"
+ placeholder="Choose start date"
+ formControlName="startDate"
+ />
+ <mat-datepicker-toggle
+ matSuffix [for]="startDate"
+ [ngClass]="{'not-allowed' : destination.type === 'СOMPUTATIONAL' && inherit || !enableSchedule }"
+ ></mat-datepicker-toggle>
+ <mat-datepicker #startDate></mat-datepicker>
+ </mat-form-field>
+ </div>
+ <div class="col-3">
<mat-form-field>
- <input matInput [matDatepicker]="finishDate" placeholder="Choose finish date"
- formControlName="finishDate">
- <mat-datepicker-toggle matSuffix [for]="finishDate" [ngClass]="{'not-allowed' : destination.type === 'СOMPUTATIONAL' && inherit || !enableSchedule }"></mat-datepicker-toggle>
+ <input
+ matInput
+ [matDatepicker]="finishDate"
+ placeholder="Choose finish date"
+ formControlName="finishDate"
+ />
+ <mat-datepicker-toggle
+ matSuffix
+ [for]="finishDate"
+ [ngClass]="{'not-allowed' : destination.type === 'СOMPUTATIONAL' && inherit || !enableSchedule }"
+ ></mat-datepicker-toggle>
<mat-datepicker #finishDate ></mat-datepicker>
</mat-form-field>
</div>
<div class="col-3" *ngIf="destination.type === 'СOMPUTATIONAL'; else timezone">
<mat-form-field>
- <input matInput [matDatepicker]="terminateDate" placeholder="Choose terminate date"
- formControlName="terminateDate" >
+ <input
+ matInput
+ [matDatepicker]="terminateDate"
+ placeholder="Choose terminate date"
+ formControlName="terminateDate"
+ />
<mat-datepicker-toggle matSuffix [for]="terminateDate"></mat-datepicker-toggle>
<mat-datepicker #terminateDate></mat-datepicker>
</mat-form-field>
@@ -102,90 +128,133 @@
</ng-template>
</div>
- <div class="row-wrap" *ngIf="destination.image !== 'docker.datalab-dataengine-service'">
- <div class="control-group col-3 time-range">
- <datalab-time-picker [(pickTime)]="startTime" [(milisecTime)]="startTimeMilliseconds"
- [label]="'Choose start time'"
- [disable]="destination.type === 'СOMPUTATIONAL' && inherit || !enableSchedule">
- {{destination.type}}
- </datalab-time-picker>
- </div>
- <div class="control-group col-3 time-range">
- <datalab-time-picker [(pickTime)]="endTime" [(milisecTime)]="endTimeMilliseconds"
- [label]="'Choose finish time'"
- [disable]="destination.type === 'СOMPUTATIONAL' && inherit ||!enableSchedule">
- </datalab-time-picker>
- </div>
- <div class="control-group col-3 time-range" *ngIf="destination.type === 'СOMPUTATIONAL'">
- <datalab-time-picker [(pickTime)]="terminateTime" [(milisecTime)]="terminateTimeMilliseconds"
- [label]="'Choose terminate time'"
- [disable]="!enableSchedule"></datalab-time-picker>
- </div>
- <div *ngIf="timeReqiered" class="error term m-bott-10 mt-5"><span>At least one of time range fields
- should be selected</span>
+ <div class="row-wrap" *ngIf="destination.image !== 'docker.datalab-dataengine-service'">
+ <div class="control-group col-3 time-range">
+ <datalab-time-picker
+ [(pickTime)]="startTime"
+ [(milisecTime)]="startTimeMilliseconds"
+ [label]="'Choose start time'"
+ [disable]="destination.type === 'СOMPUTATIONAL' && inherit || !enableSchedule"
+ >
+ {{destination.type}}
+ </datalab-time-picker>
+ </div>
+ <div class="control-group col-3 time-range">
+ <datalab-time-picker
+ [(pickTime)]="endTime"
+ [(milisecTime)]="endTimeMilliseconds"
+ [label]="'Choose finish time'"
+ [disable]="destination.type === 'СOMPUTATIONAL' && inherit ||!enableSchedule"
+ ></datalab-time-picker>
+ </div>
+ <div class="control-group col-3 time-range" *ngIf="destination.type === 'СOMPUTATIONAL'">
+ <datalab-time-picker
+ [(pickTime)]="terminateTime"
+ [(milisecTime)]="terminateTimeMilliseconds"
+ [label]="'Choose terminate time'"
+ [disable]="!enableSchedule"
+ ></datalab-time-picker>
+ </div>
+ <div *ngIf="timeReqiered" class="error term m-bott-10 mt-5">
+ <span>At least one of time range fields should be selected</span>
</div>
<div *ngIf="terminateDataReqiered">
<span class="error term m-bott-10 mt-5">Both terminate date and time fields should be selected</span>
</div>
</div>
- <div class="row-wrap"
- *ngIf="destination.type === 'СOMPUTATIONAL' && destination.image !== 'docker.datalab-dataengine-service'">
- <div class="col-3">
- <mat-form-field class="timezone-offset">
- <mat-select placeholder="Select timezone" [(value)]="tzOffset"
- [disabled]="destination.type === 'СOMPUTATIONAL' && inherit || !enableSchedule">
- <mat-option *ngFor="let zone of zones | keys" [value]="zone.key"
- matTooltip="{{ zone.value }}"
- matTooltipShowDelay="1000" matTooltipPosition="above"> GMT {{zone.key}} {{
- zone.value }}
- </mat-option>
- </mat-select>
- </mat-form-field>
- </div>
+ <div
+ class="row-wrap"
+ *ngIf="destination.type === 'СOMPUTATIONAL' && destination.image !== 'docker.datalab-dataengine-service'"
+ >
+ <div class="col-3">
+ <mat-form-field class="timezone-offset">
+ <mat-select
+ placeholder="Select timezone"
+ [(value)]="tzOffset"
+ [disabled]="destination.type === 'СOMPUTATIONAL' && inherit || !enableSchedule"
+ >
+ <mat-option
+ *ngFor="let zone of zones | keys"
+ [value]="zone.key"
+ matTooltip="{{ zone.value }}"
+ matTooltipShowDelay="1000"
+ matTooltipPosition="above"
+ >
+ GMT {{zone.key}} {{zone.value }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
</div>
- <div class="control-group"
- *ngIf="destination && destination.image !== 'docker.datalab-dataengine-service'">
- <label class="label repeat" for="options">Repeat on :</label>
- <div class="days-block">
- <label>Start date:</label>
- <mat-button-toggle *ngFor="let day of weekdays; let i = index" value="{{ day }}"
- (change)="onDaySelect($event, day, 'start')"
- [disabled]="destination.type === 'СOMPUTATIONAL' && inherit || !enableSchedule"
- [checked]="selectedStartWeekDays[day.toLowerCase()]">{{ day[0] }}
- </mat-button-toggle>
- </div>
- <div class="days-block">
+ <div
+ class="control-group"
+ *ngIf="destination && destination.image !== 'docker.datalab-dataengine-service'"
+ >
+ <label class="label repeat" for="options">Repeat on :</label>
+ <div class="days-block">
+ <label>Start date:</label>
+ <mat-button-toggle
+ *ngFor="let day of weekdays; let i = index"
+ value="{{ day }}"
+ (change)="onDaySelect($event, day, 'start')"
+ [disabled]="destination.type === 'СOMPUTATIONAL' && inherit || !enableSchedule"
+ [checked]="selectedStartWeekDays[day.toLowerCase()]"
+ >
+ {{ day[0] }}
+ </mat-button-toggle>
+ </div>
+ <div class="days-block">
<label>Stop date:</label>
- <mat-button-toggle *ngFor="let day of weekdays; let i = index" value="{{ day }}"
+ <mat-button-toggle
+ *ngFor="let day of weekdays; let i = index"
+ value="{{ day }}"
(change)="onDaySelect($event, day, 'stop')"
[disabled]="destination.type === 'СOMPUTATIONAL' && inherit || !enableSchedule"
- [checked]="selectedStopWeekDays[day.toLowerCase()]">{{ day[0] }}
+ [checked]="selectedStopWeekDays[day.toLowerCase()]"
+ >
+ {{ day[0] }}
</mat-button-toggle>
</div>
</div>
- <div class="des-block" *ngIf="destination.image === 'docker.datalab-dataengine-service'">
- <div class="row-wrap">
- <div class="col-3">
- <mat-form-field>
- <input matInput [matDatepicker]="terminateDate" placeholder="Choose terminate date"
- formControlName="terminateDate">
- <mat-datepicker-toggle matSuffix [for]="terminateDate"></mat-datepicker-toggle>
- <mat-datepicker #terminateDate></mat-datepicker>
- </mat-form-field>
- </div>
- <div class="control-group col-3 time-range" *ngIf="destination.type === 'СOMPUTATIONAL'">
- <datalab-time-picker [(pickTime)]="terminateTime" [(milisecTime)]="terminateTimeMilliseconds"
- [label]="'Choose terminate time'"
- [disable]="!enableSchedule"></datalab-time-picker>
- </div>
+ <div class="des-block" *ngIf="destination.image === 'docker.datalab-dataengine-service'">
+ <div class="row-wrap">
+ <div class="col-3">
+ <mat-form-field>
+ <input
+ matInput
+ [matDatepicker]="terminateDate"
+ placeholder="Choose terminate date"
+ formControlName="terminateDate"
+ />
+ <mat-datepicker-toggle matSuffix [for]="terminateDate"></mat-datepicker-toggle>
+ <mat-datepicker #terminateDate></mat-datepicker>
+ </mat-form-field>
+ </div>
+ <div class="control-group col-3 time-range" *ngIf="destination.type === 'СOMPUTATIONAL'">
+ <datalab-time-picker
+ [(pickTime)]="terminateTime"
+ [(milisecTime)]="terminateTimeMilliseconds"
+ [label]="'Choose terminate time'"
+ [disable]="!enableSchedule"
+ ></datalab-time-picker>
+ </div>
<div class="col-3">
<mat-form-field class="timezone-offset">
- <mat-select placeholder="Select timezone" [(value)]="tzOffset"
- [disabled]="destination.type === 'СOMPUTATIONAL' && inherit || !enableSchedule">
- <mat-option *ngFor="let zone of zones | keys" [value]="zone.key" matTooltip="{{ zone.value }}"
- matTooltipShowDelay="1000" matTooltipPosition="above"> GMT {{zone.key}} {{ zone.value }}
+ <mat-select
+ placeholder="Select timezone"
+ [(value)]="tzOffset"
+ [disabled]="destination.type === 'СOMPUTATIONAL' && inherit || !enableSchedule"
+ >
+ <mat-option
+ *ngFor="let zone of zones | keys"
+ [value]="zone.key"
+ matTooltip="{{ zone.value }}"
+ matTooltipShowDelay="1000"
+ matTooltipPosition="above"
+ >
+ GMT {{zone.key}} {{ zone.value }}
</mat-option>
</mat-select>
</mat-form-field>
@@ -196,24 +265,33 @@
</div>
</div>
- <div class="options"
- *ngIf="destination && allowInheritView && destination.image !== 'docker.datalab-dataengine-service'">
- <mat-slide-toggle labelPosition="after" [checked]="inherit" (change)="toggleInherit($event)"
- [disabled]="!enableSchedule || (!parentInherit && destination.type === 'СOMPUTATIONAL')">
+ <div
+ class="options"
+ *ngIf="destination && allowInheritView && destination.image !== 'docker.datalab-dataengine-service'"
+ >
+ <mat-slide-toggle
+ labelPosition="after"
+ [checked]="inherit"
+ (change)="toggleInherit($event)"
+ [disabled]="!enableSchedule || (!parentInherit && destination.type === 'СOMPUTATIONAL')"
+ >
<span *ngIf="destination.type === 'EXPLORATORY'; else resourcePropagation" class="hold-label">
- <span>Start all spark clusters associated with current notebook based on notebook start
- schedule</span>
+ <span>Start all spark clusters associated with current notebook based on notebook start schedule</span>
</span>
- <ng-template #resourcePropagation>
- <span class="hold-label">Inherit notebook schedule settings</span>
- </ng-template>
- </mat-slide-toggle>
+ <ng-template #resourcePropagation>
+ <span class="hold-label">Inherit notebook schedule settings</span>
+ </ng-template>
+ </mat-slide-toggle>
</div>
</div>
<div class="options" *ngIf="destination.type === 'EXPLORATORY'">
- <mat-slide-toggle labelPosition="after" [checked]="considerInactivity"
- [disabled]="!enableSchedule && !enableIdleTime" (change)="considerInactivity = !considerInactivity">
+ <mat-slide-toggle
+ labelPosition="after"
+ [checked]="considerInactivity"
+ [disabled]="!enableSchedule && !enableIdleTime"
+ (change)="considerInactivity = !considerInactivity"
+ >
<span class="hold-label">
<span>In case of running jobs on Spark standalone, notebook stop scheduler will not be triggered</span>
</span>
@@ -222,10 +300,23 @@
</form>
<div class="text-center m-top-30">
- <button mat-raised-button type="button" class="butt action" (click)="dialogRef.close()">Cancel</button>
- <button mat-raised-button type="button" class="butt butt-success action"
+ <button
+ mat-raised-button
+ type="button"
+ class="butt action"
+ (click)="dialogRef.close()"
+ >
+ Cancel
+ </button>
+ <button
+ mat-raised-button
+ type="button"
+ class="butt butt-success action"
[disabled]="enableIdleTime && !schedulerForm.controls.inactivityTime.valid"
- (click)="scheduleInstance_btnClick()">Save</button>
+ (click)="scheduleInstance_btnClick()"
+ >
+ Save
+ </button>
</div>
</div>
</div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/scheduler/scheduler.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/scheduler/scheduler.component.scss
index 54a1eed..7b0c54c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/scheduler/scheduler.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/scheduler/scheduler.component.scss
@@ -20,7 +20,7 @@
.scheduler-dialog {
.mat-form-field-appearance-legacy {
.mat-form-field-label {
- font-family: 'Open Sans', sans-serif !important;
+ font-family: "Open Sans", sans-serif !important;
font-size: 13px;
}
}
@@ -49,17 +49,17 @@
.enable-schedule {
display: flex;
- margin-bottom: 10px;
justify-content: space-between;
+ margin-bottom: 10px;
.idle {
display: flex;
width: 50%;
i {
- font-size: 20px;
padding-left: 10px;
align-self: center;
+ font-size: 20px;
user-select: none;
cursor: pointer;
}
@@ -69,17 +69,17 @@
.info-message,
.idle-control {
height: 0;
+ padding-bottom: 0;
opacity: 0;
visibility: hidden;
text-align: left;
transition: height 0.35s linear 0.2s;
- padding-bottom: 0;
&.show {
height: 30px;
+ margin-bottom: 50px;
visibility: visible;
opacity: 1;
- margin-bottom: 50px;
}
}
@@ -207,7 +207,7 @@
box-shadow: none;
margin: 2px;
border: none;
- color: #607D8B;
+ color: #607d8b;
&.mat-button-toggle-checked {
background-color: rgba(77, 184, 218, 0.62);
@@ -219,10 +219,10 @@
}
.mat-button-toggle-label-content {
- line-height: 26px;
- padding: 0px 8px;
width: 26px;
+ padding: 0px 8px;
font-size: 13px;
+ line-height: 26px;
text-align: center;
background: rgba(196, 231, 243, 0.3);
}
@@ -242,10 +242,10 @@
}
.options {
+ margin-bottom: 10px;
color: #718ba6;
font-size: 15px;
font-weight: 600;
- margin-bottom: 10px;
.mat-slide-toggle-content {
padding: 5px;
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/scheduler/scheduler.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/scheduler/scheduler.component.ts
index 79b5008..e1206e8 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/scheduler/scheduler.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/scheduler/scheduler.component.ts
@@ -25,7 +25,6 @@ import { ToastrService } from 'ngx-toastr';
import * as _moment from 'moment';
import 'moment-timezone';
-
import { SchedulerService } from '../../core/services';
import { SchedulerModel, WeekdaysModel } from './scheduler.model';
import { SchedulerCalculations } from './scheduler.calculations';
@@ -184,13 +183,16 @@ export class SchedulerComponent implements OnInit {
}
public setInactivity(...params) {
- this.model.setInactivityTime(params).subscribe((response: any) => {
- if (response.status === HTTP_STATUS_CODES.OK) {
- this.toastr.success('Schedule data were successfully saved', 'Success!');
- this.dialogRef.close();
- }
- },
- error => this.toastr.error(error.message || 'Scheduler configuration failed!', 'Oops!'));
+ this.model.setInactivityTime(params)
+ .subscribe(
+ (response: any) => {
+ if (response.status === HTTP_STATUS_CODES.OK) {
+ this.toastr.success('Schedule data were successfully saved', 'Success!');
+ this.dialogRef.close();
+ }
+ },
+ error => this.toastr.error(error.message || 'Scheduler configuration failed!', 'Oops!')
+ );
}
public inactivityCounter($event, action: string): void {
@@ -320,12 +322,13 @@ export class SchedulerComponent implements OnInit {
}
private validInactivityRange(control) {
- if (control)
+ if (control) {
return this.enableIdleTime
? (control.value
&& control.value >= this.inactivityLimits.min
&& control.value <= this.inactivityLimits.max ? null : { valid: false })
: control.value;
+ }
}
private checkIsActiveSpark() {
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org