This is an automated email from the ASF dual-hosted git repository. ankovalyshyn pushed a commit to branch feature/projects in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git
commit a825837a397303dc7ae0e69f58ba605b6dbdc27f Author: Andriana Kovalyshyn <andriana_kovalys...@epam.com> AuthorDate: Wed Jun 19 15:55:58 2019 +0300 [DLAB-805]: clusters creation validation fixes --- ...utational-resource-create-dialog.component.html | 35 +--- ...mputational-resource-create-dialog.component.ts | 190 +++++++-------------- .../resources-grid/resources-grid.component.html | 88 +++++----- 3 files changed, 112 insertions(+), 201 deletions(-) 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 f63602d..627b7de 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 @@ -24,34 +24,20 @@ </header> <div class="dialog-content selection"> <div class="content-box mat-reset"> - <form [formGroup]="resourceForm" *ngIf="cluster_types.length && resourceForm; else empty"> + <form [formGroup]="resourceForm" *ngIf="clusterTypes.length && resourceForm; else empty"> <div class="form-wrapper" [ngClass]="{ compress: selectedImage?.image === 'docker.dlab-dataengine' }"> <div class="col"> - <!-- <div class="control-group" *ngIf="PROVIDER !== 'azure'" [hidden]="model.resourceImages.length === 1"> - <label class="label">Select cluster type</label> - <div class="control"> - <dropdown-list #clusterType (selectedItem)="onUpdate($event)"></dropdown-list> - </div> - </div> - - <div class="control-group" *ngIf="PROVIDER !== 'azure'" [hidden]="!selectedImage.templates.length"> - <label class="label">Select template</label> - <div class="control"> - <dropdown-list #templatesList (selectedItem)="onUpdate($event)"></dropdown-list> - </div> - </div> --> - - <div class="control-group" *ngIf="PROVIDER !== 'azure'" [hidden]="cluster_types.length === 1"> + <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-label>Select cluster type</mat-label> <mat-select formControlName="template_name" disableOptionCentering> - <mat-option *ngFor="let type of cluster_types" [value]="type.template_name" + <mat-option *ngFor="let type of clusterTypes" [value]="type.template_name" (click)="selectImage(type)">{{ type.template_name }}</mat-option> - <mat-option *ngIf="!cluster_types.length" class="multiple-select ml-10" disabled>Clusters types list + <mat-option *ngIf="!clusterTypes.length" class="multiple-select ml-10" disabled>Clusters types list is empty</mat-option> </mat-select> <button class="caret"> @@ -112,19 +98,6 @@ </span> </div> </div> - <!-- <div class="control-group" *ngIf="selectedImage?.image"> - <label class="label" *ngIf="selectedImage">{{ DICTIONARY[selectedImage.image].data_engine_master_instance_size }}</label> - <div class="control"> - <dropdown-list #masterShapesList (selectedItem)="onUpdate($event)"></dropdown-list> - </div> - </div> --> - - <!-- <div class="control-group" *ngIf="selectedImage?.image" [hidden]="selectedImage?.image === 'docker.dlab-dataengine'"> - <label class="label">{{ DICTIONARY[selectedImage.image].data_engine_slave_instance_size }}</label> - <div class="control"> - <dropdown-list #shapesSlaveList (selectedItem)="onUpdate($event)"></dropdown-list> - </div> - </div> --> <div class="control-group" *ngIf="selectedImage?.image"> <label class="label">{{ DICTIONARY[selectedImage.image].data_engine_master_instance_size }}</label> 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 b7b77fe..85ade39 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 @@ -17,14 +17,14 @@ * under the License. */ -import { Component, OnInit, EventEmitter, Output, ViewChild, Inject } from '@angular/core'; +import { Component, OnInit, ViewChild, Inject } from '@angular/core'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; import { ToastrService } from 'ngx-toastr'; import { ComputationalResourceModel } from './computational-resource-create.model'; import { UserResourceService } from '../../../core/services'; -import { HTTP_STATUS_CODES, CheckUtils } from '../../../core/util'; +import { HTTP_STATUS_CODES, PATTERNS, CheckUtils } from '../../../core/util'; import { DICTIONARY } from '../../../../dictionary/global.dictionary'; import { CLUSTER_CONFIGURATION } from './cluster-configuration-templates'; @@ -42,17 +42,10 @@ export class ComputationalResourceCreateDialogComponent implements OnInit { readonly CheckUtils = CheckUtils; notebook_instance: any; - full_list: any; - - cluster_types = []; + resourcesList: any; + clusterTypes = []; selectedImage: any; - - shapes: any; spotInstance: boolean = true; - clusterNamePattern: string = '[-_a-zA-Z0-9]*[_-]*[a-zA-Z0-9]+'; - nodeCountPattern: string = '^[1-9]\\d*$'; - delimitersRegex = /[-_]?/g; - integerRegex = '^[0-9]*$'; public minInstanceNumber: number; public maxInstanceNumber: number; @@ -62,85 +55,29 @@ export class ComputationalResourceCreateDialogComponent implements OnInit { public maxSpotPrice: number = 0; public resourceForm: FormGroup; - // @ViewChild('name') name; - // @ViewChild('templatesList') templates_list; - // @ViewChild('masterShapesList') master_shapes_list; - // @ViewChild('shapesSlaveList') slave_shapes_list; @ViewChild('spotInstancesCheck') spotInstancesSelect; @ViewChild('preemptibleNode') preemptible; @ViewChild('configurationNode') configuration; - @Output() buildGrid: EventEmitter<{}> = new EventEmitter(); - constructor( @Inject(MAT_DIALOG_DATA) public data: any, public toastr: ToastrService, + public dialogRef: MatDialogRef<ComputationalResourceCreateDialogComponent>, private userResourceService: UserResourceService, private model: ComputationalResourceModel, - private _fb: FormBuilder, - public dialogRef: MatDialogRef<ComputationalResourceCreateDialogComponent>, + private _fb: FormBuilder ) { } ngOnInit() { this.initFormModel(); this.notebook_instance = this.data.notebook; - this.full_list = this.data.full_list; + this.resourcesList = this.data.resourcesList; this.getTemplates(this.notebook_instance.project); } - // public onUpdate($event): void { - // if ($event.model.type === 'template') { - // this.model.setSelectedTemplate($event.model.index); - // this.master_shapes_list.setDefaultOptions(this.model.selectedImage.shapes.resourcesShapeTypes, - // this.shapePlaceholder(this.model.selectedImage.shapes.resourcesShapeTypes, 'description'), 'master_shape', 'description', 'json'); - // this.slave_shapes_list.setDefaultOptions(this.model.selectedImage.shapes.resourcesShapeTypes, - // this.shapePlaceholder(this.model.selectedImage.shapes.resourcesShapeTypes, 'description'), 'slave_shape', 'description', 'json'); - - // this.shapes.master_shape = this.shapePlaceholder(this.model.selectedImage.shapes.resourcesShapeTypes, 'type'); - // this.shapes.slave_shape = this.shapePlaceholder(this.model.selectedImage.shapes.resourcesShapeTypes, 'type'); - // } - // if ($event.model.type === 'cluster_type') { - // this.model.setSelectedClusterType($event.model.index); - // this.setDefaultParams(); - // this.getComputationalResourceLimits(); - // this.selectConfiguration(); - // } - - // if (this.shapes[$event.model.type]) - // this.shapes[$event.model.type] = $event.model.value.type; - - // if (DICTIONARY.cloud_provider === 'aws') - // if ($event.model.type === 'slave_shape' && this.spotInstancesSelect.nativeElement['checked']) { - // this.spotInstance = $event.model.value.spot; - // } - // } - - public createComputationalResource(data) { - this.model.createComputationalResource(data, this.selectedImage, this.notebook_instance, this.spotInstance) - .subscribe((response: any) => { - if (response.status === HTTP_STATUS_CODES.OK) { - this.dialogRef.close(); - this.buildGrid.emit(); - } - }); - } - - public containsComputationalResource(conputational_resource_name: string): boolean { - if (conputational_resource_name) - for (let index = 0; index < this.full_list.length; index++) { - if (this.notebook_instance.name === this.full_list[index].name) { - for (let iindex = 0; iindex < this.full_list[index].resources.length; iindex++) { - const computational_name = this.full_list[index].resources[iindex].computational_name.toString().toLowerCase(); - if (this.delimitersFiltering(conputational_resource_name) === this.delimitersFiltering(computational_name)) - return true; - } - } - } - return false; - } - - public delimitersFiltering(resource): string { - return resource.replace(this.delimitersRegex, '').toString().toLowerCase(); + public selectImage($event) { + this.selectedImage = $event; + this.getComputationalResourceLimits(); } public selectSpotInstances($event?): void { @@ -154,15 +91,6 @@ export class ComputationalResourceCreateDialogComponent implements OnInit { } } - public selectTemplate($event) { - if (DICTIONARY.cloud_provider === 'aws') this.spotInstancesSelect.nativeElement['checked']; - } - - public selectImage($event) { - this.selectedImage = $event; - this.getComputationalResourceLimits(); - } - public selectPreemptibleNodes($event) { if ($event.target.checked) this.resourceForm.controls['preemptible_instance_number'].setValue(this.minPreemptibleInstanceNumber); @@ -179,15 +107,12 @@ export class ComputationalResourceCreateDialogComponent implements OnInit { } } - private filterAvailableSpots() { - const filtered = JSON.parse(JSON.stringify(this.selectedImage.computation_resources_shapes)); - for (const item in this.selectedImage.computation_resources_shapes) { - filtered[item] = filtered[item].filter(el => el.spot); - if (filtered[item].length <= 0) { - delete filtered[item]; - } - } - return filtered; + public preemptibleCounter($event, action): void { + $event.preventDefault(); + + const value = this.resourceForm.controls['preemptible_instance_number'].value; + const newValue = (action === 'increment' ? Number(value) + 1 : Number(value) - 1); + this.resourceForm.controls.preemptible_instance_number.setValue(newValue); } public isAvailableSpots(): boolean { @@ -197,12 +122,11 @@ export class ComputationalResourceCreateDialogComponent implements OnInit { return false; } - public preemptibleCounter($event, action): void { - $event.preventDefault(); - - const value = this.resourceForm.controls['preemptible_instance_number'].value; - const newValue = (action === 'increment' ? Number(value) + 1 : Number(value) - 1); - this.resourceForm.controls.preemptible_instance_number.setValue(newValue); + public createComputationalResource(data) { + this.model.createComputationalResource(data, this.selectedImage, this.notebook_instance, this.spotInstance) + .subscribe((response: any) => { + if (response.status === HTTP_STATUS_CODES.OK) this.dialogRef.close(); + }); } private initFormModel(): void { @@ -211,10 +135,10 @@ export class ComputationalResourceCreateDialogComponent implements OnInit { version: [''], shape_master: ['', Validators.required], shape_slave: [''], - cluster_alias_name: ['', [Validators.required, Validators.pattern(this.clusterNamePattern), + cluster_alias_name: ['', [Validators.required, Validators.pattern(PATTERNS.namePattern), this.providerMaxLength, this.checkDuplication.bind(this)]], - instance_number: ['', [Validators.required, Validators.pattern(this.nodeCountPattern), this.validInstanceNumberRange.bind(this)]], - preemptible_instance_number: [0, Validators.compose([Validators.pattern(this.integerRegex), this.validPreemptibleRange.bind(this)])], + instance_number: ['', [Validators.required, Validators.pattern(PATTERNS.nodeCountPattern), this.validInstanceNumberRange.bind(this)]], + preemptible_instance_number: [0, Validators.compose([Validators.pattern(PATTERNS.integerRegex), this.validPreemptibleRange.bind(this)])], instance_price: [0, [this.validInstanceSpotRange.bind(this)]], configuration_parameters: ['', [this.validConfiguration.bind(this)]] }); @@ -249,6 +173,7 @@ export class ComputationalResourceCreateDialogComponent implements OnInit { } } + // Validation private validInstanceNumberRange(control) { if (control && control.value) if (DICTIONARY.cloud_provider === 'gcp' && this.selectedImage.image === 'docker.dlab-dataengine-service') { @@ -304,25 +229,15 @@ export class ComputationalResourceCreateDialogComponent implements OnInit { return control.value.length <= 10 ? null : { valid: false }; } - private setDefaultParams(): void { - // if (this.model.selectedImage && this.model.selectedImage.shapes) { - // this.filterShapes(); - // this.shapes = { - // master_shape: this.shapePlaceholder(this.model.selectedImage.shapes.resourcesShapeTypes, 'type'), - // slave_shape: this.shapePlaceholder(this.model.selectedImage.shapes.resourcesShapeTypes, 'type') - // }; - // if (DICTIONARY.cloud_provider !== 'azure' && this.cluster_type) { - // this.cluster_type.setDefaultOptions(this.model.resourceImages, - // this.model.selectedImage.template_name, 'cluster_type', 'template_name', 'array'); - // // if (this.model.selectedImage.image === 'docker.dlab-dataengine-service') - // // this.templates_list.setDefaultOptions(this.model.templates, - // // this.model.selectedItem.version, 'template', 'version', 'array'); - // } - // this.master_shapes_list && this.master_shapes_list.setDefaultOptions(this.model.selectedImage.shapes.resourcesShapeTypes, - // this.shapePlaceholder(this.model.selectedImage.shapes.resourcesShapeTypes, 'description'), 'master_shape', 'description', 'json'); - // this.slave_shapes_list && this.slave_shapes_list.setDefaultOptions(this.model.selectedImage.shapes.resourcesShapeTypes, - // this.shapePlaceholder(this.model.selectedImage.shapes.resourcesShapeTypes, 'description'), 'slave_shape', 'description', 'json'); - // } + private getTemplates(project) { + this.userResourceService.getComputationalTemplates(project).subscribe( + clusterTypes => { + this.clusterTypes = clusterTypes; + this.selectedImage = clusterTypes[0]; + this.getComputationalResourceLimits(); + this.filterShapes(); + this.resourceForm.get('template_name').setValue(this.selectedImage.template_name) + }); } private filterShapes(): void { @@ -337,22 +252,35 @@ export class ComputationalResourceCreateDialogComponent implements OnInit { }, {}); if (DICTIONARY.cloud_provider !== 'azure') { - const images = this.cluster_types.filter(image => image.image === 'docker.dlab-dataengine'); - this.cluster_types = images; - // (images.length > 0) ? this.model.setSelectedClusterType(0) : this.model.availableTemplates = false; + const images = this.clusterTypes.filter(image => image.image === 'docker.dlab-dataengine'); + this.clusterTypes = images; } this.selectedImage.computation_resources_shapes = filtered; } } - private getTemplates(project) { - this.userResourceService.getComputationalTemplates(project).subscribe( - cluster_types => { - this.cluster_types = cluster_types; - this.selectedImage = cluster_types[0]; - this.getComputationalResourceLimits(); - this.filterShapes(); - this.resourceForm.get('template_name').setValue(this.selectedImage.template_name) - }); + private filterAvailableSpots() { + const filtered = JSON.parse(JSON.stringify(this.selectedImage.computation_resources_shapes)); + for (const item in this.selectedImage.computation_resources_shapes) { + filtered[item] = filtered[item].filter(el => el.spot); + if (filtered[item].length <= 0) { + delete filtered[item]; + } + } + return filtered; + } + + private containsComputationalResource(conputational_resource_name: string): boolean { + if (conputational_resource_name) + for (let index = 0; index < this.resourcesList.length; index++) { + if (this.notebook_instance.name === this.resourcesList[index].name) { + for (let iindex = 0; iindex < this.resourcesList[index].resources.length; iindex++) { + const computational_name = this.resourcesList[index].resources[iindex].computational_name.toString().toLowerCase(); + if (CheckUtils.delimitersFiltering(conputational_resource_name) === CheckUtils.delimitersFiltering(computational_name)) + return true; + } + } + } + return false; } } 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 68d7125..80b35f0 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,8 @@ <table class="dashboard_table data-grid"> <tr> - <th *ngFor="let column of filteringColumns" - ngClass="{{column.className || ''}}" [hidden]="column.name === 'cost' && !healthStatus?.billingEnabled"> + <th *ngFor="let column of filteringColumns" ngClass="{{column.className || ''}}" + [hidden]="column.name === 'cost' && !healthStatus?.billingEnabled"> {{column.title}} <button mat-icon-button *ngIf="column.filtering" aria-label="More" class="ar" (click)="toggleFilterRow()"> <i class="material-icons"> @@ -33,16 +33,21 @@ <tr *ngIf="filteredEnvironments && collapseFilterRow" class="filter-row"> <td> - <input placeholder="Filter by environment name" type="text" class="form-control filter-field" [value]="filterForm.name" (input)="filterForm.name = $event.target.value" /> + <input placeholder="Filter by environment name" type="text" class="form-control filter-field" + [value]="filterForm.name" (input)="filterForm.name = $event.target.value" /> </td> <td> - <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> </td> <td> - <multi-select-dropdown (selectionChange)="onUpdate($event)" [type]="DICTIONARY.cloud_provider === 'aws' ? 'shapes': 'sizes'" [items]="filterConfiguration.shapes" [model]="filterForm.shapes"></multi-select-dropdown> + <multi-select-dropdown (selectionChange)="onUpdate($event)" + [type]="DICTIONARY.cloud_provider === 'aws' ? 'shapes': 'sizes'" [items]="filterConfiguration.shapes" + [model]="filterForm.shapes"></multi-select-dropdown> </td> <td> - <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> </td> <td *ngIf="healthStatus?.billingEnabled"></td> <td> @@ -51,37 +56,44 @@ <i class="material-icons">close</i> </button> - <button mat-icon-button class="btn apply" (click)="applyFilter_btnClick(filterForm)" [disabled]="filteredEnvironments.length == 0 && !filtering"> + <button mat-icon-button class="btn apply" (click)="applyFilter_btnClick(filterForm)" + [disabled]="filteredEnvironments.length == 0 && !filtering"> <i class="material-icons" [ngClass]="{'not-allowed': filteredEnvironments.length == 0 && !filtering}">done</i> </button> </div> </td> </tr> - <tr *ngIf="(!filteredEnvironments) && !filtering || (filteredEnvironments.length == 0) && !filtering" class="message_block"> - <td [colSpan]="!healthStatus?.billingEnabled ? filteringColumns.length -1 : filteringColumns.length">To start working, please, create new environment</td> + <tr *ngIf="(!filteredEnvironments) && !filtering || (filteredEnvironments.length == 0) && !filtering" + class="message_block"> + <td [colSpan]="!healthStatus?.billingEnabled ? filteringColumns.length -1 : filteringColumns.length">To start + working, please, create new environment</td> </tr> <tr *ngIf="(filteredEnvironments.length == 0) && filtering" class="message_block"> - <td [colSpan]="!healthStatus?.billingEnabled ? filteringColumns.length -1 : filteringColumns.length">No matches found</td> + <td [colSpan]="!healthStatus?.billingEnabled ? filteringColumns.length -1 : filteringColumns.length">No matches + found</td> </tr> - <tr *ngFor="let env of filteredEnvironments;" class="dashboard_table_body" [ngClass]="{'dropdown-outscreen': isOutscreenDropdown}"> + <tr *ngFor="let env of filteredEnvironments;" class="dashboard_table_body" + [ngClass]="{'dropdown-outscreen': isOutscreenDropdown}"> <td (click)="printDetailEnvironmentModal(env)">{{env.name}}</td> <td class="status" ngClass="{{env.status.toLowerCase() || ''}}">{{ env.status | underscoreless }}</td> <td>{{env.shape}}</td> <td> - <computational-resources-list [resources]="env.resources" [environment]="env" (buildGrid)="buildGrid($event)"></computational-resources-list> + <computational-resources-list [resources]="env.resources" [environment]="env" (buildGrid)="buildGrid($event)"> + </computational-resources-list> </td> <td *ngIf="healthStatus?.billingEnabled"> <span class="total_cost">{{ env.cost || 'N/A' }} {{ env.currency_code || '' }}</span> - <span (click)="env.billing && printCostDetails(env)" class="currency_details" [ngClass]="{ 'not-allowed' : !env.billing }"> + <span (click)="env.billing && printCostDetails(env)" class="currency_details" + [ngClass]="{ 'not-allowed' : !env.billing }"> <i class="material-icons">help_outline</i> </span> </td> <td class="settings"> <span #settings (click)="actions.toggle($event, settings)" class="actions" - [ngClass]="{ 'disabled': env.status.toLowerCase() === 'creating' }"> + [ngClass]="{ 'disabled': env.status.toLowerCase() === 'creating' }"> </span> <bubble-up #actions class="list-menu" position="bottom-left" alternative="top-left"> @@ -90,50 +102,48 @@ && env.status !== 'terminating' && env.status !== 'terminated' && env.status !== 'creating image'"> - <li *ngIf="env.status !== 'stopped' && env.status !== 'stopping' && env.status !== 'starting' && env.status !== 'creating image'" - matTooltip="Unable to stop notebook because at least one computational resource is in progress" - matTooltipPosition="above" - [matTooltipDisabled]="!isResourcesInProgress(env)"> - <div (click)="exploratoryAction(env, 'stop')" [ngClass]="{'not-allowed': isResourcesInProgress(env) }"> - <i class="material-icons">pause_circle_outline</i> - <span>Stop</span> - </div> + <li + *ngIf="env.status !== 'stopped' && env.status !== 'stopping' && env.status !== 'starting' && env.status !== 'creating image'" + matTooltip="Unable to stop notebook because at least one computational resource is in progress" + matTooltipPosition="above" [matTooltipDisabled]="!isResourcesInProgress(env)"> + <div (click)="exploratoryAction(env, 'stop')" [ngClass]="{'not-allowed': isResourcesInProgress(env) }"> + <i class="material-icons">pause_circle_outline</i> + <span>Stop</span> + </div> </li> <li *ngIf="env.status.toLowerCase() === 'stopped' || env.status.toLowerCase() === 'stopping'" - matTooltip="Unable to run notebook until it will be stopped" - matTooltipPosition="above" - [matTooltipDisabled]="!isResourcesInProgress(env) && env.status.toLowerCase() !== 'stopping'"> - <div (click)="exploratoryAction(env, 'run')" [ngClass]="{'not-allowed': isResourcesInProgress(env) || env.status.toLowerCase() === 'stopping' }"> + matTooltip="Unable to run notebook until it will be stopped" matTooltipPosition="above" + [matTooltipDisabled]="!isResourcesInProgress(env) && env.status.toLowerCase() !== 'stopping'"> + <div (click)="exploratoryAction(env, 'run')" + [ngClass]="{'not-allowed': isResourcesInProgress(env) || env.status.toLowerCase() === 'stopping' }"> <i class="material-icons">play_circle_outline</i> <span>Run</span> </div> </li> <li *ngIf="env.status.toLowerCase() === 'running' || env.status.toLowerCase() === 'stopped'" - matTooltip="Unable to terminate notebook because at least one computational resource is in progress" - matTooltipPosition="above" - [matTooltipDisabled]="!isResourcesInProgress(env)"> - <div (click)="exploratoryAction(env, 'terminate')" [ngClass]="{'not-allowed': isResourcesInProgress(env) }"> + matTooltip="Unable to terminate notebook because at least one computational resource is in progress" + matTooltipPosition="above" [matTooltipDisabled]="!isResourcesInProgress(env)"> + <div (click)="exploratoryAction(env, 'terminate')" + [ngClass]="{'not-allowed': isResourcesInProgress(env) }"> <i class="material-icons">phonelink_off</i> <span>Terminate</span> </div> </li> - <li (click)="exploratoryAction(env, 'deploy')" - *ngIf="env.status != 'stopping' + <li (click)="exploratoryAction(env, 'deploy')" *ngIf="env.status != 'stopping' && env.status !== 'stopped' && env.status !== 'starting' && env.status !== 'creating image'"> <i class="material-icons">memory</i> <span>Add compute</span> </li> - <li (click)="exploratoryAction(env, 'schedule')" - *ngIf="env.status.toLowerCase() === 'running' + <li (click)="exploratoryAction(env, 'schedule')" *ngIf="env.status.toLowerCase() === 'running' || env.status.toLowerCase() === 'stopped'"> - <i class="material-icons">schedule</i> + <i class="material-icons">schedule</i> <span>Scheduler</span> </li> </div> <li (click)="exploratoryAction(env, 'ami')" - *ngIf="env.status === 'running' && DICTIONARY.cloud_provider !== 'gcp'"> + *ngIf="env.status === 'running' && DICTIONARY.cloud_provider !== 'gcp'"> <i class="material-icons">view_module</i> <span>Create {{ DICTIONARY.image }}</span> </li> @@ -141,12 +151,12 @@ <i class="material-icons">developer_board</i> <span>Manage libraries</span> </li> - <li> - <a target="_blank" [routerLink]="['/terminal', env.ip]" class="navigate"> + <!-- <li> + <a target="_blank" [routerLink]="['/terminal', env.ip]" class="navigate"> <i class="material-icons">laptop</i> <span>Open terminal</span> </a> - </li> + </li> --> </ul> </bubble-up> </td> --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@dlab.apache.org For additional commands, e-mail: commits-h...@dlab.apache.org