You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dlab.apache.org by dg...@apache.org on 2020/06/15 07:02:35 UTC
[incubator-dlab] 03/03: [DLAB-1861]: Added check box to
'Environment management' page
This is an automated email from the ASF dual-hosted git repository.
dgnatyshyn pushed a commit to branch DLAB-1861
in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git
commit 902342065ce8179493e43769a42d0247c44a6398
Author: Dmytro_Gnatyshyn <di...@ukr.net>
AuthorDate: Mon Jun 15 10:02:12 2020 +0300
[DLAB-1861]: Added check box to 'Environment management' page
---
.../management-grid/management-grid.component.html | 23 ++++-
.../management-grid/management-grid.component.scss | 8 +-
.../management-grid/management-grid.component.ts | 105 +++++++++++++++++++--
.../management/management.component.html | 2 +-
.../management/management.component.ts | 91 ++++++++++++------
.../resources/webapp/src/assets/styles/_theme.scss | 21 ++++-
6 files changed, 206 insertions(+), 44 deletions(-)
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
index a7f217a..88b005c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.html
@@ -21,7 +21,16 @@
<table mat-table [dataSource]="allFilteredEnvironmentData" class="data-grid management mat-elevation-z6">
<ng-container matColumnDef="checkbox">
<th mat-header-cell *matHeaderCellDef class="checkbox label-header">
- </th>
+ <div class="empty-checkbox header-checkbox" [ngClass]="{'checked': selected?.length === allActiveNotebooks?.length}" (click)="toggleSelectionAll();$event.stopPropagation()" >
+ <span class="checked-checkbox" *ngIf="selected?.length === allActiveNotebooks?.length"></span>
+ </div>
+ <button mat-icon-button aria-label="More" class="ar checkbox-border" (click)="toggleFilterRow()">
+ <i class="material-icons">
+<!-- <span *ngIf="filtering && filterForm.users.length > 0 && !collapsedFilterRow">filter_list</span>-->
+ <span>more_vert</span>
+ </i>
+ </button>
+ </th>
<td mat-cell *matCellDef="let element">
<div *ngIf="element.type !== 'edge node' && (element.status==='running' || element.status==='stopped')" class="empty-checkbox" [ngClass]="{'checked': element.isSelected}" (click)="toggleActionForAll(element);$event.stopPropagation()" >
<span class="checked-checkbox" *ngIf="element.isSelected"></span>
@@ -147,10 +156,14 @@
<span class="label"> Actions </span>
</th>
<td mat-cell *matCellDef="let element" class="settings actions-col">
- <span #settings class="actions" (click)="actions.toggle($event, settings)" *ngIf="element.type !== 'edge node'"
- [ngClass]="{
- 'disabled' : isActiveResources(element),
- 'disabled' : element.status !== 'running' && element.status !== 'stopped' && element.status !== 'stopping' && element.status !== 'failed' }"></span>
+ <span [ngClass]="{'not-allow' : selected?.length}">
+ <span #settings class="actions" (click)="actions.toggle($event, settings)" *ngIf="element.type !== 'edge node'"
+ [ngClass]="{
+ 'disabled' : isActiveResources(element),
+ 'disabled' : (element.status !== 'running' && element.status !== 'stopped' && element.status !== 'stopping' && element.status !== 'failed') || selected?.length}">
+
+ </span>
+ </span>
<bubble-up #actions class="list-menu" position="bottom-left" alternative="top-left">
<ul class="list-unstyled">
<li
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.scss b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.scss
index 14b700e..99fde37 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.scss
@@ -22,6 +22,12 @@
.mat-column-checkbox{
padding-left: 10px;
padding-right: 10px;
+ &.label-header{
+ width: 65px;
+ display: flex;
+ align-items: center;
+ }
+
}
.user{
width: 15%;
@@ -48,7 +54,7 @@
}
.resources {
- width: 22%;
+ width: 21%;
padding: 5px;
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
index b0295e2..38e4590 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-grid/management-grid.component.ts
@@ -65,6 +65,7 @@ export class ManagementGridComponent implements OnInit {
displayedColumns: string[] = [ 'checkbox', 'user', 'type', 'project', 'shape', 'status', 'resources', 'actions'];
displayedFilterColumns: string[] = ['checkbox-filter', 'user-filter', 'type-filter', 'project-filter', 'shape-filter', 'status-filter', 'resource-filter', 'actions-filter'];
private selected;
+ private allActiveNotebooks: any;
constructor(
private healthStatusService: HealthStatusService,
@@ -125,8 +126,8 @@ export class ManagementGridComponent implements OnInit {
filteredData = filteredData.filter(item => {
const isUser = config.users.length > 0 ? (config.users.indexOf(item.user) !== -1) : true;
- const isTypeName = item.name ?
- item.name.toLowerCase().indexOf(config.type.toLowerCase()) !== -1 : item.type.toLowerCase().indexOf(config.type.toLowerCase()) !== -1;
+ const isTypeName = item.name ? item.name.toLowerCase()
+ .indexOf(config.type.toLowerCase()) !== -1 : item.type.toLowerCase().indexOf(config.type.toLowerCase()) !== -1;
const isStatus = config.statuses.length > 0 ? (config.statuses.indexOf(item.status) !== -1) : (config.type !== 'active');
const isShape = config.shapes.length > 0 ? (config.shapes.indexOf(item.shape) !== -1) : true;
const isProject = config.projects.length > 0 ? (config.projects.indexOf(item.project) !== -1) : true;
@@ -146,6 +147,7 @@ export class ManagementGridComponent implements OnInit {
});
}
this.allFilteredEnvironmentData = filteredData;
+ this.allActiveNotebooks = this.allFilteredEnvironmentData.filter(v => v.name && (v.status === 'running' || v.status === 'stopped'));
}
getEnvironmentDataCopy() {
@@ -153,7 +155,7 @@ export class ManagementGridComponent implements OnInit {
}
toggleResourceAction(environment: any, action: string, resource?): void {
- this.actionToggle.emit({ action, environment, resource });
+ this.actionToggle.emit({ environment, action, resource });
}
isResourcesInProgress(notebook) {
@@ -215,8 +217,17 @@ export class ManagementGridComponent implements OnInit {
toggleActionForAll(element) {
element.isSelected = !element.isSelected;
this.selected = this.allFilteredEnvironmentData.filter(item => !!item.isSelected);
- console.log(this.selected);
- this.emitSelectedList.emit(this.selected)
+ this.emitSelectedList.emit(this.selected);
+ }
+
+ toggleSelectionAll() {
+ if (this.selected && this.selected.length === this.allActiveNotebooks.length) {
+ this.allActiveNotebooks.forEach(notebook => notebook.isSelected = false);
+ } else {
+ this.allActiveNotebooks.forEach(notebook => notebook.isSelected = true);
+ }
+ this.selected = this.allFilteredEnvironmentData.filter(item => !!item.isSelected);
+ this.emitSelectedList.emit(this.selected);
}
}
@@ -229,23 +240,101 @@ export class ManagementGridComponent implements OnInit {
<button type="button" class="close" (click)="dialogRef.close()">×</button>
</div>
<div mat-dialog-content class="content">
+ <div *ngIf="data.type === 'cluster'">
<p>Resource <span class="strong"> {{ data.resource_name }}</span> of user <span class="strong"> {{ data.user }} </span> will be
<span *ngIf="data.action === 'terminate'"> decommissioned.</span>
<span *ngIf="data.action === 'stop'">stopped.</span>
</p>
- <p class="m-top-20"><span class="strong">Do you want to proceed?</span></p>
+ </div>
+ <div class="resource-list" *ngIf="data.type === 'notebook'">
+ <div class="resource-list-header">
+ <div class="resource-name">Notebook</div>
+ <div class="clusters-list">
+ <div class="clusters-list-item">
+ <div class="cluster">Cluster</div>
+ <div class="status">Further status</div>
+ </div>
+ </div>
+
+ </div>
+ <div class="scrolling-content resource-heigth">
+ <div class="resource-list-row sans node" *ngFor="let notebook of notebooks">
+ <div class="resource-name ellipsis">
+ {{notebook.name}}
+ </div>
+
+ <div class="clusters-list">
+ <div class="clusters-list-item">
+ <div class="cluster"></div>
+ <div class="status"
+ [ngClass]="{
+ 'stopped': data.action==='stop', 'terminated': data.action === 'terminate'
+ }"
+ >
+ {{data.action === 'stop' ? 'Stopped' : 'Terminated'}}
+ </div>
+ </div>
+ <div class="clusters-list-item" *ngFor="let cluster of notebook?.resources">
+ <div class="cluster">{{cluster.computational_name}}</div>
+ <div class="status" [ngClass]="{
+ 'stopped': (data.action==='stop' && cluster.image==='docker.dlab-dataengine'), 'terminated': data.action === 'terminate' || (data.action==='stop' && cluster.image!=='docker.dlab-dataengine')
+ }">{{data.action === 'stop' && cluster.image === "docker.dlab-dataengine" ? 'Stopped' : 'Terminated'}}</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="text-center ">
+ <p class="strong">Do you want to proceed?</p>
</div>
- <div class="text-center">
+ <div class="text-center m-top-20">
<button type="button" class="butt" mat-raised-button (click)="dialogRef.close()">No</button>
<button type="button" class="butt butt-success" mat-raised-button (click)="dialogRef.close(true)">Yes</button>
</div>
`,
styles: [
+ `
+ .content { color: #718ba6; padding: 20px 50px; font-size: 14px; font-weight: 400; margin: 0; }
+ .info { color: #35afd5; }
+ .info .confirm-dialog { color: #607D8B; }
+ header { display: flex; justify-content: space-between; color: #607D8B; }
+ header h4 i { vertical-align: bottom; }
+ header a i { font-size: 20px; }
+ header a:hover i { color: #35afd5; cursor: pointer; }
+ .plur { font-style: normal; }
+ .scrolling-content{overflow-y: auto; max-height: 200px; }
+ .cluster { width: 50%; text-align: left; color: #577289;}
+ .status { width: 50%;text-align: left;}
+ .label { font-size: 15px; font-weight: 500; font-family: "Open Sans",sans-serif;}
+ .node { font-weight: 300;}
+ .resource-name { width: 40%;text-align: left; padding: 10px 0;line-height: 26px;}
+ .clusters-list { width: 60%;text-align: left; padding: 10px 0;line-height: 26px;}
+ .clusters-list-item { width: 100%;text-align: left;display: flex}
+ .resource-list{max-width: 100%; margin: 0 auto;margin-top: 20px; }
+ .resource-list-header{display: flex; font-weight: 600; font-size: 16px;height: 48px; border-top: 1px solid #edf1f5; border-bottom: 1px solid #edf1f5; padding: 0 20px;}
+ .resource-list-row{display: flex; border-bottom: 1px solid #edf1f5;padding: 0 20px;}
+ .confirm-resource-terminating{text-align: left; padding: 10px 20px;}
+ .confirm-message{color: #ef5c4b;font-size: 13px;min-height: 18px; text-align: center; padding-top: 20px}
+ .checkbox{margin-right: 5px;vertical-align: middle; margin-bottom: 3px;}
+ label{cursor: pointer}
+ .bottom-message{padding-top: 15px;}
+ .table-header{padding-bottom: 10px;}`
]
})
+
export class ReconfirmationDialogComponent {
+ private notebooks;
constructor(
public dialogRef: MatDialogRef<ReconfirmationDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any
- ) { }
+ ) {
+ if (data.notebooks && data.notebooks.length) {
+ this.notebooks = JSON.parse(JSON.stringify(data.notebooks));
+ this.notebooks = this.notebooks.map(notebook => {
+ notebook.resources = notebook.resources.filter(res => res.status !== 'terminated' && res.status.slice(0, 4) !== data.action);
+ return notebook;
+ });
+ }
+ }
}
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.html
index 2509429..eeb2d9b 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.html
@@ -20,7 +20,7 @@
<div class="base-retreat">
<div class="sub-nav">
<div *ngIf="healthStatus?.admin" class="admin-group">
- <div class="action-wrapper admin-group" >
+ <div class="action-select-wrapper admin-group" >
<span class="action-button-wrapper">
<button
type="button" class="butt actions-btn"
diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts
index 2c3813a..3062a07 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.component.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { Component, OnInit } from '@angular/core';
+import {Component, OnInit, ViewChild} from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ToastrService } from 'ngx-toastr';
@@ -40,7 +40,8 @@ import { ExploratoryModel } from '../../resources/resources-grid/resources-grid.
import { EnvironmentsDataService } from './management-data.service';
import { ProjectService } from '../../core/services';
import {ConfirmationDialogComponent, ConfirmationDialogType} from '../../shared/modal-dialog/confirmation-dialog';
-import {ReconfirmationDialogComponent} from './management-grid/management-grid.component';
+import {ManagementGridComponent, ReconfirmationDialogComponent} from './management-grid/management-grid.component';
+import {FolderTreeComponent} from '../../resources/bucket-browser/folder-tree/folder-tree.component';
@Component({
selector: 'environments-management',
@@ -57,6 +58,9 @@ export class ManagementComponent implements OnInit {
public selectedRunning: any[];
public selectedStopped: any[];
+ @ViewChild(ManagementGridComponent, {static: true}) managementGrid;
+
+
constructor(
public toastr: ToastrService,
public dialog: MatDialog,
@@ -186,8 +190,6 @@ export class ManagementComponent implements OnInit {
this.selectedRunning = this.selected.filter(item => item.status === 'running');
this.selectedStopped = this.selected.filter(item => item.status === 'stopped');
-
- console.log(this.selectedRunning.length, this.selectedStopped.length);
}
public toogleActions() {
@@ -199,40 +201,75 @@ export class ManagementComponent implements OnInit {
if (resource) {
const resource_name = resource ? resource.computational_name : environment.name;
this.dialog.open(ReconfirmationDialogComponent, {
- data: { action, resource_name, user: environment.user },
+ data: { action, resource_name, user: environment.user, type: 'cluster'},
width: '550px', panelClass: 'error-modalbox'
}).afterClosed().subscribe(result => {
result && this.manageEnvironmentAction({ action, environment, resource });
});
} else {
- const type = (environment.type.toLowerCase() === 'edge node')
- ? ConfirmationDialogType.StopEdgeNode : ConfirmationDialogType.StopExploratory;
-
+ const notebooks = this.selected.length ? this.selected : [environment];
if (action === 'stop') {
- this.dialog.open(ConfirmationDialogComponent, {
- data: { notebook: environment, type: type, manageAction: true }, panelClass: 'modal-md'
- }).afterClosed().subscribe(() => this.buildGrid());
+ this.dialog.open(ReconfirmationDialogComponent, {
+ data: { notebooks: notebooks, type: 'notebook', action },
+ width: '550px', panelClass: 'error-modalbox'
+ }).afterClosed().subscribe((res) => {
+ if (res) {
+ notebooks.forEach((env) => {
+ this.manageEnvironmentsService.environmentManagement(env.user, 'stop', env.project, env.name)
+ .subscribe(
+ response => {
+ console.log(response);
+ this.buildGrid();
+ },
+ error => console.log(error)
+ );
+ });
+ }
+ this.clearSelection();
+ });
} else if (action === 'terminate') {
- this.dialog.open(ConfirmationDialogComponent, {
- data: { notebook: environment, type: ConfirmationDialogType.TerminateExploratory, manageAction: true }, panelClass: 'modal-md'
- }).afterClosed().subscribe(() => this.buildGrid());
- } else if (action === 'run') {
- this.healthStatusService.runEdgeNode().subscribe(() => {
- this.buildGrid();
- this.toastr.success('Edge node is starting!', 'Processing!');
- }, () => this.toastr.error('Edge Node running failed!', 'Oops!'));
- } else if (action === 'recreate') {
- this.healthStatusService.recreateEdgeNode().subscribe(() => {
- this.buildGrid();
- this.toastr.success('Edge Node recreation is processing!', 'Processing!');
- }, () => this.toastr.error('Edge Node recreation failed!', 'Oops!'));
+ this.dialog.open(ReconfirmationDialogComponent, {
+ data: { notebooks: notebooks, type: 'notebook', action }, width: '550px', panelClass: 'error-modalbox'
+ }).afterClosed().subscribe((res) => {
+ if (res) {
+ notebooks.forEach((env) => {
+ this.manageEnvironmentsService.environmentManagement(env.user, 'terminate', env.project, env.name)
+ .subscribe(
+ response => {
+ this.buildGrid();
+
+ },
+ error => console.log(error)
+ );
+ });
+ }
+ this.clearSelection();
+ });
+ // } else if (action === 'run') {
+ // this.healthStatusService.runEdgeNode().subscribe(() => {
+ // this.buildGrid();
+ // this.toastr.success('Edge node is starting!', 'Processing!');
+ // }, () => this.toastr.error('Edge Node running failed!', 'Oops!'));
+ // } else if (action === 'recreate') {
+ // this.healthStatusService.recreateEdgeNode().subscribe(() => {
+ // this.buildGrid();
+ // this.toastr.success('Edge Node recreation is processing!', 'Processing!');
+ // }, () => this.toastr.error('Edge Node recreation failed!', 'Oops!'));
}
}
}
+ private clearSelection() {
+ this.selected = [];
+ this.isActionsOpen = false;
+ if (this.managementGrid.selected && this.managementGrid.selected.length !== 0) {
+ this.managementGrid.selected.forEach(item => item.isSelected = false);
+ this.managementGrid.selected = [];
+ }
+ }
+
+
public resourseAction(action) {
- this.selected.forEach(resource => {
- this.toggleResourceAction({environment: resource, action: action});
- })
+ this.toggleResourceAction({environment: this.selected, action: action});
}
}
diff --git a/services/self-service/src/main/resources/webapp/src/assets/styles/_theme.scss b/services/self-service/src/main/resources/webapp/src/assets/styles/_theme.scss
index 9c9963f..bf3529d 100644
--- a/services/self-service/src/main/resources/webapp/src/assets/styles/_theme.scss
+++ b/services/self-service/src/main/resources/webapp/src/assets/styles/_theme.scss
@@ -458,7 +458,7 @@ span.mat-slide-toggle-content {
}
}
-.action-wrapper{
+.action-select-wrapper{
position: relative;
display: block !important;
@@ -469,10 +469,14 @@ span.mat-slide-toggle-content {
.mat-raised-button.butt{
margin-bottom: 0;
+ padding-left: 45px;
+ text-align: left;
+
&.actions-btn{
padding-right: 38px;
+
.material-icons{
transition: ease-in-out 1s;
font-size: 25px;
@@ -504,6 +508,19 @@ span.mat-slide-toggle-content {
position: relative;
overflow: hidden;
line-height: 36px;
+ padding-left: 45px;
+ text-align: left;
+ &.action-menu-item{
+ &:hover{
+ color: #00bcd4;
+ background-color: #fafafa;
+ }
+ &.disabled{
+ &:hover{
+ color: #577289;
+ }
+ }
+ }
}
}
}
@@ -636,7 +653,7 @@ mat-horizontal-stepper {
.content {
color: #718ba6;
- padding: 20px 0;
+ padding: 20px 40px;
font-size: 14px;
font-weight: 400;
text-align: center;
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@dlab.apache.org
For additional commands, e-mail: commits-help@dlab.apache.org